From e79d3bc3b7070404e090036d1bd4e28d4bed54ca Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Sun, 12 Dec 2021 15:41:59 -0500 Subject: [PATCH 01/13] Don't assume `app.session.user` is present --- framework/core/js/src/forum/states/NotificationListState.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/core/js/src/forum/states/NotificationListState.ts b/framework/core/js/src/forum/states/NotificationListState.ts index cfe214c02..75bb07158 100644 --- a/framework/core/js/src/forum/states/NotificationListState.ts +++ b/framework/core/js/src/forum/states/NotificationListState.ts @@ -15,7 +15,7 @@ export default class NotificationListState extends PaginatedListState { - if (app.session.user.newNotificationCount()) { + if (app.session.user?.newNotificationCount()) { this.pages = []; this.location = { page: 1 }; } @@ -24,7 +24,7 @@ export default class NotificationListState extends PaginatedListState { page.items.forEach((notification) => notification.pushAttributes({ isRead: true })); From a8db0b7bb24ad70d46ded1581c8187884fee23ef Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Sun, 12 Dec 2021 15:42:49 -0500 Subject: [PATCH 02/13] Fix import of `mithril` for `VnodeElementTag` usage --- framework/core/js/src/@types/global.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/js/src/@types/global.d.ts b/framework/core/js/src/@types/global.d.ts index e3ff2fe27..37d6b81e7 100644 --- a/framework/core/js/src/@types/global.d.ts +++ b/framework/core/js/src/@types/global.d.ts @@ -21,7 +21,7 @@ declare type KeysOfType = { */ declare type KeyOfType = KeysOfType[keyof Type]; -declare type VnodeElementTag, State = Record> = string | ComponentTypes; +declare type VnodeElementTag, State = Record> = string | import('mithril').ComponentTypes; /** * @deprecated Please import `app` from a namespace instead of using it as a global variable. From d307331603ae27bc5f205795d613cc50662c1058 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Sun, 12 Dec 2021 15:43:19 -0500 Subject: [PATCH 03/13] extractText from translations where strings expected --- framework/core/js/src/admin/components/ExtensionPage.tsx | 3 ++- framework/core/js/src/common/utils/abbreviateNumber.ts | 5 +++-- framework/core/js/src/forum/ForumApplication.ts | 8 +++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/framework/core/js/src/admin/components/ExtensionPage.tsx b/framework/core/js/src/admin/components/ExtensionPage.tsx index fa7f70751..fa6acccaa 100644 --- a/framework/core/js/src/admin/components/ExtensionPage.tsx +++ b/framework/core/js/src/admin/components/ExtensionPage.tsx @@ -16,6 +16,7 @@ import RequestError from '../../common/utils/RequestError'; import { Extension } from '../AdminApplication'; import { IPageAttrs } from '../../common/components/Page'; import type Mithril from 'mithril'; +import extractText from '../../common/utils/extractText'; export interface ExtensionPageAttrs extends IPageAttrs { id: string; @@ -156,7 +157,7 @@ export default class ExtensionPage { - if (confirm(app.translator.trans('core.admin.extension.confirm_purge'))) { + if (confirm(extractText(app.translator.trans('core.admin.extension.confirm_purge')))) { app .request({ url: app.forum.attribute('apiUrl') + '/extensions/' + this.extension.id, diff --git a/framework/core/js/src/common/utils/abbreviateNumber.ts b/framework/core/js/src/common/utils/abbreviateNumber.ts index 9f3c5366e..43586876a 100644 --- a/framework/core/js/src/common/utils/abbreviateNumber.ts +++ b/framework/core/js/src/common/utils/abbreviateNumber.ts @@ -1,4 +1,5 @@ import app from '../../common/app'; +import extractText from './extractText'; /** * The `abbreviateNumber` utility converts a number to a shorter localized form. @@ -10,9 +11,9 @@ import app from '../../common/app'; export default function abbreviateNumber(number: number): string { // TODO: translation if (number >= 1000000) { - return Math.floor(number / 1000000) + app.translator.trans('core.lib.number_suffix.mega_text'); + return Math.floor(number / 1000000) + extractText(app.translator.trans('core.lib.number_suffix.mega_text')); } else if (number >= 1000) { - return (number / 1000).toFixed(1) + app.translator.trans('core.lib.number_suffix.kilo_text'); + return (number / 1000).toFixed(1) + extractText(app.translator.trans('core.lib.number_suffix.kilo_text')); } else { return number.toString(); } diff --git a/framework/core/js/src/forum/ForumApplication.ts b/framework/core/js/src/forum/ForumApplication.ts index 169e28135..3c88f4e1e 100644 --- a/framework/core/js/src/forum/ForumApplication.ts +++ b/framework/core/js/src/forum/ForumApplication.ts @@ -23,6 +23,7 @@ import isSafariMobile from './utils/isSafariMobile'; import type Notification from './components/Notification'; import type Post from './components/Post'; import Discussion from '../common/models/Discussion'; +import extractText from '../common/utils/extractText'; export default class ForumApplication extends Application { /** @@ -99,7 +100,7 @@ export default class ForumApplication extends Application { } this.routes[defaultAction].path = '/'; - this.history.push(defaultAction, this.translator.trans('core.forum.header.back_to_index_tooltip'), '/'); + this.history.push(defaultAction, extractText(this.translator.trans('core.forum.header.back_to_index_tooltip')), '/'); this.pane = new Pane(document.getElementById('app')); @@ -124,8 +125,9 @@ export default class ForumApplication extends Application { app.history.home(); // Reload the current user so that their unread notification count is refreshed. - if (app.session.user) { - app.store.find('users', app.session.user.id()); + const userId = app.session.user?.id() + if (userId) { + app.store.find('users', userId); m.redraw(); } }); From 0e2053da1f7e1b9e1eb63221a6a186bb6fee355e Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Sun, 12 Dec 2021 15:43:37 -0500 Subject: [PATCH 04/13] Allow any Mithril Children where appropriate --- framework/core/js/src/admin/components/AdminPage.tsx | 4 ++-- framework/core/js/src/admin/components/UserListPage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/core/js/src/admin/components/AdminPage.tsx b/framework/core/js/src/admin/components/AdminPage.tsx index 981a246cc..8a3b4bc1f 100644 --- a/framework/core/js/src/admin/components/AdminPage.tsx +++ b/framework/core/js/src/admin/components/AdminPage.tsx @@ -13,8 +13,8 @@ import generateElementId from '../utils/generateElementId'; import ColorPreviewInput from '../../common/components/ColorPreviewInput'; export interface AdminHeaderOptions { - title: string; - description: string; + title: Mithril.Children; + description: Mithril.Children; icon: string; /** * Will be used as the class for the AdminPage. diff --git a/framework/core/js/src/admin/components/UserListPage.tsx b/framework/core/js/src/admin/components/UserListPage.tsx index 675104aea..3502a24f9 100644 --- a/framework/core/js/src/admin/components/UserListPage.tsx +++ b/framework/core/js/src/admin/components/UserListPage.tsx @@ -21,7 +21,7 @@ type ColumnData = { /** * Column title */ - name: String; + name: Mithril.Children; /** * Component(s) to show for this column. */ From 74f223cccc7c3e689e8497bb9ce71044652a2279 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Sun, 12 Dec 2021 15:43:50 -0500 Subject: [PATCH 05/13] Fix `LoadingModal` attr typings --- framework/core/js/src/admin/components/LoadingModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/core/js/src/admin/components/LoadingModal.tsx b/framework/core/js/src/admin/components/LoadingModal.tsx index 2a9555d88..5594cb278 100644 --- a/framework/core/js/src/admin/components/LoadingModal.tsx +++ b/framework/core/js/src/admin/components/LoadingModal.tsx @@ -1,7 +1,7 @@ import app from '../../admin/app'; -import Modal from '../../common/components/Modal'; +import Modal, { IInternalModalAttrs } from '../../common/components/Modal'; -export default class LoadingModal extends Modal { +export default class LoadingModal extends Modal { /** * @inheritdoc */ From da6ae898b2c2b264baab720f9bc3817a53a1289d Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Sun, 12 Dec 2021 15:44:09 -0500 Subject: [PATCH 06/13] `alertAttrs` can be null --- framework/core/js/src/common/components/Modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/core/js/src/common/components/Modal.tsx b/framework/core/js/src/common/components/Modal.tsx index 24594f5a7..a6e17f4d5 100644 --- a/framework/core/js/src/common/components/Modal.tsx +++ b/framework/core/js/src/common/components/Modal.tsx @@ -30,7 +30,7 @@ export default abstract class Modal) { super.oninit(vnode); @@ -122,7 +122,7 @@ export default abstract class Modal Date: Sun, 12 Dec 2021 18:36:51 -0500 Subject: [PATCH 07/13] Modal typescript cleanup and conversions --- .../js/src/admin/components/LoadingModal.tsx | 4 +- .../{ReadmeModal.js => ReadmeModal.tsx} | 16 +++- framework/core/js/src/common/Model.ts | 4 +- framework/core/js/src/common/Session.ts | 5 +- .../{EditUserModal.js => EditUserModal.tsx} | 93 ++++++++++++------- .../core/js/src/common/components/Modal.tsx | 2 +- ...estErrorModal.js => RequestErrorModal.tsx} | 20 ++-- .../core/js/src/forum/ForumApplication.ts | 2 +- ...sswordModal.js => ForgotPasswordModal.tsx} | 43 ++++----- .../{LogInModal.js => LogInModal.tsx} | 71 +++++++------- .../{SignUpModal.js => SignUpModal.tsx} | 84 ++++++++--------- 11 files changed, 182 insertions(+), 162 deletions(-) rename framework/core/js/src/admin/components/{ReadmeModal.js => ReadmeModal.tsx} (70%) rename framework/core/js/src/common/components/{EditUserModal.js => EditUserModal.tsx} (67%) rename framework/core/js/src/common/components/{RequestErrorModal.js => RequestErrorModal.tsx} (59%) rename framework/core/js/src/forum/components/{ForgotPasswordModal.js => ForgotPasswordModal.tsx} (78%) rename framework/core/js/src/forum/components/{LogInModal.js => LogInModal.tsx} (79%) rename framework/core/js/src/forum/components/{SignUpModal.js => SignUpModal.tsx} (75%) diff --git a/framework/core/js/src/admin/components/LoadingModal.tsx b/framework/core/js/src/admin/components/LoadingModal.tsx index 5594cb278..082efd002 100644 --- a/framework/core/js/src/admin/components/LoadingModal.tsx +++ b/framework/core/js/src/admin/components/LoadingModal.tsx @@ -1,7 +1,9 @@ import app from '../../admin/app'; import Modal, { IInternalModalAttrs } from '../../common/components/Modal'; -export default class LoadingModal extends Modal { +export interface ILoadingModalAttrs extends IInternalModalAttrs {} + +export default class LoadingModal extends Modal { /** * @inheritdoc */ diff --git a/framework/core/js/src/admin/components/ReadmeModal.js b/framework/core/js/src/admin/components/ReadmeModal.tsx similarity index 70% rename from framework/core/js/src/admin/components/ReadmeModal.js rename to framework/core/js/src/admin/components/ReadmeModal.tsx index 425e5aac2..f932de229 100644 --- a/framework/core/js/src/admin/components/ReadmeModal.js +++ b/framework/core/js/src/admin/components/ReadmeModal.tsx @@ -1,11 +1,21 @@ import app from '../../admin/app'; -import Modal from '../../common/components/Modal'; +import Modal, { IInternalModalAttrs } from '../../common/components/Modal'; import LoadingIndicator from '../../common/components/LoadingIndicator'; import Placeholder from '../../common/components/Placeholder'; import ExtensionReadme from '../models/ExtensionReadme'; +import type Mithril from 'mithril'; +import { Extension } from '../AdminApplication'; -export default class ReadmeModal extends Modal { - oninit(vnode) { +export interface IReadmeModalAttrs extends IInternalModalAttrs { + extension: Extension; +} + +export default class ReadmeModal extends Modal { + protected name!: string; + protected extName!: string; + protected readme!: ExtensionReadme; + + oninit(vnode: Mithril.Vnode) { super.oninit(vnode); app.store.models['extension-readmes'] = ExtensionReadme; diff --git a/framework/core/js/src/common/Model.ts b/framework/core/js/src/common/Model.ts index 1bc29431e..9fce10624 100644 --- a/framework/core/js/src/common/Model.ts +++ b/framework/core/js/src/common/Model.ts @@ -32,11 +32,11 @@ export interface SavedModelData { export type ModelData = UnsavedModelData | SavedModelData; -interface SaveRelationships { +export interface SaveRelationships { [relationship: string]: Model | Model[]; } -interface SaveAttributes { +export interface SaveAttributes { [key: string]: unknown; relationships?: SaveRelationships; } diff --git a/framework/core/js/src/common/Session.ts b/framework/core/js/src/common/Session.ts index 0997bf99f..e4e15a6dc 100644 --- a/framework/core/js/src/common/Session.ts +++ b/framework/core/js/src/common/Session.ts @@ -7,11 +7,8 @@ export type LoginParams = { * The username/email */ identification: string; - - /** - * Password - */ password: string; + remember: boolean; }; /** diff --git a/framework/core/js/src/common/components/EditUserModal.js b/framework/core/js/src/common/components/EditUserModal.tsx similarity index 67% rename from framework/core/js/src/common/components/EditUserModal.js rename to framework/core/js/src/common/components/EditUserModal.tsx index 70b3ca9c5..01083ec76 100644 --- a/framework/core/js/src/common/components/EditUserModal.js +++ b/framework/core/js/src/common/components/EditUserModal.tsx @@ -1,17 +1,28 @@ import app from '../../common/app'; -import Modal from './Modal'; +import Modal, { IInternalModalAttrs } from './Modal'; import Button from './Button'; import GroupBadge from './GroupBadge'; import Group from '../models/Group'; import extractText from '../utils/extractText'; import ItemList from '../utils/ItemList'; import Stream from '../utils/Stream'; +import Mithril from 'mithril'; +import User from '../models/User'; +import { SaveAttributes, SaveRelationships } from '../Model'; -/** - * The `EditUserModal` component displays a modal dialog with a login form. - */ -export default class EditUserModal extends Modal { - oninit(vnode) { +export interface IEditUserModalAttrs extends IInternalModalAttrs { + user: User; +} + +export default class EditUserModal extends Modal { + protected username!: Stream; + protected email!: Stream; + protected isEmailConfirmed!: Stream; + protected setPassword!: Stream; + protected password!: Stream; + protected groups: Record> = {}; + + oninit(vnode: Mithril.Vnode) { super.oninit(vnode); const user = this.attrs.user; @@ -19,14 +30,15 @@ export default class EditUserModal extends Modal { this.username = Stream(user.username() || ''); this.email = Stream(user.email() || ''); this.isEmailConfirmed = Stream(user.isEmailConfirmed() || false); - this.setPassword = Stream(false); + this.setPassword = Stream(false as boolean); this.password = Stream(user.password() || ''); - this.groups = {}; + + const userGroups = user.groups() || []; app.store - .all('groups') - .filter((group) => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1) - .forEach((group) => (this.groups[group.id()] = Stream(user.groups().indexOf(group) !== -1))); + .all('groups') + .filter((group) => ![Group.GUEST_ID, Group.MEMBER_ID].includes(group.id()!)) + .forEach((group) => (this.groups[group.id()!] = Stream(userGroups.includes(group)))); } className() { @@ -49,7 +61,7 @@ export default class EditUserModal extends Modal { fields() { const items = new ItemList(); - if (app.session.user.canEditCredentials()) { + if (app.session.user?.canEditCredentials()) { items.add( 'username',
@@ -103,10 +115,11 @@ export default class EditUserModal extends Modal {