mirror of
https://github.com/flarum/core.git
synced 2025-07-10 11:26:24 +02:00
chore: 1.2 JS clean-up (#3214)
* fix: `extend.ts` TS error * docs: fix incorrect JS docblocks * chore: simplify some code * chore: remove usages of prefixed JS function * chore: consistent empty return types * chore: format * fix: typing errors * chore: remove unneeded `@public` docblock modifiers * Apply suggestions from code review * Update js/src/forum/utils/slidable.js Co-authored-by: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com>
This commit is contained in:
@ -55,7 +55,7 @@ export default class AdminNav extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list of main links to show in the admin navigation.
|
* Build an item list of main links to show in the admin navigation.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
items() {
|
items() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -120,8 +120,7 @@ export default class BasicsPage extends AdminPage {
|
|||||||
* Build a list of options for the default homepage. Each option must be an
|
* Build a list of options for the default homepage. Each option must be an
|
||||||
* object with `path` and `label` properties.
|
* object with `path` and `label` properties.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<{ path: string, label: import('mithril').Children }>}
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
homePageItems() {
|
homePageItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -8,7 +8,7 @@ export default class DashboardWidget extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the class name to apply to the widget.
|
* Get the class name to apply to the widget.
|
||||||
*
|
*
|
||||||
* @return {String}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
className() {
|
className() {
|
||||||
return '';
|
return '';
|
||||||
@ -17,9 +17,9 @@ export default class DashboardWidget extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the content of the widget.
|
* Get the content of the widget.
|
||||||
*
|
*
|
||||||
* @return {VirtualElement}
|
* @return {import('mithril').Children}
|
||||||
*/
|
*/
|
||||||
content() {
|
content() {
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ export default class HeaderPrimary extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the controls.
|
* Build an item list for the controls.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
items() {
|
items() {
|
||||||
return new ItemList();
|
return new ItemList();
|
||||||
|
@ -16,7 +16,7 @@ export default class HeaderSecondary extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the controls.
|
* Build an item list for the controls.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
items() {
|
items() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -28,7 +28,7 @@ export default class HeaderSecondary extends Component {
|
|||||||
</LinkButton>
|
</LinkButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
items.add('session', SessionDropdown.component());
|
items.add('session', <SessionDropdown />);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ export default class UploadImageButton extends Button {
|
|||||||
/**
|
/**
|
||||||
* After a successful upload/removal, reload the page.
|
* After a successful upload/removal, reload the page.
|
||||||
*
|
*
|
||||||
* @param {Object} response
|
* @param {object} response
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
success(response) {
|
success(response) {
|
||||||
@ -88,7 +88,7 @@ export default class UploadImageButton extends Button {
|
|||||||
/**
|
/**
|
||||||
* If upload/removal fails, stop loading.
|
* If upload/removal fails, stop loading.
|
||||||
*
|
*
|
||||||
* @param {Object} response
|
* @param {object} response
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
failure(response) {
|
failure(response) {
|
||||||
|
@ -451,9 +451,6 @@ export default class Application {
|
|||||||
* 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 options
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
*/
|
||||||
request<ResponseType>(originalOptions: FlarumRequestOptions<ResponseType>): Promise<ResponseType> {
|
request<ResponseType>(originalOptions: FlarumRequestOptions<ResponseType>): Promise<ResponseType> {
|
||||||
const options = this.transformRequestOptions(originalOptions);
|
const options = this.transformRequestOptions(originalOptions);
|
||||||
|
@ -30,8 +30,8 @@ export default abstract class Fragment {
|
|||||||
* containing all of the `li` elements inside the DOM element of this
|
* containing all of the `li` elements inside the DOM element of this
|
||||||
* fragment.
|
* fragment.
|
||||||
*
|
*
|
||||||
* @param {String} [selector] a jQuery-compatible selector string
|
* @param [selector] a jQuery-compatible selector string
|
||||||
* @returns {jQuery} the jQuery object for the DOM node
|
* @returns the jQuery object for the DOM node
|
||||||
* @final
|
* @final
|
||||||
*/
|
*/
|
||||||
public $(selector?: string): JQuery {
|
public $(selector?: string): JQuery {
|
||||||
|
@ -44,7 +44,7 @@ export default class Checkbox extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the template for the checkbox's display (tick/cross icon).
|
* Get the template for the checkbox's display (tick/cross icon).
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @return {import('mithril').Children}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
getDisplay() {
|
getDisplay() {
|
||||||
@ -54,7 +54,7 @@ export default class Checkbox extends Component {
|
|||||||
/**
|
/**
|
||||||
* Run a callback when the state of the checkbox is changed.
|
* Run a callback when the state of the checkbox is changed.
|
||||||
*
|
*
|
||||||
* @param {Boolean} checked
|
* @param {boolean} checked
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
onchange(checked) {
|
onchange(checked) {
|
||||||
|
@ -103,7 +103,7 @@ export default class Dropdown extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the template for the button.
|
* Get the template for the button.
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @return {import('mithril').Children}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
getButton(children) {
|
getButton(children) {
|
||||||
@ -123,7 +123,7 @@ export default class Dropdown extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the template for the button's content.
|
* Get the template for the button's content.
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @return {import('mithril').Children}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
getButtonContent(children) {
|
getButtonContent(children) {
|
||||||
|
@ -35,8 +35,8 @@ export default class LinkButton extends Button {
|
|||||||
/**
|
/**
|
||||||
* Determine whether a component with the given attrs is 'active'.
|
* Determine whether a component with the given attrs is 'active'.
|
||||||
*
|
*
|
||||||
* @param {Object} attrs
|
* @param {object} attrs
|
||||||
* @return {Boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static isActive(attrs) {
|
static isActive(attrs) {
|
||||||
return typeof attrs.active !== 'undefined' ? attrs.active : m.route.get() === attrs.href;
|
return typeof attrs.active !== 'undefined' ? attrs.active : m.route.get() === attrs.href;
|
||||||
|
@ -36,7 +36,7 @@ export default class Navigation extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the back button.
|
* Get the back button.
|
||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {import('mithril').Children}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
getBackButton() {
|
getBackButton() {
|
||||||
@ -59,7 +59,7 @@ export default class Navigation extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the pane pinned toggle button.
|
* Get the pane pinned toggle button.
|
||||||
*
|
*
|
||||||
* @return {Object|String}
|
* @return {import('mithril').Children}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
getPaneButton() {
|
getPaneButton() {
|
||||||
@ -77,7 +77,7 @@ export default class Navigation extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the drawer toggle button.
|
* Get the drawer toggle button.
|
||||||
*
|
*
|
||||||
* @return {Object|String}
|
* @return {import('mithril').Children}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
getDrawerButton() {
|
getDrawerButton() {
|
||||||
|
@ -40,7 +40,8 @@ export default class SplitDropdown extends Dropdown {
|
|||||||
* Get the first child. If the first child is an array, the first item in that
|
* Get the first child. If the first child is an array, the first item in that
|
||||||
* array will be returned.
|
* array will be returned.
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @param {unknown[] | unknown} children
|
||||||
|
* @return {unknown}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
getFirstChild(children) {
|
getFirstChild(children) {
|
||||||
|
@ -90,7 +90,7 @@ export default class TextEditor extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the text editor controls.
|
* Build an item list for the text editor controls.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
controlItems() {
|
controlItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -123,7 +123,7 @@ export default class TextEditor extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the toolbar controls.
|
* Build an item list for the toolbar controls.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
toolbarItems() {
|
toolbarItems() {
|
||||||
return new ItemList();
|
return new ItemList();
|
||||||
@ -132,7 +132,7 @@ export default class TextEditor extends Component {
|
|||||||
/**
|
/**
|
||||||
* Handle input into the textarea.
|
* Handle input into the textarea.
|
||||||
*
|
*
|
||||||
* @param {String} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
oninput(value) {
|
oninput(value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
* @param methods The name or names of the method(s) to extend
|
* @param methods The name or names of the method(s) to extend
|
||||||
* @param callback A callback which mutates the method's output
|
* @param callback A callback which mutates the method's output
|
||||||
*/
|
*/
|
||||||
export function extend<T extends object, K extends KeyOfType<T, Function>>(
|
export function extend<T extends Record<string, any>, K extends KeyOfType<T, Function>>(
|
||||||
object: T,
|
object: T,
|
||||||
methods: K | K[],
|
methods: K | K[],
|
||||||
callback: (this: T, val: ReturnType<T[K]>, ...args: Parameters<T[K]>) => void
|
callback: (this: T, val: ReturnType<T[K]>, ...args: Parameters<T[K]>) => void
|
||||||
@ -72,7 +72,7 @@ export function extend<T extends object, K extends KeyOfType<T, Function>>(
|
|||||||
* @param methods The name or names of the method(s) to override
|
* @param methods The name or names of the method(s) to override
|
||||||
* @param newMethod The method to replace it with
|
* @param newMethod The method to replace it with
|
||||||
*/
|
*/
|
||||||
export function override<T extends object, K extends KeyOfType<T, Function>>(
|
export function override<T extends Record<any, any>, K extends KeyOfType<T, Function>>(
|
||||||
object: T,
|
object: T,
|
||||||
methods: K | K[],
|
methods: K | K[],
|
||||||
newMethod: (this: T, orig: T[K], ...args: Parameters<T[K]>) => void
|
newMethod: (this: T, orig: T[K], ...args: Parameters<T[K]>) => void
|
||||||
|
@ -8,8 +8,8 @@ import app from '../../common/app';
|
|||||||
* punctuateSeries(['Toby', 'Franz', 'Dominion']) // Toby, Franz, and Dominion
|
* punctuateSeries(['Toby', 'Franz', 'Dominion']) // Toby, Franz, and Dominion
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param {Array} items
|
* @param {import('mithril').Children[]} items
|
||||||
* @return {VirtualElement}
|
* @return {import('mithril').Children}')}
|
||||||
*/
|
*/
|
||||||
export default function punctuateSeries(items) {
|
export default function punctuateSeries(items) {
|
||||||
if (items.length === 2) {
|
if (items.length === 2) {
|
||||||
|
@ -23,7 +23,7 @@ export default class AlertManagerState {
|
|||||||
/**
|
/**
|
||||||
* Show an Alert in the alerts area.
|
* Show an Alert in the alerts area.
|
||||||
*
|
*
|
||||||
* @returns The alert's ID, which can be used to dismiss the alert.
|
* @return The alert's ID, which can be used to dismiss the alert.
|
||||||
*/
|
*/
|
||||||
show(children: Mithril.Children): AlertIdentifier;
|
show(children: Mithril.Children): AlertIdentifier;
|
||||||
show(attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier;
|
show(attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier;
|
||||||
|
@ -73,7 +73,7 @@ export default class ModalManagerState {
|
|||||||
/**
|
/**
|
||||||
* Checks if a modal is currently open.
|
* Checks if a modal is currently open.
|
||||||
*
|
*
|
||||||
* @returns `true` if a modal dialog is currently open, otherwise `false`.
|
* @return `true` if a modal dialog is currently open, otherwise `false`.
|
||||||
*/
|
*/
|
||||||
isModalOpen(): boolean {
|
isModalOpen(): boolean {
|
||||||
return !!this.modal;
|
return !!this.modal;
|
||||||
|
@ -9,9 +9,8 @@ export default class PageState {
|
|||||||
/**
|
/**
|
||||||
* Determine whether the page matches the given class and data.
|
* Determine whether the page matches the given class and data.
|
||||||
*
|
*
|
||||||
* @param {object} type The page class to check against. Subclasses are
|
* @param {object} type The page class to check against. Subclasses are accepted as well.
|
||||||
* accepted as well.
|
* @param {Record<string, unknown>} data
|
||||||
* @param {object} data
|
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
matches(type, data = {}) {
|
matches(type, data = {}) {
|
||||||
|
@ -63,7 +63,6 @@ export default class Drawer {
|
|||||||
* Check whether or not the drawer is currently open.
|
* Check whether or not the drawer is currently open.
|
||||||
*
|
*
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
isOpen() {
|
isOpen() {
|
||||||
return this.appElement.classList.contains('drawerOpen');
|
return this.appElement.classList.contains('drawerOpen');
|
||||||
@ -71,8 +70,6 @@ export default class Drawer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the drawer.
|
* Hide the drawer.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
hide() {
|
hide() {
|
||||||
/**
|
/**
|
||||||
@ -100,8 +97,6 @@ export default class Drawer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the drawer.
|
* Show the drawer.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
show() {
|
show() {
|
||||||
this.appElement.classList.add('drawerOpen');
|
this.appElement.classList.add('drawerOpen');
|
||||||
|
@ -303,7 +303,7 @@ export default class ItemList<T> {
|
|||||||
*
|
*
|
||||||
* @param content The item's content (objects only)
|
* @param content The item's content (objects only)
|
||||||
* @param key The item's key
|
* @param key The item's key
|
||||||
* @returns Proxied content
|
* @return Proxied content
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
const later =
|
|
||||||
window.requestAnimationFrame ||
|
|
||||||
window.webkitRequestAnimationFrame ||
|
|
||||||
window.mozRequestAnimationFrame ||
|
|
||||||
window.msRequestAnimationFrame ||
|
|
||||||
window.oRequestAnimationFrame ||
|
|
||||||
((callback) => window.setTimeout(callback, 1000 / 60));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `ScrollListener` class sets up a listener that handles window scroll
|
* The `ScrollListener` class sets up a listener that handles window scroll
|
||||||
* events.
|
* events.
|
||||||
@ -14,7 +6,6 @@ export default class ScrollListener {
|
|||||||
/**
|
/**
|
||||||
* @param {(top: number) => void} callback The callback to run when the scroll position
|
* @param {(top: number) => void} callback The callback to run when the scroll position
|
||||||
* changes.
|
* changes.
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
constructor(callback) {
|
constructor(callback) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
@ -34,7 +25,7 @@ export default class ScrollListener {
|
|||||||
|
|
||||||
// Schedule the callback to be executed soon (TM), and stop throttling once
|
// Schedule the callback to be executed soon (TM), and stop throttling once
|
||||||
// the callback is done.
|
// the callback is done.
|
||||||
later(() => {
|
requestAnimationFrame(() => {
|
||||||
this.update();
|
this.update();
|
||||||
this.ticking = false;
|
this.ticking = false;
|
||||||
});
|
});
|
||||||
@ -44,8 +35,6 @@ export default class ScrollListener {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the callback, whether there was a scroll event or not.
|
* Run the callback, whether there was a scroll event or not.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
update() {
|
update() {
|
||||||
this.callback(window.pageYOffset);
|
this.callback(window.pageYOffset);
|
||||||
@ -53,8 +42,6 @@ export default class ScrollListener {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Start listening to and handling the window's scroll position.
|
* Start listening to and handling the window's scroll position.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
start() {
|
start() {
|
||||||
if (!this.active) {
|
if (!this.active) {
|
||||||
@ -64,8 +51,6 @@ export default class ScrollListener {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop listening to and handling the window's scroll position.
|
* Stop listening to and handling the window's scroll position.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
stop() {
|
stop() {
|
||||||
window.removeEventListener('scroll', this.active);
|
window.removeEventListener('scroll', this.active);
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
* position can be anchor to an element that is in or below the viewport, so
|
* position can be anchor to an element that is in or below the viewport, so
|
||||||
* the content in the viewport will stay the same.
|
* the content in the viewport will stay the same.
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element The element to anchor the scroll position to.
|
* @param {HTMLElement | SVGElement | Element} element The element to anchor the scroll position to.
|
||||||
* @param {Function} callback The callback to run that will change page content.
|
* @param {() => void} callback The callback to run that will change page content.
|
||||||
*/
|
*/
|
||||||
export default function anchorScroll(element, callback) {
|
export default function anchorScroll(element, callback) {
|
||||||
const $window = $(window);
|
const $window = $(window);
|
||||||
|
@ -4,10 +4,9 @@ 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.
|
||||||
*
|
*
|
||||||
* @param {...String} dependentKeys The keys of the dependent values.
|
* @param dependentKeys The keys of the dependent values.
|
||||||
* @param {function} compute The function which computes the value using the
|
* @param compute The function which computes the value using the
|
||||||
* dependent values.
|
* dependent values.
|
||||||
* @return {Function}
|
|
||||||
*/
|
*/
|
||||||
export default function computed<T, M = Model>(...args: [...string[], (this: M, ...args: unknown[]) => T]): () => T {
|
export default function computed<T, M = Model>(...args: [...string[], (this: M, ...args: unknown[]) => T]): () => T {
|
||||||
const keys = args.slice(0, -1) as string[];
|
const keys = args.slice(0, -1) as string[];
|
||||||
|
@ -13,17 +13,21 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Arrays of registered event handlers, grouped by the event name.
|
* Arrays of registered event handlers, grouped by the event name.
|
||||||
*
|
*
|
||||||
* @type {Object}
|
* @type {Record<string, unknown>}
|
||||||
* @protected
|
* @protected
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
handlers: null,
|
handlers: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all of the registered handlers for an event.
|
* Get all of the registered handlers for an event.
|
||||||
*
|
*
|
||||||
* @param {String} event The name of the event.
|
* @param {string} event The name of the event.
|
||||||
* @return {Array}
|
* @return {Function[]}
|
||||||
* @protected
|
* @protected
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
getHandlers(event) {
|
getHandlers(event) {
|
||||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||||
@ -38,9 +42,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Trigger an event.
|
* Trigger an event.
|
||||||
*
|
*
|
||||||
* @param {String} event The name of the event.
|
* @param {string} event The name of the event.
|
||||||
* @param {...*} args Arguments to pass to event handlers.
|
* @param {any[]} args Arguments to pass to event handlers.
|
||||||
* @public
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
trigger(event, ...args) {
|
trigger(event, ...args) {
|
||||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||||
@ -51,8 +56,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Register an event handler.
|
* Register an event handler.
|
||||||
*
|
*
|
||||||
* @param {String} event The name of the event.
|
* @param {string} event The name of the event.
|
||||||
* @param {function} handler The function to handle the event.
|
* @param {Function} handler The function to handle the event.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
on(event, handler) {
|
on(event, handler) {
|
||||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||||
@ -64,8 +71,10 @@ export default {
|
|||||||
* Register an event handler so that it will run only once, and then
|
* Register an event handler so that it will run only once, and then
|
||||||
* unregister itself.
|
* unregister itself.
|
||||||
*
|
*
|
||||||
* @param {String} event The name of the event.
|
* @param {string} event The name of the event.
|
||||||
* @param {function} handler The function to handle the event.
|
* @param {Function} handler The function to handle the event.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
one(event, handler) {
|
one(event, handler) {
|
||||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||||
@ -82,8 +91,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Unregister an event handler.
|
* Unregister an event handler.
|
||||||
*
|
*
|
||||||
* @param {String} event The name of the event.
|
* @param {string} event The name of the event.
|
||||||
* @param {function} handler The function that handles the event.
|
* @param {Function} handler The function that handles the event.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
off(event, handler) {
|
off(event, handler) {
|
||||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
* @example
|
* @example
|
||||||
* class MyClass extends mixin(ExistingClass, evented, etc) {}
|
* class MyClass extends mixin(ExistingClass, evented, etc) {}
|
||||||
*
|
*
|
||||||
* @param {Class} Parent The class to extend the new class from.
|
* @param {object} Parent The class to extend the new class from.
|
||||||
* @param {...Object} mixins The objects to mix in.
|
* @param {Record<string, any>[]} mixins The objects to mix in.
|
||||||
* @return {Class} A new class that extends Parent and contains the mixins.
|
* @return {object} A new class that extends Parent and contains the mixins.
|
||||||
*/
|
*/
|
||||||
export default function mixin(Parent, ...mixins) {
|
export default function mixin(Parent, ...mixins) {
|
||||||
class Mixed extends Parent {}
|
class Mixed extends Parent {}
|
||||||
|
@ -69,7 +69,7 @@ export default class AvatarEditor extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the items in the edit avatar dropdown menu.
|
* Get the items in the edit avatar dropdown menu.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
controlItems() {
|
controlItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -94,7 +94,7 @@ export default class AvatarEditor extends Component {
|
|||||||
/**
|
/**
|
||||||
* Enable dragover style
|
* Enable dragover style
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {DragEvent} e
|
||||||
*/
|
*/
|
||||||
enableDragover(e) {
|
enableDragover(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -105,7 +105,7 @@ export default class AvatarEditor extends Component {
|
|||||||
/**
|
/**
|
||||||
* Disable dragover style
|
* Disable dragover style
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {DragEvent} e
|
||||||
*/
|
*/
|
||||||
disableDragover(e) {
|
disableDragover(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -116,7 +116,7 @@ export default class AvatarEditor extends Component {
|
|||||||
/**
|
/**
|
||||||
* Upload avatar when file is dropped into dropzone.
|
* Upload avatar when file is dropped into dropzone.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {DragEvent} e
|
||||||
*/
|
*/
|
||||||
dropUpload(e) {
|
dropUpload(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -131,7 +131,7 @@ export default class AvatarEditor extends Component {
|
|||||||
* Thus, when the avatar editor's dropdown toggle button is clicked, we prompt
|
* Thus, when the avatar editor's dropdown toggle button is clicked, we prompt
|
||||||
* the user to upload an avatar immediately.
|
* the user to upload an avatar immediately.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {MouseEvent} e
|
||||||
*/
|
*/
|
||||||
quickUpload(e) {
|
quickUpload(e) {
|
||||||
if (!this.attrs.user.avatarUrl()) {
|
if (!this.attrs.user.avatarUrl()) {
|
||||||
@ -206,7 +206,7 @@ export default class AvatarEditor extends Component {
|
|||||||
* After a successful upload/removal, push the updated user data into the
|
* After a successful upload/removal, push the updated user data into the
|
||||||
* store, and force a recomputation of the user's avatar color.
|
* store, and force a recomputation of the user's avatar color.
|
||||||
*
|
*
|
||||||
* @param {Object} response
|
* @param {object} response
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
success(response) {
|
success(response) {
|
||||||
@ -220,7 +220,7 @@ export default class AvatarEditor extends Component {
|
|||||||
/**
|
/**
|
||||||
* If avatar upload/removal fails, stop loading.
|
* If avatar upload/removal fails, stop loading.
|
||||||
*
|
*
|
||||||
* @param {Object} response
|
* @param {object} response
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
failure(response) {
|
failure(response) {
|
||||||
|
@ -120,7 +120,7 @@ export default class CommentPost extends Post {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the post's header.
|
* Build an item list for the post's header.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
headerItems() {
|
headerItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -127,7 +127,7 @@ export default class Composer extends Component {
|
|||||||
/**
|
/**
|
||||||
* Resize the composer according to mouse movement.
|
* Resize the composer according to mouse movement.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {MouseEvent} e
|
||||||
*/
|
*/
|
||||||
onmousemove(e) {
|
onmousemove(e) {
|
||||||
if (!this.handle) return;
|
if (!this.handle) return;
|
||||||
@ -317,7 +317,7 @@ export default class Composer extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the composer's controls.
|
* Build an item list for the composer's controls.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
controlItems() {
|
controlItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -379,7 +379,7 @@ export default class Composer extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Default height of the Composer in case none is saved.
|
* Default height of the Composer in case none is saved.
|
||||||
* @returns {Integer}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
defaultHeight() {
|
defaultHeight() {
|
||||||
return this.$().height();
|
return this.$().height();
|
||||||
@ -387,7 +387,7 @@ export default class Composer extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a new Composer height and update the DOM.
|
* Save a new Composer height and update the DOM.
|
||||||
* @param {Integer} height
|
* @param {number} height
|
||||||
*/
|
*/
|
||||||
changeHeight(height) {
|
changeHeight(height) {
|
||||||
this.state.height = height;
|
this.state.height = height;
|
||||||
|
@ -76,7 +76,7 @@ export default class ComposerBody extends Component {
|
|||||||
/**
|
/**
|
||||||
* Check if there is any unsaved data.
|
* Check if there is any unsaved data.
|
||||||
*
|
*
|
||||||
* @return {String}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
hasChanges() {
|
hasChanges() {
|
||||||
const content = this.composer.fields.content();
|
const content = this.composer.fields.content();
|
||||||
@ -87,7 +87,7 @@ export default class ComposerBody extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the composer's header.
|
* Build an item list for the composer's header.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
headerItems() {
|
headerItems() {
|
||||||
return new ItemList();
|
return new ItemList();
|
||||||
|
@ -63,7 +63,7 @@ export default class DiscussionComposer extends ComposerBody {
|
|||||||
* Handle the title input's keydown event. When the return key is pressed,
|
* Handle the title input's keydown event. When the return key is pressed,
|
||||||
* move the focus to the start of the text editor.
|
* move the focus to the start of the text editor.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {KeyboardEvent} e
|
||||||
*/
|
*/
|
||||||
onkeydown(e) {
|
onkeydown(e) {
|
||||||
if (e.which === 13) {
|
if (e.which === 13) {
|
||||||
@ -82,7 +82,7 @@ export default class DiscussionComposer extends ComposerBody {
|
|||||||
/**
|
/**
|
||||||
* Get the data to submit to the server when the discussion is saved.
|
* Get the data to submit to the server when the discussion is saved.
|
||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {Record<string, unknown>}
|
||||||
*/
|
*/
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -23,7 +23,7 @@ export default class DiscussionHero extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the contents of the discussion hero.
|
* Build an item list for the contents of the discussion hero.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
items() {
|
items() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -143,7 +143,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
/**
|
/**
|
||||||
* Determine whether or not the discussion is currently being viewed.
|
* Determine whether or not the discussion is currently being viewed.
|
||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
active() {
|
active() {
|
||||||
return app.current.matches(DiscussionPage, { discussion: this.attrs.discussion });
|
return app.current.matches(DiscussionPage, { discussion: this.attrs.discussion });
|
||||||
@ -154,7 +154,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
* should be displayed instead of information about the most recent reply to
|
* should be displayed instead of information about the most recent reply to
|
||||||
* the discussion.
|
* the discussion.
|
||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
showFirstPost() {
|
showFirstPost() {
|
||||||
return ['newest', 'oldest'].indexOf(this.attrs.params.sort) !== -1;
|
return ['newest', 'oldest'].indexOf(this.attrs.params.sort) !== -1;
|
||||||
@ -164,7 +164,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
* Determine whether or not the number of replies should be shown instead of
|
* Determine whether or not the number of replies should be shown instead of
|
||||||
* the number of unread posts.
|
* the number of unread posts.
|
||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
showRepliesCount() {
|
showRepliesCount() {
|
||||||
return this.attrs.params.sort === 'replies';
|
return this.attrs.params.sort === 'replies';
|
||||||
@ -186,7 +186,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
* Build an item list of info for a discussion listing. By default this is
|
* Build an item list of info for a discussion listing. By default this is
|
||||||
* just the first/last post indicator.
|
* just the first/last post indicator.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
infoItems() {
|
infoItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -70,7 +70,7 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
// we'll just close it.
|
// we'll just close it.
|
||||||
app.pane?.disable();
|
app.pane?.disable();
|
||||||
|
|
||||||
if (app.composer.composingReplyTo(this.discussion) && !app.composer?.fields?.content()) {
|
if (this.discussion && app.composer.composingReplyTo(this.discussion) && !app.composer?.fields?.content()) {
|
||||||
app.composer.hide();
|
app.composer.hide();
|
||||||
} else {
|
} else {
|
||||||
app.composer.minimize();
|
app.composer.minimize();
|
||||||
@ -88,11 +88,9 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* List of components shown while the discussion is loading.
|
* List of components shown while the discussion is loading.
|
||||||
*
|
|
||||||
* @returns {ItemList}
|
|
||||||
*/
|
*/
|
||||||
loadingItems() {
|
loadingItems(): ItemList<Mithril.Children> {
|
||||||
const items = new ItemList();
|
const items = new ItemList<Mithril.Children>();
|
||||||
|
|
||||||
items.add('spinner', <LoadingIndicator />, 100);
|
items.add('spinner', <LoadingIndicator />, 100);
|
||||||
|
|
||||||
@ -101,10 +99,8 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that renders the `sidebarItems` ItemList.
|
* Function that renders the `sidebarItems` ItemList.
|
||||||
*
|
|
||||||
* @returns {import('mithril').Children}
|
|
||||||
*/
|
*/
|
||||||
sidebar() {
|
sidebar(): Mithril.Children {
|
||||||
return (
|
return (
|
||||||
<nav className="DiscussionPage-nav">
|
<nav className="DiscussionPage-nav">
|
||||||
<ul>{listItems(this.sidebarItems().toArray())}</ul>
|
<ul>{listItems(this.sidebarItems().toArray())}</ul>
|
||||||
@ -114,20 +110,16 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the discussion's hero.
|
* Renders the discussion's hero.
|
||||||
*
|
|
||||||
* @returns {import('mithril').Children}
|
|
||||||
*/
|
*/
|
||||||
hero() {
|
hero(): Mithril.Children {
|
||||||
return <DiscussionHero discussion={this.discussion} />;
|
return <DiscussionHero discussion={this.discussion} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of items rendered as the main page content.
|
* List of items rendered as the main page content.
|
||||||
*
|
|
||||||
* @returns {ItemList}
|
|
||||||
*/
|
*/
|
||||||
pageContent() {
|
pageContent(): ItemList<Mithril.Children> {
|
||||||
const items = new ItemList();
|
const items = new ItemList<Mithril.Children>();
|
||||||
|
|
||||||
items.add('hero', this.hero(), 100);
|
items.add('hero', this.hero(), 100);
|
||||||
items.add('main', <div className="container">{this.mainContent().toArray()}</div>, 10);
|
items.add('main', <div className="container">{this.mainContent().toArray()}</div>, 10);
|
||||||
@ -137,11 +129,9 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* List of items rendered inside the main page content container.
|
* List of items rendered inside the main page content container.
|
||||||
*
|
|
||||||
* @returns {ItemList}
|
|
||||||
*/
|
*/
|
||||||
mainContent() {
|
mainContent(): ItemList<Mithril.Children> {
|
||||||
const items = new ItemList();
|
const items = new ItemList<Mithril.Children>();
|
||||||
|
|
||||||
items.add('sidebar', this.sidebar(), 100);
|
items.add('sidebar', this.sidebar(), 100);
|
||||||
|
|
||||||
@ -163,7 +153,7 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
/**
|
/**
|
||||||
* Load the discussion from the API or use the preloaded one.
|
* Load the discussion from the API or use the preloaded one.
|
||||||
*/
|
*/
|
||||||
load() {
|
load(): void {
|
||||||
const preloadedDiscussion = app.preloadedApiDocument<Discussion>();
|
const preloadedDiscussion = app.preloadedApiDocument<Discussion>();
|
||||||
if (preloadedDiscussion) {
|
if (preloadedDiscussion) {
|
||||||
// We must wrap this in a setTimeout because if we are mounting this
|
// We must wrap this in a setTimeout because if we are mounting this
|
||||||
@ -183,10 +173,8 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
/**
|
/**
|
||||||
* Get the parameters that should be passed in the API request to get the
|
* Get the parameters that should be passed in the API request to get the
|
||||||
* discussion.
|
* discussion.
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
*/
|
||||||
requestParams() {
|
requestParams(): Record<string, unknown> {
|
||||||
return {
|
return {
|
||||||
bySlug: true,
|
bySlug: true,
|
||||||
page: { near: this.near },
|
page: { near: this.near },
|
||||||
@ -196,7 +184,7 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
/**
|
/**
|
||||||
* Initialize the component to display the given discussion.
|
* Initialize the component to display the given discussion.
|
||||||
*/
|
*/
|
||||||
show(discussion: ApiResponseSingle<Discussion>) {
|
show(discussion: ApiResponseSingle<Discussion>): void {
|
||||||
app.history.push('discussion', discussion.title());
|
app.history.push('discussion', discussion.title());
|
||||||
app.setTitle(discussion.title());
|
app.setTitle(discussion.title());
|
||||||
app.setTitleCount(0);
|
app.setTitleCount(0);
|
||||||
@ -242,21 +230,23 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||||||
* Build an item list for the contents of the sidebar.
|
* Build an item list for the contents of the sidebar.
|
||||||
*/
|
*/
|
||||||
sidebarItems() {
|
sidebarItems() {
|
||||||
const items = new ItemList<Mithril.Vnode>();
|
const items = new ItemList<Mithril.Children>();
|
||||||
|
|
||||||
items.add(
|
if (this.discussion) {
|
||||||
'controls',
|
items.add(
|
||||||
SplitDropdown.component(
|
'controls',
|
||||||
{
|
SplitDropdown.component(
|
||||||
icon: 'fas fa-ellipsis-v',
|
{
|
||||||
className: 'App-primaryControl',
|
icon: 'fas fa-ellipsis-v',
|
||||||
buttonClassName: 'Button--primary',
|
className: 'App-primaryControl',
|
||||||
accessibleToggleLabel: app.translator.trans('core.forum.discussion_controls.toggle_dropdown_accessible_label'),
|
buttonClassName: 'Button--primary',
|
||||||
},
|
accessibleToggleLabel: app.translator.trans('core.forum.discussion_controls.toggle_dropdown_accessible_label'),
|
||||||
DiscussionControls.controls(this.discussion, this).toArray()
|
},
|
||||||
),
|
DiscussionControls.controls(this.discussion, this).toArray()
|
||||||
100
|
),
|
||||||
);
|
100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
items.add(
|
items.add(
|
||||||
'scrubber',
|
'scrubber',
|
||||||
|
@ -62,7 +62,7 @@ export default class EditPostComposer extends ComposerBody {
|
|||||||
/**
|
/**
|
||||||
* Get the data to submit to the server when the post is saved.
|
* Get the data to submit to the server when the post is saved.
|
||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {Record<string, unknown>}
|
||||||
*/
|
*/
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -45,7 +45,7 @@ export default class EventPost extends Post {
|
|||||||
/**
|
/**
|
||||||
* Get the name of the event icon.
|
* Get the name of the event icon.
|
||||||
*
|
*
|
||||||
* @return {String}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
icon() {
|
icon() {
|
||||||
return '';
|
return '';
|
||||||
@ -54,8 +54,8 @@ export default class EventPost extends Post {
|
|||||||
/**
|
/**
|
||||||
* Get the description text for the event.
|
* Get the description text for the event.
|
||||||
*
|
*
|
||||||
* @param {Object} data
|
* @param {Record<string, unknown>} data
|
||||||
* @return {String|Object} The description to render in the DOM
|
* @return {import('mithril').Children} The description to render in the DOM
|
||||||
*/
|
*/
|
||||||
description(data) {
|
description(data) {
|
||||||
return app.translator.trans(this.descriptionKey(), data);
|
return app.translator.trans(this.descriptionKey(), data);
|
||||||
@ -64,7 +64,7 @@ export default class EventPost extends Post {
|
|||||||
/**
|
/**
|
||||||
* Get the translation key for the description of the event.
|
* Get the translation key for the description of the event.
|
||||||
*
|
*
|
||||||
* @return {String}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
descriptionKey() {
|
descriptionKey() {
|
||||||
return '';
|
return '';
|
||||||
@ -73,7 +73,7 @@ export default class EventPost extends Post {
|
|||||||
/**
|
/**
|
||||||
* Get the translation data for the description of the event.
|
* Get the translation data for the description of the event.
|
||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {Record<string, unknown>}
|
||||||
*/
|
*/
|
||||||
descriptionData() {
|
descriptionData() {
|
||||||
return {};
|
return {};
|
||||||
|
@ -14,7 +14,7 @@ export default class HeaderPrimary extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the controls.
|
* Build an item list for the controls.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
items() {
|
items() {
|
||||||
return new ItemList();
|
return new ItemList();
|
||||||
|
@ -133,7 +133,7 @@ export default class IndexPage extends Page {
|
|||||||
/**
|
/**
|
||||||
* Get the component to display as the hero.
|
* Get the component to display as the hero.
|
||||||
*
|
*
|
||||||
* @return {MithrilComponent}
|
* @return {import('mithril').Children}
|
||||||
*/
|
*/
|
||||||
hero() {
|
hero() {
|
||||||
return WelcomeHero.component();
|
return WelcomeHero.component();
|
||||||
@ -144,7 +144,7 @@ export default class IndexPage extends Page {
|
|||||||
* "New Discussion" button, and then a DropdownSelect component containing a
|
* "New Discussion" button, and then a DropdownSelect component containing a
|
||||||
* list of navigation items.
|
* list of navigation items.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
sidebarItems() {
|
sidebarItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -187,7 +187,7 @@ export default class IndexPage extends Page {
|
|||||||
* 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<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
navItems() {
|
navItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -213,7 +213,7 @@ export default class IndexPage extends Page {
|
|||||||
* 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
|
||||||
* the way discussions are sorted.
|
* the way discussions are sorted.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
viewItems() {
|
viewItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -255,7 +255,7 @@ export default class IndexPage extends Page {
|
|||||||
* 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<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
actionItems() {
|
actionItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -294,7 +294,7 @@ export default class IndexPage extends Page {
|
|||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
newDiscussionAction() {
|
newDiscussionAction() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -313,8 +313,6 @@ export default class IndexPage extends Page {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark all discussions as read.
|
* Mark all discussions as read.
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
markAllAsRead() {
|
markAllAsRead() {
|
||||||
const confirmation = confirm(app.translator.trans('core.forum.index.mark_all_as_read_confirmation'));
|
const confirmation = confirm(app.translator.trans('core.forum.index.mark_all_as_read_confirmation'));
|
||||||
|
@ -12,8 +12,7 @@ export default class LogInButtons extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build a list of LogInButton components.
|
* Build a list of LogInButton components.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
items() {
|
items() {
|
||||||
return new ItemList();
|
return new ItemList();
|
||||||
|
@ -57,7 +57,7 @@ export default class Notification extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the name of the icon that should be displayed in the notification.
|
* Get the name of the icon that should be displayed in the notification.
|
||||||
*
|
*
|
||||||
* @return {String}
|
* @return {string}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
icon() {}
|
icon() {}
|
||||||
@ -65,7 +65,7 @@ export default class Notification extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the URL that the notification should link to.
|
* Get the URL that the notification should link to.
|
||||||
*
|
*
|
||||||
* @return {String}
|
* @return {string}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
href() {}
|
href() {}
|
||||||
@ -73,7 +73,7 @@ export default class Notification extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the content of the notification.
|
* Get the content of the notification.
|
||||||
*
|
*
|
||||||
* @return {VirtualElement}
|
* @return {import('mithril').Children}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
content() {}
|
content() {}
|
||||||
@ -81,7 +81,7 @@ export default class Notification extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the excerpt of the notification.
|
* Get the excerpt of the notification.
|
||||||
*
|
*
|
||||||
* @return {VirtualElement}
|
* @return {import('mithril').Children}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
excerpt() {}
|
excerpt() {}
|
||||||
|
@ -19,21 +19,21 @@ export default class NotificationGrid extends Component {
|
|||||||
/**
|
/**
|
||||||
* Information about the available notification methods.
|
* Information about the available notification methods.
|
||||||
*
|
*
|
||||||
* @type {Array}
|
* @type {({ name: string, icon: string, label: import('mithril').Children })[]}
|
||||||
*/
|
*/
|
||||||
this.methods = this.notificationMethods().toArray();
|
this.methods = this.notificationMethods().toArray();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of which notification checkboxes are loading.
|
* A map of which notification checkboxes are loading.
|
||||||
*
|
*
|
||||||
* @type {Object}
|
* @type {Record<string, boolean>}
|
||||||
*/
|
*/
|
||||||
this.loading = {};
|
this.loading = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the available notification types.
|
* Information about the available notification types.
|
||||||
*
|
*
|
||||||
* @type {Array}
|
* @type {({ name: string, icon: string, label: import('mithril').Children })[]}
|
||||||
*/
|
*/
|
||||||
this.types = this.notificationTypes().toArray();
|
this.types = this.notificationTypes().toArray();
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ export default class NotificationGrid extends Component {
|
|||||||
* Toggle the state of the given preferences, based on the value of the first
|
* Toggle the state of the given preferences, based on the value of the first
|
||||||
* one.
|
* one.
|
||||||
*
|
*
|
||||||
* @param {Array} keys
|
* @param {string[]} keys
|
||||||
*/
|
*/
|
||||||
toggle(keys) {
|
toggle(keys) {
|
||||||
const user = this.attrs.user;
|
const user = this.attrs.user;
|
||||||
@ -128,7 +128,7 @@ export default class NotificationGrid extends Component {
|
|||||||
/**
|
/**
|
||||||
* Toggle all notification types for the given method.
|
* Toggle all notification types for the given method.
|
||||||
*
|
*
|
||||||
* @param {String} method
|
* @param {string} method
|
||||||
*/
|
*/
|
||||||
toggleMethod(method) {
|
toggleMethod(method) {
|
||||||
const keys = this.types.map((type) => this.preferenceKey(type.name, method)).filter((key) => key in this.attrs.user.preferences());
|
const keys = this.types.map((type) => this.preferenceKey(type.name, method)).filter((key) => key in this.attrs.user.preferences());
|
||||||
@ -139,7 +139,7 @@ export default class NotificationGrid extends Component {
|
|||||||
/**
|
/**
|
||||||
* Toggle all notification methods for the given type.
|
* Toggle all notification methods for the given type.
|
||||||
*
|
*
|
||||||
* @param {String} type
|
* @param {string} type
|
||||||
*/
|
*/
|
||||||
toggleType(type) {
|
toggleType(type) {
|
||||||
const keys = this.methods.map((method) => this.preferenceKey(type, method.name)).filter((key) => key in this.attrs.user.preferences());
|
const keys = this.methods.map((method) => this.preferenceKey(type, method.name)).filter((key) => key in this.attrs.user.preferences());
|
||||||
@ -151,9 +151,9 @@ export default class NotificationGrid extends Component {
|
|||||||
* Get the name of the preference key for the given notification type-method
|
* Get the name of the preference key for the given notification type-method
|
||||||
* combination.
|
* combination.
|
||||||
*
|
*
|
||||||
* @param {String} type
|
* @param {string} type
|
||||||
* @param {String} method
|
* @param {string} method
|
||||||
* @return {String}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
preferenceKey(type, method) {
|
preferenceKey(type, method) {
|
||||||
return 'notify_' + type + '_' + method;
|
return 'notify_' + type + '_' + method;
|
||||||
@ -168,7 +168,7 @@ export default class NotificationGrid extends Component {
|
|||||||
* - `icon` The icon to display in the column header.
|
* - `icon` The icon to display in the column header.
|
||||||
* - `label` The label to display in the column header.
|
* - `label` The label to display in the column header.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<{ name: string, icon: string, label: import('mithril').Children }>}
|
||||||
*/
|
*/
|
||||||
notificationMethods() {
|
notificationMethods() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -197,7 +197,7 @@ export default class NotificationGrid extends Component {
|
|||||||
* - `icon` The icon to display in the notification grid row.
|
* - `icon` The icon to display in the notification grid row.
|
||||||
* - `label` The label to display in the notification grid row.
|
* - `label` The label to display in the notification grid row.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<{ name: string, icon: string, label: import('mithril').Children}>}
|
||||||
*/
|
*/
|
||||||
notificationTypes() {
|
notificationTypes() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -102,7 +102,7 @@ export default class Post extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get attributes for the post element.
|
* Get attributes for the post element.
|
||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {Record<string, unknown>}
|
||||||
*/
|
*/
|
||||||
elementAttrs() {
|
elementAttrs() {
|
||||||
return {};
|
return {};
|
||||||
@ -111,16 +111,16 @@ export default class Post extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the post's content.
|
* Get the post's content.
|
||||||
*
|
*
|
||||||
* @return {Array}
|
* @return {import('mithril').Children}
|
||||||
*/
|
*/
|
||||||
content() {
|
content() {
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the post's classes.
|
* Get the post's classes.
|
||||||
*
|
*
|
||||||
* @param existing string
|
* @param {string} existing
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
classes(existing) {
|
classes(existing) {
|
||||||
@ -137,7 +137,7 @@ export default class Post extends Component {
|
|||||||
classes.push('Post--by-actor');
|
classes.push('Post--by-actor');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user && user.id() === discussion.attribute('startUserId')) {
|
if (user?.id() === discussion.attribute('startUserId')) {
|
||||||
classes.push('Post--by-start-user');
|
classes.push('Post--by-start-user');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ export default class Post extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the post's actions.
|
* Build an item list for the post's actions.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
actionItems() {
|
actionItems() {
|
||||||
return new ItemList();
|
return new ItemList();
|
||||||
@ -156,7 +156,7 @@ export default class Post extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the post's footer.
|
* Build an item list for the post's footer.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
footerItems() {
|
footerItems() {
|
||||||
return new ItemList();
|
return new ItemList();
|
||||||
|
@ -51,8 +51,8 @@ export default class PostMeta extends Component {
|
|||||||
/**
|
/**
|
||||||
* Get the permalink for the given post.
|
* Get the permalink for the given post.
|
||||||
*
|
*
|
||||||
* @param {Post} post
|
* @param {import('../../common/models/Post').default} post
|
||||||
* @returns {String}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getPermalink(post) {
|
getPermalink(post) {
|
||||||
return app.forum.attribute('baseUrl') + app.route.post(post);
|
return app.forum.attribute('baseUrl') + app.route.post(post);
|
||||||
|
@ -148,7 +148,7 @@ export default class PostStream extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Integer} top
|
* @param {number} top
|
||||||
*/
|
*/
|
||||||
onscroll(top = window.pageYOffset) {
|
onscroll(top = window.pageYOffset) {
|
||||||
if (this.stream.paused || this.stream.pagesLoading) return;
|
if (this.stream.paused || this.stream.pagesLoading) return;
|
||||||
@ -167,7 +167,7 @@ export default class PostStream extends Component {
|
|||||||
* Check if either extreme of the post stream is in the viewport,
|
* Check if either extreme of the post stream is in the viewport,
|
||||||
* and if so, trigger loading the next/previous page.
|
* and if so, trigger loading the next/previous page.
|
||||||
*
|
*
|
||||||
* @param {Integer} top
|
* @param {number} top
|
||||||
*/
|
*/
|
||||||
loadPostsIfNeeded(top = window.pageYOffset) {
|
loadPostsIfNeeded(top = window.pageYOffset) {
|
||||||
const marginTop = this.getMarginTop();
|
const marginTop = this.getMarginTop();
|
||||||
@ -298,7 +298,7 @@ export default class PostStream extends Component {
|
|||||||
* Get the distance from the top of the viewport to the point at which we
|
* Get the distance from the top of the viewport to the point at which we
|
||||||
* would consider a post to be the first one visible.
|
* would consider a post to be the first one visible.
|
||||||
*
|
*
|
||||||
* @return {Integer}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
getMarginTop() {
|
getMarginTop() {
|
||||||
const headerId = app.screen() === 'phone' ? '#app-navigation' : '#header';
|
const headerId = app.screen() === 'phone' ? '#app-navigation' : '#header';
|
||||||
@ -309,9 +309,9 @@ export default class PostStream extends Component {
|
|||||||
/**
|
/**
|
||||||
* Scroll down to a certain post by number and 'flash' it.
|
* Scroll down to a certain post by number and 'flash' it.
|
||||||
*
|
*
|
||||||
* @param {Integer} number
|
* @param {number} number
|
||||||
* @param {Boolean} animate
|
* @param {boolean} animate
|
||||||
* @return {jQuery.Deferred}
|
* @return {JQueryDeferred}
|
||||||
*/
|
*/
|
||||||
scrollToNumber(number, animate) {
|
scrollToNumber(number, animate) {
|
||||||
const $item = this.$(`.PostStream-item[data-number=${number}]`);
|
const $item = this.$(`.PostStream-item[data-number=${number}]`);
|
||||||
@ -322,10 +322,10 @@ export default class PostStream extends Component {
|
|||||||
/**
|
/**
|
||||||
* Scroll down to a certain post by index.
|
* Scroll down to a certain post by index.
|
||||||
*
|
*
|
||||||
* @param {Integer} index
|
* @param {number} index
|
||||||
* @param {Boolean} animate
|
* @param {boolean} animate
|
||||||
* @param {Boolean} reply Whether or not to scroll to the reply placeholder.
|
* @param {boolean} reply Whether or not to scroll to the reply placeholder.
|
||||||
* @return {jQuery.Deferred}
|
* @return {JQueryDeferred}
|
||||||
*/
|
*/
|
||||||
scrollToIndex(index, animate, reply) {
|
scrollToIndex(index, animate, reply) {
|
||||||
const $item = reply ? $('.PostStream-item:last-child') : this.$(`.PostStream-item[data-index=${index}]`);
|
const $item = reply ? $('.PostStream-item:last-child') : this.$(`.PostStream-item[data-index=${index}]`);
|
||||||
@ -340,12 +340,12 @@ export default class PostStream extends Component {
|
|||||||
/**
|
/**
|
||||||
* Scroll down to the given post.
|
* Scroll down to the given post.
|
||||||
*
|
*
|
||||||
* @param {jQuery} $item
|
* @param {JQuery} $item
|
||||||
* @param {Boolean} animate
|
* @param {boolean} animate
|
||||||
* @param {Boolean} force Whether or not to force scrolling to the item, even
|
* @param {boolean} force Whether or not to force scrolling to the item, even
|
||||||
* if it is already in the viewport.
|
* if it is already in the viewport.
|
||||||
* @param {Boolean} reply Whether or not to scroll to the reply placeholder.
|
* @param {boolean} reply Whether or not to scroll to the reply placeholder.
|
||||||
* @return {jQuery.Deferred}
|
* @return {JQueryDeferred}
|
||||||
*/
|
*/
|
||||||
scrollToItem($item, animate, force, reply) {
|
scrollToItem($item, animate, force, reply) {
|
||||||
const $container = $('html, body').stop(true);
|
const $container = $('html, body').stop(true);
|
||||||
@ -418,7 +418,7 @@ export default class PostStream extends Component {
|
|||||||
/**
|
/**
|
||||||
* 'Flash' the given post, drawing the user's attention to it.
|
* 'Flash' the given post, drawing the user's attention to it.
|
||||||
*
|
*
|
||||||
* @param {jQuery} $item
|
* @param {JQuery} $item
|
||||||
*/
|
*/
|
||||||
flashItem($item) {
|
flashItem($item) {
|
||||||
// This might execute before the fadeIn class has been removed in PostStreamItem's
|
// This might execute before the fadeIn class has been removed in PostStreamItem's
|
||||||
|
@ -158,7 +158,7 @@ export default class PostStreamScrubber extends Component {
|
|||||||
* Update the scrollbar's position to reflect the current values of the
|
* Update the scrollbar's position to reflect the current values of the
|
||||||
* index/visible properties.
|
* index/visible properties.
|
||||||
*
|
*
|
||||||
* @param {Boolean} animate
|
* @param {Partial<{fromScroll: boolean, forceHeightChange: boolean, animate: boolean}>} options
|
||||||
*/
|
*/
|
||||||
updateScrubberValues(options = {}) {
|
updateScrubberValues(options = {}) {
|
||||||
const index = this.stream.index;
|
const index = this.stream.index;
|
||||||
@ -301,7 +301,7 @@ export default class PostStreamScrubber extends Component {
|
|||||||
* Get the percentage of the height of the scrubber that should be allocated
|
* Get the percentage of the height of the scrubber that should be allocated
|
||||||
* to each post.
|
* to each post.
|
||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {{ index: number, visible: number }}
|
||||||
* @property {Number} index The percent per post for posts on either side of
|
* @property {Number} index The percent per post for posts on either side of
|
||||||
* the visible part of the scrubber.
|
* the visible part of the scrubber.
|
||||||
* @property {Number} visible The percent per post for the visible part of the
|
* @property {Number} visible The percent per post for the visible part of the
|
||||||
|
@ -38,7 +38,7 @@ export default class PostsUserPage extends UserPage {
|
|||||||
/**
|
/**
|
||||||
* The number of activity items to load per request.
|
* The number of activity items to load per request.
|
||||||
*
|
*
|
||||||
* @type {Integer}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
this.loadLimit = 20;
|
this.loadLimit = 20;
|
||||||
|
|
||||||
@ -100,8 +100,6 @@ export default class PostsUserPage extends UserPage {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear and reload the user's activity feed.
|
* Clear and reload the user's activity feed.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
refresh() {
|
refresh() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
@ -115,8 +113,8 @@ export default class PostsUserPage extends UserPage {
|
|||||||
/**
|
/**
|
||||||
* Load a new page of the user's activity feed.
|
* Load a new page of the user's activity feed.
|
||||||
*
|
*
|
||||||
* @param {Integer} [offset] The position to start getting results from.
|
* @param {number} [offset] The position to start getting results from.
|
||||||
* @return {Promise}
|
* @return {Promise<import('../../common/models/Post').default[]>}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
loadResults(offset) {
|
loadResults(offset) {
|
||||||
@ -132,8 +130,6 @@ export default class PostsUserPage extends UserPage {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the next page of results.
|
* Load the next page of results.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
loadMore() {
|
loadMore() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
@ -143,13 +139,13 @@ export default class PostsUserPage extends UserPage {
|
|||||||
/**
|
/**
|
||||||
* Parse results and append them to the activity feed.
|
* Parse results and append them to the activity feed.
|
||||||
*
|
*
|
||||||
* @param {Post[]} results
|
* @param {import('../../common/models/Post').default[]} results
|
||||||
* @return {Post[]}
|
* @return {import('../../common/models/Post').default[]}
|
||||||
*/
|
*/
|
||||||
parseResults(results) {
|
parseResults(results) {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
[].push.apply(this.posts, results);
|
this.posts.push(results);
|
||||||
|
|
||||||
this.moreResults = results.length >= this.loadLimit;
|
this.moreResults = results.length >= this.loadLimit;
|
||||||
m.redraw();
|
m.redraw();
|
||||||
|
@ -59,7 +59,7 @@ export default class ReplyComposer extends ComposerBody {
|
|||||||
/**
|
/**
|
||||||
* Get the data to submit to the server when the reply is saved.
|
* Get the data to submit to the server when the reply is saved.
|
||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {Record<string, unknown>}
|
||||||
*/
|
*/
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -35,7 +35,7 @@ export default class SessionDropdown extends Dropdown {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the contents of the dropdown menu.
|
* Build an item list for the contents of the dropdown menu.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
items() {
|
items() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -33,7 +33,7 @@ export default class SettingsPage extends UserPage {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the user's settings controls.
|
* Build an item list for the user's settings controls.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
settingsItems() {
|
settingsItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -53,7 +53,7 @@ export default class SettingsPage extends UserPage {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the user's account settings.
|
* Build an item list for the user's account settings.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
accountItems() {
|
accountItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -78,7 +78,7 @@ export default class SettingsPage extends UserPage {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the user's notification settings.
|
* Build an item list for the user's notification settings.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
notificationsItems() {
|
notificationsItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -91,7 +91,7 @@ export default class SettingsPage extends UserPage {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the user's privacy settings.
|
* Build an item list for the user's privacy settings.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
privacyItems() {
|
privacyItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -145,8 +145,6 @@ export default class SignUpModal<CustomAttrs extends ISignupModalAttrs = ISignup
|
|||||||
/**
|
/**
|
||||||
* Open the log in modal, prefilling it with an email/username/password if
|
* Open the log in modal, prefilling it with an email/username/password if
|
||||||
* the user has entered one.
|
* the user has entered one.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
logIn() {
|
logIn() {
|
||||||
const attrs = {
|
const attrs = {
|
||||||
|
@ -73,7 +73,7 @@ export default class UserCard extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list of tidbits of info to show on this user's profile.
|
* Build an item list of tidbits of info to show on this user's profile.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
infoItems() {
|
infoItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -30,6 +30,11 @@ export default class UserPage extends Page {
|
|||||||
this.bodyClass = 'App--user';
|
this.bodyClass = 'App--user';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base view template for the user page.
|
||||||
|
*
|
||||||
|
* @return {import('mithril').Children}
|
||||||
|
*/
|
||||||
view() {
|
view() {
|
||||||
return (
|
return (
|
||||||
<div className="UserPage">
|
<div className="UserPage">
|
||||||
@ -60,7 +65,7 @@ export default class UserPage extends Page {
|
|||||||
/**
|
/**
|
||||||
* Get the content to display in the user page.
|
* Get the content to display in the user page.
|
||||||
*
|
*
|
||||||
* @return {VirtualElement}
|
* @return {import('mithril').Children}
|
||||||
*/
|
*/
|
||||||
content() {}
|
content() {}
|
||||||
|
|
||||||
@ -68,7 +73,7 @@ export default class UserPage extends Page {
|
|||||||
* Initialize the component with a user, and trigger the loading of their
|
* Initialize the component with a user, and trigger the loading of their
|
||||||
* activity feed.
|
* activity feed.
|
||||||
*
|
*
|
||||||
* @param {User} user
|
* @param {import('../../common/models/User').default} user
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
show(user) {
|
show(user) {
|
||||||
@ -85,7 +90,7 @@ export default class UserPage extends Page {
|
|||||||
* Given a username, load the user's profile from the store, or make a request
|
* Given a username, load the user's profile from the store, or make a request
|
||||||
* if we don't have it yet. Then initialize the profile page with that user.
|
* if we don't have it yet. Then initialize the profile page with that user.
|
||||||
*
|
*
|
||||||
* @param {String} username
|
* @param {string} username
|
||||||
*/
|
*/
|
||||||
loadUser(username) {
|
loadUser(username) {
|
||||||
const lowercaseUsername = username.toLowerCase();
|
const lowercaseUsername = username.toLowerCase();
|
||||||
@ -110,7 +115,7 @@ export default class UserPage extends Page {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the content of the sidebar.
|
* Build an item list for the content of the sidebar.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
sidebarItems() {
|
sidebarItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -128,7 +133,7 @@ export default class UserPage extends Page {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the navigation in the sidebar.
|
* Build an item list for the navigation in the sidebar.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
*/
|
*/
|
||||||
navItems() {
|
navItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
@ -17,7 +17,7 @@ class ComposerState {
|
|||||||
* The composer's intended height, which can be modified by the user
|
* The composer's intended height, which can be modified by the user
|
||||||
* (by dragging the composer handle).
|
* (by dragging the composer handle).
|
||||||
*
|
*
|
||||||
* @type {Integer}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
this.height = null;
|
this.height = null;
|
||||||
|
|
||||||
@ -41,8 +41,7 @@ class ComposerState {
|
|||||||
/**
|
/**
|
||||||
* Load a content component into the composer.
|
* Load a content component into the composer.
|
||||||
*
|
*
|
||||||
* @param {ComposerBody} componentClass
|
* @param {typeof import('../components/ComposerBody').default} componentClass
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
load(componentClass, attrs) {
|
load(componentClass, attrs) {
|
||||||
const body = { componentClass, attrs };
|
const body = { componentClass, attrs };
|
||||||
@ -82,8 +81,6 @@ class ComposerState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the composer.
|
* Show the composer.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
show() {
|
show() {
|
||||||
if (this.position === ComposerState.Position.NORMAL || this.position === ComposerState.Position.FULLSCREEN) return;
|
if (this.position === ComposerState.Position.NORMAL || this.position === ComposerState.Position.FULLSCREEN) return;
|
||||||
@ -94,8 +91,6 @@ class ComposerState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the composer.
|
* Close the composer.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
hide() {
|
hide() {
|
||||||
this.clear();
|
this.clear();
|
||||||
@ -105,8 +100,6 @@ class ComposerState {
|
|||||||
/**
|
/**
|
||||||
* Confirm with the user so they don't lose their content, then close the
|
* Confirm with the user so they don't lose their content, then close the
|
||||||
* composer.
|
* composer.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
close() {
|
close() {
|
||||||
if (this.preventExit()) return;
|
if (this.preventExit()) return;
|
||||||
@ -116,8 +109,6 @@ class ComposerState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimize the composer. Has no effect if the composer is hidden.
|
* Minimize the composer. Has no effect if the composer is hidden.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
minimize() {
|
minimize() {
|
||||||
if (!this.isVisible()) return;
|
if (!this.isVisible()) return;
|
||||||
@ -129,8 +120,6 @@ class ComposerState {
|
|||||||
/**
|
/**
|
||||||
* Take the composer into fullscreen mode. Has no effect if the composer is
|
* Take the composer into fullscreen mode. Has no effect if the composer is
|
||||||
* hidden.
|
* hidden.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
fullScreen() {
|
fullScreen() {
|
||||||
if (!this.isVisible()) return;
|
if (!this.isVisible()) return;
|
||||||
@ -141,8 +130,6 @@ class ComposerState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Exit fullscreen mode.
|
* Exit fullscreen mode.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
exitFullScreen() {
|
exitFullScreen() {
|
||||||
if (this.position !== ComposerState.Position.FULLSCREEN) return;
|
if (this.position !== ComposerState.Position.FULLSCREEN) return;
|
||||||
@ -154,8 +141,7 @@ class ComposerState {
|
|||||||
/**
|
/**
|
||||||
* Determine whether the body matches the given component class and data.
|
* Determine whether the body matches the given component class and data.
|
||||||
*
|
*
|
||||||
* @param {object} type The component class to check against. Subclasses are
|
* @param {object} type The component class to check against. Subclasses are accepted as well.
|
||||||
* accepted as well.
|
|
||||||
* @param {object} data
|
* @param {object} data
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
@ -186,8 +172,7 @@ class ComposerState {
|
|||||||
* This will be true if the Composer is in full-screen mode on desktop,
|
* This will be true if the Composer is in full-screen mode on desktop,
|
||||||
* or if we are on a mobile device, where we always consider the composer as full-screen..
|
* or if we are on a mobile device, where we always consider the composer as full-screen..
|
||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @return {boolean}
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
isFullScreen() {
|
isFullScreen() {
|
||||||
return this.position === ComposerState.Position.FULLSCREEN || app.screen() === 'phone';
|
return this.position === ComposerState.Position.FULLSCREEN || app.screen() === 'phone';
|
||||||
@ -197,8 +182,8 @@ class ComposerState {
|
|||||||
* Check whether or not the user is currently composing a reply to a
|
* Check whether or not the user is currently composing a reply to a
|
||||||
* discussion.
|
* discussion.
|
||||||
*
|
*
|
||||||
* @param {Discussion} discussion
|
* @param {import('../../common/models/Discussion').default} discussion
|
||||||
* @return {Boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
composingReplyTo(discussion) {
|
composingReplyTo(discussion) {
|
||||||
return this.isVisible() && this.bodyMatches(ReplyComposer, { discussion });
|
return this.isVisible() && this.bodyMatches(ReplyComposer, { discussion });
|
||||||
@ -208,7 +193,7 @@ class ComposerState {
|
|||||||
* Confirm with the user that they want to close the composer and lose their
|
* Confirm with the user that they want to close the composer and lose their
|
||||||
* content.
|
* content.
|
||||||
*
|
*
|
||||||
* @return {Boolean} Whether or not the exit was cancelled.
|
* @return {boolean} Whether or not the exit was cancelled.
|
||||||
*/
|
*/
|
||||||
preventExit() {
|
preventExit() {
|
||||||
if (!this.isVisible()) return;
|
if (!this.isVisible()) return;
|
||||||
@ -226,8 +211,8 @@ class ComposerState {
|
|||||||
* confirmation is necessary. If the callback returns true at the time of
|
* confirmation is necessary. If the callback returns true at the time of
|
||||||
* closing, the provided text will be shown in a standard confirmation dialog.
|
* closing, the provided text will be shown in a standard confirmation dialog.
|
||||||
*
|
*
|
||||||
* @param {Function} callback
|
* @param {() => boolean} callback
|
||||||
* @param {String} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
preventClosingWhen(callback, message) {
|
preventClosingWhen(callback, message) {
|
||||||
this.onExit = { callback, message };
|
this.onExit = { callback, message };
|
||||||
@ -235,7 +220,7 @@ class ComposerState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum height of the Composer.
|
* Minimum height of the Composer.
|
||||||
* @returns {Integer}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
minimumHeight() {
|
minimumHeight() {
|
||||||
return 200;
|
return 200;
|
||||||
@ -243,7 +228,7 @@ class ComposerState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Maxmimum height of the Composer.
|
* Maxmimum height of the Composer.
|
||||||
* @returns {Integer}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
maximumHeight() {
|
maximumHeight() {
|
||||||
return $(window).height() - $('#header').outerHeight();
|
return $(window).height() - $('#header').outerHeight();
|
||||||
@ -251,9 +236,9 @@ class ComposerState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed the composer's current height, based on the intended height, and
|
* Computed the composer's current height, based on the intended height, and
|
||||||
* the composer's current state. This will be applied to the composer's
|
* the composer's current state. This will be applied to the composer
|
||||||
* content's DOM element.
|
* content's DOM element.
|
||||||
* @returns {Integer|String}
|
* @returns {number | string}
|
||||||
*/
|
*/
|
||||||
computedHeight() {
|
computedHeight() {
|
||||||
// If the composer is minimized, then we don't want to set a height; we'll
|
// If the composer is minimized, then we don't want to set a height; we'll
|
||||||
|
@ -60,8 +60,6 @@ class PostStreamState {
|
|||||||
/**
|
/**
|
||||||
* Update the stream so that it loads and includes the latest posts in the
|
* Update the stream so that it loads and includes the latest posts in the
|
||||||
* discussion, if the end is being viewed.
|
* discussion, if the end is being viewed.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
update() {
|
update() {
|
||||||
if (!this.viewingEnd()) return Promise.resolve();
|
if (!this.viewingEnd()) return Promise.resolve();
|
||||||
@ -74,7 +72,7 @@ class PostStreamState {
|
|||||||
/**
|
/**
|
||||||
* Load and scroll up to the first post in the discussion.
|
* Load and scroll up to the first post in the discussion.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
goToFirst() {
|
goToFirst() {
|
||||||
return this.goToIndex(0);
|
return this.goToIndex(0);
|
||||||
@ -83,7 +81,7 @@ class PostStreamState {
|
|||||||
/**
|
/**
|
||||||
* Load and scroll down to the last post in the discussion.
|
* Load and scroll down to the last post in the discussion.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
goToLast() {
|
goToLast() {
|
||||||
return this.goToIndex(this.count() - 1, true);
|
return this.goToIndex(this.count() - 1, true);
|
||||||
@ -92,10 +90,9 @@ class PostStreamState {
|
|||||||
/**
|
/**
|
||||||
* Load and scroll to a post with a certain number.
|
* Load and scroll to a post with a certain number.
|
||||||
*
|
*
|
||||||
* @param {number|String} number The post number to go to. If 'reply', go to
|
* @param {number | string} number The post number to go to. If 'reply', go to the last post and scroll the reply preview into view.
|
||||||
* the last post and scroll the reply preview into view.
|
* @param {boolean} [noAnimation]
|
||||||
* @param {Boolean} noAnimation
|
* @return {Promise<void>}
|
||||||
* @return {Promise}
|
|
||||||
*/
|
*/
|
||||||
goToNumber(number, noAnimation = false) {
|
goToNumber(number, noAnimation = false) {
|
||||||
// If we want to go to the reply preview, then we will go to the end of the
|
// If we want to go to the reply preview, then we will go to the end of the
|
||||||
@ -127,8 +124,8 @@ class PostStreamState {
|
|||||||
* Load and scroll to a certain index within the discussion.
|
* Load and scroll to a certain index within the discussion.
|
||||||
*
|
*
|
||||||
* @param {number} index
|
* @param {number} index
|
||||||
* @param {Boolean} noAnimation
|
* @param {boolean} [noAnimation]
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
goToIndex(index, noAnimation = false) {
|
goToIndex(index, noAnimation = false) {
|
||||||
this.paused = true;
|
this.paused = true;
|
||||||
@ -151,7 +148,7 @@ class PostStreamState {
|
|||||||
* resolved immediately.
|
* resolved immediately.
|
||||||
*
|
*
|
||||||
* @param {number} number
|
* @param {number} number
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
loadNearNumber(number) {
|
loadNearNumber(number) {
|
||||||
if (this.posts().some((post) => post && Number(post.number()) === Number(number))) {
|
if (this.posts().some((post) => post && Number(post.number()) === Number(number))) {
|
||||||
@ -174,7 +171,7 @@ class PostStreamState {
|
|||||||
* index is already loaded, the promise will be resolved immediately.
|
* index is already loaded, the promise will be resolved immediately.
|
||||||
*
|
*
|
||||||
* @param {number} index
|
* @param {number} index
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
loadNearIndex(index) {
|
loadNearIndex(index) {
|
||||||
if (index >= this.visibleStart && index < this.visibleEnd) {
|
if (index >= this.visibleStart && index < this.visibleEnd) {
|
||||||
@ -240,7 +237,7 @@ class PostStreamState {
|
|||||||
*
|
*
|
||||||
* @param {number} start
|
* @param {number} start
|
||||||
* @param {number} end
|
* @param {number} end
|
||||||
* @param {Boolean} backwards
|
* @param {boolean} backwards
|
||||||
*/
|
*/
|
||||||
loadPage(start, end, backwards = false) {
|
loadPage(start, end, backwards = false) {
|
||||||
this.pagesLoading++;
|
this.pagesLoading++;
|
||||||
@ -271,7 +268,7 @@ class PostStreamState {
|
|||||||
*
|
*
|
||||||
* @param {number} start
|
* @param {number} start
|
||||||
* @param {number} end
|
* @param {number} end
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
loadRange(start, end) {
|
loadRange(start, end) {
|
||||||
const loadIds = [];
|
const loadIds = [];
|
||||||
@ -302,7 +299,7 @@ class PostStreamState {
|
|||||||
/**
|
/**
|
||||||
* Set up the stream with the given array of posts.
|
* Set up the stream with the given array of posts.
|
||||||
*
|
*
|
||||||
* @param {Post[]} posts
|
* @param {import('../../common/models/Post').default[]} posts
|
||||||
*/
|
*/
|
||||||
show(posts) {
|
show(posts) {
|
||||||
this.visibleStart = posts.length ? this.discussion.postIds().indexOf(posts[0].id()) : 0;
|
this.visibleStart = posts.length ? this.discussion.postIds().indexOf(posts[0].id()) : 0;
|
||||||
@ -350,7 +347,7 @@ class PostStreamState {
|
|||||||
* Check whether or not the scrubber should be disabled, i.e. if all of the
|
* Check whether or not the scrubber should be disabled, i.e. if all of the
|
||||||
* posts are visible in the viewport.
|
* posts are visible in the viewport.
|
||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
disabled() {
|
disabled() {
|
||||||
return this.visible >= this.count();
|
return this.visible >= this.count();
|
||||||
|
@ -16,11 +16,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get a list of controls for a discussion.
|
* Get a list of controls for a discussion.
|
||||||
*
|
*
|
||||||
* @param {Discussion} discussion
|
* @param {import('../../common/models/Discussion').default} discussion
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
controls(discussion, context) {
|
controls(discussion, context) {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -40,10 +39,10 @@ export default {
|
|||||||
* Get controls for a discussion pertaining to the current user (e.g. reply,
|
* Get controls for a discussion pertaining to the current user (e.g. reply,
|
||||||
* follow).
|
* follow).
|
||||||
*
|
*
|
||||||
* @param {Discussion} discussion
|
* @param {import('../../common/models/Discussion').default} discussion
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
userControls(discussion, context) {
|
userControls(discussion, context) {
|
||||||
@ -88,10 +87,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get controls for a discussion pertaining to moderation (e.g. rename, lock).
|
* Get controls for a discussion pertaining to moderation (e.g. rename, lock).
|
||||||
*
|
*
|
||||||
* @param {Discussion} discussion
|
* @param {import('../../common/models/Discussion').default} discussion
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
moderationControls(discussion) {
|
moderationControls(discussion) {
|
||||||
@ -116,10 +115,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get controls for a discussion which are destructive (e.g. delete).
|
* Get controls for a discussion which are destructive (e.g. delete).
|
||||||
*
|
*
|
||||||
* @param {Discussion} discussion
|
* @param {import('../../common/models/Discussion').default} discussion
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
destructiveControls(discussion) {
|
destructiveControls(discussion) {
|
||||||
@ -175,11 +174,10 @@ export default {
|
|||||||
* logged in, they will be prompted. If they don't have permission to
|
* logged in, they will be prompted. If they don't have permission to
|
||||||
* reply, the promise will be rejected.
|
* reply, the promise will be rejected.
|
||||||
*
|
*
|
||||||
* @param {Boolean} goToLast Whether or not to scroll down to the last post if
|
* @param {boolean} goToLast Whether or not to scroll down to the last post if the discussion is being viewed.
|
||||||
* the discussion is being viewed.
|
* @param {boolean} forceRefresh Whether or not to force a reload of the composer component, even if it is already open for this discussion.
|
||||||
* @param {Boolean} forceRefresh Whether or not to force a reload of the
|
*
|
||||||
* composer component, even if it is already open for this discussion.
|
* @return {Promise<void>}
|
||||||
* @return {Promise}
|
|
||||||
*/
|
*/
|
||||||
replyAction(goToLast, forceRefresh) {
|
replyAction(goToLast, forceRefresh) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -212,7 +210,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Hide a discussion.
|
* Hide a discussion.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
hideAction() {
|
hideAction() {
|
||||||
this.pushAttributes({ hiddenAt: new Date(), hiddenUser: app.session.user });
|
this.pushAttributes({ hiddenAt: new Date(), hiddenUser: app.session.user });
|
||||||
@ -223,7 +221,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Restore a discussion.
|
* Restore a discussion.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
restoreAction() {
|
restoreAction() {
|
||||||
this.pushAttributes({ hiddenAt: null, hiddenUser: null });
|
this.pushAttributes({ hiddenAt: null, hiddenUser: null });
|
||||||
@ -234,7 +232,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Delete the discussion after confirming with the user.
|
* Delete the discussion after confirming with the user.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
deleteAction() {
|
deleteAction() {
|
||||||
if (confirm(extractText(app.translator.trans('core.forum.discussion_controls.delete_confirmation')))) {
|
if (confirm(extractText(app.translator.trans('core.forum.discussion_controls.delete_confirmation')))) {
|
||||||
@ -250,8 +248,6 @@ export default {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Rename the discussion.
|
* Rename the discussion.
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
*/
|
||||||
renameAction() {
|
renameAction() {
|
||||||
return app.modal.show(RenameDiscussionModal, {
|
return app.modal.show(RenameDiscussionModal, {
|
||||||
|
@ -52,8 +52,6 @@ export default class Pane {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable the pane.
|
* Enable the pane.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
enable() {
|
enable() {
|
||||||
this.active = true;
|
this.active = true;
|
||||||
@ -62,8 +60,6 @@ export default class Pane {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the pane.
|
* Disable the pane.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
disable() {
|
disable() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
@ -73,8 +69,6 @@ export default class Pane {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the pane.
|
* Show the pane.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
show() {
|
show() {
|
||||||
clearTimeout(this.hideTimeout);
|
clearTimeout(this.hideTimeout);
|
||||||
@ -84,8 +78,6 @@ export default class Pane {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the pane.
|
* Hide the pane.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
hide() {
|
hide() {
|
||||||
this.showing = false;
|
this.showing = false;
|
||||||
@ -95,8 +87,6 @@ export default class Pane {
|
|||||||
/**
|
/**
|
||||||
* Begin a timeout to hide the pane, which can be cancelled by showing the
|
* Begin a timeout to hide the pane, which can be cancelled by showing the
|
||||||
* pane.
|
* pane.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
onmouseleave() {
|
onmouseleave() {
|
||||||
this.hideTimeout = setTimeout(this.hide.bind(this), 250);
|
this.hideTimeout = setTimeout(this.hide.bind(this), 250);
|
||||||
@ -104,8 +94,6 @@ export default class Pane {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle whether or not the pane is pinned.
|
* Toggle whether or not the pane is pinned.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
togglePinned() {
|
togglePinned() {
|
||||||
this.pinned = !this.pinned;
|
this.pinned = !this.pinned;
|
||||||
|
@ -13,11 +13,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get a list of controls for a post.
|
* Get a list of controls for a post.
|
||||||
*
|
*
|
||||||
* @param {Post} post
|
* @param {import('../../common/models/Post').default} post
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}')}
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
controls(post, context) {
|
controls(post, context) {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -36,10 +35,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get controls for a post pertaining to the current user (e.g. report).
|
* Get controls for a post pertaining to the current user (e.g. report).
|
||||||
*
|
*
|
||||||
* @param {Post} post
|
* @param {import('../../common/models/Post').default} post
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}')}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
userControls(post, context) {
|
userControls(post, context) {
|
||||||
@ -49,10 +48,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get controls for a post pertaining to moderation (e.g. edit).
|
* Get controls for a post pertaining to moderation (e.g. edit).
|
||||||
*
|
*
|
||||||
* @param {Post} post
|
* @param {import('../../common/models/Post').default} post
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}')}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
moderationControls(post, context) {
|
moderationControls(post, context) {
|
||||||
@ -79,10 +78,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get controls for a post that are destructive (e.g. delete).
|
* Get controls for a post that are destructive (e.g. delete).
|
||||||
*
|
*
|
||||||
* @param {Post} post
|
* @param {import('../../common/models/Post').default} post
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}')}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
destructiveControls(post, context) {
|
destructiveControls(post, context) {
|
||||||
@ -134,7 +133,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Open the composer to edit a post.
|
* Open the composer to edit a post.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
editAction() {
|
editAction() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@ -148,7 +147,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Hide a post.
|
* Hide a post.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
hideAction() {
|
hideAction() {
|
||||||
if (!confirm(extractText(app.translator.trans('core.forum.post_controls.hide_confirmation')))) return;
|
if (!confirm(extractText(app.translator.trans('core.forum.post_controls.hide_confirmation')))) return;
|
||||||
@ -160,7 +159,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Restore a post.
|
* Restore a post.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
restoreAction() {
|
restoreAction() {
|
||||||
this.pushAttributes({ hiddenAt: null, hiddenUser: null });
|
this.pushAttributes({ hiddenAt: null, hiddenUser: null });
|
||||||
@ -171,7 +170,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Delete a post.
|
* Delete a post.
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
deleteAction(context) {
|
deleteAction(context) {
|
||||||
if (!confirm(extractText(app.translator.trans('core.forum.post_controls.delete_confirmation')))) return;
|
if (!confirm(extractText(app.translator.trans('core.forum.post_controls.delete_confirmation')))) return;
|
||||||
|
@ -13,11 +13,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get a list of controls for a user.
|
* Get a list of controls for a user.
|
||||||
*
|
*
|
||||||
* @param {User} user
|
* @param {import('../../common/models/User').default} user
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
controls(user, context) {
|
controls(user, context) {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
@ -36,10 +35,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get controls for a user pertaining to the current user (e.g. poke, follow).
|
* Get controls for a user pertaining to the current user (e.g. poke, follow).
|
||||||
*
|
*
|
||||||
* @param {User} user
|
* @param {import('../../common/models/User').default} user
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
userControls() {
|
userControls() {
|
||||||
@ -49,10 +48,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get controls for a user pertaining to moderation (e.g. suspend, edit).
|
* Get controls for a user pertaining to moderation (e.g. suspend, edit).
|
||||||
*
|
*
|
||||||
* @param {User} user
|
* @param {import('../../common/models/User').default} user
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
moderationControls(user) {
|
moderationControls(user) {
|
||||||
@ -73,10 +72,10 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get controls for a user which are destructive (e.g. delete).
|
* Get controls for a user which are destructive (e.g. delete).
|
||||||
*
|
*
|
||||||
* @param {User} user
|
* @param {import('../../common/models/User').default} user
|
||||||
* @param {*} context The parent component under which the controls menu will
|
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||||
* be displayed.
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList<import('mithril').Children>}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
destructiveControls(user) {
|
destructiveControls(user) {
|
||||||
@ -97,7 +96,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Delete the user.
|
* Delete the user.
|
||||||
*
|
*
|
||||||
* @param {User} user
|
* @param {import('../../common/models/User').default} user
|
||||||
*/
|
*/
|
||||||
deleteAction(user) {
|
deleteAction(user) {
|
||||||
if (!confirm(app.translator.trans('core.forum.user_controls.delete_confirmation'))) {
|
if (!confirm(app.translator.trans('core.forum.user_controls.delete_confirmation'))) {
|
||||||
@ -120,7 +119,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Show deletion alert of user.
|
* Show deletion alert of user.
|
||||||
*
|
*
|
||||||
* @param {User} user
|
* @param {import('../../common/models/User').default} user
|
||||||
* @param {string} type
|
* @param {string} type
|
||||||
*/
|
*/
|
||||||
showDeletionAlert(user, type) {
|
showDeletionAlert(user, type) {
|
||||||
@ -141,7 +140,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Edit the user.
|
* Edit the user.
|
||||||
*
|
*
|
||||||
* @param {User} user
|
* @param {import('../../common/models/User').default} user
|
||||||
*/
|
*/
|
||||||
editAction(user) {
|
editAction(user) {
|
||||||
app.modal.show(EditUserModal, { user });
|
app.modal.show(EditUserModal, { user });
|
||||||
|
@ -6,7 +6,7 @@ import Component from '../../common/Component';
|
|||||||
/**
|
/**
|
||||||
* Shows an alert if the user has not yet confirmed their email address.
|
* Shows an alert if the user has not yet confirmed their email address.
|
||||||
*
|
*
|
||||||
* @param {ForumApplication} app
|
* @param {import('../ForumApplication').default} app
|
||||||
*/
|
*/
|
||||||
export default function alertEmailConfirmation(app) {
|
export default function alertEmailConfirmation(app) {
|
||||||
const user = app.session.user;
|
const user = app.session.user;
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
* controls.
|
* controls.
|
||||||
*
|
*
|
||||||
* It relies on the element having children with particular CSS classes.
|
* It relies on the element having children with particular CSS classes.
|
||||||
* TODO: document
|
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element
|
* The function returns a record with a `reset` proeprty. This is a function
|
||||||
* @return {Object}
|
* which reverts the slider to its original position. This should be called,
|
||||||
* @property {function} reset Revert the slider to its original position. This
|
* for example, when a controls dropdown is closed.
|
||||||
* should be called, for example, when a controls dropdown is closed.
|
*
|
||||||
|
* @param {HTMLElement | SVGElement | Element} element
|
||||||
|
* @return {{ reset : () => void }}
|
||||||
*/
|
*/
|
||||||
export default function slidable(element) {
|
export default function slidable(element) {
|
||||||
const $element = $(element);
|
const $element = $(element);
|
||||||
@ -27,15 +28,15 @@ export default function slidable(element) {
|
|||||||
/**
|
/**
|
||||||
* Animate the slider to a new position.
|
* Animate the slider to a new position.
|
||||||
*
|
*
|
||||||
* @param {Integer} newPos
|
* @param {number} newPos
|
||||||
* @param {Object} [options]
|
* @param {Partial<JQueryAnimationOptions>} [options]
|
||||||
*/
|
*/
|
||||||
const animatePos = (newPos, options = {}) => {
|
const animatePos = (newPos, options = {}) => {
|
||||||
// Since we can't animate the transform property with jQuery, we'll use a
|
// Since we can't animate the transform property with jQuery, we'll use a
|
||||||
// bit of a workaround. We set up the animation with a step function that
|
// bit of a workaround. We set up the animation with a step function that
|
||||||
// will set the transform property, but then we animate an unused property
|
// will set the transform property, but then we animate an unused property
|
||||||
// (background-position-x) with jQuery.
|
// (background-position-x) with jQuery.
|
||||||
options.duration = options.duration || 'fast';
|
options.duration ||= 'fast';
|
||||||
options.step = function (x) {
|
options.step = function (x) {
|
||||||
$(this).css('transform', 'translate(' + x + 'px, 0)');
|
$(this).css('transform', 'translate(' + x + 'px, 0)');
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user