refactor: convert core modals to TypeScript (#3515)

* refactor: convert core modals to TypeScript
* chore: excplicitly specify return type instead
* chore: `yarn format`

Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
This commit is contained in:
Sami Mazouz 2022-07-13 21:42:45 +01:00 committed by GitHub
parent d86440506d
commit 1af506d4b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 81 additions and 55 deletions

View File

@ -134,8 +134,11 @@ export type SettingsComponentOptions =
*/ */
export type AdminHeaderAttrs = AdminHeaderOptions & Partial<Omit<Mithril.Attributes, 'class'>>; export type AdminHeaderAttrs = AdminHeaderOptions & Partial<Omit<Mithril.Attributes, 'class'>>;
export type SettingValue = string;
export type MutableSettings = Record<string, Stream<SettingValue>>;
export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends Page<CustomAttrs> { export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends Page<CustomAttrs> {
settings: Record<string, Stream<string>> = {}; settings: MutableSettings = {};
loading: boolean = false; loading: boolean = false;
view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children { view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children {

View File

@ -1,18 +1,31 @@
import app from '../../admin/app'; import app from '../../admin/app';
import Modal from '../../common/components/Modal'; import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import Button from '../../common/components/Button'; import Button from '../../common/components/Button';
import Badge from '../../common/components/Badge'; import Badge from '../../common/components/Badge';
import Group from '../../common/models/Group'; import Group from '../../common/models/Group';
import ItemList from '../../common/utils/ItemList'; import ItemList from '../../common/utils/ItemList';
import Switch from '../../common/components/Switch'; import Switch from '../../common/components/Switch';
import Stream from '../../common/utils/Stream'; import Stream from '../../common/utils/Stream';
import Mithril from 'mithril';
import extractText from '../../common/utils/extractText';
export interface IEditGroupModalAttrs extends IInternalModalAttrs {
group?: Group;
}
/** /**
* The `EditGroupModal` component shows a modal dialog which allows the user * The `EditGroupModal` component shows a modal dialog which allows the user
* to create or edit a group. * to create or edit a group.
*/ */
export default class EditGroupModal extends Modal { export default class EditGroupModal<CustomAttrs extends IEditGroupModalAttrs = IEditGroupModalAttrs> extends Modal<CustomAttrs> {
oninit(vnode) { group!: Group;
nameSingular!: Stream<string>;
namePlural!: Stream<string>;
icon!: Stream<string>;
color!: Stream<string>;
isHidden!: Stream<boolean>;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode); super.oninit(vnode);
this.group = this.attrs.group || app.store.createRecord('groups'); this.group = this.attrs.group || app.store.createRecord('groups');
@ -134,7 +147,7 @@ export default class EditGroupModal extends Modal {
}; };
} }
onsubmit(e) { onsubmit(e: SubmitEvent) {
e.preventDefault(); e.preventDefault();
this.loading = true; this.loading = true;
@ -149,7 +162,7 @@ export default class EditGroupModal extends Modal {
} }
deleteGroup() { deleteGroup() {
if (confirm(app.translator.trans('core.admin.edit_group.delete_confirmation'))) { if (confirm(extractText(app.translator.trans('core.admin.edit_group.delete_confirmation')))) {
this.group.delete().then(() => m.redraw()); this.group.delete().then(() => m.redraw());
this.hide(); this.hide();
} }

View File

@ -1,18 +1,18 @@
import app from '../../admin/app'; import app from '../../admin/app';
import Modal from '../../common/components/Modal'; import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import Button from '../../common/components/Button'; import Button from '../../common/components/Button';
import Stream from '../../common/utils/Stream'; import Stream from '../../common/utils/Stream';
import saveSettings from '../utils/saveSettings'; import saveSettings from '../utils/saveSettings';
import Mithril from 'mithril';
import { MutableSettings, SettingValue } from './AdminPage';
export default class SettingsModal extends Modal { export interface ISettingsModalAttrs extends IInternalModalAttrs {}
oninit(vnode) {
super.oninit(vnode);
this.settings = {}; export default abstract class SettingsModal<CustomAttrs extends ISettingsModalAttrs = ISettingsModalAttrs> extends Modal<CustomAttrs> {
this.loading = false; settings: MutableSettings = {};
} loading: boolean = false;
form() { form(): Mithril.Children {
return ''; return '';
} }
@ -28,7 +28,7 @@ export default class SettingsModal extends Modal {
); );
} }
submitButton() { submitButton(): Mithril.Children {
return ( return (
<Button type="submit" className="Button Button--primary" loading={this.loading} disabled={!this.changed()}> <Button type="submit" className="Button Button--primary" loading={this.loading} disabled={!this.changed()}>
{app.translator.trans('core.admin.settings.submit_button')} {app.translator.trans('core.admin.settings.submit_button')}
@ -36,14 +36,14 @@ export default class SettingsModal extends Modal {
); );
} }
setting(key, fallback = '') { setting(key: string, fallback: string = ''): Stream<SettingValue> {
this.settings[key] = this.settings[key] || Stream(app.data.settings[key] || fallback); this.settings[key] = this.settings[key] || Stream(app.data.settings[key] || fallback);
return this.settings[key]; return this.settings[key];
} }
dirty() { dirty() {
const dirty = {}; const dirty: Record<string, SettingValue> = {};
Object.keys(this.settings).forEach((key) => { Object.keys(this.settings).forEach((key) => {
const value = this.settings[key](); const value = this.settings[key]();
@ -60,7 +60,7 @@ export default class SettingsModal extends Modal {
return Object.keys(this.dirty()).length; return Object.keys(this.dirty()).length;
} }
onsubmit(e) { onsubmit(e: SubmitEvent) {
e.preventDefault(); e.preventDefault();
this.loading = true; this.loading = true;

View File

@ -1,35 +1,34 @@
import app from '../../forum/app'; import app from '../../forum/app';
import Modal from '../../common/components/Modal'; import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import Button from '../../common/components/Button'; import Button from '../../common/components/Button';
import Stream from '../../common/utils/Stream'; import Stream from '../../common/utils/Stream';
import Mithril from 'mithril';
import RequestError from '../../common/utils/RequestError';
/** /**
* The `ChangeEmailModal` component shows a modal dialog which allows the user * The `ChangeEmailModal` component shows a modal dialog which allows the user
* to change their email address. * to change their email address.
*/ */
export default class ChangeEmailModal extends Modal { export default class ChangeEmailModal<CustomAttrs extends IInternalModalAttrs = IInternalModalAttrs> extends Modal<CustomAttrs> {
oninit(vnode) { /**
* The value of the email input.
*/
email!: Stream<string>;
/**
* The value of the password input.
*/
password!: Stream<string>;
/**
* Whether or not the email has been changed successfully.
*/
success: boolean = false;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode); super.oninit(vnode);
/** this.email = Stream(app.session.user!.email() || '');
* Whether or not the email has been changed successfully.
*
* @type {Boolean}
*/
this.success = false;
/**
* The value of the email input.
*
* @type {function}
*/
this.email = Stream(app.session.user.email());
/**
* The value of the password input.
*
* @type {function}
*/
this.password = Stream(''); this.password = Stream('');
} }
@ -67,7 +66,7 @@ export default class ChangeEmailModal extends Modal {
type="email" type="email"
name="email" name="email"
className="FormControl" className="FormControl"
placeholder={app.session.user.email()} placeholder={app.session.user!.email()}
bidi={this.email} bidi={this.email}
disabled={this.loading} disabled={this.loading}
/> />
@ -98,12 +97,12 @@ export default class ChangeEmailModal extends Modal {
); );
} }
onsubmit(e) { onsubmit(e: SubmitEvent) {
e.preventDefault(); e.preventDefault();
// If the user hasn't actually entered a different email address, we don't // If the user hasn't actually entered a different email address, we don't
// need to do anything. Woot! // need to do anything. Woot!
if (this.email() === app.session.user.email()) { if (this.email() === app.session.user!.email()) {
this.hide(); this.hide();
return; return;
} }
@ -111,8 +110,8 @@ export default class ChangeEmailModal extends Modal {
this.loading = true; this.loading = true;
this.alertAttrs = null; this.alertAttrs = null;
app.session.user app.session
.save( .user!.save(
{ email: this.email() }, { email: this.email() },
{ {
errorHandler: this.onerror.bind(this), errorHandler: this.onerror.bind(this),
@ -126,8 +125,8 @@ export default class ChangeEmailModal extends Modal {
.then(this.loaded.bind(this)); .then(this.loaded.bind(this));
} }
onerror(error) { onerror(error: RequestError) {
if (error.status === 401) { if (error.status === 401 && error.alert) {
error.alert.content = app.translator.trans('core.forum.change_email.incorrect_password_message'); error.alert.content = app.translator.trans('core.forum.change_email.incorrect_password_message');
} }

View File

@ -1,12 +1,12 @@
import app from '../../forum/app'; import app from '../../forum/app';
import Modal from '../../common/components/Modal'; import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import Button from '../../common/components/Button'; import Button from '../../common/components/Button';
/** /**
* The `ChangePasswordModal` component shows a modal dialog which allows the * The `ChangePasswordModal` component shows a modal dialog which allows the
* user to send themself a password reset email. * user to send themself a password reset email.
*/ */
export default class ChangePasswordModal extends Modal { export default class ChangePasswordModal<CustomAttrs extends IInternalModalAttrs = IInternalModalAttrs> extends Modal<CustomAttrs> {
className() { className() {
return 'ChangePasswordModal Modal--small'; return 'ChangePasswordModal Modal--small';
} }
@ -35,7 +35,7 @@ export default class ChangePasswordModal extends Modal {
); );
} }
onsubmit(e) { onsubmit(e: SubmitEvent) {
e.preventDefault(); e.preventDefault();
this.loading = true; this.loading = true;
@ -44,7 +44,7 @@ export default class ChangePasswordModal extends Modal {
.request({ .request({
method: 'POST', method: 'POST',
url: app.forum.attribute('apiUrl') + '/forgot', url: app.forum.attribute('apiUrl') + '/forgot',
body: { email: app.session.user.email() }, body: { email: app.session.user!.email() },
}) })
.then(this.hide.bind(this), this.loaded.bind(this)); .then(this.hide.bind(this), this.loaded.bind(this));
} }

View File

@ -1,13 +1,24 @@
import app from '../../forum/app'; import app from '../../forum/app';
import Modal from '../../common/components/Modal'; import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import Button from '../../common/components/Button'; import Button from '../../common/components/Button';
import Stream from '../../common/utils/Stream'; import Stream from '../../common/utils/Stream';
import Mithril from 'mithril';
import Discussion from '../../common/models/Discussion';
export interface IRenameDiscussionModalAttrs extends IInternalModalAttrs {
discussion: Discussion;
currentTitle: string;
}
/** /**
* The 'RenameDiscussionModal' displays a modal dialog with an input to rename a discussion * The 'RenameDiscussionModal' displays a modal dialog with an input to rename a discussion
*/ */
export default class RenameDiscussionModal extends Modal { export default class RenameDiscussionModal<CustomAttrs extends IRenameDiscussionModalAttrs = IRenameDiscussionModalAttrs> extends Modal<CustomAttrs> {
oninit(vnode) { discussion!: Discussion;
currentTitle!: string;
newTitle!: Stream<string>;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode); super.oninit(vnode);
this.discussion = this.attrs.discussion; this.discussion = this.attrs.discussion;
@ -45,7 +56,7 @@ export default class RenameDiscussionModal extends Modal {
); );
} }
onsubmit(e) { onsubmit(e: SubmitEvent): Promise<void> | void {
e.preventDefault(); e.preventDefault();
this.loading = true; this.loading = true;