diff --git a/extend.php b/extend.php index 475b884..2a461bf 100644 --- a/extend.php +++ b/extend.php @@ -47,12 +47,9 @@ ->add(RegisterMiddleware::class), (new Extend\Model(User::class)) - ->relationship('fofTermsPolicies', function (AbstractModel $user): BelongsToMany { - return $user->belongsToMany(Policy::class, 'fof_terms_policy_user')->withPivot('accepted_at'); - }) - ->relationship('fofTermsPoliciesState', function (AbstractModel $user): BelongsToMany { - return $user->belongsToMany(Policy::class, 'fof_terms_policy_user')->withPivot('is_accepted'); - }), + ->relationship('fofTermsPolicies', fn (AbstractModel $user): BelongsToMany => $user + ->belongsToMany(Policy::class, 'fof_terms_policy_user') + ->withPivot(['accepted_at', 'is_accepted'])), (new Extend\User()) ->permissionGroups(function ($actor, $groupIds) { diff --git a/js/src/@types/Model/User.d.ts b/js/src/@types/Model/User.d.ts new file mode 100644 index 0000000..a9b11cc --- /dev/null +++ b/js/src/@types/Model/User.d.ts @@ -0,0 +1,17 @@ +export * from 'flarum/common/models/User'; + +declare module 'flarum/common/models/User' { + export default interface User { + fofTermsPoliciesHasUpdate(): boolean; + fofTermsPoliciesMustAccept(): boolean; + fofTermsPoliciesState(): Record< + number, + { + accepted_at: string; + has_update: boolean; + is_accepted: boolean; + must_accept: boolean; + } + >; + } +} diff --git a/js/src/common/models/Policy.js b/js/src/common/models/Policy.ts similarity index 54% rename from js/src/common/models/Policy.js rename to js/src/common/models/Policy.ts index 88a54de..32f8943 100644 --- a/js/src/common/models/Policy.js +++ b/js/src/common/models/Policy.ts @@ -2,12 +2,12 @@ import Model from 'flarum/common/Model'; import computed from 'flarum/common/utils/computed'; export default class Policy extends Model { - sort = Model.attribute('sort'); - name = Model.attribute('name'); - url = Model.attribute('url'); - update_message = Model.attribute('update_message'); - terms_updated_at = Model.attribute('terms_updated_at'); - optional = Model.attribute('optional'); + sort = Model.attribute('sort'); + name = Model.attribute('name'); + url = Model.attribute('url'); + update_message = Model.attribute('update_message'); + terms_updated_at = Model.attribute('terms_updated_at'); + optional = Model.attribute('optional'); additional_info = Model.attribute('additional_info'); form_key = computed('id', (id) => 'fof_terms_policy_' + id); diff --git a/js/src/forum/components/AcceptPoliciesModal.js b/js/src/forum/components/AcceptPoliciesModal.js index b7ebc5c..2b59660 100644 --- a/js/src/forum/components/AcceptPoliciesModal.js +++ b/js/src/forum/components/AcceptPoliciesModal.js @@ -10,7 +10,9 @@ export default class AcceptPoliciesModal extends Modal { super.oninit(vnode); app.store.all('fof-terms-policies').forEach((policy) => { - this[policy.form_key()] = false; + const state = app.session.user.fofTermsPoliciesState()[policy.id()]; + // For optional policies, maintain current acceptance status + this[policy.form_key()] = policy.optional() ? state?.is_accepted || false : false; }); } @@ -23,7 +25,7 @@ export default class AcceptPoliciesModal extends Modal { } content() { - return m('.Modal-body', this.body()); + return
{this.body()}
; } body() { @@ -36,91 +38,83 @@ export default class AcceptPoliciesModal extends Modal { ); if (policies.length === 0) { - return Button.component( - { - className: 'Button', - onclick() { + return ( + ); } - return policies.map((policy) => - m('div', [ - m('h2', policy.name()), - app.forum.attribute('fof-terms.hide-updated-at') - ? null - : m( - 'p', - policy.terms_updated_at() - ? app.translator.trans('fof-terms.forum.accept-modal.updated-at', { - date: dayjs(policy.terms_updated_at()).format(app.forum.attribute('fof-terms.date-format')), - }) - : app.translator.trans('fof-terms.forum.accept-modal.updated-recently') - ), - policy.update_message() ? m('p', policy.update_message()) : null, - m( - '.Form-group', - m( - '.FoF-Terms-Check.FoF-Terms-Check--login', - m('label.checkbox', [ - m('input', { - type: 'checkbox', - checked: this[policy.form_key()], - onchange: () => { + return policies.map((policy) => ( +
+

{policy.name()}

+ {app.forum.attribute('fof-terms.hide-updated-at') ? null : ( +

+ {policy.terms_updated_at() + ? app.translator.trans('fof-terms.forum.accept-modal.updated-at', { + date: dayjs(policy.terms_updated_at()).format(app.forum.attribute('fof-terms.date-format')), + }) + : app.translator.trans('fof-terms.forum.accept-modal.updated-recently')} +

+ )} + {policy.update_message() ?

{policy.update_message()}

: null} +
+
+ +
+
+ +
+ )); } } diff --git a/js/src/forum/components/UpdateAlert.js b/js/src/forum/components/UpdateAlert.js deleted file mode 100644 index b241c06..0000000 --- a/js/src/forum/components/UpdateAlert.js +++ /dev/null @@ -1,68 +0,0 @@ -import app from 'flarum/forum/app'; -import Button from 'flarum/common/components/Button'; -import listItems from 'flarum/common/helpers/listItems'; -import AcceptPoliciesModal from './AcceptPoliciesModal'; - -/* global m */ - -let temporarilyHidden = false; - -/** - * Renders similarly to Flarum's Alert, but with an additional .container inside - */ -export default class UpdateAlert { - shouldShowAlert() { - if (temporarilyHidden) { - return false; - } - - const user = app.session.user; - - return user && user.fofTermsPoliciesHasUpdate(); - } - - view() { - if (!this.shouldShowAlert()) { - return m('div'); - } - - const controls = [ - Button.component( - { - className: 'Button Button--link', - onclick: () => { - app.modal.show(AcceptPoliciesModal); - }, - }, - app.translator.trans('fof-terms.forum.update-alert.review') - ), - ]; - - const dismissControl = []; - - if (!app.session.user.fofTermsPoliciesMustAccept()) { - dismissControl.push( - Button.component({ - icon: 'fas fa-times', - className: 'Button Button--link Button--icon Alert-dismiss', - onclick: () => { - temporarilyHidden = true; - }, - }) - ); - } - - return m( - '.Alert.Alert-info', - m('.container', [ - m( - 'span.Alert-body', - app.session.user.fofTermsPoliciesMustAccept() - ? app.translator.trans('fof-terms.forum.update-alert.must-accept-message') - : app.translator.trans('fof-terms.forum.update-alert.can-accept-message') - ), - m('ul.Alert-controls', listItems(controls.concat(dismissControl))), - ]) - ); - } -} diff --git a/js/src/forum/components/UpdateAlert.tsx b/js/src/forum/components/UpdateAlert.tsx new file mode 100644 index 0000000..1a2e648 --- /dev/null +++ b/js/src/forum/components/UpdateAlert.tsx @@ -0,0 +1,75 @@ +import app from 'flarum/forum/app'; +import Button from 'flarum/common/components/Button'; +import listItems from 'flarum/common/helpers/listItems'; +import AcceptPoliciesModal from './AcceptPoliciesModal'; + +let temporarilyHidden = false; + +/** + * Renders similarly to Flarum's Alert, but with an additional .container inside + */ +export default class UpdateAlert { + shouldShowAlert() { + if (temporarilyHidden) { + return false; + } + + const { user } = app.session; + + return user && user.fofTermsPoliciesHasUpdate(); + } + + hasOnlyOptionalUpdates() { + const { user } = app.session; + return user && !user.fofTermsPoliciesMustAccept() && user.fofTermsPoliciesHasUpdate(); + } + + view() { + const { user } = app.session; + + if (!this.shouldShowAlert() || !user) { + return null; + } + + const controls = [ + , + ]; + + const dismissControl = []; + + if (!user.fofTermsPoliciesMustAccept()) { + dismissControl.push( +