1
0
mirror of https://github.com/flarum/core.git synced 2025-08-16 05:14:20 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
SychO9
eb3a60338f chore: Release v1.1.1 2021-10-22 10:28:37 +01:00
Sami Mazouz
9c8dceff33 perf: Temporary quick fix for tags state performance (#3117) 2021-10-21 21:56:12 +01:00
382 changed files with 17013 additions and 14264 deletions

View File

@@ -18,67 +18,21 @@ jobs:
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}
cache: "yarn" cache: "npm"
cache-dependency-path: js/yarn.lock cache-dependency-path: js/package-lock.json
- name: Install JS dependencies - name: Install JS dependencies
run: yarn install --immutable run: npm ci
working-directory: ./js working-directory: ./js
- name: Check JS formatting - name: Check JS formatting
run: yarn run format-check run: npm run format-check
working-directory: ./js
typecheck:
name: Typecheck
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
cache: "yarn"
cache-dependency-path: js/yarn.lock
- name: Install JS dependencies
run: yarn --frozen-lockfile
working-directory: ./js
- name: Typecheck
run: yarn run check-typings
working-directory: ./js
type-coverage:
name: Type Coverage
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
cache: "yarn"
cache-dependency-path: js/yarn.lock
- name: Install JS dependencies
run: yarn --frozen-lockfile
working-directory: ./js
- name: Check type coverage
run: yarn run check-typings-coverage
working-directory: ./js working-directory: ./js
build-prod: build-prod:
name: Build and commit name: Build and commit
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [prettier, typecheck, type-coverage] needs: [prettier]
# Only commit JS on push to master branch # Only commit JS on push to master branch
# Remember to change in `build-test` job too # Remember to change in `build-test` job too
@@ -92,8 +46,8 @@ jobs:
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}
cache: "yarn" cache: "npm"
cache-dependency-path: js/yarn.lock cache-dependency-path: js/package-lock.json
# Our action will install npm, cd into `./js`, run `npm run build` and # Our action will install npm, cd into `./js`, run `npm run build` and
# `npm run build-typings`, then commit and upload any changes # `npm run build-typings`, then commit and upload any changes
@@ -102,13 +56,13 @@ jobs:
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
build_script: build build_script: build
package_manager: yarn package_manager: npm
typings_script: build-typings typings_script: build-typings
build-test: build-test:
name: Test build name: Test build
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [prettier, typecheck, type-coverage] needs: [prettier]
# Inverse check of `build-prod` # Inverse check of `build-prod`
# Remember to change in `build-prod` job too # Remember to change in `build-prod` job too
@@ -122,8 +76,8 @@ jobs:
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}
cache: "yarn" cache: "npm"
cache-dependency-path: js/yarn.lock cache-dependency-path: js/package-lock.json
# Our action will install npm, cd into `./js`, run `npm run build` and # Our action will install npm, cd into `./js`, run `npm run build` and
# `npm run build-typings`, then commit and upload any changes # `npm run build-typings`, then commit and upload any changes
@@ -132,6 +86,6 @@ jobs:
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
build_script: build build_script: build
package_manager: yarn package_manager: npm
typings_script: build-typings typings_script: build-typings
do_not_commit: true do_not_commit: true

View File

@@ -8,7 +8,7 @@ jobs:
strategy: strategy:
matrix: matrix:
php: [7.3, 7.4, '8.0', '8.1'] php: [7.3, 7.4, '8.0']
service: ['mysql:5.7', mariadb] service: ['mysql:5.7', mariadb]
prefix: ['', flarum_] prefix: ['', flarum_]

2
.gitignore vendored
View File

@@ -1,6 +1,7 @@
/vendor /vendor
composer.lock composer.lock
composer.phar composer.phar
node_modules
.DS_Store .DS_Store
Thumbs.db Thumbs.db
tests/.phpunit.result.cache tests/.phpunit.result.cache
@@ -8,4 +9,3 @@ tests/.phpunit.result.cache
.vagrant .vagrant
.idea/* .idea/*
.vscode .vscode
js/coverage-ts

View File

@@ -1,5 +1,9 @@
# Changelog # Changelog
## [1.1.1](https://github.com/flarum/core/compare/v1.1.0...v1.1.1)
### Fixed
- Performance issue with very large communities.
## [1.1.0](https://github.com/flarum/core/compare/v1.0.4...v1.1.0) ## [1.1.0](https://github.com/flarum/core/compare/v1.0.4...v1.1.0)

9
js/.gitignore vendored
View File

@@ -1,9 +0,0 @@
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
node_modules

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
yarnPath: .yarn/releases/yarn-3.1.0.cjs
nodeLinker: node-modules

View File

@@ -1,41 +1,3 @@
declare type Writable<T> = { -readonly [P in keyof T]: T[P] };
declare type DeepWritable<T> = { -readonly [P in keyof T]: DeepWritable<T[P]> };
declare type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };
/**
* UTILITY TYPES
*/
/**
* Type that returns an array of all keys of a provided object that are of
* of the provided type, or a subtype of the type.
*/
declare type KeysOfType<Type extends object, Match> = {
[Key in keyof Type]-?: Type[Key] extends Match ? Key : never;
};
/**
* Type that matches one of the keys of an object that is of the provided
* type, or a subtype of it.
*/
declare type KeyOfType<Type extends object, Match> = KeysOfType<Type, Match>[keyof Type];
type Component<A> = import('mithril').Component<A>;
declare type ComponentClass<Attrs = Record<string, unknown>, C extends Component<Attrs> = Component<Attrs>> = {
new (...args: any[]): Component<Attrs>;
prototype: C;
};
/**
* Unfortunately, TypeScript only supports strings and classes for JSX tags.
* Therefore, our type definition should only allow for those two types.
*
* @see https://github.com/microsoft/TypeScript/issues/14789#issuecomment-412247771
*/
declare type VnodeElementTag<Attrs = Record<string, unknown>, C extends Component<Attrs> = Component<Attrs>> = string | ComponentClass<Attrs, C>;
/** /**
* @deprecated Please import `app` from a namespace instead of using it as a global variable. * @deprecated Please import `app` from a namespace instead of using it as a global variable.
* *
@@ -59,17 +21,6 @@ declare const app: never;
declare const m: import('mithril').Static; declare const m: import('mithril').Static;
declare const dayjs: typeof import('dayjs'); declare const dayjs: typeof import('dayjs');
/**
* From https://github.com/lokesh/color-thief/issues/188
*/
declare module 'color-thief-browser' {
type Color = [number, number, number];
export default class ColorThief {
getColor: (img: HTMLImageElement | null) => Color;
getPalette: (img: HTMLImageElement | null) => Color[];
}
}
type ESModule = { __esModule: true; [key: string]: unknown }; type ESModule = { __esModule: true; [key: string]: unknown };
/** /**
@@ -129,10 +80,3 @@ interface JSX {
attrs: Record<string, unknown>; attrs: Record<string, unknown>;
}; };
} }
interface Event {
/**
* Whether this event should trigger a Mithril redraw.
*/
redraw: boolean;
}

View File

@@ -1,30 +1,3 @@
import Application from '../common/Application';
import ExtensionData from './utils/ExtensionData';
export declare type Extension = {
id: string;
version: string;
description?: string;
icon?: {
name: string;
};
links: {
authors?: {
name?: string;
link?: string;
}[];
discuss?: string;
documentation?: string;
support?: string;
website?: string;
donate?: string;
source?: string;
};
extra: {
'flarum-extension': {
title: string;
};
};
};
export default class AdminApplication extends Application { export default class AdminApplication extends Application {
extensionData: ExtensionData; extensionData: ExtensionData;
extensionCategories: { extensionCategories: {
@@ -35,27 +8,10 @@ export default class AdminApplication extends Application {
history: { history: {
canGoBack: () => boolean; canGoBack: () => boolean;
getPrevious: () => void; getPrevious: () => void;
backUrl: () => string; backUrl: () => any;
back: () => void; back: () => void;
}; };
/** getRequiredPermissions(permission: any): string[];
* Settings are serialized to the admin dashboard as strings.
* Additional encoding/decoding is possible, but must take
* place on the client side.
*
* @inheritdoc
*/
data: Application['data'] & {
extensions: Record<string, Extension>;
settings: Record<string, string>;
modelStatistics: Record<string, {
total: number;
}>;
};
constructor();
/**
* @inheritdoc
*/
mount(): void;
getRequiredPermissions(permission: string): string[];
} }
import Application from "../common/Application";
import ExtensionData from "./utils/ExtensionData";

View File

@@ -1,5 +1,5 @@
declare var _default: { declare var _default: {
extend: any; extend: typeof import("../common/extend");
Session: typeof import("../common/Session").default; Session: typeof import("../common/Session").default;
Store: typeof import("../common/Store").default; Store: typeof import("../common/Store").default;
'utils/BasicEditorDriver': typeof import("../common/utils/BasicEditorDriver").default; 'utils/BasicEditorDriver': typeof import("../common/utils/BasicEditorDriver").default;
@@ -32,15 +32,17 @@ declare var _default: {
'utils/subclassOf': typeof import("../common/utils/subclassOf").default; 'utils/subclassOf': typeof import("../common/utils/subclassOf").default;
'utils/setRouteWithForcedRefresh': typeof import("../common/utils/setRouteWithForcedRefresh").default; 'utils/setRouteWithForcedRefresh': typeof import("../common/utils/setRouteWithForcedRefresh").default;
'utils/patchMithril': typeof import("../common/utils/patchMithril").default; 'utils/patchMithril': typeof import("../common/utils/patchMithril").default;
'utils/proxifyCompat': typeof import("../common/utils/proxifyCompat").default; 'utils/proxifyCompat': (compat: {
[key: string]: any;
}, namespace: string) => {
[key: string]: any;
};
'utils/classList': (...classes: import("clsx").ClassValue[]) => string; 'utils/classList': (...classes: import("clsx").ClassValue[]) => string;
'utils/extractText': typeof import("../common/utils/extractText").default; 'utils/extractText': typeof import("../common/utils/extractText").default;
'utils/formatNumber': typeof import("../common/utils/formatNumber").default; 'utils/formatNumber': typeof import("../common/utils/formatNumber").default;
'utils/mapRoutes': typeof import("../common/utils/mapRoutes").default; 'utils/mapRoutes': typeof import("../common/utils/mapRoutes").default;
'utils/withAttr': (key: string, cb: Function) => (this: Element) => void; 'utils/withAttr': (key: string, cb: Function) => (this: Element) => void;
'utils/throttleDebounce': typeof import("../common/utils/throttleDebounce"); 'utils/throttleDebounce': typeof import("../common/utils/throttleDebounce");
'utils/isObject': typeof import("../common/utils/isObject").default;
'utils/focusTrap': typeof import("../common/utils/focusTrap");
'models/Notification': typeof import("../common/models/Notification").default; 'models/Notification': typeof import("../common/models/Notification").default;
'models/User': typeof import("../common/models/User").default; 'models/User': typeof import("../common/models/User").default;
'models/Post': typeof import("../common/models/Post").default; 'models/Post': typeof import("../common/models/Post").default;
@@ -67,7 +69,6 @@ declare var _default: {
'components/Link': typeof import("../common/components/Link").default; 'components/Link': typeof import("../common/components/Link").default;
'components/LinkButton': typeof import("../common/components/LinkButton").default; 'components/LinkButton': typeof import("../common/components/LinkButton").default;
'components/Checkbox': typeof import("../common/components/Checkbox").default; 'components/Checkbox': typeof import("../common/components/Checkbox").default;
'components/ColorPreviewInput': typeof import("../common/components/ColorPreviewInput").default;
'components/SelectDropdown': typeof import("../common/components/SelectDropdown").default; 'components/SelectDropdown': typeof import("../common/components/SelectDropdown").default;
'components/ModalManager': typeof import("../common/components/ModalManager").default; 'components/ModalManager': typeof import("../common/components/ModalManager").default;
'components/Button': typeof import("../common/components/Button").default; 'components/Button': typeof import("../common/components/Button").default;

View File

@@ -7,8 +7,8 @@ export default class AdminNav extends Component<import("../../common/Component")
* *
* @return {ItemList} * @return {ItemList}
*/ */
items(): ItemList<any>; items(): ItemList;
extensionItems(): ItemList<any>; extensionItems(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import Stream from "../../common/utils/Stream"; import Stream from "../../common/utils/Stream";

View File

@@ -1,167 +0,0 @@
import type Mithril from 'mithril';
import Page, { IPageAttrs } from '../../common/components/Page';
import Stream from '../../common/utils/Stream';
export interface AdminHeaderOptions {
title: Mithril.Children;
description: Mithril.Children;
icon: string;
/**
* Will be used as the class for the AdminPage.
*
* Will also be appended with `-header` and set as the class for the `AdminHeader` component.
*/
className: string;
}
/**
* A type that matches any valid value for the `type` attribute on an HTML `<input>` element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-type
*
* Note: this will be exported from a different location in the future.
*
* @see https://github.com/flarum/core/issues/3039
*/
export declare type HTMLInputTypes = 'button' | 'checkbox' | 'color' | 'date' | 'datetime-local' | 'email' | 'file' | 'hidden' | 'image' | 'month' | 'number' | 'password' | 'radio' | 'range' | 'reset' | 'search' | 'submit' | 'tel' | 'text' | 'time' | 'url' | 'week';
interface CommonSettingsItemOptions extends Mithril.Attributes {
setting: string;
label: Mithril.Children;
help?: Mithril.Children;
className?: string;
}
/**
* Valid options for the setting component builder to generate an HTML input element.
*/
export interface HTMLInputSettingsComponentOptions extends CommonSettingsItemOptions {
/**
* Any valid HTML input `type` value.
*/
type: HTMLInputTypes;
}
declare const BooleanSettingTypes: readonly ["bool", "checkbox", "switch", "boolean"];
declare const SelectSettingTypes: readonly ["select", "dropdown", "selectdropdown"];
declare const TextareaSettingTypes: readonly ["textarea"];
declare const ColorPreviewSettingType = "color-preview";
/**
* Valid options for the setting component builder to generate a Switch.
*/
export interface SwitchSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof BooleanSettingTypes[number];
}
/**
* Valid options for the setting component builder to generate a Select dropdown.
*/
export interface SelectSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof SelectSettingTypes[number];
/**
* Map of values to their labels
*/
options: {
[value: string]: Mithril.Children;
};
default: string;
}
/**
* Valid options for the setting component builder to generate a Textarea.
*/
export interface TextareaSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof TextareaSettingTypes[number];
}
/**
* Valid options for the setting component builder to generate a ColorPreviewInput.
*/
export interface ColorPreviewSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof ColorPreviewSettingType;
}
/**
* All valid options for the setting component builder.
*/
export declare type SettingsComponentOptions = HTMLInputSettingsComponentOptions | SwitchSettingComponentOptions | SelectSettingComponentOptions | TextareaSettingComponentOptions | ColorPreviewSettingComponentOptions;
/**
* Valid attrs that can be returned by the `headerInfo` function
*/
export declare type AdminHeaderAttrs = AdminHeaderOptions & Partial<Omit<Mithril.Attributes, 'class'>>;
export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends Page<CustomAttrs> {
settings: Record<string, Stream<string>>;
loading: boolean;
view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
/**
* Returns the content of the AdminPage.
*/
abstract content(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
/**
* Returns the submit button for this AdminPage.
*
* Calls `this.saveSettings` when the button is clicked.
*/
submitButton(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
/**
* Returns the Header component for this AdminPage.
*/
header(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
/**
* Returns the options passed to the AdminHeader component.
*/
headerInfo(): AdminHeaderAttrs;
/**
* `buildSettingComponent` takes a settings object and turns it into a component.
* Depending on the type of input, you can set the type to 'bool', 'select', or
* any standard <input> type. Any values inside the 'extra' object will be added
* to the component as an attribute.
*
* Alternatively, you can pass a callback that will be executed in ExtensionPage's
* context to include custom JSX elements.
*
* @example
*
* {
* setting: 'acme.checkbox',
* label: app.translator.trans('acme.admin.setting_label'),
* type: 'bool',
* help: app.translator.trans('acme.admin.setting_help'),
* className: 'Setting-item'
* }
*
* @example
*
* {
* setting: 'acme.select',
* label: app.translator.trans('acme.admin.setting_label'),
* type: 'select',
* options: {
* 'option1': 'Option 1 label',
* 'option2': 'Option 2 label',
* },
* default: 'option1',
* }
*
* @example
*
* () => {
* return <p>My cool component</p>;
* }
*/
buildSettingComponent(entry: ((this: this) => Mithril.Children) | SettingsComponentOptions): Mithril.Children;
/**
* Called when `saveSettings` completes successfully.
*/
onsaved(): void;
/**
* Returns a function that fetches the setting from the `app` global.
*/
setting(key: string, fallback?: string): Stream<string>;
/**
* Returns a map of settings keys to values which includes only those which have been modified but not yet saved.
*/
dirty(): Record<string, string>;
/**
* Returns the number of settings that have been modified.
*/
isChanged(): number;
/**
* Saves the modified settings to the database.
*/
saveSettings(e: SubmitEvent & {
redraw: boolean;
}): Promise<void>;
}
export {};

View File

@@ -1,6 +1,4 @@
export default class AppearancePage extends AdminPage<import("../../common/components/Page").IPageAttrs> { export default class AppearancePage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
constructor(); constructor();
colorItems(): ItemList<any>;
} }
import AdminPage from "./AdminPage"; import AdminPage from "./AdminPage";
import ItemList from "../../common/utils/ItemList";

View File

@@ -10,7 +10,7 @@ export default class BasicsPage extends AdminPage<import("../../common/component
* @return {ItemList} * @return {ItemList}
* @public * @public
*/ */
public homePageItems(): ItemList<any>; public homePageItems(): ItemList;
} }
import AdminPage from "./AdminPage"; import AdminPage from "./AdminPage";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,6 +1,6 @@
export default class DashboardPage extends AdminPage<import("../../common/components/Page").IPageAttrs> { export default class DashboardPage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
constructor(); constructor();
availableWidgets(): ItemList<any>; availableWidgets(): ItemList;
} }
import AdminPage from "./AdminPage"; import AdminPage from "./AdminPage";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -2,15 +2,14 @@
* 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<import("../../common/components/Modal").IInternalModalAttrs> { export default class EditGroupModal extends Modal {
constructor();
group: any; group: any;
nameSingular: Stream<any> | undefined; nameSingular: Stream<any> | undefined;
namePlural: Stream<any> | undefined; namePlural: Stream<any> | undefined;
icon: Stream<any> | undefined; icon: Stream<any> | undefined;
color: Stream<any> | undefined; color: Stream<any> | undefined;
isHidden: Stream<any> | undefined; isHidden: Stream<any> | undefined;
fields(): ItemList<any>; fields(): ItemList;
submitData(): { submitData(): {
nameSingular: any; nameSingular: any;
namePlural: any; namePlural: any;

View File

@@ -1,5 +1,5 @@
export default class ExtensionLinkButton extends LinkButton { export default class ExtensionLinkButton extends LinkButton {
statusItems(name: any): ItemList<any>; statusItems(name: any): ItemList;
} }
import LinkButton from "../../common/components/LinkButton"; import LinkButton from "../../common/components/LinkButton";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,15 +1,7 @@
import ItemList from '../../common/utils/ItemList'; export default class ExtensionPage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
import AdminPage from './AdminPage'; constructor();
import RequestError from '../../common/utils/RequestError'; extension: any;
import { Extension } from '../AdminApplication'; changingState: boolean | undefined;
import { IPageAttrs } from '../../common/components/Page';
import type Mithril from 'mithril';
export interface ExtensionPageAttrs extends IPageAttrs {
id: string;
}
export default class ExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs> extends AdminPage<Attrs> {
extension: Extension;
changingState: boolean;
infoFields: { infoFields: {
discuss: string; discuss: string;
documentation: string; documentation: string;
@@ -17,16 +9,14 @@ export default class ExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionP
website: string; website: string;
donate: string; donate: string;
source: string; source: string;
}; } | undefined;
oninit(vnode: Mithril.Vnode<Attrs, this>): void;
className(): string; className(): string;
view(vnode: Mithril.VnodeDOM<Attrs, this>): JSX.Element | null; sections(): ItemList;
header(): JSX.Element[]; topItems(): ItemList;
sections(vnode: Mithril.VnodeDOM<Attrs, this>): ItemList<unknown>; infoItems(): ItemList;
content(vnode: Mithril.VnodeDOM<Attrs, this>): JSX.Element;
topItems(): ItemList<Mithril.Children>;
infoItems(): ItemList<Mithril.Children>;
toggle(): void; toggle(): void;
isEnabled(): any; isEnabled(): any;
onerror(e: RequestError): void; onerror(e: any): void;
} }
import AdminPage from "./AdminPage";
import ItemList from "../../common/utils/ItemList";

View File

@@ -1,19 +1,4 @@
import PermissionGrid, { PermissionGridEntry } from './PermissionGrid'; export default class ExtensionPermissionGrid extends PermissionGrid {
import ItemList from '../../common/utils/ItemList'; extensionId: any;
import Mithril from 'mithril';
export interface IExtensionPermissionGridAttrs {
extensionId: string;
}
export default class ExtensionPermissionGrid<CustomAttrs extends IExtensionPermissionGridAttrs = IExtensionPermissionGridAttrs> extends PermissionGrid<CustomAttrs> {
protected extensionId: string;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
permissionItems(): ItemList<{
label: Mithril.Children;
children: PermissionGridEntry[];
}>;
viewItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
startItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
replyItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
moderateItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
scopeControlItems(): ItemList<unknown>;
} }
import PermissionGrid from "./PermissionGrid";

View File

@@ -10,7 +10,7 @@ export default class HeaderPrimary extends Component<import("../../common/Compon
* *
* @return {ItemList} * @return {ItemList}
*/ */
items(): ItemList<any>; items(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -8,7 +8,7 @@ export default class HeaderSecondary extends Component<import("../../common/Comp
* *
* @return {ItemList} * @return {ItemList}
*/ */
items(): ItemList<any>; items(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,14 +1,3 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" /> export default class LoadingModal extends Modal {
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
export interface ILoadingModalAttrs extends IInternalModalAttrs {
}
export default class LoadingModal<ModalAttrs extends ILoadingModalAttrs = ILoadingModalAttrs> extends Modal<ModalAttrs> {
/**
* @inheritdoc
*/
static readonly isDismissible: boolean;
className(): string;
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
content(): string;
onsubmit(e: Event): void;
} }
import Modal from "../../common/components/Modal";

View File

@@ -1,36 +1,12 @@
import Component, { ComponentAttrs } from '../../common/Component'; export default class PermissionGrid extends Component<import("../../common/Component").ComponentAttrs, undefined> {
import ItemList from '../../common/utils/ItemList'; constructor();
import type Mithril from 'mithril'; permissionItems(): ItemList;
export interface PermissionConfig { viewItems(): ItemList;
permission: string; startItems(): ItemList;
icon: string; replyItems(): ItemList;
label: Mithril.Children; moderateItems(): ItemList;
allowGuest?: boolean; scopeItems(): ItemList;
} scopeControlItems(): ItemList;
export interface PermissionSetting {
setting: () => Mithril.Children;
icon: string;
label: Mithril.Children;
}
export declare type PermissionGridEntry = PermissionConfig | PermissionSetting;
export declare type PermissionType = 'view' | 'start' | 'reply' | 'moderate';
export interface ScopeItem {
label: Mithril.Children;
render: (permission: PermissionGridEntry) => Mithril.Children;
onremove?: () => void;
}
export interface IPermissionGridAttrs extends ComponentAttrs {
}
export default class PermissionGrid<CustomAttrs extends IPermissionGridAttrs = IPermissionGridAttrs> extends Component<CustomAttrs> {
view(vnode: Mithril.Vnode<CustomAttrs, this>): JSX.Element;
permissionItems(): ItemList<{
label: Mithril.Children;
children: PermissionGridEntry[];
}>;
viewItems(): ItemList<PermissionGridEntry>;
startItems(): ItemList<PermissionGridEntry>;
replyItems(): ItemList<PermissionGridEntry>;
moderateItems(): ItemList<PermissionGridEntry>;
scopeItems(): ItemList<ScopeItem>;
scopeControlItems(): ItemList<unknown>;
} }
import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList";

View File

@@ -1,18 +0,0 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" />
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import ExtensionReadme from '../models/ExtensionReadme';
import type Mithril from 'mithril';
import type { Extension } from '../AdminApplication';
export interface IReadmeModalAttrs extends IInternalModalAttrs {
extension: Extension;
}
export default class ReadmeModal<CustomAttrs extends IReadmeModalAttrs = IReadmeModalAttrs> extends Modal<CustomAttrs> {
protected name: string;
protected extName: string;
protected readme: ExtensionReadme;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
className(): string;
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
content(): JSX.Element;
loadReadme(): Promise<void>;
}

View File

@@ -8,7 +8,7 @@ export default class SessionDropdown extends Dropdown {
* *
* @return {ItemList} * @return {ItemList}
*/ */
items(): ItemList<any>; items(): ItemList;
} }
import Dropdown from "../../common/components/Dropdown"; import Dropdown from "../../common/components/Dropdown";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,5 +1,4 @@
export default class SettingsModal extends Modal<import("../../common/components/Modal").IInternalModalAttrs> { export default class SettingsModal extends Modal {
constructor();
settings: {} | undefined; settings: {} | undefined;
form(): string; form(): string;
submitButton(): JSX.Element; submitButton(): JSX.Element;

View File

@@ -1,6 +1,5 @@
export default class StatusWidget extends DashboardWidget { export default class StatusWidget extends DashboardWidget {
items(): ItemList<any>; items(): ItemList;
toolsItems(): ItemList<any>;
handleClearCache(e: any): void; handleClearCache(e: any): void;
} }
import DashboardWidget from "./DashboardWidget"; import DashboardWidget from "./DashboardWidget";

View File

@@ -1,18 +1,6 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" /> /// <reference types="mithril" />
import type Mithril from 'mithril';
import type User from '../../common/models/User';
import ItemList from '../../common/utils/ItemList'; import ItemList from '../../common/utils/ItemList';
import AdminPage from './AdminPage'; import AdminPage from './AdminPage';
declare type ColumnData = {
/**
* Column title
*/
name: Mithril.Children;
/**
* Component(s) to show for this column.
*/
content: (user: User) => Mithril.Children;
};
/** /**
* Admin page which displays a paginated list of all users on the forum. * Admin page which displays a paginated list of all users on the forum.
*/ */
@@ -62,12 +50,12 @@ export default class UserListPage extends AdminPage {
* *
* See `UserListPage.tsx` for examples. * See `UserListPage.tsx` for examples.
*/ */
columns(): ItemList<ColumnData>; columns(): ItemList;
headerInfo(): { headerInfo(): {
className: string; className: string;
icon: string; icon: string;
title: import("@askvortsov/rich-icu-message-formatter").NestedStringArray; title: any;
description: import("@askvortsov/rich-icu-message-formatter").NestedStringArray; description: any;
}; };
/** /**
* Asynchronously fetch the next set of users to be rendered. * Asynchronously fetch the next set of users to be rendered.
@@ -82,4 +70,3 @@ export default class UserListPage extends AdminPage {
nextPage(): void; nextPage(): void;
previousPage(): void; previousPage(): void;
} }
export {};

View File

@@ -1,3 +1,5 @@
import app from './app'; import app from './app';
export { app }; export { app };
export declare const compat: Record<string, unknown>; export declare const compat: {
[key: string]: any;
};

View File

@@ -1,4 +0,0 @@
export default class ExtensionReadme extends Model {
content: () => any;
}
import Model from "../../common/Model";

View File

@@ -1,10 +1,9 @@
import DefaultResolver from '../../common/resolvers/DefaultResolver'; import DefaultResolver from '../../common/resolvers/DefaultResolver';
import ExtensionPage, { ExtensionPageAttrs } from '../components/ExtensionPage';
/** /**
* A custom route resolver for ExtensionPage that generates handles routes * A custom route resolver for ExtensionPage that generates handles routes
* to default extension pages or a page provided by an extension. * to default extension pages or a page provided by an extension.
*/ */
export default class ExtensionPageResolver<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs, RouteArgs extends Record<string, unknown> = {}> extends DefaultResolver<Attrs, ExtensionPage<Attrs>, RouteArgs> { export default class ExtensionPageResolver extends DefaultResolver {
static extension: string | null; static extension: string | null;
onmatch(args: Attrs & RouteArgs, requestedPath: string, route: string): new () => ExtensionPage<Attrs>; onmatch(args: any, requestedPath: any, route: any): any;
} }

View File

@@ -1,5 +1,6 @@
import AdminApplication from './AdminApplication';
/** /**
* The `routes` initializer defines the forum app's routes. * The `routes` initializer defines the forum app's routes.
*
* @param {App} app
*/ */
export default function (app: AdminApplication): void; export default function _default(app: any): void;

View File

@@ -1,46 +1,17 @@
import type Mithril from 'mithril';
import ItemList from '../../common/utils/ItemList';
import { SettingsComponentOptions } from '../components/AdminPage';
import ExtensionPage, { ExtensionPageAttrs } from '../components/ExtensionPage';
import { PermissionConfig, PermissionType } from '../components/PermissionGrid';
declare type SettingConfigInput = SettingsComponentOptions | (() => Mithril.Children);
declare type SettingConfigInternal = SettingsComponentOptions | ((() => Mithril.Children) & {
setting: string;
});
export declare type CustomExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs> = new () => ExtensionPage<Attrs>;
declare type ExtensionConfig = {
settings?: ItemList<SettingConfigInternal>;
permissions?: {
view?: ItemList<PermissionConfig>;
start?: ItemList<PermissionConfig>;
reply?: ItemList<PermissionConfig>;
moderate?: ItemList<PermissionConfig>;
};
page?: CustomExtensionPage;
};
declare type InnerDataNoActiveExtension = {
currentExtension: null;
data: {
[key: string]: ExtensionConfig | undefined;
};
};
declare type InnerDataActiveExtension = {
currentExtension: string;
data: {
[key: string]: ExtensionConfig;
};
};
export default class ExtensionData { export default class ExtensionData {
protected state: InnerDataActiveExtension | InnerDataNoActiveExtension; data: {};
currentExtension: any;
/** /**
* This function simply takes the extension id * This function simply takes the extension id
* *
* @example * @example
* app.extensionData.for('flarum-tags') * app.extensionData.load('flarum-tags')
* *
* flarum/flags -> flarum-flags | acme/extension -> acme-extension * flarum/flags -> flarum-flags | acme/extension -> acme-extension
*
* @param extension
*/ */
for(extension: string): this; for(extension: any): ExtensionData;
/** /**
* This function registers your settings with Flarum * This function registers your settings with Flarum
* *
@@ -53,8 +24,13 @@ export default class ExtensionData {
* type: 'text', // This will be inputted into the input tag for the setting (text/number/etc) * type: 'text', // This will be inputted into the input tag for the setting (text/number/etc)
* label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label') * label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label')
* }, 15) // priority is optional (ItemList) * }, 15) // priority is optional (ItemList)
*
*
* @param content
* @param priority
* @returns {ExtensionData}
*/ */
registerSetting(content: SettingConfigInput, priority?: number): this; registerSetting(content: any, priority?: number): ExtensionData;
/** /**
* This function registers your permission with Flarum * This function registers your permission with Flarum
* *
@@ -65,32 +41,58 @@ export default class ExtensionData {
* label: app.translator.trans('flarum-flags.admin.permissions.view_flags_label'), * label: app.translator.trans('flarum-flags.admin.permissions.view_flags_label'),
* permission: 'discussion.viewFlags' * permission: 'discussion.viewFlags'
* }, 'moderate', 65) * }, 'moderate', 65)
*
* @param content
* @param permissionType
* @param priority
* @returns {ExtensionData}
*/ */
registerPermission(content: PermissionConfig, permissionType: PermissionType, priority?: number): this; registerPermission(content: any, permissionType?: any, priority?: number): ExtensionData;
/** /**
* Replace the default extension page with a custom component. * Replace the default extension page with a custom component.
* This component would typically extend ExtensionPage * This component would typically extend ExtensionPage
*
* @param component
* @returns {ExtensionData}
*/ */
registerPage(component: CustomExtensionPage): this; registerPage(component: any): ExtensionData;
/** /**
* Get an extension's registered settings * Get an extension's registered settings
*
* @param extensionId
* @returns {boolean|*}
*/ */
getSettings(extensionId: string): SettingConfigInternal[] | undefined; getSettings(extensionId: any): boolean | any;
/** /**
*
* Get an ItemList of all extensions' registered permissions * Get an ItemList of all extensions' registered permissions
*
* @param extension
* @param type
* @returns {ItemList}
*/ */
getAllExtensionPermissions(type: PermissionType): ItemList<PermissionConfig>; getAllExtensionPermissions(type: any): ItemList;
/** /**
* Get a singular extension's registered permissions * Get a singular extension's registered permissions
*
* @param extension
* @param type
* @returns {boolean|*}
*/ */
getExtensionPermissions(extension: string, type: PermissionType): ItemList<PermissionConfig>; getExtensionPermissions(extension: any, type: any): boolean | any;
/** /**
* Checks whether a given extension has registered permissions. * Checks whether a given extension has registered permissions.
*
* @param extension
* @returns {boolean}
*/ */
extensionHasPermissions(extension: string): boolean; extensionHasPermissions(extension: any): boolean;
/** /**
* Returns an extension's custom page component if it exists. * Returns an extension's custom page component if it exists.
*
* @param extension
* @returns {boolean|*}
*/ */
getPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs>(extension: string): CustomExtensionPage<Attrs> | undefined; getPage(extension: any): boolean | any;
} }
export {}; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,89 +1,3 @@
import ItemList from './utils/ItemList';
import Translator from './Translator';
import Store, { ApiPayload, ApiResponsePlural, ApiResponseSingle } from './Store';
import Session from './Session';
import Drawer from './utils/Drawer';
import RequestError, { InternalFlarumRequestOptions } from './utils/RequestError';
import Forum from './models/Forum';
import PageState from './states/PageState';
import ModalManagerState from './states/ModalManagerState';
import AlertManagerState from './states/AlertManagerState';
import type DefaultResolver from './resolvers/DefaultResolver';
import type Mithril from 'mithril';
import type Component from './Component';
import type { ComponentAttrs } from './Component';
import Model, { SavedModelData } from './Model';
export declare type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';
export declare type FlarumGenericRoute = RouteItem<any, any, any>;
export interface FlarumRequestOptions<ResponseType> extends Omit<Mithril.RequestOptions<ResponseType>, 'extract'> {
errorHandler?: (error: RequestError) => void;
url: string;
/**
* Manipulate the response text before it is parsed into JSON.
*
* @deprecated Please use `modifyText` instead.
*/
extract?: (responseText: string) => string;
/**
* Manipulate the response text before it is parsed into JSON.
*
* This overrides any `extract` method provided.
*/
modifyText?: (responseText: string) => string;
}
/**
* A valid route definition.
*/
export declare type RouteItem<Attrs extends ComponentAttrs, Comp extends Component<Attrs & {
routeName: string;
}>, RouteArgs extends Record<string, unknown> = {}> = {
/**
* The path for your route.
*
* This might be a specific URL path (e.g.,`/myPage`), or it might
* contain a variable used by a resolver (e.g., `/myPage/:id`).
*
* @see https://docs.flarum.org/extend/frontend-pages.html#route-resolvers-advanced
*/
path: `/${string}`;
} & ({
/**
* The component to render when this route matches.
*/
component: new () => Comp;
/**
* A custom resolver class.
*
* This should be the class itself, and **not** an instance of the
* class.
*/
resolverClass?: new (component: new () => Comp, routeName: string) => DefaultResolver<Attrs, Comp, RouteArgs>;
} | {
/**
* An instance of a route resolver.
*/
resolver: RouteResolver<Attrs, Comp, RouteArgs>;
});
export interface RouteResolver<Attrs extends ComponentAttrs, Comp extends Component<Attrs & {
routeName: string;
}>, RouteArgs extends Record<string, unknown> = {}> {
/**
* A method which selects which component to render based on
* conditional logic.
*
* Returns the component class, and **not** a Vnode or JSX
* expression.
*/
onmatch(this: this, args: RouteArgs, requestedPath: string, route: string): {
new (): Comp;
};
/**
* A function which renders the provided component.
*
* Returns a Mithril Vnode or other children.
*/
render(this: this, vnode: Mithril.Vnode<Attrs, Comp>): Mithril.Children;
}
/** /**
* The `App` class provides a container for an application, as well as various * The `App` class provides a container for an application, as well as various
* utilities for the rest of the app to use. * utilities for the rest of the app to use.
@@ -91,8 +5,11 @@ export interface RouteResolver<Attrs extends ComponentAttrs, Comp extends Compon
export default class Application { export default class Application {
/** /**
* The forum model for this application. * The forum model for this application.
*
* @type {Forum}
* @public
*/ */
forum: Forum; public forum: Forum;
/** /**
* A map of routes, keyed by a unique route name. Each route is an object * A map of routes, keyed by a unique route name. Each route is an object
* containing the following properties: * containing the following properties:
@@ -101,42 +18,71 @@ export default class Application {
* - `component` The Mithril component to render when this route is active. * - `component` The Mithril component to render when this route is active.
* *
* @example * @example
* app.routes.discussion = { path: '/d/:id', component: DiscussionPage }; * app.routes.discussion = {path: '/d/:id', component: DiscussionPage.component()};
*
* @type {Object}
* @public
*/ */
routes: Record<string, FlarumGenericRoute>; public routes: Object;
/** /**
* An ordered list of initializers to bootstrap the application. * An ordered list of initializers to bootstrap the application.
*
* @type {ItemList}
* @public
*/ */
initializers: ItemList<(app: this) => void>; public initializers: ItemList;
/** /**
* The app's session. * The app's session.
* *
* Stores info about the current user. * @type {Session}
* @public
*/ */
session: Session; public session: Session;
/** /**
* The app's translator. * The app's translator.
*
* @type {Translator}
* @public
*/ */
translator: Translator; public translator: Translator;
/** /**
* The app's data store. * The app's data store.
*
* @type {Store}
* @public
*/ */
store: Store; public store: Store;
/** /**
* A local cache that can be used to store data at the application level, so * A local cache that can be used to store data at the application level, so
* that is persists between different routes. * that is persists between different routes.
*
* @type {Object}
* @public
*/ */
cache: Record<string, unknown>; public cache: Object;
/** /**
* Whether or not the app has been booted. * Whether or not the app has been booted.
*
* @type {Boolean}
* @public
*/ */
booted: boolean; public booted: boolean;
/**
* The key for an Alert that was shown as a result of an AJAX request error.
* If present, it will be dismissed on the next successful request.
*
* @type {int}
* @private
*/
private requestErrorAlert;
/** /**
* The page the app is currently on. * The page the app is currently on.
* *
* This object holds information about the type of page we are currently * This object holds information about the type of page we are currently
* visiting, and sometimes additional arbitrary page state that may be * visiting, and sometimes additional arbitrary page state that may be
* relevant to lower-level components. * relevant to lower-level components.
*
* @type {PageState}
*/ */
current: PageState; current: PageState;
/** /**
@@ -145,89 +91,84 @@ export default class Application {
* Once the application navigates to another page, the object previously * Once the application navigates to another page, the object previously
* assigned to this.current will be moved to this.previous, while this.current * assigned to this.current will be moved to this.previous, while this.current
* is re-initialized. * is re-initialized.
*
* @type {PageState}
*/ */
previous: PageState; previous: PageState;
/**
* An object that manages modal state.
*/
modal: ModalManagerState; modal: ModalManagerState;
/** /**
* An object that manages the state of active alerts. * An object that manages the state of active alerts.
*
* @type {AlertManagerState}
*/ */
alerts: AlertManagerState; alerts: AlertManagerState;
/** data: any;
* An object that manages the state of the navigation drawer. title: string;
*/ titleCount: number;
drawer: Drawer; initialRoute: any;
data: { load(payload: any): void;
apiDocument: ApiPayload | null;
locale: string;
locales: Record<string, string>;
resources: SavedModelData[];
session: {
userId: number;
csrfToken: string;
};
[key: string]: unknown;
};
private _title;
private _titleCount;
private set title(value);
get title(): string;
private set titleCount(value);
get titleCount(): number;
/**
* The key for an Alert that was shown as a result of an AJAX request error.
* If present, it will be dismissed on the next successful request.
*/
private requestErrorAlert;
initialRoute: string;
load(payload: Application['data']): void;
boot(): void; boot(): void;
bootExtensions(extensions: Record<string, { bootExtensions(extensions: any): void;
extend?: unknown[]; mount(basePath?: string): void;
}>): void; drawer: Drawer | undefined;
protected mount(basePath?: string): void;
/** /**
* Get the API response document that has been preloaded into the application. * Get the API response document that has been preloaded into the application.
*
* @return {Object|null}
* @public
*/ */
preloadedApiDocument<M extends Model>(): ApiResponseSingle<M> | null; public preloadedApiDocument(): Object | null;
preloadedApiDocument<Ms extends Model[]>(): ApiResponsePlural<Ms[number]> | null;
/** /**
* Determine the current screen mode, based on our media queries. * Determine the current screen mode, based on our media queries.
*/
screen(): FlarumScreens;
/**
* Set the `<title>` of the page.
* *
* @param title New page title * @returns {String} - one of "phone", "tablet", "desktop" or "desktop-hd"
*/ */
setTitle(title: string): void; screen(): string;
/** /**
* Set a number to display in the `<title>` of the page. * Set the <title> of the page.
* *
* @param count Number to display in title * @param {String} title
* @public
*/ */
setTitleCount(count: number): void; public setTitle(title: string): void;
/**
* Set a number to display in the <title> of the page.
*
* @param {Integer} count
*/
setTitleCount(count: any): void;
updateTitle(): void; updateTitle(): void;
protected transformRequestOptions<ResponseType>(flarumOptions: FlarumRequestOptions<ResponseType>): InternalFlarumRequestOptions<ResponseType>;
/** /**
* Make an AJAX request, handling any low-level errors that may occur. * Make an AJAX request, handling any low-level errors that may occur.
* *
* @see https://mithril.js.org/request.html * @see https://mithril.js.org/request.html
* * @param {Object} options
* @param options
* @return {Promise} * @return {Promise}
* @public
*/ */
request<ResponseType>(originalOptions: FlarumRequestOptions<ResponseType>): Promise<ResponseType>; public request(originalOptions: any): Promise<any>;
/** /**
* By default, show an error alert, and log the error to the console. * @param {RequestError} error
* @param {string[]} [formattedError]
* @private
*/ */
protected requestErrorCatch<ResponseType>(error: RequestError, customErrorHandler: FlarumRequestOptions<ResponseType>['errorHandler']): Promise<never>;
protected requestErrorDefaultHandler(e: unknown, isDebug: boolean, formattedErrors: string[]): void;
private showDebug; private showDebug;
/** /**
* Construct a URL to the route with the given name. * Construct a URL to the route with the given name.
*
* @param {String} name
* @param {Object} params
* @return {String}
* @public
*/ */
route(name: string, params?: Record<string, unknown>): string; public route(name: string, params?: Object): string;
} }
import Forum from "./models/Forum";
import ItemList from "./utils/ItemList";
import Session from "./Session";
import Translator from "./Translator";
import Store from "./Store";
import PageState from "./states/PageState";
import ModalManagerState from "./states/ModalManagerState";
import AlertManagerState from "./states/AlertManagerState";
import Drawer from "./utils/Drawer";

View File

@@ -99,7 +99,7 @@ export default abstract class Component<Attrs extends ComponentAttrs = Component
* *
* @see https://mithril.js.org/hyperscript.html#mselector,-attributes,-children * @see https://mithril.js.org/hyperscript.html#mselector,-attributes,-children
*/ */
static component<SAttrs extends ComponentAttrs = ComponentAttrs>(attrs?: SAttrs, children?: Mithril.Children): Mithril.Vnode; static component(attrs?: {}, children?: null): Mithril.Vnode;
/** /**
* Saves a reference to the vnode attrs after running them through initAttrs, * Saves a reference to the vnode attrs after running them through initAttrs,
* and checking for common issues. * and checking for common issues.

View File

@@ -32,7 +32,7 @@ export default abstract class Fragment {
* @returns {jQuery} the jQuery object for the DOM node * @returns {jQuery} the jQuery object for the DOM node
* @final * @final
*/ */
$(selector?: string): JQuery; $(selector: any): JQuery<any>;
/** /**
* Get the renderable virtual DOM that represents the fragment's view. * Get the renderable virtual DOM that represents the fragment's view.
* *

View File

@@ -1,147 +1,149 @@
import { FlarumRequestOptions } from './Application';
import Store, { ApiPayloadSingle, ApiResponseSingle, MetaInformation } from './Store';
export interface ModelIdentifier {
type: string;
id: string;
}
export interface ModelAttributes {
[key: string]: unknown;
}
export interface ModelRelationships {
[relationship: string]: {
data: ModelIdentifier | ModelIdentifier[];
};
}
export interface UnsavedModelData {
type?: string;
attributes?: ModelAttributes;
relationships?: ModelRelationships;
}
export interface SavedModelData {
type: string;
id: string;
attributes?: ModelAttributes;
relationships?: ModelRelationships;
}
export declare type ModelData = UnsavedModelData | SavedModelData;
export interface SaveRelationships {
[relationship: string]: Model | Model[];
}
export interface SaveAttributes {
[key: string]: unknown;
relationships?: SaveRelationships;
}
/** /**
* The `Model` class represents a local data resource. It provides methods to * The `Model` class represents a local data resource. It provides methods to
* persist changes via the API. * persist changes via the API.
*
* @abstract
*/ */
export default abstract class Model { export default class Model {
/**
* The resource object from the API.
*/
data: ModelData;
/**
* The time at which the model's data was last updated. Watching the value
* of this property is a fast way to retain/cache a subtree if data hasn't
* changed.
*/
freshness: Date;
/**
* Whether or not the resource exists on the server.
*/
exists: boolean;
/**
* The data store that this resource should be persisted to.
*/
protected store: Store;
/**
* @param data A resource object from the API.
* @param store The data store that this model should be persisted to.
*/
constructor(data?: ModelData, store?: Store);
/**
* Get the model's ID.
*
* @final
*/
id(): string | undefined;
/**
* Get one of the model's attributes.
*
* @final
*/
attribute<T = unknown>(attribute: string): T;
/**
* Merge new data into this model locally.
*
* @param data A resource object to merge into this model
*/
pushData(data: ModelData | {
relationships?: SaveRelationships;
}): this;
/**
* Merge new attributes into this model locally.
*
* @param attributes The attributes to merge.
*/
pushAttributes(attributes: ModelAttributes): void;
/**
* Merge new attributes into this model, both locally and with persistence.
*
* @param attributes The attributes to save. If a 'relationships' key
* exists, it will be extracted and relationships will also be saved.
*/
save(attributes: SaveAttributes, options?: Omit<FlarumRequestOptions<ApiPayloadSingle>, 'url'> & {
meta?: MetaInformation;
}): Promise<ApiResponseSingle<this>>;
/**
* Send a request to delete the resource.
*
* @param body Data to send along with the DELETE request.
*/
delete(body?: FlarumRequestOptions<void>['body'], options?: Omit<FlarumRequestOptions<void>, 'url'>): Promise<void>;
/**
* Construct a path to the API endpoint for this resource.
*/
protected apiEndpoint(): string;
protected copyData(): ModelData;
protected rawRelationship<M extends Model>(relationship: string): undefined | ModelIdentifier;
protected rawRelationship<M extends Model[]>(relationship: string): undefined | ModelIdentifier[];
/** /**
* Generate a function which returns the value of the given attribute. * Generate a function which returns the value of the given attribute.
* *
* @param transform A function to transform the attribute value * @param {String} name
* @param {function} [transform] A function to transform the attribute value
* @return {*}
* @public
*/ */
static attribute<T>(name: string): () => T; public static attribute(name: string, transform?: Function | undefined): any;
static attribute<T, O = unknown>(name: string, transform: (attr: O) => T): () => T;
/** /**
* Generate a function which returns the value of the given has-one * Generate a function which returns the value of the given has-one
* relationship. * relationship.
* *
* @return false if no information about the * @param {String} name
* @return {Model|Boolean|undefined} false if no information about the
* relationship exists; undefined if the relationship exists but the model * relationship exists; undefined if the relationship exists but the model
* has not been loaded; or the model if it has been loaded. * has not been loaded; or the model if it has been loaded.
* @public
*/ */
static hasOne<M extends Model>(name: string): () => M | false; public static hasOne(name: string): Model | boolean | undefined;
static hasOne<M extends Model | null>(name: string): () => M | null | false;
/** /**
* Generate a function which returns the value of the given has-many * Generate a function which returns the value of the given has-many
* relationship. * relationship.
* *
* @return false if no information about the relationship * @param {String} name
* @return {Array|Boolean} false if no information about the relationship
* exists; an array if it does, containing models if they have been * exists; an array if it does, containing models if they have been
* loaded, and undefined for those that have not. * loaded, and undefined for those that have not.
* @public
*/ */
static hasMany<M extends Model>(name: string): () => (M | undefined)[] | false; public static hasMany(name: string): any[] | boolean;
/** /**
* Transform the given value into a Date object. * Transform the given value into a Date object.
*
* @param {String} value
* @return {Date|null}
* @public
*/ */
static transformDate(value: string): Date; public static transformDate(value: string): Date | null;
static transformDate(value: string | null): Date | null;
static transformDate(value: string | undefined): Date | undefined;
static transformDate(value: string | null | undefined): Date | null | undefined;
/** /**
* Get a resource identifier object for the given model. * Get a resource identifier object for the given model.
*
* @param {Model} model
* @return {Object}
* @protected
*/ */
protected static getIdentifier(model: Model): ModelIdentifier; protected static getIdentifier(model: Model): Object;
/**
* @param {Object} data A resource object from the API.
* @param {Store} store The data store that this model should be persisted to.
* @public
*/
constructor(data?: Object, store?: any);
/**
* The resource object from the API.
*
* @type {Object}
* @public
*/
public data: Object;
/**
* The time at which the model's data was last updated. Watching the value
* of this property is a fast way to retain/cache a subtree if data hasn't
* changed.
*
* @type {Date}
* @public
*/
public freshness: Date;
/**
* Whether or not the resource exists on the server.
*
* @type {Boolean}
* @public
*/
public exists: boolean;
/**
* The data store that this resource should be persisted to.
*
* @type {Store}
* @protected
*/
protected store: any;
/**
* Get the model's ID.
*
* @return {Integer}
* @public
* @final
*/
public id(): any;
/**
* Get one of the model's attributes.
*
* @param {String} attribute
* @return {*}
* @public
* @final
*/
public attribute(attribute: string): any;
/**
* Merge new data into this model locally.
*
* @param {Object} data A resource object to merge into this model
* @public
*/
public pushData(data: Object): void;
/**
* Merge new attributes into this model locally.
*
* @param {Object} attributes The attributes to merge.
* @public
*/
public pushAttributes(attributes: Object): void;
/**
* Merge new attributes into this model, both locally and with persistence.
*
* @param {Object} attributes The attributes to save. If a 'relationships' key
* exists, it will be extracted and relationships will also be saved.
* @param {Object} [options]
* @return {Promise}
* @public
*/
public save(attributes: Object, options?: Object | undefined): Promise<any>;
/**
* Send a request to delete the resource.
*
* @param {Object} body Data to send along with the DELETE request.
* @param {Object} [options]
* @return {Promise}
* @public
*/
public delete(body: Object, options?: Object | undefined): Promise<any>;
/**
* Construct a path to the API endpoint for this resource.
*
* @return {String}
* @protected
*/
protected apiEndpoint(): string;
copyData(): any;
} }

View File

@@ -1,33 +1,37 @@
import User from './models/User';
import { FlarumRequestOptions } from './Application';
export declare type LoginParams = {
/**
* The username/email
*/
identification: string;
password: string;
remember: boolean;
};
/** /**
* The `Session` class defines the current user session. It stores a reference * The `Session` class defines the current user session. It stores a reference
* to the current authenticated user, and provides methods to log in/out. * to the current authenticated user, and provides methods to log in/out.
*/ */
export default class Session { export default class Session {
constructor(user: any, csrfToken: any);
/** /**
* The current authenticated user. * The current authenticated user.
*
* @type {User|null}
* @public
*/ */
user: User | null; public user: any | null;
/** /**
* The CSRF token. * The CSRF token.
*
* @type {String|null}
* @public
*/ */
csrfToken: string; public csrfToken: string | null;
constructor(user: User | null, csrfToken: string);
/** /**
* Attempt to log in a user. * Attempt to log in a user.
*
* @param {String} identification The username/email.
* @param {String} password
* @param {Object} [options]
* @return {Promise}
* @public
*/ */
login(body: LoginParams, options?: Omit<FlarumRequestOptions<any>, 'url' | 'body' | 'method'>): Promise<any>; public login(body: any, options?: Object | undefined): Promise<any>;
/** /**
* Log the user out. * Log the user out.
*
* @public
*/ */
logout(): void; public logout(): void;
} }

View File

@@ -1,127 +1,97 @@
import { FlarumRequestOptions } from './Application';
import Model, { ModelData, SavedModelData } from './Model';
export interface MetaInformation {
[key: string]: any;
}
export interface ApiQueryParamsSingle {
fields?: string[];
include?: string;
bySlug?: boolean;
meta?: MetaInformation;
}
export interface ApiQueryParamsPlural {
fields?: string[];
include?: string;
filter?: {
q: string;
[key: string]: string;
};
page?: {
offset?: number;
number?: number;
limit?: number;
size?: number;
};
sort?: string;
meta?: MetaInformation;
}
export declare type ApiQueryParams = ApiQueryParamsPlural | ApiQueryParamsSingle;
export interface ApiPayloadSingle {
data: SavedModelData;
included?: SavedModelData[];
meta?: MetaInformation;
}
export interface ApiPayloadPlural {
data: SavedModelData[];
included?: SavedModelData[];
links?: {
first: string;
next?: string;
prev?: string;
};
meta?: MetaInformation;
}
export declare type ApiPayload = ApiPayloadSingle | ApiPayloadPlural;
export declare type ApiResponseSingle<M extends Model> = M & {
payload: ApiPayloadSingle;
};
export declare type ApiResponsePlural<M extends Model> = M[] & {
payload: ApiPayloadPlural;
};
export declare type ApiResponse<M extends Model> = ApiResponseSingle<M> | ApiResponsePlural<M>;
interface ApiQueryRequestOptions<ResponseType> extends Omit<FlarumRequestOptions<ResponseType>, 'url'> {
}
interface StoreData {
[type: string]: Partial<Record<string, Model>>;
}
export declare function payloadIsPlural(payload: ApiPayload): payload is ApiPayloadPlural;
/** /**
* The `Store` class defines a local data store, and provides methods to * The `Store` class defines a local data store, and provides methods to
* retrieve data from the API. * retrieve data from the API.
*/ */
export default class Store { export default class Store {
constructor(models: any);
/** /**
* The local data store. A tree of resource types to IDs, such that * The local data store. A tree of resource types to IDs, such that
* accessing data[type][id] will return the model for that type/ID. * accessing data[type][id] will return the model for that type/ID.
*
* @type {Object}
* @protected
*/ */
protected data: StoreData; protected data: Object;
/** /**
* The model registry. A map of resource types to the model class that * The model registry. A map of resource types to the model class that
* should be used to represent resources of that type. * should be used to represent resources of that type.
*
* @type {Object}
* @public
*/ */
models: Record<string, typeof Model>; public models: Object;
constructor(models: Record<string, typeof Model>);
/** /**
* Push resources contained within an API payload into the store. * Push resources contained within an API payload into the store.
* *
* @return The model(s) representing the resource(s) contained * @param {Object} payload
* @return {Model|Model[]} The model(s) representing the resource(s) contained
* within the 'data' key of the payload. * within the 'data' key of the payload.
* @public
*/ */
pushPayload<M extends Model>(payload: ApiPayloadSingle): ApiResponseSingle<M>; public pushPayload(payload: Object): any | any[];
pushPayload<Ms extends Model[]>(payload: ApiPayloadPlural): ApiResponseSingle<Ms[number]>;
/** /**
* Create a model to represent a resource object (or update an existing one), * Create a model to represent a resource object (or update an existing one),
* and push it into the store. * and push it into the store.
* *
* @param data The resource object * @param {Object} data The resource object
* @return The model, or null if no model class has been * @return {Model|null} The model, or null if no model class has been
* registered for this resource type. * registered for this resource type.
* @public
*/ */
pushObject<M extends Model>(data: SavedModelData): M | null; public pushObject(data: Object): any | null;
pushObject<M extends Model>(data: SavedModelData, allowUnregistered: false): M;
/** /**
* Make a request to the API to find record(s) of a specific type. * Make a request to the API to find record(s) of a specific type.
*
* @param {String} type The resource type.
* @param {Integer|Integer[]|Object} [id] The ID(s) of the model(s) to retrieve.
* Alternatively, if an object is passed, it will be handled as the
* `query` parameter.
* @param {Object} [query]
* @param {Object} [options]
* @return {Promise}
* @public
*/ */
find<M extends Model>(type: string, params: ApiQueryParamsSingle): Promise<ApiResponseSingle<M>>; public find(type: string, id?: any | any[] | Object, query?: Object | undefined, options?: Object | undefined): Promise<any>;
find<Ms extends Model[]>(type: string, params: ApiQueryParamsPlural): Promise<ApiResponsePlural<Ms[number]>>;
find<M extends Model>(type: string, id: string, params?: ApiQueryParamsSingle, options?: ApiQueryRequestOptions<ApiPayloadSingle>): Promise<ApiResponseSingle<M>>;
find<Ms extends Model[]>(type: string, ids: string[], params?: ApiQueryParamsPlural, options?: ApiQueryRequestOptions<ApiPayloadPlural>): Promise<ApiResponsePlural<Ms[number]>>;
/** /**
* Get a record from the store by ID. * Get a record from the store by ID.
*
* @param {String} type The resource type.
* @param {Integer} id The resource ID.
* @return {Model}
* @public
*/ */
getById<M extends Model>(type: string, id: string): M | undefined; public getById(type: string, id: any): any;
/** /**
* Get a record from the store by the value of a model attribute. * Get a record from the store by the value of a model attribute.
* *
* @param type The resource type. * @param {String} type The resource type.
* @param key The name of the method on the model. * @param {String} key The name of the method on the model.
* @param value The value of the model attribute. * @param {*} value The value of the model attribute.
* @return {Model}
* @public
*/ */
getBy<M extends Model, T = unknown>(type: string, key: keyof M, value: T): M | undefined; public getBy(type: string, key: string, value: any): any;
/** /**
* Get all loaded records of a specific type. * Get all loaded records of a specific type.
*
* @param {String} type
* @return {Model[]}
* @public
*/ */
all<M extends Model>(type: string): M[]; public all(type: string): any[];
/** /**
* Remove the given model from the store. * Remove the given model from the store.
*
* @param {Model} model
*/ */
remove(model: Model): void; remove(model: any): void;
/** /**
* Create a new record of the given type. * Create a new record of the given type.
* *
* @param type The resource type * @param {String} type The resource type
* @param data Any data to initialize the model with * @param {Object} [data] Any data to initialize the model with
* @return {Model}
* @public
*/ */
createRecord<M extends Model>(type: string, data?: ModelData): M; public createRecord(type: string, data?: Object | undefined): any;
} }
export {};

View File

@@ -1,6 +1,3 @@
/// <reference path="../../src/common/translator-icu-rich.d.ts" />
import { RichMessageFormatter } from '@askvortsov/rich-icu-message-formatter';
import { pluralTypeHandler, selectTypeHandler } from '@ultraq/icu-message-formatter';
declare type Translations = Record<string, string>; declare type Translations = Record<string, string>;
declare type TranslatorParameters = Record<string, unknown>; declare type TranslatorParameters = Record<string, unknown>;
export default class Translator { export default class Translator {
@@ -11,15 +8,15 @@ export default class Translator {
/** /**
* The underlying ICU MessageFormatter util. * The underlying ICU MessageFormatter util.
*/ */
protected formatter: RichMessageFormatter; protected formatter: any;
setLocale(locale: string): void; setLocale(locale: string): void;
addTranslations(translations: Translations): void; addTranslations(translations: Translations): void;
/** /**
* An extensible entrypoint for extenders to register type handlers for translations. * An extensible entrypoint for extenders to register type handlers for translations.
*/ */
protected formatterTypeHandlers(): { protected formatterTypeHandlers(): {
plural: typeof pluralTypeHandler; plural: any;
select: typeof selectTypeHandler; select: any;
}; };
/** /**
* A temporary system to preprocess parameters. * A temporary system to preprocess parameters.
@@ -29,6 +26,6 @@ export default class Translator {
* @internal * @internal
*/ */
protected preprocessParameters(parameters: TranslatorParameters): TranslatorParameters; protected preprocessParameters(parameters: TranslatorParameters): TranslatorParameters;
trans(id: string, parameters?: TranslatorParameters): import("@askvortsov/rich-icu-message-formatter").NestedStringArray; trans(id: string, parameters?: TranslatorParameters): any;
} }
export {}; export {};

View File

@@ -1,5 +1,5 @@
declare var _default: { declare var _default: {
extend: any; extend: typeof extend;
Session: typeof Session; Session: typeof Session;
Store: typeof Store; Store: typeof Store;
'utils/BasicEditorDriver': typeof BasicEditorDriver; 'utils/BasicEditorDriver': typeof BasicEditorDriver;
@@ -32,15 +32,17 @@ declare var _default: {
'utils/subclassOf': typeof subclassOf; 'utils/subclassOf': typeof subclassOf;
'utils/setRouteWithForcedRefresh': typeof setRouteWithForcedRefresh; 'utils/setRouteWithForcedRefresh': typeof setRouteWithForcedRefresh;
'utils/patchMithril': typeof patchMithril; 'utils/patchMithril': typeof patchMithril;
'utils/proxifyCompat': typeof proxifyCompat; 'utils/proxifyCompat': (compat: {
[key: string]: any;
}, namespace: string) => {
[key: string]: any;
};
'utils/classList': (...classes: import("clsx").ClassValue[]) => string; 'utils/classList': (...classes: import("clsx").ClassValue[]) => string;
'utils/extractText': typeof extractText; 'utils/extractText': typeof extractText;
'utils/formatNumber': typeof formatNumber; 'utils/formatNumber': typeof formatNumber;
'utils/mapRoutes': typeof mapRoutes; 'utils/mapRoutes': typeof mapRoutes;
'utils/withAttr': (key: string, cb: Function) => (this: Element) => void; 'utils/withAttr': (key: string, cb: Function) => (this: Element) => void;
'utils/throttleDebounce': typeof ThrottleDebounce; 'utils/throttleDebounce': typeof ThrottleDebounce;
'utils/isObject': typeof isObject;
'utils/focusTrap': typeof FocusTrap;
'models/Notification': typeof Notification; 'models/Notification': typeof Notification;
'models/User': typeof User; 'models/User': typeof User;
'models/Post': typeof Post; 'models/Post': typeof Post;
@@ -67,7 +69,6 @@ declare var _default: {
'components/Link': typeof Link; 'components/Link': typeof Link;
'components/LinkButton': typeof LinkButton; 'components/LinkButton': typeof LinkButton;
'components/Checkbox': typeof Checkbox; 'components/Checkbox': typeof Checkbox;
'components/ColorPreviewInput': typeof ColorPreviewInput;
'components/SelectDropdown': typeof SelectDropdown; 'components/SelectDropdown': typeof SelectDropdown;
'components/ModalManager': typeof ModalManager; 'components/ModalManager': typeof ModalManager;
'components/Button': typeof Button; 'components/Button': typeof Button;
@@ -92,6 +93,7 @@ declare var _default: {
'states/PaginatedListState': typeof PaginatedListState; 'states/PaginatedListState': typeof PaginatedListState;
}; };
export default _default; export default _default;
import * as extend from "./extend";
import Session from "./Session"; import Session from "./Session";
import Store from "./Store"; import Store from "./Store";
import BasicEditorDriver from "./utils/BasicEditorDriver"; import BasicEditorDriver from "./utils/BasicEditorDriver";
@@ -116,13 +118,10 @@ import Stream from "./utils/Stream";
import subclassOf from "./utils/subclassOf"; import subclassOf from "./utils/subclassOf";
import setRouteWithForcedRefresh from "./utils/setRouteWithForcedRefresh"; import setRouteWithForcedRefresh from "./utils/setRouteWithForcedRefresh";
import patchMithril from "./utils/patchMithril"; import patchMithril from "./utils/patchMithril";
import proxifyCompat from "./utils/proxifyCompat";
import extractText from "./utils/extractText"; import extractText from "./utils/extractText";
import formatNumber from "./utils/formatNumber"; import formatNumber from "./utils/formatNumber";
import mapRoutes from "./utils/mapRoutes"; import mapRoutes from "./utils/mapRoutes";
import * as ThrottleDebounce from "./utils/throttleDebounce"; import * as ThrottleDebounce from "./utils/throttleDebounce";
import isObject from "./utils/isObject";
import * as FocusTrap from "./utils/focusTrap";
import Notification from "./models/Notification"; import Notification from "./models/Notification";
import User from "./models/User"; import User from "./models/User";
import Post from "./models/Post"; import Post from "./models/Post";
@@ -149,7 +148,6 @@ import Alert from "./components/Alert";
import Link from "./components/Link"; import Link from "./components/Link";
import LinkButton from "./components/LinkButton"; import LinkButton from "./components/LinkButton";
import Checkbox from "./components/Checkbox"; import Checkbox from "./components/Checkbox";
import ColorPreviewInput from "./components/ColorPreviewInput";
import SelectDropdown from "./components/SelectDropdown"; import SelectDropdown from "./components/SelectDropdown";
import ModalManager from "./components/ModalManager"; import ModalManager from "./components/ModalManager";
import Button from "./components/Button"; import Button from "./components/Button";

View File

@@ -15,5 +15,5 @@ export interface AlertAttrs extends ComponentAttrs {
* some controls, and may be dismissible. * some controls, and may be dismissible.
*/ */
export default class Alert<T extends AlertAttrs = AlertAttrs> extends Component<T> { export default class Alert<T extends AlertAttrs = AlertAttrs> extends Component<T> {
view(vnode: Mithril.VnodeDOM<T, this>): JSX.Element; view(vnode: Mithril.Vnode): JSX.Element;
} }

View File

@@ -60,8 +60,8 @@ export interface IButtonAttrs extends ComponentAttrs {
* styles can be applied by providing `className="Button"` to the Button component. * styles can be applied by providing `className="Button"` to the Button component.
*/ */
export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> extends Component<CustomAttrs> { export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> extends Component<CustomAttrs> {
view(vnode: Mithril.VnodeDOM<CustomAttrs, this>): JSX.Element; view(vnode: Mithril.Vnode<IButtonAttrs, never>): JSX.Element;
oncreate(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void; oncreate(vnode: Mithril.VnodeDOM<IButtonAttrs, this>): void;
/** /**
* Get the template for the button's content. * Get the template for the button's content.
*/ */

View File

@@ -1,6 +0,0 @@
import type Mithril from 'mithril';
import Component, { ComponentAttrs } from '../Component';
export default class ColorPreviewInput extends Component {
value?: string;
view(vnode: Mithril.Vnode<ComponentAttrs, this>): JSX.Element;
}

View File

@@ -1,31 +1,25 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" /> /**
import Modal, { IInternalModalAttrs } from './Modal'; * The `EditUserModal` component displays a modal dialog with a login form.
import ItemList from '../utils/ItemList'; */
import Stream from '../utils/Stream'; export default class EditUserModal extends Modal {
import type Mithril from 'mithril'; username: Stream<any> | undefined;
import type User from '../models/User'; email: Stream<any> | undefined;
import type { SaveAttributes } from '../Model'; isEmailConfirmed: Stream<any> | undefined;
export interface IEditUserModalAttrs extends IInternalModalAttrs { setPassword: Stream<boolean> | undefined;
user: User; password: Stream<any> | undefined;
} groups: {} | undefined;
export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEditUserModalAttrs> extends Modal<CustomAttrs> { fields(): ItemList;
protected username: Stream<string>;
protected email: Stream<string>;
protected isEmailConfirmed: Stream<boolean>;
protected setPassword: Stream<boolean>;
protected password: Stream<string>;
protected groups: Record<string, Stream<boolean>>;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
className(): string;
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
content(): JSX.Element;
fields(): ItemList<unknown>;
activate(): void; activate(): void;
data(): SaveAttributes; data(): {
onsubmit(e: SubmitEvent): void; relationships: {};
nonAdminEditingAdmin(): boolean | null; };
nonAdminEditingAdmin(): any;
/** /**
* @internal * @internal
* @protected
*/ */
protected userIsAdmin(user: User | null): boolean | null; protected userIsAdmin(user: any): any;
} }
import Modal from "./Modal";
import Stream from "../utils/Stream";
import ItemList from "../utils/ItemList";

View File

@@ -1,68 +1,67 @@
import Component from '../Component';
import { AlertAttrs } from './Alert';
import type Mithril from 'mithril';
import type ModalManagerState from '../states/ModalManagerState';
import type RequestError from '../utils/RequestError';
import type ModalManager from './ModalManager';
export interface IInternalModalAttrs {
state: ModalManagerState;
animateShow: ModalManager['animateShow'];
animateHide: ModalManager['animateHide'];
}
/** /**
* The `Modal` component displays a modal dialog, wrapped in a form. Subclasses * The `Modal` component displays a modal dialog, wrapped in a form. Subclasses
* should implement the `className`, `title`, and `content` methods. * should implement the `className`, `title`, and `content` methods.
*
* @abstract
*/ */
export default abstract class Modal<ModalAttrs extends IInternalModalAttrs = IInternalModalAttrs> extends Component<ModalAttrs> { export default class Modal extends Component<import("../Component").ComponentAttrs, undefined> {
/** /**
* Determine whether or not the modal should be dismissible via an 'x' button. * Determine whether or not the modal should be dismissible via an 'x' button.
*/ */
static readonly isDismissible: boolean; static isDismissible: boolean;
protected loading: boolean; constructor();
/** /**
* Attributes for an alert component to show below the header. * Attributes for an alert component to show below the header.
*
* @type {object}
*/ */
alertAttrs: AlertAttrs | null; alertAttrs: object;
oninit(vnode: Mithril.Vnode<ModalAttrs, this>): void;
oncreate(vnode: Mithril.VnodeDOM<ModalAttrs, this>): void;
onbeforeremove(vnode: Mithril.VnodeDOM<ModalAttrs, this>): Promise<void> | void;
/**
* @todo split into FormModal and Modal in 2.0
*/
view(): JSX.Element;
/** /**
* Get the class name to apply to the modal. * Get the class name to apply to the modal.
*
* @return {String}
* @abstract
*/ */
abstract className(): string; className(): string;
/** /**
* Get the title of the modal dialog. * Get the title of the modal dialog.
*
* @return {String}
* @abstract
*/ */
abstract title(): Mithril.Children; title(): string;
/** /**
* Get the content of the modal. * Get the content of the modal.
*
* @return {VirtualElement}
* @abstract
*/ */
abstract content(): Mithril.Children; content(): any;
/** /**
* Handle the modal form's submit event. * Handle the modal form's submit event.
*/
onsubmit(e: SubmitEvent): void;
/**
* Callback executed when the modal is shown and ready to be interacted with.
* *
* @remark Focuses the first input in the modal. * @param {Event} e
*/
onsubmit(): void;
/**
* Focus on the first input when the modal is ready to be used.
*/ */
onready(): void; onready(): void;
/** /**
* Hides the modal. * Hide the modal.
*/ */
hide(): void; hide(): void;
/** /**
* Sets `loading` to false and triggers a redraw. * Stop loading.
*/ */
loaded(): void; loaded(): void;
loading: boolean | undefined;
/** /**
* Shows an alert describing an error returned from the API, and gives focus to * Show an alert describing an error returned from the API, and give focus to
* the first relevant field involved in the error. * the first relevant field.
*
* @param {RequestError} error
*/ */
onerror(error: RequestError): void; onerror(error: any): void;
} }
import Component from "../Component";

View File

@@ -1,25 +1,11 @@
import Component from '../Component';
import { FocusTrap } from '../utils/focusTrap';
import type ModalManagerState from '../states/ModalManagerState';
import type Mithril from 'mithril';
interface IModalManagerAttrs {
state: ModalManagerState;
}
/** /**
* The `ModalManager` component manages a modal dialog. Only one modal dialog * The `ModalManager` component manages a modal dialog. Only one modal dialog
* can be shown at once; loading a new component into the ModalManager will * can be shown at once; loading a new component into the ModalManager will
* overwrite the previous one. * overwrite the previous one.
*/ */
export default class ModalManager extends Component<IModalManagerAttrs> { export default class ModalManager extends Component<import("../Component").ComponentAttrs, undefined> {
protected focusTrap: FocusTrap | undefined; constructor();
/** animateShow(readyCallback: any): void;
* Whether a modal is currently shown by this modal manager.
*/
protected modalShown: boolean;
view(vnode: Mithril.VnodeDOM<IModalManagerAttrs, this>): Mithril.Children;
oncreate(vnode: Mithril.VnodeDOM<IModalManagerAttrs, this>): void;
onupdate(vnode: Mithril.VnodeDOM<IModalManagerAttrs, this>): void;
animateShow(readyCallback: () => void): void;
animateHide(): void; animateHide(): void;
} }
export {}; import Component from "../Component";

View File

@@ -1,4 +1,3 @@
import type Mithril from 'mithril';
import Component from '../Component'; import Component from '../Component';
export interface IPageAttrs { export interface IPageAttrs {
key?: number; key?: number;
@@ -10,19 +9,7 @@ export interface IPageAttrs {
* @abstract * @abstract
*/ */
export default abstract class Page<CustomAttrs extends IPageAttrs = IPageAttrs> extends Component<CustomAttrs> { export default abstract class Page<CustomAttrs extends IPageAttrs = IPageAttrs> extends Component<CustomAttrs> {
/** oninit(vnode: any): void;
* A class name to apply to the body while the route is active. oncreate(vnode: any): void;
*/ onremove(vnode: any): void;
protected bodyClass: string;
/**
* Whether we should scroll to the top of the page when its rendered.
*/
protected scrollTopOnCreate: boolean;
/**
* Whether the browser should restore scroll state on refreshes.
*/
protected useBrowserScrollRestoration: boolean;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
oncreate(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void;
onremove(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void;
} }

View File

@@ -1,12 +1,3 @@
/// <reference types="mithril" /> export default class RequestErrorModal extends Modal {
import type RequestError from '../utils/RequestError';
import Modal, { IInternalModalAttrs } from './Modal';
export interface IRequestErrorModalAttrs extends IInternalModalAttrs {
error: RequestError;
formattedError: string[];
}
export default class RequestErrorModal<CustomAttrs extends IRequestErrorModalAttrs = IRequestErrorModalAttrs> extends Modal<CustomAttrs> {
className(): string;
title(): string;
content(): JSX.Element;
} }
import Modal from "./Modal";

View File

@@ -38,13 +38,13 @@ export default class TextEditor extends Component<import("../Component").Compone
* *
* @return {ItemList} * @return {ItemList}
*/ */
controlItems(): ItemList<any>; controlItems(): ItemList;
/** /**
* Build an item list for the toolbar controls. * Build an item list for the toolbar controls.
* *
* @return {ItemList} * @return {ItemList}
*/ */
toolbarItems(): ItemList<any>; toolbarItems(): ItemList;
/** /**
* Handle input into the textarea. * Handle input into the textarea.
* *

View File

@@ -19,11 +19,11 @@
* // something that needs to be run on creation and update * // something that needs to be run on creation and update
* }); * });
* *
* @param object The object that owns the method * @param {object} object The object that owns the method
* @param methods The name or names of the method(s) to extend * @param {string|string[]} methods The name or names of the method(s) to extend
* @param callback A callback which mutates the method's output * @param {function} callback A callback which mutates the method's output
*/ */
export declare function extend<T extends object, K extends KeyOfType<T, Function>>(object: T, methods: K | K[], callback: (this: T, val: ReturnType<T[K]>, ...args: Parameters<T[K]>) => void): void; export function extend(object: object, methods: string | string[], callback: Function): void;
/** /**
* Override an object's method by replacing it with a new function, so that the * Override an object's method by replacing it with a new function, so that the
* new function will be run every time the object's method is called. * new function will be run every time the object's method is called.
@@ -47,8 +47,8 @@ export declare function extend<T extends object, K extends KeyOfType<T, Function
* // something that needs to be run on creation and update * // something that needs to be run on creation and update
* }); * });
* *
* @param object The object that owns the method * @param {object} object The object that owns the method
* @param methods The name or names of the method(s) to override * @param {string|string[]} method The name or names of the method(s) to override
* @param newMethod The method to replace it with * @param {function} newMethod The method to replace it with
*/ */
export declare function override<T extends object, K extends KeyOfType<T, Function>>(object: T, methods: K | K[], newMethod: (this: T, orig: T[K], ...args: Parameters<T[K]>) => void): void; export function override(object: object, methods: any, newMethod: Function): void;

View File

@@ -1,5 +1,5 @@
export default class Model { export default class Model {
constructor(type: any, model?: null); constructor(type: any, model?: any);
type: any; type: any;
attributes: any[]; attributes: any[];
hasOnes: any[]; hasOnes: any[];

View File

@@ -1,12 +1,9 @@
import type Mithril from 'mithril'; import type Mithril from 'mithril';
import type { ComponentAttrs } from '../Component';
import User from '../models/User'; import User from '../models/User';
export interface AvatarAttrs extends ComponentAttrs {
}
/** /**
* The `avatar` helper displays a user's avatar. * The `avatar` helper displays a user's avatar.
* *
* @param user * @param user
* @param attrs Attributes to apply to the avatar element * @param attrs Attributes to apply to the avatar element
*/ */
export default function avatar(user: User, attrs?: ComponentAttrs): Mithril.Vnode; export default function avatar(user: User, attrs?: Object): Mithril.Vnode;

View File

@@ -1,34 +1,6 @@
import type Mithril from 'mithril'; import type Mithril from 'mithril';
import { ComponentAttrs } from '../Component';
declare type ModdedVnodeAttrs = {
itemClassName?: string;
key?: string;
};
declare type ModdedTag = Mithril.Vnode['tag'] & {
isListItem?: boolean;
isActive?: (attrs: ComponentAttrs) => boolean;
};
declare type ModdedVnode = Mithril.Vnode<ModdedVnodeAttrs> & {
itemName?: string;
itemClassName?: string;
tag: ModdedTag;
};
declare type ModdedChild = ModdedVnode | string | number | boolean | null | undefined;
declare type ModdedChildArray = ModdedChildren[];
declare type ModdedChildren = ModdedChild | ModdedChildArray;
/** /**
* This type represents an element of a list returned by `ItemList.toArray()`, * The `listItems` helper wraps a collection of components in <li> tags,
* coupled with some static properties used on various components.
*/
export declare type ModdedChildrenWithItemName = ModdedChildren & {
itemName?: string;
};
/**
* The `listItems` helper wraps an array of components in the provided tag,
* stripping out any unnecessary `Separator` components. * stripping out any unnecessary `Separator` components.
*
* By default, this tag is an `<li>` tag, but this is customisable through the
* second function parameter, `customTag`.
*/ */
export default function listItems<Attrs extends ComponentAttrs>(rawItems: ModdedChildrenWithItemName[], customTag?: VnodeElementTag<Attrs>, attributes?: Attrs): Mithril.Vnode[]; export default function listItems(items: Mithril.Vnode | Array<Mithril.Vnode>): Array<Mithril.Vnode>;
export {};

View File

@@ -3,4 +3,4 @@ import User from '../models/User';
/** /**
* The `useronline` helper displays a green circle if the user is online * The `useronline` helper displays a green circle if the user is online
*/ */
export default function userOnline(user: User): Mithril.Vnode<{}, {}> | null; export default function userOnline(user: User): Mithril.Vnode;

View File

@@ -4,4 +4,4 @@ import User from '../models/User';
* The `username` helper displays a user's username in a <span class="username"> * The `username` helper displays a user's username in a <span class="username">
* tag. If the user doesn't exist, the username will be displayed as [deleted]. * tag. If the user doesn't exist, the username will be displayed as [deleted].
*/ */
export default function username(user: User | null | undefined | false): Mithril.Vnode; export default function username(user: User): Mithril.Vnode;

View File

@@ -1,48 +1,3 @@
import Model from '../Model';
import ItemList from '../utils/ItemList';
import Mithril from 'mithril';
import Post from './Post';
import User from './User';
export default class Discussion extends Model { export default class Discussion extends Model {
title(): string;
slug(): string;
createdAt(): Date | undefined;
user(): false | User | null;
firstPost(): false | Post | null;
lastPostedAt(): Date | null | undefined;
lastPostedUser(): false | User | null;
lastPost(): false | Post | null;
lastPostNumber(): number | null | undefined;
commentCount(): number | undefined;
replyCount(): Number;
posts(): false | (Post | undefined)[];
mostRelevantPost(): false | Post | null;
lastReadAt(): Date | null | undefined;
lastReadPostNumber(): number | null | undefined;
isUnread(): boolean;
isRead(): boolean;
hiddenAt(): Date | null | undefined;
hiddenUser(): false | User | null;
isHidden(): boolean;
canReply(): boolean | undefined;
canRename(): boolean | undefined;
canHide(): boolean | undefined;
canDelete(): boolean | undefined;
/**
* Remove a post from the discussion's posts relationship.
*/
removePost(id: string): void;
/**
* Get the estimated number of unread posts in this discussion for the current
* user.
*/
unreadCount(): number;
/**
* Get the Badge components that apply to this discussion.
*/
badges(): ItemList<Mithril.Children>;
/**
* Get a list of all of the post IDs in this discussion.
*/
postIds(): string[];
} }
import Model from "../Model";

View File

@@ -1,4 +1,3 @@
import Model from '../Model';
export default class Forum extends Model { export default class Forum extends Model {
apiEndpoint(): string;
} }
import Model from "../Model";

View File

@@ -1,11 +1,9 @@
import Model from '../Model'; export default Group;
export default class Group extends Model { declare class Group extends Model {
static ADMINISTRATOR_ID: string;
static GUEST_ID: string;
static MEMBER_ID: string;
nameSingular(): string;
namePlural(): string;
color(): string | null;
icon(): string | null;
isHidden(): boolean;
} }
declare namespace Group {
const ADMINISTRATOR_ID: string;
const GUEST_ID: string;
const MEMBER_ID: string;
}
import Model from "../Model";

View File

@@ -1,11 +1,3 @@
import Model from '../Model';
import User from './User';
export default class Notification extends Model { export default class Notification extends Model {
contentType(): string;
content(): string;
createdAt(): Date;
isRead(): boolean;
user(): false | User;
fromUser(): false | User | null;
subject(): false | Model | null;
} }
import Model from "../Model";

View File

@@ -1,23 +1,3 @@
import Model from '../Model';
import Discussion from './Discussion';
import User from './User';
export default class Post extends Model { export default class Post extends Model {
number(): number;
discussion(): Discussion;
createdAt(): Date;
user(): false | User;
contentType(): string | null;
content(): string | null | undefined;
contentHtml(): string | null | undefined;
renderFailed(): boolean | undefined;
contentPlain(): string | null | undefined;
editedAt(): Date | null | undefined;
editedUser(): false | User | null;
isEdited(): boolean;
hiddenAt(): Date | null | undefined;
hiddenUser(): false | User | null;
isHidden(): boolean;
canEdit(): boolean | undefined;
canHide(): boolean | undefined;
canDelete(): boolean | undefined;
} }
import Model from "../Model";

View File

@@ -1,46 +1,3 @@
import { Color } from 'color-thief-browser';
import Model from '../Model';
import ItemList from '../utils/ItemList';
import Mithril from 'mithril';
import Group from './Group';
export default class User extends Model { export default class User extends Model {
username(): string;
slug(): string;
displayName(): string;
email(): string | undefined;
isEmailConfirmed(): boolean | undefined;
password(): string | undefined;
avatarUrl(): string | null;
preferences(): Record<string, any> | null | undefined;
groups(): false | (Group | undefined)[];
joinTime(): Date | null | undefined;
lastSeenAt(): Date | null | undefined;
markedAllAsReadAt(): Date | null | undefined;
unreadNotificationCount(): number | undefined;
newNotificationCount(): number | undefined;
discussionCount(): number | undefined;
commentCount(): number | undefined;
canEdit(): boolean | undefined;
canEditCredentials(): boolean | undefined;
canEditGroups(): boolean | undefined;
canDelete(): boolean | undefined;
color(): string;
protected avatarColor: Color | null;
/**
* Check whether or not the user has been seen in the last 5 minutes.
*/
isOnline(): boolean;
/**
* Get the Badge components that apply to this user.
*/
badges(): ItemList<Mithril.Children>;
/**
* Calculate the dominant color of the user's avatar. The dominant color will
* be set to the `avatarColor` property once it has been calculated.
*/
protected calculateAvatarColor(): void;
/**
* Update the user's preferences.
*/
savePreferences(newPreferences: Record<string, unknown>): Promise<this>;
} }
import Model from "../Model";

View File

@@ -1,30 +1,21 @@
import type Mithril from 'mithril'; import type Mithril from 'mithril';
import type { RouteResolver } from '../Application';
import type { default as Component, ComponentAttrs } from '../Component';
/** /**
* Generates a route resolver for a given component. * Generates a route resolver for a given component.
*
* In addition to regular route resolver functionality: * In addition to regular route resolver functionality:
* - It provide the current route name as an attr * - It provide the current route name as an attr
* - It sets a key on the component so a rerender will be triggered on route change. * - It sets a key on the component so a rerender will be triggered on route change.
*/ */
export default class DefaultResolver<Attrs extends ComponentAttrs, Comp extends Component<Attrs & { export default class DefaultResolver {
component: Mithril.Component;
routeName: string; routeName: string;
}>, RouteArgs extends Record<string, unknown> = {}> implements RouteResolver<Attrs, Comp, RouteArgs> { constructor(component: any, routeName: any);
component: new () => Comp;
routeName: string;
constructor(component: new () => Comp, routeName: string);
/** /**
* When a route change results in a changed key, a full page * When a route change results in a changed key, a full page
* rerender occurs. This method can be overriden in subclasses * rerender occurs. This method can be overriden in subclasses
* to prevent rerenders on some route changes. * to prevent rerenders on some route changes.
*/ */
makeKey(): string; makeKey(): string;
makeAttrs(vnode: Mithril.Vnode<Attrs, Comp>): Attrs & { makeAttrs(vnode: any): any;
routeName: string; onmatch(args: any, requestedPath: any, route: any): Mithril.Component<{}, {}>;
}; render(vnode: any): any[];
onmatch(args: RouteArgs, requestedPath: string, route: string): {
new (): Comp;
};
render(vnode: Mithril.Vnode<Attrs, Comp>): Mithril.Children;
} }

View File

@@ -24,11 +24,11 @@ export default class AlertManagerState {
*/ */
show(children: Mithril.Children): AlertIdentifier; show(children: Mithril.Children): AlertIdentifier;
show(attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier; show(attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier;
show(componentClass: typeof Alert, attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier; show(componentClass: Alert, attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier;
/** /**
* Dismiss an alert. * Dismiss an alert.
*/ */
dismiss(key: AlertIdentifier | null): void; dismiss(key: AlertIdentifier): void;
/** /**
* Clear all alerts. * Clear all alerts.
*/ */

View File

@@ -1,52 +1,19 @@
import type Component from '../Component';
import Modal from '../components/Modal';
/**
* Ideally, `show` would take a higher-kinded generic, ala:
* `show<Attrs, C>(componentClass: C<Attrs>, attrs: Attrs): void`
* Unfortunately, TypeScript does not support this:
* https://github.com/Microsoft/TypeScript/issues/1213
* Therefore, we have to use this ugly, messy workaround.
*/
declare type UnsafeModalClass = ComponentClass<any, Modal> & {
isDismissible: boolean;
component: typeof Component.component;
};
/**
* Class used to manage modal state.
*
* Accessible on the `app` object via `app.modal` property.
*/
export default class ModalManagerState { export default class ModalManagerState {
modal: {
componentClass: any;
attrs: any;
} | null;
/** /**
* @internal * Show a modal dialog.
*
* @public
*/ */
modal: null | { public show(componentClass: any, attrs: any): void;
componentClass: UnsafeModalClass;
attrs?: Record<string, unknown>;
};
private closeTimeout?;
/** /**
* Shows a modal dialog. * Close the modal dialog.
* *
* If a modal is already open, the existing one will close and the new modal will replace it. * @public
*
* @example <caption>Show a modal</caption>
* app.modal.show(MyCoolModal, { attr: 'value' });
*
* @example <caption>Show a modal from a lifecycle method (`oncreate`, `view`, etc.)</caption>
* // This "hack" is needed due to quirks with nested redraws in Mithril.
* setTimeout(() => app.modal.show(MyCoolModal, { attr: 'value' }), 0);
*/ */
show(componentClass: UnsafeModalClass, attrs?: Record<string, unknown>): void; public close(): void;
/** closeTimeout: number | undefined;
* Closes the currently open dialog, if one is open.
*/
close(): void;
/**
* Checks if a modal is currently open.
*
* @returns `true` if a modal dialog is currently open, otherwise `false`.
*/
isModalOpen(): boolean;
} }
export {};

View File

@@ -1,5 +1,4 @@
import Model from '../Model'; import Model from '../Model';
import { ApiQueryParamsPlural, ApiResponsePlural } from '../Store';
export interface Page<TModel> { export interface Page<TModel> {
number: number; number: number;
items: TModel[]; items: TModel[];
@@ -11,30 +10,24 @@ export interface PaginationLocation {
startIndex?: number; startIndex?: number;
endIndex?: number; endIndex?: number;
} }
export interface PaginatedListParams { export default abstract class PaginatedListState<T extends Model> {
[key: string]: any;
}
export interface PaginatedListRequestParams extends Omit<ApiQueryParamsPlural, 'include'> {
include?: string | string[];
}
export default abstract class PaginatedListState<T extends Model, P extends PaginatedListParams = PaginatedListParams> {
protected location: PaginationLocation; protected location: PaginationLocation;
protected pageSize: number; protected pageSize: number;
protected pages: Page<T>[]; protected pages: Page<T>[];
protected params: P; protected params: any;
protected initialLoading: boolean; protected initialLoading: boolean;
protected loadingPrev: boolean; protected loadingPrev: boolean;
protected loadingNext: boolean; protected loadingNext: boolean;
protected constructor(params?: P, page?: number, pageSize?: number); protected constructor(params?: any, page?: number, pageSize?: number);
abstract get type(): string; abstract get type(): string;
clear(): void; clear(): void;
loadPrev(): Promise<void>; loadPrev(): Promise<void>;
loadNext(): Promise<void>; loadNext(): Promise<void>;
protected parseResults(pg: number, results: ApiResponsePlural<T>): void; protected parseResults(pg: number, results: T[]): void;
/** /**
* Load a new page of results. * Load a new page of results.
*/ */
protected loadPage(page?: number): Promise<ApiResponsePlural<T>>; protected loadPage(page?: number): Promise<T[]>;
/** /**
* Get the parameters that should be passed in the API request. * Get the parameters that should be passed in the API request.
* Do not include page offset unless subclass overrides loadPage. * Do not include page offset unless subclass overrides loadPage.
@@ -42,7 +35,7 @@ export default abstract class PaginatedListState<T extends Model, P extends Pagi
* @abstract * @abstract
* @see loadPage * @see loadPage
*/ */
protected requestParams(): PaginatedListRequestParams; protected requestParams(): any;
/** /**
* Update the `this.params` object, calling `refresh` if they have changed. * Update the `this.params` object, calling `refresh` if they have changed.
* Use `requestParams` for converting `this.params` into API parameters * Use `requestParams` for converting `this.params` into API parameters
@@ -51,8 +44,8 @@ export default abstract class PaginatedListState<T extends Model, P extends Pagi
* @param page * @param page
* @see requestParams * @see requestParams
*/ */
refreshParams(newParams: P, page: number): Promise<void>; refreshParams(newParams: any, page: number): any;
refresh(page?: number): Promise<void>; refresh(page?: number): any;
getPages(): Page<T>[]; getPages(): Page<T>[];
getLocation(): PaginationLocation; getLocation(): PaginationLocation;
isLoading(): boolean; isLoading(): boolean;
@@ -77,9 +70,9 @@ export default abstract class PaginatedListState<T extends Model, P extends Pagi
/** /**
* Stored state parameters. * Stored state parameters.
*/ */
getParams(): P; getParams(): any;
protected getNextPageNumber(): number; protected getNextPageNumber(): number;
protected getPrevPageNumber(): number; protected getPrevPageNumber(): number;
protected paramsChanged(newParams: P): boolean; protected paramsChanged(newParams: any): boolean;
protected getAllItems(): T[]; protected getAllItems(): T[];
} }

View File

@@ -3,8 +3,8 @@ import ItemList from './ItemList';
export default class BasicEditorDriver implements EditorDriverInterface { export default class BasicEditorDriver implements EditorDriverInterface {
el: HTMLTextAreaElement; el: HTMLTextAreaElement;
constructor(dom: HTMLElement, params: EditorDriverParams); constructor(dom: HTMLElement, params: EditorDriverParams);
protected build(dom: HTMLElement, params: EditorDriverParams): void; build(dom: HTMLElement, params: EditorDriverParams): void;
protected keyHandlers(params: EditorDriverParams): ItemList<(e: KeyboardEvent) => void>; keyHandlers(params: EditorDriverParams): ItemList;
moveCursorTo(position: number): void; moveCursorTo(position: number): void;
getSelectionRange(): Array<number>; getSelectionRange(): Array<number>;
getLastNChars(n: number): string; getLastNChars(n: number): string;

View File

@@ -4,36 +4,10 @@
* footer. * footer.
*/ */
export default class Drawer { export default class Drawer {
/**
* @type {import('./focusTrap').FocusTrap}
*/
focusTrap: import('./focusTrap').FocusTrap;
/**
* @type {HTMLDivElement}
*/
appElement: HTMLDivElement;
/**
* @internal
* @type {MediaQueryList}
*/
drawerAvailableMediaQuery: MediaQueryList;
/**
* Handler for the `resize` event on `window`.
*
* This is used to close the drawer when the viewport is widened past the `phone` size.
* At this point, the drawer turns into the standard header that we see on desktop, but
* the drawer is still registered as 'open' internally.
*
* This causes issues with the focus trap, resulting in focus becoming trapped within
* the header on desktop viewports.
*
* @internal
*/
resizeHandler: (e: any) => void;
/** /**
* Check whether or not the drawer is currently open. * Check whether or not the drawer is currently open.
* *
* @return {boolean} * @return {Boolean}
* @public * @public
*/ */
public isOpen(): boolean; public isOpen(): boolean;

View File

@@ -1,31 +1,20 @@
export interface IItemObject<T> { declare class Item {
content: T; content: any;
itemName: string;
priority: number; priority: number;
} key?: number;
declare class Item<T> { constructor(content: any, priority?: number);
content: T;
priority: number;
constructor(content: T, priority: number);
} }
/** /**
* The `ItemList` class collects items and then arranges them into an array * The `ItemList` class collects items and then arranges them into an array
* by priority. * by priority.
*/ */
export default class ItemList<T> { export default class ItemList {
/** /**
* The items in the list. * The items in the list
*/ */
protected _items: Record<string, Item<T>>; items: {
/** [key: string]: Item;
* A **read-only copy** of items in the list. };
*
* We don't allow adding new items to the ItemList via setting new properties,
* nor do we allow modifying existing items directly.
*
* @deprecated Use {@link ItemList.toObject} instead.
*/
get items(): DeepReadonly<Record<string, Item<T>>>;
/** /**
* Check whether the list is empty. * Check whether the list is empty.
*/ */
@@ -37,165 +26,33 @@ export default class ItemList<T> {
/** /**
* Get the content of an item. * Get the content of an item.
*/ */
get(key: string): T; get(key: string): any;
/**
* Get the priority of an item.
*/
getPriority(key: string): number;
/** /**
* Add an item to the list. * Add an item to the list.
* *
* @param key A unique key for the item. * @param key A unique key for the item.
* @param content The item's content. * @param content The item's content.
* @param priority The priority of the item. Items with a higher priority * @param [priority] The priority of the item. Items with a higher
* will be positioned before items with a lower priority. * priority will be positioned before items with a lower priority.
*/ */
add(key: string, content: T, priority?: number): this; add(key: string, content: any, priority?: number): this;
/** /**
* Replace an item and/or priority in the list, only if it is already present. * Replace an item in the list, only if it is already present.
*
* If `content` or `priority` are `null`, these values will not be replaced.
*
* If the provided `key` is not present, nothing will happen.
*
* @deprecated Please use the {@link ItemList.setContent} and {@link ItemList.setPriority}
* methods to replace items and their priorities. This method will be removed in Flarum 2.0.
*
* @param key The key of the item in the list
* @param content The item's new content
* @param priority The item's new priority
*
* @example <caption>Replace priority and not content.</caption>
* items.replace('myItem', null, 10);
*
* @example <caption>Replace content and not priority.</caption>
* items.replace('myItem', <p>My new value.</p>);
*
* @example <caption>Replace content and priority.</caption>
* items.replace('myItem', <p>My new value.</p>, 10);
*/ */
replace(key: string, content?: T | null, priority?: number | null): this; replace(key: string, content?: any, priority?: number): this;
/**
* Replaces an item's content, if the provided item key exists.
*
* If the provided `key` is not present, nothing will happen.
*
* @param key The key of the item in the list
* @param content The item's new content
*
* @example <caption>Replace item content.</caption>
* items.setContent('myItem', <p>My new value.</p>);
*
* @example <caption>Replace item content and priority.</caption>
* items
* .setContent('myItem', <p>My new value.</p>)
* .setPriority('myItem', 10);
*
* @throws If the provided `key` is not present in the ItemList.
*/
setContent(key: string, content: T): this;
/**
* Replaces an item's priority, if the provided item key exists.
*
* If the provided `key` is not present, nothing will happen.
*
* @param key The key of the item in the list
* @param priority The item's new priority
*
* @example <caption>Replace item priority.</caption>
* items.setPriority('myItem', 10);
*
* @example <caption>Replace item priority and content.</caption>
* items
* .setPriority('myItem', 10)
* .setContent('myItem', <p>My new value.</p>);
*
* @throws If the provided `key` is not present in the ItemList.
*/
setPriority(key: string, priority: number): this;
/** /**
* Remove an item from the list. * Remove an item from the list.
*/ */
remove(key: string): this; remove(key: string): this;
/** /**
* Merge another list's items into this one. * Merge another list's items into this one.
*
* The list passed to this function will overwrite items which already exist
* with the same key.
*/ */
merge(otherList: ItemList<T>): ItemList<T>; merge(items: this): this;
/** /**
* Convert the list into an array of item content arranged by priority. * Convert the list into an array of item content arranged by priority. Each
* * item's content will be assigned an `itemName` property equal to the item's
* This **does not** preserve the original types of primitives and proxies * unique key.
* all content values to make `itemName` accessible on them.
*
* **NOTE:** If your ItemList holds primitive types (such as numbers, booleans
* or strings), these will be converted to their object counterparts if you do
* not provide `true` to this function.
*
* **NOTE:** Modifying any objects in the final array may also update the
* content of the original ItemList.
*
* @param keepPrimitives Converts item content to objects and sets the
* `itemName` property on them.
*
* @see https://github.com/flarum/core/issues/3030
*/ */
toArray(keepPrimitives?: false): (T & { toArray(): any[];
itemName: string;
})[];
/**
* Convert the list into an array of item content arranged by priority.
*
* Content values that are already objects will be proxied and have
* `itemName` accessible on them. Primitive values will not have the
* `itemName` property accessible.
*
* **NOTE:** Modifying any objects in the final array may also update the
* content of the original ItemList.
*
* @param keepPrimitives Converts item content to objects and sets the
* `itemName` property on them.
*/
toArray(keepPrimitives: true): (T extends object ? T & Readonly<{
itemName: string;
}> : T)[];
/**
* A read-only map of all keys to their respective items in no particular order.
*
* We don't allow adding new items to the ItemList via setting new properties,
* nor do we allow modifying existing items directly. You should use the
* {@link ItemList.add}, {@link ItemList.setContent} and
* {@link ItemList.setPriority} methods instead.
*
* To match the old behaviour of the `ItemList.items` property, call
* `Object.values(ItemList.toObject())`.
*
* @example
* const items = new ItemList();
* items.add('b', 'My cool value', 20);
* items.add('a', 'My value', 10);
* items.toObject();
* // {
* // a: { content: 'My value', priority: 10, itemName: 'a' },
* // b: { content: 'My cool value', priority: 20, itemName: 'b' },
* // }
*/
toObject(): DeepReadonly<Record<string, IItemObject<T>>>;
/**
* Proxies an item's content, adding the `itemName` readonly property to it.
*
* @example
* createItemContentProxy({ foo: 'bar' }, 'myItem');
* // { foo: 'bar', itemName: 'myItem' }
*
* @param content The item's content (objects only)
* @param key The item's key
* @returns Proxied content
*
* @internal
*/
private createItemContentProxy;
} }
export {}; export {};

View File

@@ -1,21 +1,9 @@
import type Mithril from 'mithril'; export default class RequestError {
import type { AlertAttrs } from '../components/Alert'; status: string;
export declare type InternalFlarumRequestOptions<ResponseType> = Mithril.RequestOptions<ResponseType> & { options: object;
url: string;
};
export default class RequestError<ResponseType = string> {
status: number;
options: InternalFlarumRequestOptions<ResponseType>;
xhr: XMLHttpRequest; xhr: XMLHttpRequest;
responseText: string | null; responseText: string | null;
response: { response: object | null;
[key: string]: unknown; alert: any;
errors?: { constructor(status: string, responseText: string | null, options: object, xhr: XMLHttpRequest);
detail?: string;
code?: string;
[key: string]: unknown;
}[];
} | null;
alert: AlertAttrs | null;
constructor(status: number, responseText: string | null, options: InternalFlarumRequestOptions<ResponseType>, xhr: XMLHttpRequest);
} }

View File

@@ -4,12 +4,12 @@
*/ */
export default class ScrollListener { export default class ScrollListener {
/** /**
* @param {(top: number) => void} callback The callback to run when the scroll position * @param {Function} callback The callback to run when the scroll position
* changes. * changes.
* @public * @public
*/ */
constructor(callback: (top: number) => void); constructor(callback: Function);
callback: (top: number) => void; callback: Function;
ticking: boolean; ticking: boolean;
/** /**
* On each animation frame, as long as the listener is active, run the * On each animation frame, as long as the listener is active, run the

View File

@@ -22,23 +22,31 @@
* @see https://mithril.js.org/lifecycle-methods.html#onbeforeupdate * @see https://mithril.js.org/lifecycle-methods.html#onbeforeupdate
*/ */
export default class SubtreeRetainer { export default class SubtreeRetainer {
protected callbacks: (() => any)[];
protected data: Record<string, any>;
/** /**
* @param callbacks Functions returning data to keep track of. * @param {...callbacks} callbacks Functions returning data to keep track of.
*/ */
constructor(...callbacks: (() => any)[]); constructor(...callbacks: any[]);
callbacks: any[];
data: {};
/** /**
* Return whether any data has changed since the last check. * Return whether any data has changed since the last check.
* If so, Mithril needs to re-diff the vnode and its children. * If so, Mithril needs to re-diff the vnode and its children.
*
* @return {boolean}
* @public
*/ */
needsRebuild(): boolean; public needsRebuild(): boolean;
/** /**
* Add another callback to be checked. * Add another callback to be checked.
*
* @param {...Function} callbacks
* @public
*/ */
check(...callbacks: (() => any)[]): void; public check(...callbacks: Function[]): void;
/** /**
* Invalidate the subtree, forcing it to be redrawn. * Invalidate the subtree, forcing it to be rerendered.
*
* @public
*/ */
invalidate(): void; public invalidate(): void;
} }

View File

@@ -1,4 +1,3 @@
import Model from '../Model';
/** /**
* The `computed` utility creates a function that will cache its output until * The `computed` utility creates a function that will cache its output until
* any of the dependent values are dirty. * any of the dependent values are dirty.
@@ -8,4 +7,4 @@ import Model from '../Model';
* dependent values. * dependent values.
* @return {Function} * @return {Function}
*/ */
export default function computed<T, M = Model>(...args: [...string[], (this: M, ...args: unknown[]) => T]): () => T; export default function computed(...dependentKeys: string[]): Function;

View File

@@ -1,5 +1,7 @@
import type Mithril from 'mithril';
/** /**
* Extract the text nodes from a virtual element. * Extract the text nodes from a virtual element.
*
* @param {VirtualElement} vdom
* @return {String}
*/ */
export default function extractText(vdom: Mithril.Children): string; export default function extractText(vdom: any): string;

View File

@@ -1,20 +0,0 @@
import { createFocusTrap as _createFocusTrap } from 'focus-trap';
/**
* Creates a focus trap for the given element with the given options.
*
* This function applies some default options that are different to the library.
* Your own options still override these custom defaults:
*
* ```json
* {
escapeDeactivates: false,
* }
* ```
*
* @param element The element to be the focus trap, or a selector that will be used to find the element.
*
* @see https://github.com/focus-trap/focus-trap#readme - Library documentation
*/
declare function createFocusTrap(...args: Parameters<typeof _createFocusTrap>): ReturnType<typeof _createFocusTrap>;
export * from 'focus-trap';
export { createFocusTrap };

View File

@@ -1,9 +1,9 @@
/** /**
* The `formatNumber` utility localizes a number into a string with the * The `formatNumber` utility localizes a number into a string with the
* appropriate punctuation based on the provided locale otherwise will default to the users locale. * appropriate punctuation.
* *
* @example * @example
* formatNumber(1234); * formatNumber(1234);
* // 1,234 * // 1,234
*/ */
export default function formatNumber(number: number, locale?: string): string; export default function formatNumber(number: number): string;

View File

@@ -1,6 +1,5 @@
import dayjs from 'dayjs';
/** /**
* The `humanTime` utility converts a date to a localized, human-readable time- * The `humanTime` utility converts a date to a localized, human-readable time-
* ago string. * ago string.
*/ */
export default function humanTime(time: dayjs.ConfigType): string; export default function humanTime(time: Date): string;

View File

@@ -1,24 +0,0 @@
/**
* Returns if the passed value is an object.
*
* In this context, "object" refers to **any non-primitive value**, including
* arrays, function, maps, dates, and more.
*
* @example
* isObject({}); // true
* @example
* isObject([]); // true
* @example
* isObject(function () {}); // true
* @example
* isObject(Object(1)); // true
* @example
* isObject(null); // false
* @example
* isObject(1); // false
* @example
* isObject("hello world"); // false
*
* @see https://github.com/jashkenas/underscore/blob/943977e34e2279503528a71ddcc2dd5f96483945/underscore.js#L87-L91
*/
export default function isObject(obj: unknown): obj is object;

View File

@@ -1,13 +1,11 @@
import type { FlarumGenericRoute, RouteResolver } from '../Application';
import type Component from '../Component';
/** /**
* The `mapRoutes` utility converts a map of named application routes into a * The `mapRoutes` utility converts a map of named application routes into a
* format that can be understood by Mithril, and wraps them in route resolvers * format that can be understood by Mithril, and wraps them in route resolvers
* to provide each route with the current route name. * to provide each route with the current route name.
* *
* @see https://mithril.js.org/route.html#signature * @see https://mithril.js.org/route.html#signature
* @param {Object} routes
* @param {String} [basePath]
* @return {Object}
*/ */
export default function mapRoutes(routes: Record<string, FlarumGenericRoute>, basePath?: string): Record<string, RouteResolver<Record<string, unknown>, Component<{ export default function mapRoutes(routes: Object, basePath?: string | undefined): Object;
[key: string]: unknown;
routeName: string;
}, undefined>, Record<string, unknown>>>;

View File

@@ -1 +1,6 @@
export default function proxifyCompat(compat: Record<string, unknown>, namespace: string): Record<string, unknown>; declare const _default: (compat: {
[key: string]: any;
}, namespace: string) => {
[key: string]: any;
};
export default _default;

View File

@@ -9,7 +9,6 @@ interface StyleArgs {
scanFor: string; scanFor: string;
surroundWithNewlines: boolean; surroundWithNewlines: boolean;
orderedList: boolean; orderedList: boolean;
unorderedList: boolean;
trimFirst: boolean; trimFirst: boolean;
} }
export default function styleSelectedText(textarea: HTMLTextAreaElement, styleArgs: StyleArgs): void; export default function styleSelectedText(textarea: HTMLTextAreaElement, styleArgs: StyleArgs): void;

View File

@@ -1,60 +1,51 @@
import History from './utils/History';
import Pane from './utils/Pane';
import { makeRouteHelpers } from './routes';
import Application from '../common/Application';
import NotificationListState from './states/NotificationListState';
import GlobalSearchState from './states/GlobalSearchState';
import DiscussionListState from './states/DiscussionListState';
import ComposerState from './states/ComposerState';
import type Notification from './components/Notification';
import type Post from './components/Post';
import Discussion from '../common/models/Discussion';
export default class ForumApplication extends Application { export default class ForumApplication extends Application {
/** /**
* A map of notification types to their components. * A map of notification types to their components.
*
* @type {Object}
*/ */
notificationComponents: Record<string, typeof Notification>; notificationComponents: Object;
/** /**
* A map of post types to their components. * A map of post types to their components.
*
* @type {Object}
*/ */
postComponents: Record<string, typeof Post>; postComponents: Object;
/** /**
* An object which controls the state of the page's side pane. * An object which controls the state of the page's side pane.
*
* @type {Pane}
*/ */
pane: Pane | null; pane: Pane;
/** /**
* The app's history stack, which keeps track of which routes the user visits * The app's history stack, which keeps track of which routes the user visits
* so that they can easily navigate back to the previous route. * so that they can easily navigate back to the previous route.
*
* @type {History}
*/ */
history: History; history: History;
/** /**
* An object which controls the state of the user's notifications. * An object which controls the state of the user's notifications.
*
* @type {NotificationListState}
*/ */
notifications: NotificationListState; notifications: NotificationListState;
/**
* An object which stores previously searched queries and provides convenient
* tools for retrieving and managing search values.
*/
search: GlobalSearchState; search: GlobalSearchState;
/**
* An object which controls the state of the composer.
*/
composer: ComposerState; composer: ComposerState;
/** /**
* An object which controls the state of the cached discussion list, which * An object which controls the state of the cached discussion list, which
* is used in the index page and the slideout pane. * is used in the index page and the slideout pane.
*
* @type {DiscussionListState}
*/ */
discussions: DiscussionListState; discussions: DiscussionListState;
route: typeof Application.prototype.route & ReturnType<typeof makeRouteHelpers>;
constructor();
/**
* @inheritdoc
*/
mount(): void;
/** /**
* Check whether or not the user is currently viewing a discussion. * Check whether or not the user is currently viewing a discussion.
*
* @param {Discussion} discussion
* @return {Boolean}
*/ */
viewingDiscussion(discussion: Discussion): boolean; viewingDiscussion(discussion: any): boolean;
/** /**
* Callback for when an external authenticator (social login) action has * Callback for when an external authenticator (social login) action has
* completed. * completed.
@@ -62,6 +53,18 @@ export default class ForumApplication extends Application {
* If the payload indicates that the user has been logged in, then the page * If the payload indicates that the user has been logged in, then the page
* will be reloaded. Otherwise, a SignUpModal will be opened, prefilled * will be reloaded. Otherwise, a SignUpModal will be opened, prefilled
* with the provided details. * with the provided details.
*
* @param {Object} payload A dictionary of attrs to pass into the sign up
* modal. A truthy `loggedIn` attr indicates that the user has logged
* in, and thus the page is reloaded.
* @public
*/ */
authenticationComplete(payload: Record<string, unknown>): void; public authenticationComplete(payload: Object): void;
} }
import Application from "../common/Application";
import Pane from "./utils/Pane";
import History from "./utils/History";
import NotificationListState from "./states/NotificationListState";
import GlobalSearchState from "./states/GlobalSearchState";
import ComposerState from "./states/ComposerState";
import DiscussionListState from "./states/DiscussionListState";

View File

@@ -1,5 +1,5 @@
declare var _default: { declare var _default: {
extend: any; extend: typeof import("../common/extend");
Session: typeof import("../common/Session").default; Session: typeof import("../common/Session").default;
Store: typeof import("../common/Store").default; Store: typeof import("../common/Store").default;
'utils/BasicEditorDriver': typeof BasicEditorDriver; 'utils/BasicEditorDriver': typeof BasicEditorDriver;
@@ -32,15 +32,17 @@ declare var _default: {
'utils/subclassOf': typeof import("../common/utils/subclassOf").default; 'utils/subclassOf': typeof import("../common/utils/subclassOf").default;
'utils/setRouteWithForcedRefresh': typeof import("../common/utils/setRouteWithForcedRefresh").default; 'utils/setRouteWithForcedRefresh': typeof import("../common/utils/setRouteWithForcedRefresh").default;
'utils/patchMithril': typeof import("../common/utils/patchMithril").default; 'utils/patchMithril': typeof import("../common/utils/patchMithril").default;
'utils/proxifyCompat': typeof import("../common/utils/proxifyCompat").default; 'utils/proxifyCompat': (compat: {
[key: string]: any;
}, namespace: string) => {
[key: string]: any;
};
'utils/classList': (...classes: import("clsx").ClassValue[]) => string; 'utils/classList': (...classes: import("clsx").ClassValue[]) => string;
'utils/extractText': typeof import("../common/utils/extractText").default; 'utils/extractText': typeof import("../common/utils/extractText").default;
'utils/formatNumber': typeof import("../common/utils/formatNumber").default; 'utils/formatNumber': typeof import("../common/utils/formatNumber").default;
'utils/mapRoutes': typeof import("../common/utils/mapRoutes").default; 'utils/mapRoutes': typeof import("../common/utils/mapRoutes").default;
'utils/withAttr': (key: string, cb: Function) => (this: Element) => void; 'utils/withAttr': (key: string, cb: Function) => (this: Element) => void;
'utils/throttleDebounce': typeof import("../common/utils/throttleDebounce"); 'utils/throttleDebounce': typeof import("../common/utils/throttleDebounce");
'utils/isObject': typeof import("../common/utils/isObject").default;
'utils/focusTrap': typeof import("../common/utils/focusTrap");
'models/Notification': typeof import("../common/models/Notification").default; 'models/Notification': typeof import("../common/models/Notification").default;
'models/User': typeof import("../common/models/User").default; 'models/User': typeof import("../common/models/User").default;
'models/Post': typeof import("../common/models/Post").default; 'models/Post': typeof import("../common/models/Post").default;
@@ -67,7 +69,6 @@ declare var _default: {
'components/Link': typeof import("../common/components/Link").default; 'components/Link': typeof import("../common/components/Link").default;
'components/LinkButton': typeof import("../common/components/LinkButton").default; 'components/LinkButton': typeof import("../common/components/LinkButton").default;
'components/Checkbox': typeof import("../common/components/Checkbox").default; 'components/Checkbox': typeof import("../common/components/Checkbox").default;
'components/ColorPreviewInput': typeof import("../common/components/ColorPreviewInput").default;
'components/SelectDropdown': typeof import("../common/components/SelectDropdown").default; 'components/SelectDropdown': typeof import("../common/components/SelectDropdown").default;
'components/ModalManager': typeof import("../common/components/ModalManager").default; 'components/ModalManager': typeof import("../common/components/ModalManager").default;
'components/Button': typeof import("../common/components/Button").default; 'components/Button': typeof import("../common/components/Button").default;
@@ -92,10 +93,10 @@ declare var _default: {
'states/PaginatedListState': typeof import("../common/states/PaginatedListState").default; 'states/PaginatedListState': typeof import("../common/states/PaginatedListState").default;
} & { } & {
'utils/PostControls': { 'utils/PostControls': {
controls(post: any, context: any): import("../common/utils/ItemList").default<any>; controls(post: any, context: any): import("../common/utils/ItemList").default;
userControls(post: any, context: any): import("../common/utils/ItemList").default<any>; userControls(post: any, context: any): import("../common/utils/ItemList").default;
moderationControls(post: any, context: any): import("../common/utils/ItemList").default<any>; moderationControls(post: any, context: any): import("../common/utils/ItemList").default;
destructiveControls(post: any, context: any): import("../common/utils/ItemList").default<any>; destructiveControls(post: any, context: any): import("../common/utils/ItemList").default;
editAction(): Promise<any>; editAction(): Promise<any>;
hideAction(): Promise<any>; hideAction(): Promise<any>;
restoreAction(): Promise<any>; restoreAction(): Promise<any>;
@@ -105,10 +106,10 @@ declare var _default: {
'utils/slidable': typeof slidable; 'utils/slidable': typeof slidable;
'utils/History': typeof History; 'utils/History': typeof History;
'utils/DiscussionControls': { 'utils/DiscussionControls': {
controls(discussion: any, context: any): import("../common/utils/ItemList").default<any>; controls(discussion: any, context: any): import("../common/utils/ItemList").default;
userControls(discussion: any, context: any): import("../common/utils/ItemList").default<any>; userControls(discussion: any, context: any): import("../common/utils/ItemList").default;
moderationControls(discussion: any): import("../common/utils/ItemList").default<any>; moderationControls(discussion: any): import("../common/utils/ItemList").default;
destructiveControls(discussion: any): import("../common/utils/ItemList").default<any>; destructiveControls(discussion: any): import("../common/utils/ItemList").default;
replyAction(goToLast: boolean, forceRefresh: boolean): Promise<any>; replyAction(goToLast: boolean, forceRefresh: boolean): Promise<any>;
hideAction(): Promise<any>; hideAction(): Promise<any>;
restoreAction(): Promise<any>; restoreAction(): Promise<any>;
@@ -117,10 +118,10 @@ declare var _default: {
}; };
'utils/alertEmailConfirmation': typeof alertEmailConfirmation; 'utils/alertEmailConfirmation': typeof alertEmailConfirmation;
'utils/UserControls': { 'utils/UserControls': {
controls(user: any, context: any): import("../common/utils/ItemList").default<any>; controls(user: any, context: any): import("../common/utils/ItemList").default;
userControls(): import("../common/utils/ItemList").default<any>; userControls(): import("../common/utils/ItemList").default;
moderationControls(user: any): import("../common/utils/ItemList").default<any>; moderationControls(user: any): import("../common/utils/ItemList").default;
destructiveControls(user: any): import("../common/utils/ItemList").default<any>; destructiveControls(user: any): import("../common/utils/ItemList").default;
deleteAction(user: any): void; deleteAction(user: any): void;
showDeletionAlert(user: any, type: string): void; showDeletionAlert(user: any, type: string): void;
editAction(user: any): void; editAction(user: any): void;

View File

@@ -26,7 +26,7 @@ export default class AvatarEditor extends Component<import("../../common/Compone
* *
* @return {ItemList} * @return {ItemList}
*/ */
controlItems(): ItemList<any>; controlItems(): ItemList;
/** /**
* Enable dragover style * Enable dragover style
* *

View File

@@ -2,8 +2,7 @@
* 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<import("../../common/components/Modal").IInternalModalAttrs> { export default class ChangeEmailModal extends Modal {
constructor();
/** /**
* Whether or not the email has been changed successfully. * Whether or not the email has been changed successfully.
* *

View File

@@ -2,7 +2,6 @@
* 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<import("../../common/components/Modal").IInternalModalAttrs> { export default class ChangePasswordModal extends Modal {
constructor();
} }
import Modal from "../../common/components/Modal"; import Modal from "../../common/components/Modal";

View File

@@ -34,7 +34,7 @@ export default class CommentPost extends Post {
* *
* @return {ItemList} * @return {ItemList}
*/ */
headerItems(): ItemList<any>; headerItems(): ItemList;
} }
import Post from "./Post"; import Post from "./Post";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -85,7 +85,7 @@ export default class Composer extends Component<import("../../common/Component")
* *
* @return {ItemList} * @return {ItemList}
*/ */
controlItems(): ItemList<any>; controlItems(): ItemList;
/** /**
* Initialize default Composer height. * Initialize default Composer height.
*/ */

View File

@@ -35,7 +35,7 @@ export default class ComposerBody extends Component<import("../../common/Compone
* *
* @return {ItemList} * @return {ItemList}
*/ */
headerItems(): ItemList<any>; headerItems(): ItemList;
/** /**
* Handle the submit event of the text editor. * Handle the submit event of the text editor.
* *

View File

@@ -13,6 +13,6 @@
export default class ComposerPostPreview extends Component<import("../../common/Component").ComponentAttrs, undefined> { export default class ComposerPostPreview extends Component<import("../../common/Component").ComponentAttrs, undefined> {
static initAttrs(attrs: any): void; static initAttrs(attrs: any): void;
constructor(); constructor();
updateInterval: NodeJS.Timer | undefined; updateInterval: number | undefined;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";

View File

@@ -12,7 +12,7 @@ export default class DiscussionHero extends Component<import("../../common/Compo
* *
* @return {ItemList} * @return {ItemList}
*/ */
items(): ItemList<any>; items(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -51,8 +51,7 @@ export default class DiscussionListItem extends Component<import("../../common/C
* *
* @return {ItemList} * @return {ItemList}
*/ */
infoItems(): ItemList<any>; infoItems(): ItemList;
replyCountItem(): JSX.Element;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import SubtreeRetainer from "../../common/utils/SubtreeRetainer"; import SubtreeRetainer from "../../common/utils/SubtreeRetainer";

View File

@@ -1,64 +1,53 @@
import type Mithril from 'mithril';
import Page, { IPageAttrs } from '../../common/components/Page';
import ItemList from '../../common/utils/ItemList';
import PostStreamState from '../states/PostStreamState';
import Discussion from '../../common/models/Discussion';
import { ApiResponseSingle } from '../../common/Store';
export interface IDiscussionPageAttrs extends IPageAttrs {
id: string;
near?: number;
}
/** /**
* The `DiscussionPage` component displays a whole discussion page, including * The `DiscussionPage` component displays a whole discussion page, including
* the discussion list pane, the hero, the posts, and the sidebar. * the discussion list pane, the hero, the posts, and the sidebar.
*/ */
export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = IDiscussionPageAttrs> extends Page<CustomAttrs> { export default class DiscussionPage extends Page<import("../../common/components/Page").IPageAttrs> {
constructor();
useBrowserScrollRestoration: boolean | undefined;
/** /**
* The discussion that is being viewed. * The discussion that is being viewed.
*
* @type {Discussion}
*/ */
protected discussion: Discussion | null; discussion: any;
/**
* A public API for interacting with the post stream.
*/
protected stream: PostStreamState | null;
/** /**
* The number of the first post that is currently visible in the viewport. * The number of the first post that is currently visible in the viewport.
*
* @type {number}
*/ */
protected near: number; near: number | undefined;
protected useBrowserScrollRestoration: boolean; bodyClass: string | undefined;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
onremove(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void;
view(): JSX.Element;
/** /**
* List of components shown while the discussion is loading. * List of components shown while the discussion is loading.
* *
* @returns {ItemList} * @returns {ItemList}
*/ */
loadingItems(): ItemList<unknown>; loadingItems(): ItemList;
/** /**
* Function that renders the `sidebarItems` ItemList. * Function that renders the `sidebarItems` ItemList.
* *
* @returns {import('mithril').Children} * @returns {import('mithril').Children}
*/ */
sidebar(): JSX.Element; sidebar(): import('mithril').Children;
/** /**
* Renders the discussion's hero. * Renders the discussion's hero.
* *
* @returns {import('mithril').Children} * @returns {import('mithril').Children}
*/ */
hero(): JSX.Element; hero(): import('mithril').Children;
/** /**
* List of items rendered as the main page content. * List of items rendered as the main page content.
* *
* @returns {ItemList} * @returns {ItemList}
*/ */
pageContent(): ItemList<unknown>; pageContent(): ItemList;
/** /**
* List of items rendered inside the main page content container. * List of items rendered inside the main page content container.
* *
* @returns {ItemList} * @returns {ItemList}
*/ */
mainContent(): ItemList<unknown>; mainContent(): ItemList;
/** /**
* Load the discussion from the API or use the preloaded one. * Load the discussion from the API or use the preloaded one.
*/ */
@@ -69,23 +58,29 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
* *
* @return {Object} * @return {Object}
*/ */
requestParams(): { requestParams(): Object;
bySlug: boolean;
page: {
near: number;
};
};
/** /**
* Initialize the component to display the given discussion. * Initialize the component to display the given discussion.
*
* @param {Discussion} discussion
*/ */
show(discussion: ApiResponseSingle<Discussion>): void; show(discussion: any): void;
stream: PostStreamState | undefined;
/** /**
* Build an item list for the contents of the sidebar. * Build an item list for the contents of the sidebar.
*
* @return {ItemList}
*/ */
sidebarItems(): ItemList<Mithril.Vnode<{}, {}>>; sidebarItems(): ItemList;
/** /**
* When the posts that are visible in the post stream change (i.e. the user * When the posts that are visible in the post stream change (i.e. the user
* scrolls up or down), then we update the URL and mark the posts as read. * scrolls up or down), then we update the URL and mark the posts as read.
*
* @param {Integer} startNumber
* @param {Integer} endNumber
*/ */
positionChanged(startNumber: number, endNumber: number): void; positionChanged(startNumber: any, endNumber: any): void;
} }
import Page from "../../common/components/Page";
import ItemList from "../../common/utils/ItemList";
import PostStreamState from "../states/PostStreamState";

View File

@@ -1,12 +1,11 @@
import { SearchSource } from './Search'; import { SearchSource } from './Search';
import type Mithril from 'mithril'; import type Mithril from 'mithril';
import Discussion from '../../common/models/Discussion';
/** /**
* The `DiscussionsSearchSource` finds and displays discussion search results in * The `DiscussionsSearchSource` finds and displays discussion search results in
* the search dropdown. * the search dropdown.
*/ */
export default class DiscussionsSearchSource implements SearchSource { export default class DiscussionsSearchSource implements SearchSource {
protected results: Map<string, Discussion[]>; protected results: Map<string, unknown[]>;
search(query: string): Promise<void>; search(query: string): Promise<Map<string, unknown[]>>;
view(query: string): Array<Mithril.Vnode>; view(query: string): Array<Mithril.Vnode>;
} }

View File

@@ -1,25 +1,24 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" />
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import Stream from '../../common/utils/Stream';
import Mithril from 'mithril';
import RequestError from '../../common/utils/RequestError';
export interface IForgotPasswordModalAttrs extends IInternalModalAttrs {
email?: string;
}
/** /**
* The `ForgotPasswordModal` component displays a modal which allows the user to * The `ForgotPasswordModal` component displays a modal which allows the user to
* enter their email address and request a link to reset their password. * enter their email address and request a link to reset their password.
*
* ### Attrs
*
* - `email`
*/ */
export default class ForgotPasswordModal<CustomAttrs extends IForgotPasswordModalAttrs = IForgotPasswordModalAttrs> extends Modal<CustomAttrs> { export default class ForgotPasswordModal extends Modal {
/** /**
* The value of the email input. * The value of the email input.
*
* @type {Function}
*/ */
email: Stream<string>; email: Function | undefined;
success: boolean; /**
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void; * Whether or not the password reset email was sent successfully.
className(): string; *
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray; * @type {Boolean}
content(): JSX.Element; */
onsubmit(e: SubmitEvent): void; success: boolean | undefined;
onerror(error: RequestError): void; alert: any;
} }
import Modal from "../../common/components/Modal";

View File

@@ -9,7 +9,7 @@ export default class HeaderPrimary extends Component<import("../../common/Compon
* *
* @return {ItemList} * @return {ItemList}
*/ */
items(): ItemList<any>; items(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -10,7 +10,7 @@ export default class HeaderSecondary extends Component<import("../../common/Comp
* *
* @return {ItemList} * @return {ItemList}
*/ */
items(): ItemList<any>; items(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -6,6 +6,8 @@ export default class IndexPage extends Page<import("../../common/components/Page
static providesInitialSearch: boolean; static providesInitialSearch: boolean;
constructor(); constructor();
lastDiscussion: any; lastDiscussion: any;
bodyClass: string | undefined;
scrollTopOnCreate: boolean | undefined;
setTitle(): void; setTitle(): void;
/** /**
* Get the component to display as the hero. * Get the component to display as the hero.
@@ -20,14 +22,14 @@ export default class IndexPage extends Page<import("../../common/components/Page
* *
* @return {ItemList} * @return {ItemList}
*/ */
sidebarItems(): ItemList<any>; sidebarItems(): ItemList;
/** /**
* Build an item list for the navigation in the sidebar of the index page. By * Build an item list for the navigation in the sidebar of the index page. By
* default this is just the 'All Discussions' link. * default this is just the 'All Discussions' link.
* *
* @return {ItemList} * @return {ItemList}
*/ */
navItems(): ItemList<any>; navItems(): ItemList;
/** /**
* Build an item list for the part of the toolbar which is concerned with how * Build an item list for the part of the toolbar which is concerned with how
* the results are displayed. By default this is just a select box to change * the results are displayed. By default this is just a select box to change
@@ -35,14 +37,14 @@ export default class IndexPage extends Page<import("../../common/components/Page
* *
* @return {ItemList} * @return {ItemList}
*/ */
viewItems(): ItemList<any>; viewItems(): ItemList;
/** /**
* Build an item list for the part of the toolbar which is about taking action * Build an item list for the part of the toolbar which is about taking action
* on the results. By default this is just a "mark all as read" button. * on the results. By default this is just a "mark all as read" button.
* *
* @return {ItemList} * @return {ItemList}
*/ */
actionItems(): ItemList<any>; actionItems(): ItemList;
/** /**
* Open the composer for a new discussion or prompt the user to login. * Open the composer for a new discussion or prompt the user to login.
* *

View File

@@ -9,7 +9,7 @@ export default class LogInButtons extends Component<import("../../common/Compone
* @return {ItemList} * @return {ItemList}
* @public * @public
*/ */
public items(): ItemList<any>; public items(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,45 +1,47 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" /> /**
import Modal, { IInternalModalAttrs } from '../../common/components/Modal'; * The `LogInModal` component displays a modal dialog with a login form.
import ItemList from '../../common/utils/ItemList'; *
import Stream from '../../common/utils/Stream'; * ### Attrs
import type Mithril from 'mithril'; *
import RequestError from '../../common/utils/RequestError'; * - `identification`
export interface ILoginModalAttrs extends IInternalModalAttrs { * - `password`
identification?: string; */
password?: string; export default class LogInModal extends Modal {
remember?: boolean;
}
export default class LogInModal<CustomAttrs extends ILoginModalAttrs = ILoginModalAttrs> extends Modal<CustomAttrs> {
/** /**
* The value of the identification input. * The value of the identification input.
*
* @type {Function}
*/ */
identification: Stream<string>; identification: Function | undefined;
/** /**
* The value of the password input. * The value of the password input.
*
* @type {Function}
*/ */
password: Stream<string>; password: Function | undefined;
/** /**
* The value of the remember me input. * The value of the remember me input.
*
* @type {Function}
*/ */
remember: Stream<boolean>; remember: Function | undefined;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
className(): string;
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
content(): JSX.Element[];
body(): JSX.Element[]; body(): JSX.Element[];
fields(): ItemList<unknown>; fields(): ItemList;
footer(): (string | JSX.Element)[]; footer(): (string | JSX.Element)[];
/** /**
* Open the forgot password modal, prefilling it with an email if the user has * Open the forgot password modal, prefilling it with an email if the user has
* entered one. * entered one.
*
* @public
*/ */
forgotPassword(): void; public forgotPassword(): void;
/** /**
* Open the sign up modal, prefilling it with an email/username/password if * Open the sign up modal, prefilling it with an email/username/password if
* the user has entered one. * the user has entered one.
*
* @public
*/ */
signUp(): void; public signUp(): void;
onready(): void;
onsubmit(e: SubmitEvent): void;
onerror(error: RequestError): void;
} }
import Modal from "../../common/components/Modal";
import ItemList from "../../common/utils/ItemList";

Some files were not shown because too many files have changed in this diff Show More