From f2437afb5201cc183389ecf961bfdef7efb982c8 Mon Sep 17 00:00:00 2001 From: David Wheatley Date: Thu, 23 Dec 2021 13:23:44 +0100 Subject: [PATCH] fix: rewrite to use relations for fields (#68) --- extend.php | 32 +++++++--- js/package-lock.json | 12 ++-- js/src/forum/index.js | 1 + js/src/forum/mutateUserHero.js | 1 + js/src/forum/panes/ProfileConfigurePane.js | 57 +++++++++++------ js/src/forum/panes/ProfilePane.js | 60 ------------------ js/src/forum/panes/ProfilePane.tsx | 62 +++++++++++++++++++ js/src/forum/panes/RootMasqueradePane.tsx | 1 - js/src/forum/types/TypeFactory.js | 2 +- js/src/lib/models/Answer.js | 11 ---- js/src/lib/models/Answer.ts | 14 +++++ .../Controllers/UserConfigureController.php | 9 ++- src/Api/Serializers/AnswerSerializer.php | 7 ++- src/LoadAllMasqueradeFieldsRelationship.php | 25 ++++++++ 14 files changed, 186 insertions(+), 108 deletions(-) delete mode 100644 js/src/forum/panes/ProfilePane.js create mode 100644 js/src/forum/panes/ProfilePane.tsx delete mode 100644 js/src/lib/models/Answer.js create mode 100644 js/src/lib/models/Answer.ts create mode 100644 src/LoadAllMasqueradeFieldsRelationship.php diff --git a/extend.php b/extend.php index 7ea0b7d..1c1b18e 100644 --- a/extend.php +++ b/extend.php @@ -6,6 +6,7 @@ use Flarum\Api\Controller\ListPostsController; use Flarum\Api\Controller\ListUsersController; use Flarum\Api\Controller\ShowDiscussionController; +use Flarum\Api\Controller\ShowForumController; use Flarum\Api\Controller\ShowUserController; use Flarum\Api\Controller\UpdateUserController; use Flarum\Api\Serializer\BasicUserSerializer; @@ -17,6 +18,7 @@ use FoF\Masquerade\Api\Controllers as Api; use Flarum\Extend; use FoF\Masquerade\Api\Serializers\AnswerSerializer; +use FoF\Masquerade\Api\Serializers\FieldSerializer; return [ (new Extend\Frontend('forum')) @@ -42,23 +44,33 @@ (new Extend\Locales(__DIR__ . '/resources/locale')), + (new Extend\ApiController(ShowForumController::class)) + ->prepareDataForSerialization(LoadAllMasqueradeFieldsRelationship::class) + ->addInclude('masqueradeFields'), + (new Extend\ApiController(ShowUserController::class)) - ->addInclude('bioFields.field'), + ->addInclude('bioFields.field') + ->addInclude('masqueradeAnswers'), (new Extend\ApiController(UpdateUserController::class)) - ->addInclude('bioFields.field'), + ->addInclude('bioFields.field') + ->addInclude('masqueradeAnswers'), (new Extend\ApiController(CreateUserController::class)) - ->addInclude('bioFields.field'), + ->addInclude('bioFields.field') + ->addInclude('masqueradeAnswers'), (new Extend\ApiController(ListUsersController::class)) - ->addInclude('bioFields.field'), + ->addInclude('bioFields.field') + ->addInclude('masqueradeAnswers'), (new Extend\ApiController(ListPostsController::class)) - ->addInclude('user.bioFields.field'), + ->addInclude('user.bioFields.field') + ->addInclude('user.masqueradeAnswers'), (new Extend\ApiController(ShowDiscussionController::class)) - ->addInclude('posts.user.bioFields.field'), + ->addInclude('posts.user.bioFields.field') + ->addInclude('posts.user.masqueradeAnswers'), (new Extend\Model(User::class)) ->relationship('bioFields', function (User $model) { @@ -66,10 +78,12 @@ ->whereHas('field', function ($q) { $q->where('on_bio', true); }); - }), + }) + ->hasMany('masqueradeAnswers', Answer::class), (new Extend\ApiSerializer(BasicUserSerializer::class)) ->hasMany('bioFields', AnswerSerializer::class) + ->hasMany('masqueradeAnswers', AnswerSerializer::class) ->attributes(function (BasicUserSerializer $serializer, User $user): array { $actor = $serializer->getActor(); @@ -77,13 +91,15 @@ // When the relationships are auto-loaded later, // this one will be skipped because it has already been set to null $user->setRelation('bioFields', null); + $user->setRelation('masqueradeAnswers', null); } return []; }), (new Extend\ApiSerializer(ForumSerializer::class)) - ->attributes(ForumAttributes::class), + ->attributes(ForumAttributes::class) + ->hasMany('masqueradeFields', FieldSerializer::class), (new Extend\ApiSerializer(UserSerializer::class)) ->attributes(UserAttributes::class), diff --git a/js/package-lock.json b/js/package-lock.json index 6f83424..9ae9e5d 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -2182,9 +2182,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001233", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001233.tgz", - "integrity": "sha512-BmkbxLfStqiPA7IEzQpIk0UFZFf3A4E6fzjPJ6OR+bFC2L8ES9J8zGA/asoi47p8XDVkev+WJo2I2Nc8c/34Yg==", + "version": "1.0.30001292", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", + "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/browserslist" @@ -7856,9 +7856,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001233", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001233.tgz", - "integrity": "sha512-BmkbxLfStqiPA7IEzQpIk0UFZFf3A4E6fzjPJ6OR+bFC2L8ES9J8zGA/asoi47p8XDVkev+WJo2I2Nc8c/34Yg==" + "version": "1.0.30001292", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", + "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==" }, "chalk": { "version": "2.4.2", diff --git a/js/src/forum/index.js b/js/src/forum/index.js index 20b2dd0..9cec139 100644 --- a/js/src/forum/index.js +++ b/js/src/forum/index.js @@ -23,6 +23,7 @@ app.initializers.add('fof-masquerade', (app) => { app.store.models['masquerade-answer'] = Answer; User.prototype.bioFields = Model.hasMany('bioFields'); + User.prototype.masqueradeAnswers = Model.hasMany('masqueradeAnswers'); User.prototype.canEditMasqueradeProfile = Model.attribute('canEditMasqueradeProfile'); addProfilePane(); diff --git a/js/src/forum/mutateUserHero.js b/js/src/forum/mutateUserHero.js index 24fa34c..d957a55 100644 --- a/js/src/forum/mutateUserHero.js +++ b/js/src/forum/mutateUserHero.js @@ -1,4 +1,5 @@ import { extend } from 'flarum/common/extend'; +import app from 'flarum/forum/app'; import UserCard from 'flarum/forum/components/UserCard'; import TypeFactory from './types/TypeFactory'; diff --git a/js/src/forum/panes/ProfileConfigurePane.js b/js/src/forum/panes/ProfileConfigurePane.js index 36ffbac..374e121 100644 --- a/js/src/forum/panes/ProfileConfigurePane.js +++ b/js/src/forum/panes/ProfileConfigurePane.js @@ -2,7 +2,7 @@ import app from 'flarum/forum/app'; import Button from 'flarum/common/components/Button'; import Link from 'flarum/common/components/Link'; -import TypeFactory from './../types/TypeFactory'; +import TypeFactory from '../types/TypeFactory'; import Component from 'flarum/common/Component'; export default class ProfileConfigurePane extends Component { @@ -13,8 +13,8 @@ export default class ProfileConfigurePane extends Component { this.enforceProfileCompletion = app.forum.attribute('masquerade.force-profile-completion') || false; this.profileCompleted = app.forum.attribute('masquerade.profile-completed') || false; this.profileNowCompleted = false; // Show "after required" text - this.fields = []; - this.answers = {}; + this.answers = []; + this.answerValues = {}; this.user = this.attrs.user; this.load(); @@ -31,13 +31,10 @@ export default class ProfileConfigurePane extends Component { )}
- {this.fields + {app.store + .all('masquerade-field') .sort((a, b) => a.sort() - b.sort()) .map((field) => { - if (!this.answers.hasOwnProperty(field.id())) { - this.answers[field.id()] = field.answer() ? field.answer().content() : ''; - } - return this.field(field); })}
@@ -61,23 +58,45 @@ export default class ProfileConfigurePane extends Component { const type = TypeFactory.typeForField({ field, set: this.set.bind(this, field), - value: this.answers[field.id()], + value: this.answerValues[field.id()], }); return type.editorField(); } load() { - app - .request({ - method: 'GET', - url: app.forum.attribute('apiUrl') + '/masquerade/configure/' + this.user.id(), - }) - .then(this.parseResponse.bind(this)); + this.answers = this.user.masqueradeAnswers(); + + if (this.answers === false) { + this.answers = []; + app.store.find('users', this.user.id(), { include: 'masqueradeAnswers' }).then(() => { + this.answers = this.user.masqueradeAnswers(); + this.answerValues = {}; + + app.store.all('masquerade-field').forEach((field) => { + const answer = this.answers.find((a) => a.field().id() === field.id()); + + this.answerValues[field.id()] = answer ? answer.content() : ''; + }); + + this.loading = false; + m.redraw(); + }); + } else { + this.loading = false; + + app.store.all('masquerade-field').forEach((field) => { + const answer = this.answers.find((a) => a.field().id() === field.id()); + + this.answerValues[field.id()] = answer ? answer.content() : ''; + }); + } + + m.redraw(); } set(field, value) { - this.answers[field.id()] = value; + this.answerValues[field.id()] = value; this.dirty = true; } @@ -90,10 +109,11 @@ export default class ProfileConfigurePane extends Component { .request({ method: 'POST', url: app.forum.attribute('apiUrl') + '/masquerade/configure/' + this.user.id(), - body: this.answers, + body: this.answerValues, }) .then((response) => { this.dirty = false; + if (!this.profileCompleted) { this.profileCompleted = true; this.profileNowCompleted = true; @@ -108,7 +128,8 @@ export default class ProfileConfigurePane extends Component { } parseResponse(response) { - this.fields = app.store.pushPayload(response); + console.log(response); + this.answers = app.store.pushPayload(response); this.loading = false; m.redraw(); } diff --git a/js/src/forum/panes/ProfilePane.js b/js/src/forum/panes/ProfilePane.js deleted file mode 100644 index 62fb993..0000000 --- a/js/src/forum/panes/ProfilePane.js +++ /dev/null @@ -1,60 +0,0 @@ -import app from 'flarum/forum/app'; - -import Component from 'flarum/common/Component'; -import TypeFactory from './../types/TypeFactory'; - -export default class ProfilePane extends Component { - oninit(vnode) { - super.oninit(vnode); - this.loading = true; - - this.fields = []; - this.answers = {}; - this.user = this.attrs.user; - - this.load(this.user); - } - - view() { - return ( -
-
- {this.fields - .sort((a, b) => a.sort() - b.sort()) - .map((field) => { - // UserID check must be done with == because userId() is number while id() is string - this.answers[field.id()] = field.answer() && field.answer().userId() == this.user.id() ? field.answer().content() : null; - - return this.field(field); - })} -
-
- ); - } - - field(field) { - const type = TypeFactory.typeForField({ - field, - value: this.answers[field.id()], - }); - - return type.answerField(); - } - - load(user) { - app - .request({ - method: 'GET', - url: app.forum.attribute('apiUrl') + '/masquerade/profile/' + user.id(), - }) - .then(this.parseResponse.bind(this)); - } - - parseResponse(response) { - this.answers = {}; - this.fields = app.store.pushPayload(response); - - this.loading = false; - m.redraw(); - } -} diff --git a/js/src/forum/panes/ProfilePane.tsx b/js/src/forum/panes/ProfilePane.tsx new file mode 100644 index 0000000..d21610b --- /dev/null +++ b/js/src/forum/panes/ProfilePane.tsx @@ -0,0 +1,62 @@ +import app from 'flarum/forum/app'; + +import Component from 'flarum/common/Component'; +import TypeFactory from '../types/TypeFactory'; +import type Answer from '../../lib/models/Answer'; +import type Field from 'src/lib/models/Field'; +import type User from 'flarum/common/models/User'; + +export default class ProfilePane extends Component { + answers!: Answer[]; + user!: User; + + oninit(vnode) { + super.oninit(vnode); + this.loading = true; + + this.answers = []; + this.user = this.attrs.user; + + this.load(this.user); + } + + view() { + return ( +
+
+ {app.store + .all('masquerade-field') + .sort((a, b) => a.sort() - b.sort()) + .map((field) => { + const answer = this.answers.find((a) => a.field().id() === field.id()); + + return this.field(field, answer?.content() || ''); + })} +
+
+ ); + } + + field(field: Field, content) { + const type = TypeFactory.typeForField({ + field, + value: content, + }); + + return type.answerField(); + } + + load() { + this.answers = this.user.masqueradeAnswers(); + + if (this.answers === false) { + this.answers = []; + app.store.find('users', this.user.id(), { include: 'masqueradeAnswers' }).then(() => { + this.answers = this.user.masqueradeAnswers(); + m.redraw(); + }); + } + + m.redraw(); + } +} diff --git a/js/src/forum/panes/RootMasqueradePane.tsx b/js/src/forum/panes/RootMasqueradePane.tsx index 347dc83..1f8d0c6 100644 --- a/js/src/forum/panes/RootMasqueradePane.tsx +++ b/js/src/forum/panes/RootMasqueradePane.tsx @@ -14,7 +14,6 @@ export default class RootMasqueradePane extends UserPage { pageContentComponent() { if (!this.user) return null; - if (this.user.canEditMasqueradeProfile()) return ; else return ; diff --git a/js/src/forum/types/TypeFactory.js b/js/src/forum/types/TypeFactory.js index 419a1c3..3c445e4 100644 --- a/js/src/forum/types/TypeFactory.js +++ b/js/src/forum/types/TypeFactory.js @@ -5,7 +5,7 @@ import SelectField from './SelectField'; import UrlField from './UrlField'; export default class TypeFactory { - static typeForField({ field, set, value }) { + static typeForField({ field, set = undefined, value }) { let className = BaseField; const type = this.identify(field); diff --git a/js/src/lib/models/Answer.js b/js/src/lib/models/Answer.js deleted file mode 100644 index 35ae546..0000000 --- a/js/src/lib/models/Answer.js +++ /dev/null @@ -1,11 +0,0 @@ -import Model from 'flarum/common/Model'; - -export default class Answer extends Model { - content = Model.attribute('content'); - field = Model.hasOne('field'); - userId = Model.attribute('user_id'); - - apiEndpoint() { - return '/masquerade/configure' + (this.exists ? '/' + this.data.id : ''); - } -} diff --git a/js/src/lib/models/Answer.ts b/js/src/lib/models/Answer.ts new file mode 100644 index 0000000..d2f90cf --- /dev/null +++ b/js/src/lib/models/Answer.ts @@ -0,0 +1,14 @@ +import app from 'flarum/common/app'; +import Model from 'flarum/common/Model'; +import computed from 'flarum/common/utils/computed'; + +import type Field from './Field'; + +export default class Answer extends Model { + content = Model.attribute('content'); + fieldId = Model.attribute('fieldId'); + field = computed('fieldId', (fieldId: string) => { + return app.store.getById('masquerade-field', fieldId); + }); + userId = Model.attribute('user_id'); +} diff --git a/src/Api/Controllers/UserConfigureController.php b/src/Api/Controllers/UserConfigureController.php index 61cb0e7..997a0b6 100644 --- a/src/Api/Controllers/UserConfigureController.php +++ b/src/Api/Controllers/UserConfigureController.php @@ -10,13 +10,14 @@ use Flarum\Api\Controller\AbstractListController; use Flarum\User\User; use Flarum\User\UserRepository; +use FoF\Masquerade\Api\Serializers\AnswerSerializer; use Illuminate\Support\Arr; use Psr\Http\Message\ServerRequestInterface; use Tobscure\JsonApi\Document; class UserConfigureController extends AbstractListController { - public $serializer = FieldSerializer::class; + public $serializer = AnswerSerializer::class; public $include = ['answer']; @@ -63,7 +64,11 @@ protected function data(ServerRequestInterface $request, Document $document) $this->processUpdate($user, $request->getParsedBody(), $fields); } - return $fields; + return $fields->map(function (Field $field) use ($actor) { + return $field->answers()->firstOrNew([ + 'user_id' => $actor->id, + ]); + }); } /** diff --git a/src/Api/Serializers/AnswerSerializer.php b/src/Api/Serializers/AnswerSerializer.php index 44ee3b7..922c767 100644 --- a/src/Api/Serializers/AnswerSerializer.php +++ b/src/Api/Serializers/AnswerSerializer.php @@ -7,9 +7,14 @@ class AnswerSerializer extends AbstractSerializer { + /** + * @param \FoF\Masquerade\Answer $model + */ protected function getDefaultAttributes($model): array { - return $model->toArray(); + return array_merge($model->toArray(), [ + 'fieldId' => $model->field_id + ]); } public function getType($model): string diff --git a/src/LoadAllMasqueradeFieldsRelationship.php b/src/LoadAllMasqueradeFieldsRelationship.php new file mode 100644 index 0000000..a6ac001 --- /dev/null +++ b/src/LoadAllMasqueradeFieldsRelationship.php @@ -0,0 +1,25 @@ +