Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: convert page components to TypeScript #3538

Merged
merged 2 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions framework/core/js/src/admin/AdminApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export interface AdminApplicationData extends ApplicationData {
extensions: Record<string, Extension>;
settings: Record<string, string>;
modelStatistics: Record<string, { total: number }>;
displayNameDrivers: string[];
slugDrivers: Record<string, string[]>;
}

export default class AdminApplication extends Application {
Expand Down
8 changes: 5 additions & 3 deletions framework/core/js/src/admin/components/AdminPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export type HTMLInputTypes =

export interface CommonSettingsItemOptions extends Mithril.Attributes {
setting: string;
label: Mithril.Children;
label?: Mithril.Children;
help?: Mithril.Children;
className?: string;
}
Expand Down Expand Up @@ -137,6 +137,8 @@ export type AdminHeaderAttrs = AdminHeaderOptions & Partial<Omit<Mithril.Attribu
export type SettingValue = string;
export type MutableSettings = Record<string, Stream<SettingValue>>;

export type SaveSubmitEvent = SubmitEvent & { redraw: boolean };

export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends Page<CustomAttrs> {
settings: MutableSettings = {};
loading: boolean = false;
Expand All @@ -162,7 +164,7 @@ export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAt
*
* Calls `this.saveSettings` when the button is clicked.
*/
submitButton(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children {
submitButton(): Mithril.Children {
return (
<Button onclick={this.saveSettings.bind(this)} className="Button Button--primary" loading={this.loading} disabled={!this.isChanged()}>
{app.translator.trans('core.admin.settings.submit_button')}
Expand Down Expand Up @@ -385,7 +387,7 @@ export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAt
/**
* Saves the modified settings to the database.
*/
saveSettings(e: SubmitEvent & { redraw: boolean }) {
saveSettings(e: SaveSubmitEvent) {
e.preventDefault();

app.alerts.clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import EditCustomFooterModal from './EditCustomFooterModal';
import UploadImageButton from './UploadImageButton';
import AdminPage from './AdminPage';
import ItemList from '../../common/utils/ItemList';
import type Mithril from 'mithril';

export default class AppearancePage extends AdminPage {
headerInfo() {
Expand Down Expand Up @@ -77,7 +78,7 @@ export default class AppearancePage extends AdminPage {
}

colorItems() {
const items = new ItemList();
const items = new ItemList<Mithril.Children>();

items.add('helpText', <div className="helpText">{app.translator.trans('core.admin.appearance.colors_text')}</div>, 80);

Expand All @@ -88,11 +89,13 @@ export default class AppearancePage extends AdminPage {
type: 'color-preview',
setting: 'theme_primary_color',
placeholder: '#aaaaaa',
ariaLabel: app.translator.trans('core.admin.appearance.colors_primary_label'),
})}
{this.buildSettingComponent({
type: 'color-preview',
setting: 'theme_secondary_color',
placeholder: '#aaaaaa',
ariaLabel: app.translator.trans('core.admin.appearance.colors_secondary_label'),
})}
</div>,
70
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,27 @@ import app from '../../admin/app';
import FieldSet from '../../common/components/FieldSet';
import ItemList from '../../common/utils/ItemList';
import AdminPage from './AdminPage';
import type { IPageAttrs } from '../../common/components/Page';
import type Mithril from 'mithril';

export default class BasicsPage extends AdminPage {
oninit(vnode) {
export type HomePageItem = { path: string; label: Mithril.Children };

export default class BasicsPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends AdminPage<CustomAttrs> {
localeOptions: Record<string, string> = {};
displayNameOptions: Record<string, string> = {};
slugDriverOptions: Record<string, Record<string, string>> = {};

oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode);

this.localeOptions = {};
const locales = app.data.locales;
for (const i in locales) {
this.localeOptions[i] = `${locales[i]} (${i})`;
}
Object.keys(app.data.locales).forEach((i) => {
this.localeOptions[i] = `${app.data.locales[i]} (${i})`;
});

this.displayNameOptions = {};
const displayNameDrivers = app.data.displayNameDrivers;
displayNameDrivers.forEach(function (identifier) {
app.data.displayNameDrivers.forEach((identifier) => {
this.displayNameOptions[identifier] = identifier;
}, this);
});

this.slugDriverOptions = {};
Object.keys(app.data.slugDrivers).forEach((model) => {
this.slugDriverOptions[model] = {};

Expand Down Expand Up @@ -100,6 +103,7 @@ export default class BasicsPage extends AdminPage {

{Object.keys(this.slugDriverOptions).map((model) => {
const options = this.slugDriverOptions[model];

if (Object.keys(options).length > 1) {
return this.buildSettingComponent({
type: 'select',
Expand All @@ -109,6 +113,8 @@ export default class BasicsPage extends AdminPage {
help: app.translator.trans('core.admin.basics.slug_driver_text', { model }),
});
}

return null;
})}

{this.submitButton()}
Expand All @@ -119,11 +125,9 @@ export default class BasicsPage extends AdminPage {
/**
* Build a list of options for the default homepage. Each option must be an
* object with `path` and `label` properties.
*
* @return {ItemList<{ path: string, label: import('mithril').Children }>}
*/
homePageItems() {
const items = new ItemList();
const items = new ItemList<HomePageItem>();

items.add('allDiscussions', {
path: '/all',
Expand Down
2 changes: 1 addition & 1 deletion framework/core/js/src/admin/components/ExtensionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export default class ExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionP
{settings ? (
<div className="Form">
{settings.map(this.buildSettingComponent.bind(this))}
<div className="Form-group">{this.submitButton(vnode)}</div>
<div className="Form-group">{this.submitButton()}</div>
</div>
) : (
<h3 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.no_settings')}</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,30 @@ import Button from '../../common/components/Button';
import Alert from '../../common/components/Alert';
import LoadingIndicator from '../../common/components/LoadingIndicator';
import AdminPage from './AdminPage';
import type { IPageAttrs } from '../../common/components/Page';
import type { AlertIdentifier } from '../../common/states/AlertManagerState';
import type Mithril from 'mithril';
import type { SaveSubmitEvent } from './AdminPage';

export interface MailSettings {
data: {
attributes: {
fields: Record<string, any>;
sending: boolean;
errors: any[];
};
};
}

export default class MailPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends AdminPage<CustomAttrs> {
sendingTest = false;
status?: { sending: boolean; errors: any };
driverFields?: Record<string, any>;
testEmailSuccessAlert?: AlertIdentifier;

export default class MailPage extends AdminPage {
oninit(vnode) {
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode);

this.sendingTest = false;
this.refresh();
}

Expand All @@ -28,14 +46,14 @@ export default class MailPage extends AdminPage {
this.status = { sending: false, errors: {} };

app
.request({
.request<MailSettings>({
method: 'GET',
url: app.forum.attribute('apiUrl') + '/mail/settings',
})
.then((response) => {
this.driverFields = response['data']['attributes']['fields'];
this.status.sending = response['data']['attributes']['sending'];
this.status.errors = response['data']['attributes']['errors'];
this.driverFields = response.data.attributes.fields;
this.status!.sending = response.data.attributes.sending;
this.status!.errors = response.data.attributes.errors;

this.loading = false;
m.redraw();
Expand All @@ -47,7 +65,7 @@ export default class MailPage extends AdminPage {
return <LoadingIndicator />;
}

const fields = this.driverFields[this.setting('mail_driver')()];
const fields = this.driverFields![this.setting('mail_driver')()];
const fieldKeys = Object.keys(fields);

return (
Expand All @@ -60,10 +78,10 @@ export default class MailPage extends AdminPage {
{this.buildSettingComponent({
type: 'select',
setting: 'mail_driver',
options: Object.keys(this.driverFields).reduce((memo, val) => ({ ...memo, [val]: val }), {}),
options: Object.keys(this.driverFields!).reduce((memo, val) => ({ ...memo, [val]: val }), {}),
label: app.translator.trans('core.admin.email.driver_heading'),
})}
{this.status.sending ||
{this.status!.sending ||
Alert.component(
{
dismissible: false,
Expand All @@ -84,7 +102,7 @@ export default class MailPage extends AdminPage {
setting: field,
options: fieldInfo,
}),
this.status.errors[field] && <p className="ValidationError">{this.status.errors[field]}</p>,
this.status!.errors[field] && <p className="ValidationError">{this.status!.errors[field]}</p>,
];
})}
</div>
Expand All @@ -93,7 +111,7 @@ export default class MailPage extends AdminPage {
{this.submitButton()}

<FieldSet label={app.translator.trans('core.admin.email.send_test_mail_heading')} className="MailPage-MailSettings">
<div className="helpText">{app.translator.trans('core.admin.email.send_test_mail_text', { email: app.session.user.email() })}</div>
<div className="helpText">{app.translator.trans('core.admin.email.send_test_mail_text', { email: app.session.user!.email() })}</div>
{Button.component(
{
className: 'Button Button--primary',
Expand All @@ -108,10 +126,11 @@ export default class MailPage extends AdminPage {
}

sendTestEmail() {
if (this.saving || this.sendingTest) return;
if (this.sendingTest) return;

this.sendingTest = true;
app.alerts.dismiss(this.testEmailSuccessAlert);

if (this.testEmailSuccessAlert) app.alerts.dismiss(this.testEmailSuccessAlert);

app
.request({
Expand All @@ -129,7 +148,7 @@ export default class MailPage extends AdminPage {
});
}

saveSettings(e) {
super.saveSettings(e).then(this.refresh());
saveSettings(e: SaveSubmitEvent) {
return super.saveSettings(e).then(() => this.refresh());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export default class PermissionsPage extends AdminPage {
return [
<div className="PermissionsPage-groups">
{app.store
.all('groups')
.filter((group) => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
.all<Group>('groups')
.filter((group) => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()!) === -1)
.map((group) => (
<button className="Button Group" onclick={() => app.modal.show(EditGroupModal, { group })}>
{GroupBadge.component({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import app from '../../forum/app';
import Page from '../../common/components/Page';
import Page, { IPageAttrs } from '../../common/components/Page';
import NotificationList from './NotificationList';
import type Mithril from 'mithril';
import extractText from '../../common/utils/extractText';

/**
* The `NotificationsPage` component shows the notifications list. It is only
* used on mobile devices where the notifications dropdown is within the drawer.
*/
export default class NotificationsPage extends Page {
oninit(vnode) {
export default class NotificationsPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends Page<CustomAttrs> {
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode);

app.history.push('notifications');
app.history.push('notifications', extractText(app.translator.trans('core.forum.notifications.title')));

app.notifications.load();

Expand Down
Loading