diff --git a/resources/ts/Components/Component.ts b/resources/ts/Components/Component.ts deleted file mode 100644 index 674def771..000000000 --- a/resources/ts/Components/Component.ts +++ /dev/null @@ -1,174 +0,0 @@ -import classnames, {Argument as ClassNames} from 'classnames'; -import collect, {Collection} from 'collect.js'; -import Mithril from 'mithril'; -import type { - Children, - ClassComponent, - Vnode, - VnodeDOM -} from 'mithril'; - -export interface Attributes { -} - -interface AttributesCollection extends Collection { - addClassNames(...classNames: ClassNames[]): void; -} - -// noinspection SpellCheckingInspection,JSUnusedGlobalSymbols,JSUnusedLocalSymbols - -/** - * @abstract - * - * The `Component` class defines a user interface 'building block'. A component - * generates a virtual DOM to be rendered on each redraw. - * - * Essentially, this is a wrapper for Mithril's components that adds several useful features: - * - * — In the `oninit` and `onbeforeupdate` lifecycle hooks, we store vnode attrs in `this.attrs. - * This allows us to use attrs across components without having to pass the vnode to every single - * method. - * — The static `initAttrs` method allows a convenient way to provide defaults (or to otherwise - * modify) the attrs that have been passed into a component. - * — When the component is created in the DOM, we store its DOM element under `this.element`; - * this lets us use Cash to modify child DOM state from internal methods via the `this.$()` - * method. - * — A convenience `component` method, which serves as an alternative to hyperscript and JSX. - * - * As with other Mithril components, components extending Component can be initialized - * and nested using JSX. The `component` method can also - * be used. - * - * @example - * return m('div',

Hello World

); - * - * @see https://mithril.js.org/components.html - */ - -export abstract class Component< - A extends Attributes = Attributes, - S = undefined -> implements ClassComponent { - /** - * The root DOM element for the component. - */ - element!: Element; - - /** - * The attributes passed into the component. - * - * @see https://mithril.js.org/components.html#passing-data-to-components - * @see initAttrs - */ - attrs!: AttributesCollection; - - /** - * Class component state that is persisted between redraws. - * - * Updating this will **not** automatically trigger a redraw, unlike - * other frameworks. - * - * This is different to Vnode state, which is always an instance of your - * class component. - * - * This is `undefined` by default. - */ - state!: S; - - /** - * Used for attribute code completion in JSX. - * @private - */ - private __attrs!: A; - - /** - * @inheritdoc - */ - abstract view(vnode: Vnode): Children; - - /** - * @inheritdoc - */ - oninit(vnode: Vnode) { - this.setAttrs(vnode.attrs); - } - - /** - * @inheritdoc - */ - oncreate(vnode: VnodeDOM) { - this.element = vnode.dom; - } - - /** - * @inheritdoc - */ - onbeforeupdate(vnode: VnodeDOM) { - this.setAttrs(vnode.attrs); - } - - /** - * @inheritdoc - */ - onupdate(vnode: VnodeDOM) { - } - - /** - * @inheritdoc - */ - onbeforeremove(vnode: VnodeDOM) { - } - - /** - * @inheritdoc - */ - onremove(vnode: VnodeDOM) { - } - - /** - * Saves a reference to the vnode attrs after running them through initAttrs, - * and checking for common issues. - * - * @private - */ - setAttrs(attributes: A): void { - this.initAttrs(attributes); - - if (attributes) { - if ('children' in attributes) { - // noinspection JSUnresolvedVariable - throw new Error( - `[${this.constructor.name}] The "children" attribute of attrs should never be used. Either pass children in as - the vnode children or rename the attribute` - ); - } - - if ('tag' in attributes) { - // noinspection JSUnresolvedVariable - throw new Error( - `[${this.constructor.name}] You cannot use the "tag" attribute name with Mithril 2.` - ); - } - } - - const attributesCollection = collect(attributes); - attributesCollection.macro('addClassNames', (...classNames: ClassNames[]) => { - attributesCollection.put( - 'className', - classnames(attributesCollection.get('className') as ClassNames, ...classNames) - ); - }); - this.attrs = attributesCollection as AttributesCollection; - } - - // noinspection JSUnusedLocalSymbols - /** - * Initialize the component's attrs. - * - * This can be used to assign default values for missing, optional attrs. - * - * @protected - */ - initAttrs(attributes: A): void { - } -} diff --git a/resources/ts/Components/DataTable/DataTable.tsx b/resources/ts/Components/DataTable/DataTable.tsx index 80001c6b6..4169ead0c 100644 --- a/resources/ts/Components/DataTable/DataTable.tsx +++ b/resources/ts/Components/DataTable/DataTable.tsx @@ -14,7 +14,7 @@ import { import { Attributes, Component -} from '~/Components/Component'; +} from 'mithril-utilities'; import MdIcon from '~/Components/MdIcon'; export interface DataTableAttributes extends Attributes { diff --git a/resources/ts/Components/DataTable/DataTableColumn.tsx b/resources/ts/Components/DataTable/DataTableColumn.tsx index b91378daf..4a34bf417 100644 --- a/resources/ts/Components/DataTable/DataTableColumn.tsx +++ b/resources/ts/Components/DataTable/DataTableColumn.tsx @@ -10,7 +10,7 @@ import {Vnode} from 'mithril'; import { Attributes, Component -} from '~/Components/Component'; +} from 'mithril-utilities'; import MdIcon from '~/Components/MdIcon'; export interface DataTableColumnAttributes extends Attributes, Partial { diff --git a/resources/ts/Components/Dialogs/AddEditRecordDialog.tsx b/resources/ts/Components/Dialogs/AddEditRecordDialog.tsx index 7182f6a97..bddd1e9c4 100644 --- a/resources/ts/Components/Dialogs/AddEditRecordDialog.tsx +++ b/resources/ts/Components/Dialogs/AddEditRecordDialog.tsx @@ -5,12 +5,12 @@ import { Vnode, VnodeDOM } from 'mithril'; + +import {Form} from 'mithril-utilities'; import Stream from 'mithril/stream'; import {Class} from 'type-fest'; - -import Form from '~/Components/Form'; -import MdIcon from '~/Components/MdIcon'; import RecordDialog, {RecordDialogAttributes} from '~/Components/Dialogs/RecordDialog'; +import MdIcon from '~/Components/MdIcon'; import Model from '~/Models/Model'; import { VnodeCollection, diff --git a/resources/ts/Components/Dialogs/DeleteRecordDialog.tsx b/resources/ts/Components/Dialogs/DeleteRecordDialog.tsx index 702c827c8..05ced4d5a 100644 --- a/resources/ts/Components/Dialogs/DeleteRecordDialog.tsx +++ b/resources/ts/Components/Dialogs/DeleteRecordDialog.tsx @@ -2,10 +2,10 @@ import { Children, Vnode } from 'mithril'; +import {RequestError} from 'mithril-utilities'; import Model from '~/Models/Model'; import {showSnackbar} from '~/utils/misc'; -import {RequestError} from '~/utils/Request'; import RecordDialog, {RecordDialogAttributes} from './RecordDialog'; diff --git a/resources/ts/Components/Dialogs/Dialog.tsx b/resources/ts/Components/Dialogs/Dialog.tsx index 2d08724b7..582b7d829 100644 --- a/resources/ts/Components/Dialogs/Dialog.tsx +++ b/resources/ts/Components/Dialogs/Dialog.tsx @@ -6,12 +6,12 @@ import { Vnode, VnodeDOM } from 'mithril'; -import Stream from 'mithril/stream'; import { Attributes, Component -} from '../Component'; +} from 'mithril-utilities'; +import Stream from 'mithril/stream'; export interface DialogAttributes extends Attributes, Partial> { - onsubmit?: (event: FormSubmitEvent) => void, - state?: Record> | Map> -} - -export default class Form extends Component> | Map>> { - element!: HTMLFormElement; - onsubmitFunction?: A['onsubmit']; - // TODO: Change all states to Map? - state!: NonNullable; - - oninit(vnode: Vnode) { - super.oninit(vnode); - this.onsubmitFunction = vnode.attrs.onsubmit; - this.state = vnode.attrs.state ?? {}; - delete vnode.attrs.state; - } - - view(vnode: Vnode) { - const attributes = this.attrs.except(['onsubmit', 'state']); - return ( -
- {(vnode.children as ChildArray).map(this.attachStreamToInput.bind(this))} -
- ); - } - - attachStreamToInput(child: Children) { - // Check if child is a Vnode - if (isVnode<{name?: string, id: string, value: unknown, oninput?: (event: Event) => void, state?: Stream}>(child)) { - const stream = child.attrs.state ?? this.getState(child.attrs.name ?? child.attrs.id); - if (stream) { - child.attrs.value = stream(); - - const originalOninput = child.attrs.oninput; - // This ESLint rule is disabled because it doesn't recognize that the `oninput` attribute is being set and Mithril uses it instead of adding an event listener - // eslint-disable-next-line unicorn/prefer-add-event-listener - child.attrs.oninput = (event: Event) => { - stream((event.target as HTMLInputElement).value); - if (originalOninput) { - originalOninput(event); - } - }; - - delete child.attrs.state; - } - - // Check if `child` has children and recursively call this function on them. - if (Array.isArray(child.children)) { - child.children = child.children.map(this.attachStreamToInput.bind(this)); - } - } - - return child; - } - - oncreate(vnode: VnodeDOM) { - super.oncreate(vnode); - const submitter = this.element.querySelector('[type="submit"]'); - if (submitter) { - submitter.addEventListener('click', () => { - // TODO: Add submitter when https://github.com/material-components/material-web/issues/3941 is completed - this.element.requestSubmit(); - }); - } - } - - onsubmit(e: FormSubmitEvent) { - e.preventDefault(); - e.data = new FormData(e.target as HTMLFormElement); - if (this.onsubmitFunction) { - this.onsubmitFunction(e); - } - } - - private getState(key: string) { - if (this.state instanceof Map) { - return this.state.get(key); - } - return this.state[key]; - } -} diff --git a/resources/ts/Components/MdIcon.tsx b/resources/ts/Components/MdIcon.tsx index 896a3ac92..b013a7db9 100644 --- a/resources/ts/Components/MdIcon.tsx +++ b/resources/ts/Components/MdIcon.tsx @@ -2,7 +2,7 @@ import '@material/web/icon/icon.js'; import type MaterialIcons from '@mdi/js'; -import {Component} from './Component'; +import {Component} from 'mithril-utilities'; export interface Attributes extends Partial { icon: typeof MaterialIcons | string; diff --git a/resources/ts/Components/Page.tsx b/resources/ts/Components/Page.tsx index 997b14817..6a21348f4 100644 --- a/resources/ts/Components/Page.tsx +++ b/resources/ts/Components/Page.tsx @@ -6,14 +6,14 @@ import { Vnode, VnodeDOM } from 'mithril'; +import { + Attributes, + Component +} from 'mithril-utilities'; import {Footer} from '~/Components/layout/Footer'; import logoUrl from '../../images/logo_completo.png'; -import { - Attributes, - Component -} from './Component'; import TopAppBar from './layout/TopAppBar'; export interface PageAttributes
& {external?: boolean} = Record> extends Attributes, Required> { diff --git a/resources/ts/Components/layout/Drawer.tsx b/resources/ts/Components/layout/Drawer.tsx index 8009ef15c..b9f266670 100644 --- a/resources/ts/Components/layout/Drawer.tsx +++ b/resources/ts/Components/layout/Drawer.tsx @@ -10,12 +10,12 @@ import { Children, Vnode } from 'mithril'; -import Stream from 'mithril/stream'; import { Attributes, Component -} from '~/Components/Component'; +} from 'mithril-utilities'; +import Stream from 'mithril/stream'; import MdIcon from '~/Components/MdIcon'; import {VnodeCollectionItem} from '~/typings/jsx'; import {isMobile} from '~/utils/misc'; diff --git a/resources/ts/Components/layout/DrawerEntry.tsx b/resources/ts/Components/layout/DrawerEntry.tsx index 9fb6d22ef..fe0f9fd54 100644 --- a/resources/ts/Components/layout/DrawerEntry.tsx +++ b/resources/ts/Components/layout/DrawerEntry.tsx @@ -4,12 +4,12 @@ import {ListItemLink} from '@material/web/list/lib/listitemlink/list-item-link'; import '@material/web/list/list-item-link.js'; import type * as MaterialIcons from '@mdi/js'; import {Vnode} from 'mithril'; -import {ValueOf} from 'type-fest'; import { Attributes, Component -} from '~/Components/Component'; +} from 'mithril-utilities'; +import {ValueOf} from 'type-fest'; import MdIcon from '~/Components/MdIcon'; type Icons = ValueOf; diff --git a/resources/ts/Components/layout/Footer.tsx b/resources/ts/Components/layout/Footer.tsx index 14717b016..5ea7c9d56 100644 --- a/resources/ts/Components/layout/Footer.tsx +++ b/resources/ts/Components/layout/Footer.tsx @@ -1,4 +1,4 @@ -import {Component} from '../Component'; +import {Component} from 'mithril-utilities'; export class Footer extends Component { view() { diff --git a/resources/ts/Components/layout/TopAppBar.tsx b/resources/ts/Components/layout/TopAppBar.tsx index 2a3a9d76f..10e145277 100644 --- a/resources/ts/Components/layout/TopAppBar.tsx +++ b/resources/ts/Components/layout/TopAppBar.tsx @@ -1,8 +1,4 @@ import '@material/web/iconbutton/standard-icon-button.js'; -import '~/WebComponents/TopAppBar'; - -import {IconButton} from '@material/web/iconbutton/lib/icon-button'; -import {Menu} from '@material/web/menu/lib/menu'; import { mdiMenu, mdiMenuOpen @@ -12,13 +8,13 @@ import { Vnode, VnodeDOM } from 'mithril'; -import Stream from 'mithril/stream'; - -import logo from '~/../images/logo.png'; import { Attributes, Component -} from '~/Components/Component'; +} from 'mithril-utilities'; +import Stream from 'mithril/stream'; + +import logo from '~/../images/logo.png'; import Drawer from '~/Components/layout/Drawer'; import NotificationsAction from '~/Components/layout/topappbar_actions/NotificationsAction'; import PeriodSwitcherAction from '~/Components/layout/topappbar_actions/PeriodSwitcherAction'; @@ -30,6 +26,7 @@ import { isMobile, mobileMediaQuery } from '~/utils/misc'; +import '~/WebComponents/TopAppBar'; export default class TopAppBar extends Component { drawerOpenState = Stream(!isMobile()); diff --git a/resources/ts/Components/layout/topappbar_actions/TopAppBarAction.tsx b/resources/ts/Components/layout/topappbar_actions/TopAppBarAction.tsx index 3f06e9c6b..f114c7b87 100644 --- a/resources/ts/Components/layout/topappbar_actions/TopAppBarAction.tsx +++ b/resources/ts/Components/layout/topappbar_actions/TopAppBarAction.tsx @@ -3,7 +3,7 @@ import { Vnode } from 'mithril'; -import {Component} from '~/Components/Component'; +import {Component} from 'mithril-utilities'; import MdIcon, {Attributes as MdIconAttributes} from '~/Components/MdIcon'; export default abstract class TopAppBarAction extends Component { diff --git a/resources/ts/Components/layout/topappbar_actions/UserInfoAction.tsx b/resources/ts/Components/layout/topappbar_actions/UserInfoAction.tsx index ee62776bd..8bbe64dc8 100644 --- a/resources/ts/Components/layout/topappbar_actions/UserInfoAction.tsx +++ b/resources/ts/Components/layout/topappbar_actions/UserInfoAction.tsx @@ -1,18 +1,17 @@ +import {router} from '@maicol07/inertia-mithril'; import '@material/web/button/outlined-button.js'; import '@material/web/button/text-button.js'; - -import {router} from '@maicol07/inertia-mithril'; import { mdiAccountCircleOutline, mdiAccountOutline, mdiLogoutVariant } from '@mdi/js'; import {Vnode} from 'mithril'; +import {Request} from 'mithril-utilities'; import Stream from 'mithril/stream'; import Dialog from '~/Components/Dialogs/Dialog'; import MdIcon from '~/Components/MdIcon'; -import Request from '~/utils/Request'; import TopAppBarAction from './TopAppBarAction'; diff --git a/resources/ts/Models/Http/RequestHttpClient.ts b/resources/ts/Models/Http/RequestHttpClient.ts index 69a97c274..5f4564514 100644 --- a/resources/ts/Models/Http/RequestHttpClient.ts +++ b/resources/ts/Models/Http/RequestHttpClient.ts @@ -1,9 +1,10 @@ -import RequestHttpClientPromise from './RequestHttpClientPromise'; -import Request from '~/utils/Request'; import type { HttpClient, HttpClientPromise } from 'coloquent'; +import {Request} from 'mithril-utilities'; + +import RequestHttpClientPromise from './RequestHttpClientPromise'; /** * @class RequestHttpClient diff --git a/resources/ts/Views/LoginPage.tsx b/resources/ts/Views/LoginPage.tsx index 478bc1555..656475e71 100644 --- a/resources/ts/Views/LoginPage.tsx +++ b/resources/ts/Views/LoginPage.tsx @@ -3,7 +3,6 @@ import '@material/web/button/filled-button.js'; import '@material/web/button/text-button.js'; import '@material/web/checkbox/checkbox.js'; import '@material/web/dialog/dialog.js'; -import '~/Components/m3/FilledTextField'; import {Dialog} from '@material/web/dialog/lib/dialog'; import { @@ -18,18 +17,19 @@ import type { Vnode, VnodeDOM } from 'mithril'; -import Stream from 'mithril/stream'; -import Form, {FormSubmitEvent} from '~/Components/Form'; +import { + Form, + FormSubmitEvent, + Request, + RequestError +} from 'mithril-utilities'; +import Stream from 'mithril/stream'; +import '~/Components/m3/FilledTextField'; import MdIcon from '~/Components/MdIcon'; -import Page, { - PageAttributes -} from '~/Components/Page'; +import Page, {PageAttributes} from '~/Components/Page'; import {VnodeCollectionItem} from '~/typings/jsx'; import {showSnackbar} from '~/utils/misc'; -import Request, { - RequestError -} from '~/utils/Request'; export default class LoginPage extends Page { form = { diff --git a/resources/ts/Views/ResetPasswordPage.tsx b/resources/ts/Views/ResetPasswordPage.tsx index 22b7fd00d..54b8db6be 100644 --- a/resources/ts/Views/ResetPasswordPage.tsx +++ b/resources/ts/Views/ResetPasswordPage.tsx @@ -1,30 +1,25 @@ +import {router} from '@maicol07/inertia-mithril'; import '@maicol07/material-web-additions/card/elevated-card.js'; import '@material/web/button/filled-button.js'; -import '~/Components/m3/FilledTextField'; - -import {router} from '@maicol07/inertia-mithril'; import { mdiAccountOutline, mdiLockCheckOutline, mdiLockOutline } from '@mdi/js'; -import logoUrl from '~/../images/logo_completo.png'; import collect from 'collect.js'; -import type { - Vnode, - VnodeDOM -} from 'mithril'; +import type {Vnode} from 'mithril'; +import { + Form, + FormSubmitEvent, + Request, + RequestError +} from 'mithril-utilities'; import Stream from 'mithril/stream'; -import Form, {FormSubmitEvent} from '~/Components/Form'; +import '~/Components/m3/FilledTextField'; import MdIcon from '~/Components/MdIcon'; -import Page, { - PageAttributes -} from '~/Components/Page'; +import Page, {PageAttributes} from '~/Components/Page'; import {VnodeCollectionItem} from '~/typings/jsx'; import {showSnackbar} from '~/utils/misc'; -import Request, { - RequestError -} from '~/utils/Request'; export default class ResetPasswordPage extends Page { form = { diff --git a/resources/ts/Views/Setup/SetupPage.tsx b/resources/ts/Views/Setup/SetupPage.tsx index 51552b8f8..68b8d476a 100644 --- a/resources/ts/Views/Setup/SetupPage.tsx +++ b/resources/ts/Views/Setup/SetupPage.tsx @@ -1,18 +1,14 @@ -import '@maicol07/material-web-additions/card/elevated-card.js'; - import {router} from '@maicol07/inertia-mithril'; -import type { - Vnode -} from 'mithril'; +import '@maicol07/material-web-additions/card/elevated-card.js'; +import type {Vnode} from 'mithril'; +import { + Request, + RequestError +} from 'mithril-utilities'; import Stream from 'mithril/stream'; -import Page, { - PageAttributes -} from '~/Components/Page'; +import Page, {PageAttributes} from '~/Components/Page'; import {showSnackbar} from '~/utils/misc'; -import Request, { - RequestError -} from '~/utils/Request'; import AdminUserStep from '~/Views/Setup/Steps/AdminUserStep'; import DatabaseStep from '~/Views/Setup/Steps/DatabaseStep'; import RegionalSettings from '~/Views/Setup/Steps/RegionalSettings'; diff --git a/resources/ts/Views/Setup/Steps/AdminUserStep.tsx b/resources/ts/Views/Setup/Steps/AdminUserStep.tsx index 72fabf272..c65435298 100644 --- a/resources/ts/Views/Setup/Steps/AdminUserStep.tsx +++ b/resources/ts/Views/Setup/Steps/AdminUserStep.tsx @@ -9,9 +9,12 @@ import { } from '@mdi/js'; import collect from 'collect.js'; import {Vnode} from 'mithril'; -import Stream from 'mithril/stream'; -import Form, {FormSubmitEvent} from '~/Components/Form'; +import { + Form, + FormSubmitEvent +} from 'mithril-utilities'; +import Stream from 'mithril/stream'; import MdIcon from '~/Components/MdIcon'; import {VnodeCollectionItem} from '~/typings/jsx'; import { diff --git a/resources/ts/Views/Setup/Steps/DatabaseStep.tsx b/resources/ts/Views/Setup/Steps/DatabaseStep.tsx index 0fe7bde54..6e17b6636 100644 --- a/resources/ts/Views/Setup/Steps/DatabaseStep.tsx +++ b/resources/ts/Views/Setup/Steps/DatabaseStep.tsx @@ -1,5 +1,4 @@ import '@maicol07/material-web-additions/layout-grid/layout-grid.js'; -import '~/Components/m3/FilledTextField'; import { mdiAccountOutline, @@ -14,15 +13,14 @@ import { Children, Vnode } from 'mithril'; -import Stream from 'mithril/stream'; -import Form from '~/Components/Form'; +import Form from 'mithril-utilities'; +import Request, {RequestError} from 'mithril-utilities'; +import Stream from 'mithril/stream'; +import '~/Components/m3/FilledTextField'; import MdIcon from '~/Components/MdIcon'; import {VnodeCollectionItem} from '~/typings/jsx'; import {showSnackbar} from '~/utils/misc'; -import Request, { - RequestError -} from '~/utils/Request'; import { SetupStep, diff --git a/resources/ts/Views/Setup/Steps/RegionalSettings.tsx b/resources/ts/Views/Setup/Steps/RegionalSettings.tsx index d837b88ed..dd506c131 100644 --- a/resources/ts/Views/Setup/Steps/RegionalSettings.tsx +++ b/resources/ts/Views/Setup/Steps/RegionalSettings.tsx @@ -5,9 +5,9 @@ import { } from '@mdi/js'; import collect from 'collect.js'; import dayjs from 'dayjs'; -import Stream from 'mithril/stream'; -import Form from '~/Components/Form'; +import {Form} from 'mithril-utilities'; +import Stream from 'mithril/stream'; import MdIcon from '~/Components/MdIcon'; import {VnodeCollectionItem} from '~/typings/jsx'; diff --git a/resources/ts/Views/Setup/Steps/SetupStep.tsx b/resources/ts/Views/Setup/Steps/SetupStep.tsx index 3b1f1c7e9..f55870700 100644 --- a/resources/ts/Views/Setup/Steps/SetupStep.tsx +++ b/resources/ts/Views/Setup/Steps/SetupStep.tsx @@ -12,7 +12,7 @@ import { import { Attributes, Component -} from '~/Components/Component'; +} from 'mithril-utilities'; import MdIcon from '~/Components/MdIcon'; diff --git a/resources/ts/Views/Setup/Steps/WelcomeStep.tsx b/resources/ts/Views/Setup/Steps/WelcomeStep.tsx index c02713544..2a1f4d0df 100644 --- a/resources/ts/Views/Setup/Steps/WelcomeStep.tsx +++ b/resources/ts/Views/Setup/Steps/WelcomeStep.tsx @@ -1,7 +1,6 @@ +import type {MdCheckbox} from '@material/web/checkbox/checkbox'; import '@material/web/checkbox/checkbox.js'; import '@material/web/field/outlined-field.js'; - -import type {MdCheckbox} from '@material/web/checkbox/checkbox'; import {mdiLicense} from '@mdi/js'; import { Vnode, @@ -14,13 +13,7 @@ import { getFlag, getLocaleDisplayName } from '~/utils/i18n'; -import { - capitalize, - showSnackbar -} from '~/utils/misc'; -import Request, { - RequestError -} from '~/utils/Request'; +import {capitalize} from '~/utils/misc'; import { SetupStep, diff --git a/resources/ts/utils/Request.ts b/resources/ts/utils/Request.ts deleted file mode 100644 index 86ede5424..000000000 --- a/resources/ts/utils/Request.ts +++ /dev/null @@ -1,142 +0,0 @@ -import {RequestOptions as MithrilRequestOptions} from 'mithril'; -import {Cookies} from 'typescript-cookie'; - -export type RequestMethods = 'get' | 'head' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | 'patch' | string; - -export interface RequestOptions extends MithrilRequestOptions { - url?: string, - renewCSRF?: boolean, - renewCSRFOnFailure?: boolean, - method?: RequestMethods - beforeRequest?: (options: RequestOptionsWithUrl) => Promise - afterRequest?: (response: Promise, options: RequestOptionsWithUrl) => void, - xsrfCookieName?: string, - xsrfHeaderName?: string -} - -export interface RequestOptionsWithUrl extends RequestOptions { - url: string; -} - -export interface RequestError extends Error { - code: number; - response: T; -} - -export default class Request { - options: RequestOptionsWithUrl = { - url: '', - headers: {}, - renewCSRF: false, - renewCSRFOnFailure: false, // Renew CSRF token if request fails with 419 status code (CSRF token expired; risky since it can cause an infinite loop) - withCredentials: true, - xsrfCookieName: 'XSRF-TOKEN', - xsrfHeaderName: 'X-XSRF-TOKEN' - }; - - constructor(options: RequestOptions) { - this.options = {...this.options, ...options}; - } - - /** - * Sends the request - * - * @throws {RequestError} If request has an error - */ - public async send() { - await this.beforeSendingRequest(); - const response = m.request(this.options); - this.afterSendingRequest(response); - - return response; - } - - /** - * Actions to perform before sending the request - * @private - */ - private async beforeSendingRequest() { - this.phpWorkaround(); - this.xsfrAutoHeader(); - - await this.options.beforeRequest?.(this.options); - } - - /** - * Workaround for PHP issue with PUT/PATCH/DELETE requests and FormData - * - * @see https://bugs.php.net/bug.php?id=55815 - * @private - */ - private phpWorkaround() { - if (this.options.method && !['get', 'post'].includes(this.options.method) && this.options.body instanceof FormData) { - this.options.body.append('_method', this.options.method); - this.options.method = 'post'; - } - } - - /** - * Automatically set the XSRF header if the cookie is set - * - * @private - */ - private xsfrAutoHeader() { - const token = Cookies.get(this.options.xsrfCookieName) as string | undefined; - if (token && this.options.xsrfHeaderName) { - this.options.headers![this.options.xsrfHeaderName] = decodeURIComponent(token); - } - } - - /** - * Actions to perform after sending the request - * - * @private - */ - private afterSendingRequest(response: Promise) { - this.options.afterRequest?.(response, this.options); - } - - static get(url: string, parameters?: RequestOptions['params'], options?: RequestOptions) { - return this.sendRequest('get', url, parameters, options); - } - - static post(url: string, data?: RequestOptions['body'], options?: RequestOptions) { - return this.sendRequest('post', url, data, options); - } - - static put(url: string, data?: RequestOptions['body'], options?: RequestOptions) { - return this.sendRequest('put', url, data, options); - } - - static patch(url: string, data?: RequestOptions['body'], options?: RequestOptions) { - return this.sendRequest('patch', url, data, options); - } - - static delete(url: string, options?: RequestOptions) { - return this.sendRequest('delete', url, undefined, options); - } - - /** - * Sends a request - * @param method The method of the request (get, post, put, patch, delete) - * @param url The URL of the request - * @param data The data to send - * @param options The options of the request - * @private - */ - static sendRequest( - method: RequestMethods, - url: string, - data?: RequestOptions['params'] | RequestOptions['body'], - options: RequestOptions = {} - ) { - if (method === 'get') { - options.params = data as RequestOptions['params']; - } else { - options.body = data; - } - options.url ??= url; - - return (new Request({method, ...options})).send(); - } -}