Skip to content

Commit

Permalink
refactor: move gambits to frontend (#3885)
Browse files Browse the repository at this point in the history
* refactor: move gambits to frontend
* Apply fixes from StyleCI
* test: GambitManager
  • Loading branch information
SychO9 authored Sep 22, 2023
1 parent ec5cb98 commit 336ce00
Show file tree
Hide file tree
Showing 45 changed files with 441 additions and 172 deletions.
1 change: 1 addition & 0 deletions extensions/lock/js/src/admin/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as default } from '../common/extend';
2 changes: 2 additions & 0 deletions extensions/lock/js/src/admin/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import app from 'flarum/admin/app';

export { default as extend } from './extend';

app.initializers.add('lock', () => {
app.extensionData.for('flarum-lock').registerPermission(
{
Expand Down
7 changes: 7 additions & 0 deletions extensions/lock/js/src/common/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Extend from 'flarum/common/extenders';
import LockedGambit from './query/discussions/LockedGambit';

export default [
new Extend.Search() //
.gambit('discussions', LockedGambit),
];
15 changes: 15 additions & 0 deletions extensions/lock/js/src/common/query/discussions/LockedGambit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import IGambit from 'flarum/common/query/IGambit';

export default class LockedGambit implements IGambit {
pattern(): string {
return 'is:locked';
}

toFilter(_matches: string[], negate: boolean): Record<string, any> {
const key = (negate ? '-' : '') + 'locked';

return {
[key]: true,
};
}
}
4 changes: 4 additions & 0 deletions extensions/lock/js/src/forum/extend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import Extend from 'flarum/common/extenders';
import Discussion from 'flarum/common/models/Discussion';
import DiscussionLockedPost from './components/DiscussionLockedPost';

import commonExtend from '../common/extend';

export default [
...commonExtend,

new Extend.PostTypes() //
.add('discussionLocked', DiscussionLockedPost),

Expand Down
1 change: 1 addition & 0 deletions extensions/sticky/js/src/admin/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as default } from '../common/extend';
2 changes: 2 additions & 0 deletions extensions/sticky/js/src/admin/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import app from 'flarum/admin/app';

export { default as extend } from './extend';

app.initializers.add('flarum-sticky', () => {
app.extensionData.for('flarum-sticky').registerPermission(
{
Expand Down
7 changes: 7 additions & 0 deletions extensions/sticky/js/src/common/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Extend from 'flarum/common/extenders';
import StickyGambit from './query/discussions/StickyGambit';

export default [
new Extend.Search() //
.gambit('discussions', StickyGambit),
];
15 changes: 15 additions & 0 deletions extensions/sticky/js/src/common/query/discussions/StickyGambit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import IGambit from 'flarum/common/query/IGambit';

export default class StickyGambit implements IGambit {
pattern(): string {
return 'is:sticky';
}

toFilter(_matches: string[], negate: boolean): Record<string, any> {
const key = (negate ? '-' : '') + 'sticky';

return {
[key]: true,
};
}
}
4 changes: 4 additions & 0 deletions extensions/sticky/js/src/forum/extend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import Extend from 'flarum/common/extenders';
import Discussion from 'flarum/common/models/Discussion';
import DiscussionStickiedPost from './components/DiscussionStickiedPost';

import commonExtend from '../common/extend';

export default [
...commonExtend,

new Extend.PostTypes() //
.add('discussionStickied', DiscussionStickiedPost),

Expand Down
3 changes: 3 additions & 0 deletions extensions/subscriptions/extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
use Flarum\User\User;

return [
(new Extend\Frontend('admin'))
->js(__DIR__.'/js/dist/admin.js'),

(new Extend\Frontend('forum'))
->js(__DIR__.'/js/dist/forum.js')
->css(__DIR__.'/less/forum.less')
Expand Down
1 change: 1 addition & 0 deletions extensions/subscriptions/js/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src/admin';
1 change: 1 addition & 0 deletions extensions/subscriptions/js/src/admin/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as default } from '../common/extend';
1 change: 1 addition & 0 deletions extensions/subscriptions/js/src/admin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as extend } from './extend';
7 changes: 7 additions & 0 deletions extensions/subscriptions/js/src/common/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Extend from 'flarum/common/extenders';
import SubscriptionGambit from './query/discussions/SubscriptionGambit';

export default [
new Extend.Search() //
.gambit('discussions', SubscriptionGambit),
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import IGambit from 'flarum/common/query/IGambit';

export default class SubscriptionGambit implements IGambit {
pattern(): string {
return 'is:(follow|ignor)(?:ing|ed)';
}

toFilter(matches: string[], negate: boolean): Record<string, any> {
const type = matches[1] === 'follow' ? 'following' : 'ignoring';

return {
subscription: type,
};
}
}
4 changes: 4 additions & 0 deletions extensions/subscriptions/js/src/forum/extend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import Extend from 'flarum/common/extenders';
import IndexPage from 'flarum/forum/components/IndexPage';
import Discussion from 'flarum/common/models/Discussion';

import commonExtend from '../common/extend';

export default [
...commonExtend,

new Extend.Routes() //
.add('following', '/following', IndexPage),

Expand Down
1 change: 1 addition & 0 deletions extensions/suspend/js/src/admin/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as default } from '../common/extend';
2 changes: 2 additions & 0 deletions extensions/suspend/js/src/admin/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import app from 'flarum/admin/app';

export { default as extend } from './extend';

app.initializers.add('flarum-suspend', () => {
app.extensionData.for('flarum-suspend').registerPermission(
{
Expand Down
7 changes: 7 additions & 0 deletions extensions/suspend/js/src/common/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Extend from 'flarum/common/extenders';
import SuspendedGambit from './query/users/SuspendedGambit';

export default [
new Extend.Search() //
.gambit('users', SuspendedGambit),
];
15 changes: 15 additions & 0 deletions extensions/suspend/js/src/common/query/users/SuspendedGambit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import IGambit from 'flarum/common/query/IGambit';

export default class SuspendedGambit implements IGambit {
pattern(): string {
return 'is:suspended';
}

toFilter(_matches: string[], negate: boolean): Record<string, any> {
const key = (negate ? '-' : '') + 'suspended';

return {
[key]: true,
};
}
}
6 changes: 5 additions & 1 deletion extensions/suspend/js/src/forum/extend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import Extend from 'flarum/common/extenders';
import User from 'flarum/common/models/User';
import Model from 'flarum/common/Model';

import commonExtend from '../common/extend';

export default [
...commonExtend,

new Extend.Model(User)
.attribute<boolean>('canSuspend')
.attribute<Date, string | null | undefined>('suspendedUntil', Model.transformDate)
.attribute<Date | null | undefined, string | null | undefined>('suspendedUntil', Model.transformDate)
.attribute<string | null | undefined>('suspendReason')
.attribute<string | null | undefined>('suspendMessage'),
];
4 changes: 4 additions & 0 deletions extensions/tags/js/src/common/extend.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import Extend from 'flarum/common/extenders';
import Tag from './models/Tag';
import TagGambit from './query/discussions/TagGambit';

export default [
new Extend.Store() //
.add('tags', Tag),

new Extend.Search() //
.gambit('discussions', TagGambit),
];
15 changes: 15 additions & 0 deletions extensions/tags/js/src/common/query/discussions/TagGambit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import IGambit from 'flarum/common/query/IGambit';

export default class TagGambit implements IGambit {
pattern(): string {
return 'tag:(.+)';
}

toFilter(matches: string[], negate: boolean): Record<string, any> {
const key = (negate ? '-' : '') + 'tag';

return {
[key]: matches[1].split(','),
};
}
}
51 changes: 51 additions & 0 deletions framework/core/js/src/common/GambitManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import IGambit from './query/IGambit';
import AuthorGambit from './query/discussions/AuthorGambit';
import CreatedGambit from './query/discussions/CreatedGambit';
import HiddenGambit from './query/discussions/HiddenGambit';
import UnreadGambit from './query/discussions/UnreadGambit';
import EmailGambit from './query/users/EmailGambit';
import GroupGambit from './query/users/GroupGambit';

/**
* The gambit registry. A map of resource types to gambit classes that
* should be used to filter resources of that type. Gambits are automatically
* converted to API filters when requesting resources. Gambits must be applied
* on a filter object that has a `q` property containing the search query.
*/
export default class GambitManager {
gambits: Record<string, Array<new () => IGambit>> = {
discussions: [AuthorGambit, CreatedGambit, HiddenGambit, UnreadGambit],
users: [EmailGambit, GroupGambit],
};

public apply(type: string, filter: Record<string, any>): Record<string, any> {
const gambits = this.gambits[type] || [];

if (gambits.length === 0) return filter;

const bits: string[] = filter.q.split(' ');

for (const gambitClass of gambits) {
const gambit = new gambitClass();

for (const bit of bits) {
const pattern = `^(-?)${gambit.pattern()}$`;
let matches = bit.match(pattern);

if (matches) {
const negate = matches[1] === '-';

matches.splice(1, 1);

Object.assign(filter, gambit.toFilter(matches, negate));

filter.q = filter.q.replace(bit, '');
}
}
}

filter.q = filter.q.trim().replace(/\s+/g, ' ');

return filter;
}
}
13 changes: 12 additions & 1 deletion framework/core/js/src/common/Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import app from '../common/app';
import { FlarumRequestOptions } from './Application';
import { fireDeprecationWarning } from './helpers/fireDebugWarning';
import Model, { ModelData, SavedModelData } from './Model';
import GambitManager from './GambitManager';

export interface MetaInformation {
[key: string]: any;
Expand All @@ -21,7 +22,7 @@ export interface ApiQueryParamsPlural {
| {
q: string;
}
| Record<string, string>;
| Record<string, any>;
page?: {
near?: number;
offset?: number;
Expand Down Expand Up @@ -85,6 +86,12 @@ export default class Store {
*/
models: Record<string, { new (): Model }>;

/**
* The gambit manager that will convert search query gambits
* into API filters.
*/
gambits = new GambitManager();

constructor(models: Record<string, { new (): Model }>) {
this.models = models;
}
Expand Down Expand Up @@ -178,6 +185,10 @@ export default class Store {
url += '/' + idOrParams;
}

if ('filter' in params && params?.filter?.q) {
params.filter = this.gambits.apply(type, params.filter);
}

return app
.request<M extends Array<infer _T> ? ApiPayloadPlural : ApiPayloadSingle>({
method: 'GET',
Expand Down
24 changes: 24 additions & 0 deletions framework/core/js/src/common/extenders/Search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type IExtender from './IExtender';
import type { IExtensionModule } from './IExtender';
import type Application from '../Application';
import IGambit from '../query/IGambit';

export default class Search implements IExtender {
protected gambits: Record<string, Array<new () => IGambit>> = {};

public gambit(modelType: string, gambit: new () => IGambit): this {
this.gambits[modelType] = this.gambits[modelType] || [];
this.gambits[modelType].push(gambit);

return this;
}

extend(app: Application, extension: IExtensionModule): void {
for (const [modelType, gambits] of Object.entries(this.gambits)) {
for (const gambit of gambits) {
app.store.gambits.gambits[modelType] = app.store.gambits.gambits[modelType] || [];
app.store.gambits.gambits[modelType].push(gambit);
}
}
}
}
2 changes: 2 additions & 0 deletions framework/core/js/src/common/extenders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import Model from './Model';
import PostTypes from './PostTypes';
import Routes from './Routes';
import Store from './Store';
import Search from './Search';

const extenders = {
Model,
PostTypes,
Routes,
Store,
Search,
};

export default extenders;
4 changes: 4 additions & 0 deletions framework/core/js/src/common/query/IGambit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default interface IGambit {
pattern(): string;
toFilter(matches: string[], negate: boolean): Record<string, any>;
}
15 changes: 15 additions & 0 deletions framework/core/js/src/common/query/discussions/AuthorGambit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import IGambit from '../IGambit';

export default class AuthorGambit implements IGambit {
public pattern(): string {
return 'author:(.+)';
}

public toFilter(matches: string[], negate: boolean): Record<string, any> {
const key = (negate ? '-' : '') + 'author';

return {
[key]: matches[1].split(','),
};
}
}
20 changes: 20 additions & 0 deletions framework/core/js/src/common/query/discussions/CreatedGambit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import IGambit from '../IGambit';

export default class CreatedGambit implements IGambit {
pattern(): string {
return 'created:(\\d{4}\\-\\d\\d\\-\\d\\d)(?:\\.\\.(\\d{4}\\-\\d\\d\\-\\d\\d))?';
}

toFilter(matches: string[], negate: boolean): Record<string, any> {
const key = (negate ? '-' : '') + 'created';

return {
[key]: matches[2]
? {
from: matches[1],
to: matches[2],
}
: matches[1],
};
}
}
Loading

0 comments on commit 336ce00

Please sign in to comment.