mirror of
https://github.com/flarum/framework.git
synced 2024-11-22 12:48:28 +08: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:
parent
4df72e5ac6
commit
d268894e61
|
@ -55,7 +55,7 @@ export default class AdminNav extends Component {
|
|||
/**
|
||||
* Build an item list of main links to show in the admin navigation.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
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
|
||||
* object with `path` and `label` properties.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @public
|
||||
* @return {ItemList<{ path: string, label: import('mithril').Children }>}
|
||||
*/
|
||||
homePageItems() {
|
||||
const items = new ItemList();
|
||||
|
|
|
@ -8,7 +8,7 @@ export default class DashboardWidget extends Component {
|
|||
/**
|
||||
* Get the class name to apply to the widget.
|
||||
*
|
||||
* @return {String}
|
||||
* @return {string}
|
||||
*/
|
||||
className() {
|
||||
return '';
|
||||
|
@ -17,9 +17,9 @@ export default class DashboardWidget extends Component {
|
|||
/**
|
||||
* Get the content of the widget.
|
||||
*
|
||||
* @return {VirtualElement}
|
||||
* @return {import('mithril').Children}
|
||||
*/
|
||||
content() {
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export default class HeaderPrimary extends Component {
|
|||
/**
|
||||
* Build an item list for the controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
return new ItemList();
|
||||
|
|
|
@ -16,7 +16,7 @@ export default class HeaderSecondary extends Component {
|
|||
/**
|
||||
* Build an item list for the controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
@ -28,7 +28,7 @@ export default class HeaderSecondary extends Component {
|
|||
</LinkButton>
|
||||
);
|
||||
|
||||
items.add('session', SessionDropdown.component());
|
||||
items.add('session', <SessionDropdown />);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ export default class UploadImageButton extends Button {
|
|||
/**
|
||||
* After a successful upload/removal, reload the page.
|
||||
*
|
||||
* @param {Object} response
|
||||
* @param {object} response
|
||||
* @protected
|
||||
*/
|
||||
success(response) {
|
||||
|
@ -88,7 +88,7 @@ export default class UploadImageButton extends Button {
|
|||
/**
|
||||
* If upload/removal fails, stop loading.
|
||||
*
|
||||
* @param {Object} response
|
||||
* @param {object} response
|
||||
* @protected
|
||||
*/
|
||||
failure(response) {
|
||||
|
|
|
@ -451,9 +451,6 @@ export default class Application {
|
|||
* Make an AJAX request, handling any low-level errors that may occur.
|
||||
*
|
||||
* @see https://mithril.js.org/request.html
|
||||
*
|
||||
* @param options
|
||||
* @return {Promise}
|
||||
*/
|
||||
request<ResponseType>(originalOptions: FlarumRequestOptions<ResponseType>): Promise<ResponseType> {
|
||||
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
|
||||
* fragment.
|
||||
*
|
||||
* @param {String} [selector] a jQuery-compatible selector string
|
||||
* @returns {jQuery} the jQuery object for the DOM node
|
||||
* @param [selector] a jQuery-compatible selector string
|
||||
* @returns the jQuery object for the DOM node
|
||||
* @final
|
||||
*/
|
||||
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).
|
||||
*
|
||||
* @return {*}
|
||||
* @return {import('mithril').Children}
|
||||
* @protected
|
||||
*/
|
||||
getDisplay() {
|
||||
|
@ -54,7 +54,7 @@ export default class Checkbox extends Component {
|
|||
/**
|
||||
* Run a callback when the state of the checkbox is changed.
|
||||
*
|
||||
* @param {Boolean} checked
|
||||
* @param {boolean} checked
|
||||
* @protected
|
||||
*/
|
||||
onchange(checked) {
|
||||
|
|
|
@ -103,7 +103,7 @@ export default class Dropdown extends Component {
|
|||
/**
|
||||
* Get the template for the button.
|
||||
*
|
||||
* @return {*}
|
||||
* @return {import('mithril').Children}
|
||||
* @protected
|
||||
*/
|
||||
getButton(children) {
|
||||
|
@ -123,7 +123,7 @@ export default class Dropdown extends Component {
|
|||
/**
|
||||
* Get the template for the button's content.
|
||||
*
|
||||
* @return {*}
|
||||
* @return {import('mithril').Children}
|
||||
* @protected
|
||||
*/
|
||||
getButtonContent(children) {
|
||||
|
|
|
@ -35,8 +35,8 @@ export default class LinkButton extends Button {
|
|||
/**
|
||||
* Determine whether a component with the given attrs is 'active'.
|
||||
*
|
||||
* @param {Object} attrs
|
||||
* @return {Boolean}
|
||||
* @param {object} attrs
|
||||
* @return {boolean}
|
||||
*/
|
||||
static isActive(attrs) {
|
||||
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.
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {import('mithril').Children}
|
||||
* @protected
|
||||
*/
|
||||
getBackButton() {
|
||||
|
@ -59,7 +59,7 @@ export default class Navigation extends Component {
|
|||
/**
|
||||
* Get the pane pinned toggle button.
|
||||
*
|
||||
* @return {Object|String}
|
||||
* @return {import('mithril').Children}
|
||||
* @protected
|
||||
*/
|
||||
getPaneButton() {
|
||||
|
@ -77,7 +77,7 @@ export default class Navigation extends Component {
|
|||
/**
|
||||
* Get the drawer toggle button.
|
||||
*
|
||||
* @return {Object|String}
|
||||
* @return {import('mithril').Children}
|
||||
* @protected
|
||||
*/
|
||||
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
|
||||
* array will be returned.
|
||||
*
|
||||
* @return {*}
|
||||
* @param {unknown[] | unknown} children
|
||||
* @return {unknown}
|
||||
* @protected
|
||||
*/
|
||||
getFirstChild(children) {
|
||||
|
|
|
@ -90,7 +90,7 @@ export default class TextEditor extends Component {
|
|||
/**
|
||||
* Build an item list for the text editor controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
controlItems() {
|
||||
const items = new ItemList();
|
||||
|
@ -123,7 +123,7 @@ export default class TextEditor extends Component {
|
|||
/**
|
||||
* Build an item list for the toolbar controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
toolbarItems() {
|
||||
return new ItemList();
|
||||
|
@ -132,7 +132,7 @@ export default class TextEditor extends Component {
|
|||
/**
|
||||
* Handle input into the textarea.
|
||||
*
|
||||
* @param {String} value
|
||||
* @param {string} value
|
||||
*/
|
||||
oninput(value) {
|
||||
this.value = value;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
* @param methods The name or names of the method(s) to extend
|
||||
* @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,
|
||||
methods: K | K[],
|
||||
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 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,
|
||||
methods: K | K[],
|
||||
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
|
||||
* ```
|
||||
*
|
||||
* @param {Array} items
|
||||
* @return {VirtualElement}
|
||||
* @param {import('mithril').Children[]} items
|
||||
* @return {import('mithril').Children}')}
|
||||
*/
|
||||
export default function punctuateSeries(items) {
|
||||
if (items.length === 2) {
|
||||
|
|
|
@ -23,7 +23,7 @@ export default class AlertManagerState {
|
|||
/**
|
||||
* 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(attrs: AlertAttrs, children: Mithril.Children): AlertIdentifier;
|
||||
|
|
|
@ -73,7 +73,7 @@ export default class ModalManagerState {
|
|||
/**
|
||||
* 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 {
|
||||
return !!this.modal;
|
||||
|
|
|
@ -9,9 +9,8 @@ export default class PageState {
|
|||
/**
|
||||
* Determine whether the page matches the given class and data.
|
||||
*
|
||||
* @param {object} type The page class to check against. Subclasses are
|
||||
* accepted as well.
|
||||
* @param {object} data
|
||||
* @param {object} type The page class to check against. Subclasses are accepted as well.
|
||||
* @param {Record<string, unknown>} data
|
||||
* @return {boolean}
|
||||
*/
|
||||
matches(type, data = {}) {
|
||||
|
|
|
@ -63,7 +63,6 @@ export default class Drawer {
|
|||
* Check whether or not the drawer is currently open.
|
||||
*
|
||||
* @return {boolean}
|
||||
* @public
|
||||
*/
|
||||
isOpen() {
|
||||
return this.appElement.classList.contains('drawerOpen');
|
||||
|
@ -71,8 +70,6 @@ export default class Drawer {
|
|||
|
||||
/**
|
||||
* Hide the drawer.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
hide() {
|
||||
/**
|
||||
|
@ -100,8 +97,6 @@ export default class Drawer {
|
|||
|
||||
/**
|
||||
* Show the drawer.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
show() {
|
||||
this.appElement.classList.add('drawerOpen');
|
||||
|
|
|
@ -303,7 +303,7 @@ export default class ItemList<T> {
|
|||
*
|
||||
* @param content The item's content (objects only)
|
||||
* @param key The item's key
|
||||
* @returns Proxied content
|
||||
* @return Proxied content
|
||||
*
|
||||
* @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
|
||||
* events.
|
||||
|
@ -14,7 +6,6 @@ export default class ScrollListener {
|
|||
/**
|
||||
* @param {(top: number) => void} callback The callback to run when the scroll position
|
||||
* changes.
|
||||
* @public
|
||||
*/
|
||||
constructor(callback) {
|
||||
this.callback = callback;
|
||||
|
@ -34,7 +25,7 @@ export default class ScrollListener {
|
|||
|
||||
// Schedule the callback to be executed soon (TM), and stop throttling once
|
||||
// the callback is done.
|
||||
later(() => {
|
||||
requestAnimationFrame(() => {
|
||||
this.update();
|
||||
this.ticking = false;
|
||||
});
|
||||
|
@ -44,8 +35,6 @@ export default class ScrollListener {
|
|||
|
||||
/**
|
||||
* Run the callback, whether there was a scroll event or not.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
update() {
|
||||
this.callback(window.pageYOffset);
|
||||
|
@ -53,8 +42,6 @@ export default class ScrollListener {
|
|||
|
||||
/**
|
||||
* Start listening to and handling the window's scroll position.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
start() {
|
||||
if (!this.active) {
|
||||
|
@ -64,8 +51,6 @@ export default class ScrollListener {
|
|||
|
||||
/**
|
||||
* Stop listening to and handling the window's scroll position.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
stop() {
|
||||
window.removeEventListener('scroll', this.active);
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* position can be anchor to an element that is in or below the viewport, so
|
||||
* the content in the viewport will stay the same.
|
||||
*
|
||||
* @param {DOMElement} element The element to anchor the scroll position to.
|
||||
* @param {Function} callback The callback to run that will change page content.
|
||||
* @param {HTMLElement | SVGElement | Element} element The element to anchor the scroll position to.
|
||||
* @param {() => void} callback The callback to run that will change page content.
|
||||
*/
|
||||
export default function anchorScroll(element, callback) {
|
||||
const $window = $(window);
|
||||
|
|
|
@ -4,10 +4,9 @@ import Model from '../Model';
|
|||
* The `computed` utility creates a function that will cache its output until
|
||||
* any of the dependent values are dirty.
|
||||
*
|
||||
* @param {...String} dependentKeys The keys of the dependent values.
|
||||
* @param {function} compute The function which computes the value using the
|
||||
* @param dependentKeys The keys of the dependent values.
|
||||
* @param compute The function which computes the value using the
|
||||
* dependent values.
|
||||
* @return {Function}
|
||||
*/
|
||||
export default function computed<T, M = Model>(...args: [...string[], (this: M, ...args: unknown[]) => T]): () => T {
|
||||
const keys = args.slice(0, -1) as string[];
|
||||
|
|
|
@ -13,17 +13,21 @@ export default {
|
|||
/**
|
||||
* Arrays of registered event handlers, grouped by the event name.
|
||||
*
|
||||
* @type {Object}
|
||||
* @type {Record<string, unknown>}
|
||||
* @protected
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
handlers: null,
|
||||
|
||||
/**
|
||||
* Get all of the registered handlers for an event.
|
||||
*
|
||||
* @param {String} event The name of the event.
|
||||
* @return {Array}
|
||||
* @param {string} event The name of the event.
|
||||
* @return {Function[]}
|
||||
* @protected
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
getHandlers(event) {
|
||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||
|
@ -38,9 +42,10 @@ export default {
|
|||
/**
|
||||
* Trigger an event.
|
||||
*
|
||||
* @param {String} event The name of the event.
|
||||
* @param {...*} args Arguments to pass to event handlers.
|
||||
* @public
|
||||
* @param {string} event The name of the event.
|
||||
* @param {any[]} args Arguments to pass to event handlers.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
trigger(event, ...args) {
|
||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||
|
@ -51,8 +56,10 @@ export default {
|
|||
/**
|
||||
* Register an event handler.
|
||||
*
|
||||
* @param {String} event The name of the event.
|
||||
* @param {function} handler The function to handle the event.
|
||||
* @param {string} event The name of the event.
|
||||
* @param {Function} handler The function to handle the event.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
on(event, handler) {
|
||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||
|
@ -64,8 +71,10 @@ export default {
|
|||
* Register an event handler so that it will run only once, and then
|
||||
* unregister itself.
|
||||
*
|
||||
* @param {String} event The name of the event.
|
||||
* @param {function} handler The function to handle the event.
|
||||
* @param {string} event The name of the event.
|
||||
* @param {Function} handler The function to handle the event.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
one(event, handler) {
|
||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||
|
@ -82,8 +91,10 @@ export default {
|
|||
/**
|
||||
* Unregister an event handler.
|
||||
*
|
||||
* @param {String} event The name of the event.
|
||||
* @param {function} handler The function that handles the event.
|
||||
* @param {string} event The name of the event.
|
||||
* @param {Function} handler The function that handles the event.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
off(event, handler) {
|
||||
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* @example
|
||||
* class MyClass extends mixin(ExistingClass, evented, etc) {}
|
||||
*
|
||||
* @param {Class} Parent The class to extend the new class from.
|
||||
* @param {...Object} mixins The objects to mix in.
|
||||
* @return {Class} A new class that extends Parent and contains the mixins.
|
||||
* @param {object} Parent The class to extend the new class from.
|
||||
* @param {Record<string, any>[]} mixins The objects to mix in.
|
||||
* @return {object} A new class that extends Parent and contains the mixins.
|
||||
*/
|
||||
export default function mixin(Parent, ...mixins) {
|
||||
class Mixed extends Parent {}
|
||||
|
|
|
@ -69,7 +69,7 @@ export default class AvatarEditor extends Component {
|
|||
/**
|
||||
* Get the items in the edit avatar dropdown menu.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
controlItems() {
|
||||
const items = new ItemList();
|
||||
|
@ -94,7 +94,7 @@ export default class AvatarEditor extends Component {
|
|||
/**
|
||||
* Enable dragover style
|
||||
*
|
||||
* @param {Event} e
|
||||
* @param {DragEvent} e
|
||||
*/
|
||||
enableDragover(e) {
|
||||
e.preventDefault();
|
||||
|
@ -105,7 +105,7 @@ export default class AvatarEditor extends Component {
|
|||
/**
|
||||
* Disable dragover style
|
||||
*
|
||||
* @param {Event} e
|
||||
* @param {DragEvent} e
|
||||
*/
|
||||
disableDragover(e) {
|
||||
e.preventDefault();
|
||||
|
@ -116,7 +116,7 @@ export default class AvatarEditor extends Component {
|
|||
/**
|
||||
* Upload avatar when file is dropped into dropzone.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @param {DragEvent} e
|
||||
*/
|
||||
dropUpload(e) {
|
||||
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
|
||||
* the user to upload an avatar immediately.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @param {MouseEvent} e
|
||||
*/
|
||||
quickUpload(e) {
|
||||
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
|
||||
* store, and force a recomputation of the user's avatar color.
|
||||
*
|
||||
* @param {Object} response
|
||||
* @param {object} response
|
||||
* @protected
|
||||
*/
|
||||
success(response) {
|
||||
|
@ -220,7 +220,7 @@ export default class AvatarEditor extends Component {
|
|||
/**
|
||||
* If avatar upload/removal fails, stop loading.
|
||||
*
|
||||
* @param {Object} response
|
||||
* @param {object} response
|
||||
* @protected
|
||||
*/
|
||||
failure(response) {
|
||||
|
|
|
@ -120,7 +120,7 @@ export default class CommentPost extends Post {
|
|||
/**
|
||||
* Build an item list for the post's header.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
headerItems() {
|
||||
const items = new ItemList();
|
||||
|
|
|
@ -127,7 +127,7 @@ export default class Composer extends Component {
|
|||
/**
|
||||
* Resize the composer according to mouse movement.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @param {MouseEvent} e
|
||||
*/
|
||||
onmousemove(e) {
|
||||
if (!this.handle) return;
|
||||
|
@ -317,7 +317,7 @@ export default class Composer extends Component {
|
|||
/**
|
||||
* Build an item list for the composer's controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
controlItems() {
|
||||
const items = new ItemList();
|
||||
|
@ -379,7 +379,7 @@ export default class Composer extends Component {
|
|||
|
||||
/**
|
||||
* Default height of the Composer in case none is saved.
|
||||
* @returns {Integer}
|
||||
* @returns {number}
|
||||
*/
|
||||
defaultHeight() {
|
||||
return this.$().height();
|
||||
|
@ -387,7 +387,7 @@ export default class Composer extends Component {
|
|||
|
||||
/**
|
||||
* Save a new Composer height and update the DOM.
|
||||
* @param {Integer} height
|
||||
* @param {number} height
|
||||
*/
|
||||
changeHeight(height) {
|
||||
this.state.height = height;
|
||||
|
|
|
@ -76,7 +76,7 @@ export default class ComposerBody extends Component {
|
|||
/**
|
||||
* Check if there is any unsaved data.
|
||||
*
|
||||
* @return {String}
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasChanges() {
|
||||
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.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
headerItems() {
|
||||
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,
|
||||
* move the focus to the start of the text editor.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @param {KeyboardEvent} e
|
||||
*/
|
||||
onkeydown(e) {
|
||||
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.
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {Record<string, unknown>}
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -23,7 +23,7 @@ export default class DiscussionHero extends Component {
|
|||
/**
|
||||
* Build an item list for the contents of the discussion hero.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
|
|
@ -143,7 +143,7 @@ export default class DiscussionListItem extends Component {
|
|||
/**
|
||||
* Determine whether or not the discussion is currently being viewed.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
active() {
|
||||
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
|
||||
* the discussion.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
showFirstPost() {
|
||||
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
|
||||
* the number of unread posts.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
showRepliesCount() {
|
||||
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
|
||||
* just the first/last post indicator.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
infoItems() {
|
||||
const items = new ItemList();
|
||||
|
|
|
@ -70,7 +70,7 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||
// we'll just close it.
|
||||
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();
|
||||
} else {
|
||||
app.composer.minimize();
|
||||
|
@ -88,11 +88,9 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||
|
||||
/**
|
||||
* List of components shown while the discussion is loading.
|
||||
*
|
||||
* @returns {ItemList}
|
||||
*/
|
||||
loadingItems() {
|
||||
const items = new ItemList();
|
||||
loadingItems(): ItemList<Mithril.Children> {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
items.add('spinner', <LoadingIndicator />, 100);
|
||||
|
||||
|
@ -101,10 +99,8 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||
|
||||
/**
|
||||
* Function that renders the `sidebarItems` ItemList.
|
||||
*
|
||||
* @returns {import('mithril').Children}
|
||||
*/
|
||||
sidebar() {
|
||||
sidebar(): Mithril.Children {
|
||||
return (
|
||||
<nav className="DiscussionPage-nav">
|
||||
<ul>{listItems(this.sidebarItems().toArray())}</ul>
|
||||
|
@ -114,20 +110,16 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||
|
||||
/**
|
||||
* Renders the discussion's hero.
|
||||
*
|
||||
* @returns {import('mithril').Children}
|
||||
*/
|
||||
hero() {
|
||||
hero(): Mithril.Children {
|
||||
return <DiscussionHero discussion={this.discussion} />;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of items rendered as the main page content.
|
||||
*
|
||||
* @returns {ItemList}
|
||||
*/
|
||||
pageContent() {
|
||||
const items = new ItemList();
|
||||
pageContent(): ItemList<Mithril.Children> {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
items.add('hero', this.hero(), 100);
|
||||
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.
|
||||
*
|
||||
* @returns {ItemList}
|
||||
*/
|
||||
mainContent() {
|
||||
const items = new ItemList();
|
||||
mainContent(): ItemList<Mithril.Children> {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
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() {
|
||||
load(): void {
|
||||
const preloadedDiscussion = app.preloadedApiDocument<Discussion>();
|
||||
if (preloadedDiscussion) {
|
||||
// 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
|
||||
* discussion.
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
requestParams() {
|
||||
requestParams(): Record<string, unknown> {
|
||||
return {
|
||||
bySlug: true,
|
||||
page: { near: this.near },
|
||||
|
@ -196,7 +184,7 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
|
|||
/**
|
||||
* Initialize the component to display the given discussion.
|
||||
*/
|
||||
show(discussion: ApiResponseSingle<Discussion>) {
|
||||
show(discussion: ApiResponseSingle<Discussion>): void {
|
||||
app.history.push('discussion', discussion.title());
|
||||
app.setTitle(discussion.title());
|
||||
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.
|
||||
*/
|
||||
sidebarItems() {
|
||||
const items = new ItemList<Mithril.Vnode>();
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
items.add(
|
||||
'controls',
|
||||
SplitDropdown.component(
|
||||
{
|
||||
icon: 'fas fa-ellipsis-v',
|
||||
className: 'App-primaryControl',
|
||||
buttonClassName: 'Button--primary',
|
||||
accessibleToggleLabel: app.translator.trans('core.forum.discussion_controls.toggle_dropdown_accessible_label'),
|
||||
},
|
||||
DiscussionControls.controls(this.discussion, this).toArray()
|
||||
),
|
||||
100
|
||||
);
|
||||
if (this.discussion) {
|
||||
items.add(
|
||||
'controls',
|
||||
SplitDropdown.component(
|
||||
{
|
||||
icon: 'fas fa-ellipsis-v',
|
||||
className: 'App-primaryControl',
|
||||
buttonClassName: 'Button--primary',
|
||||
accessibleToggleLabel: app.translator.trans('core.forum.discussion_controls.toggle_dropdown_accessible_label'),
|
||||
},
|
||||
DiscussionControls.controls(this.discussion, this).toArray()
|
||||
),
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
items.add(
|
||||
'scrubber',
|
||||
|
|
|
@ -62,7 +62,7 @@ export default class EditPostComposer extends ComposerBody {
|
|||
/**
|
||||
* Get the data to submit to the server when the post is saved.
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {Record<string, unknown>}
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -45,7 +45,7 @@ export default class EventPost extends Post {
|
|||
/**
|
||||
* Get the name of the event icon.
|
||||
*
|
||||
* @return {String}
|
||||
* @return {string}
|
||||
*/
|
||||
icon() {
|
||||
return '';
|
||||
|
@ -54,8 +54,8 @@ export default class EventPost extends Post {
|
|||
/**
|
||||
* Get the description text for the event.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @return {String|Object} The description to render in the DOM
|
||||
* @param {Record<string, unknown>} data
|
||||
* @return {import('mithril').Children} The description to render in the DOM
|
||||
*/
|
||||
description(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.
|
||||
*
|
||||
* @return {String}
|
||||
* @return {string}
|
||||
*/
|
||||
descriptionKey() {
|
||||
return '';
|
||||
|
@ -73,7 +73,7 @@ export default class EventPost extends Post {
|
|||
/**
|
||||
* Get the translation data for the description of the event.
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {Record<string, unknown>}
|
||||
*/
|
||||
descriptionData() {
|
||||
return {};
|
||||
|
|
|
@ -14,7 +14,7 @@ export default class HeaderPrimary extends Component {
|
|||
/**
|
||||
* Build an item list for the controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
return new ItemList();
|
||||
|
|
|
@ -133,7 +133,7 @@ export default class IndexPage extends Page {
|
|||
/**
|
||||
* Get the component to display as the hero.
|
||||
*
|
||||
* @return {MithrilComponent}
|
||||
* @return {import('mithril').Children}
|
||||
*/
|
||||
hero() {
|
||||
return WelcomeHero.component();
|
||||
|
@ -144,7 +144,7 @@ export default class IndexPage extends Page {
|
|||
* "New Discussion" button, and then a DropdownSelect component containing a
|
||||
* list of navigation items.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
sidebarItems() {
|
||||
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
|
||||
* default this is just the 'All Discussions' link.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
navItems() {
|
||||
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 way discussions are sorted.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
viewItems() {
|
||||
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
|
||||
* on the results. By default this is just a "mark all as read" button.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
actionItems() {
|
||||
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.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
newDiscussionAction() {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -313,8 +313,6 @@ export default class IndexPage extends Page {
|
|||
|
||||
/**
|
||||
* Mark all discussions as read.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
markAllAsRead() {
|
||||
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.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @public
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
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.
|
||||
*
|
||||
* @return {String}
|
||||
* @return {string}
|
||||
* @abstract
|
||||
*/
|
||||
icon() {}
|
||||
|
@ -65,7 +65,7 @@ export default class Notification extends Component {
|
|||
/**
|
||||
* Get the URL that the notification should link to.
|
||||
*
|
||||
* @return {String}
|
||||
* @return {string}
|
||||
* @abstract
|
||||
*/
|
||||
href() {}
|
||||
|
@ -73,7 +73,7 @@ export default class Notification extends Component {
|
|||
/**
|
||||
* Get the content of the notification.
|
||||
*
|
||||
* @return {VirtualElement}
|
||||
* @return {import('mithril').Children}
|
||||
* @abstract
|
||||
*/
|
||||
content() {}
|
||||
|
@ -81,7 +81,7 @@ export default class Notification extends Component {
|
|||
/**
|
||||
* Get the excerpt of the notification.
|
||||
*
|
||||
* @return {VirtualElement}
|
||||
* @return {import('mithril').Children}
|
||||
* @abstract
|
||||
*/
|
||||
excerpt() {}
|
||||
|
|
|
@ -19,21 +19,21 @@ export default class NotificationGrid extends Component {
|
|||
/**
|
||||
* Information about the available notification methods.
|
||||
*
|
||||
* @type {Array}
|
||||
* @type {({ name: string, icon: string, label: import('mithril').Children })[]}
|
||||
*/
|
||||
this.methods = this.notificationMethods().toArray();
|
||||
|
||||
/**
|
||||
* A map of which notification checkboxes are loading.
|
||||
*
|
||||
* @type {Object}
|
||||
* @type {Record<string, boolean>}
|
||||
*/
|
||||
this.loading = {};
|
||||
|
||||
/**
|
||||
* Information about the available notification types.
|
||||
*
|
||||
* @type {Array}
|
||||
* @type {({ name: string, icon: string, label: import('mithril').Children })[]}
|
||||
*/
|
||||
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
|
||||
* one.
|
||||
*
|
||||
* @param {Array} keys
|
||||
* @param {string[]} keys
|
||||
*/
|
||||
toggle(keys) {
|
||||
const user = this.attrs.user;
|
||||
|
@ -128,7 +128,7 @@ export default class NotificationGrid extends Component {
|
|||
/**
|
||||
* Toggle all notification types for the given method.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {string} method
|
||||
*/
|
||||
toggleMethod(method) {
|
||||
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.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {string} type
|
||||
*/
|
||||
toggleType(type) {
|
||||
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
|
||||
* combination.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {String} method
|
||||
* @return {String}
|
||||
* @param {string} type
|
||||
* @param {string} method
|
||||
* @return {string}
|
||||
*/
|
||||
preferenceKey(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.
|
||||
* - `label` The label to display in the column header.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<{ name: string, icon: string, label: import('mithril').Children }>}
|
||||
*/
|
||||
notificationMethods() {
|
||||
const items = new ItemList();
|
||||
|
@ -197,7 +197,7 @@ export default class NotificationGrid extends Component {
|
|||
* - `icon` The icon 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() {
|
||||
const items = new ItemList();
|
||||
|
|
|
@ -102,7 +102,7 @@ export default class Post extends Component {
|
|||
/**
|
||||
* Get attributes for the post element.
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {Record<string, unknown>}
|
||||
*/
|
||||
elementAttrs() {
|
||||
return {};
|
||||
|
@ -111,16 +111,16 @@ export default class Post extends Component {
|
|||
/**
|
||||
* Get the post's content.
|
||||
*
|
||||
* @return {Array}
|
||||
* @return {import('mithril').Children}
|
||||
*/
|
||||
content() {
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the post's classes.
|
||||
*
|
||||
* @param existing string
|
||||
* @param {string} existing
|
||||
* @returns {string[]}
|
||||
*/
|
||||
classes(existing) {
|
||||
|
@ -137,7 +137,7 @@ export default class Post extends Component {
|
|||
classes.push('Post--by-actor');
|
||||
}
|
||||
|
||||
if (user && user.id() === discussion.attribute('startUserId')) {
|
||||
if (user?.id() === discussion.attribute('startUserId')) {
|
||||
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.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
actionItems() {
|
||||
return new ItemList();
|
||||
|
@ -156,7 +156,7 @@ export default class Post extends Component {
|
|||
/**
|
||||
* Build an item list for the post's footer.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
footerItems() {
|
||||
return new ItemList();
|
||||
|
|
|
@ -51,8 +51,8 @@ export default class PostMeta extends Component {
|
|||
/**
|
||||
* Get the permalink for the given post.
|
||||
*
|
||||
* @param {Post} post
|
||||
* @returns {String}
|
||||
* @param {import('../../common/models/Post').default} post
|
||||
* @returns {string}
|
||||
*/
|
||||
getPermalink(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) {
|
||||
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,
|
||||
* and if so, trigger loading the next/previous page.
|
||||
*
|
||||
* @param {Integer} top
|
||||
* @param {number} top
|
||||
*/
|
||||
loadPostsIfNeeded(top = window.pageYOffset) {
|
||||
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
|
||||
* would consider a post to be the first one visible.
|
||||
*
|
||||
* @return {Integer}
|
||||
* @return {number}
|
||||
*/
|
||||
getMarginTop() {
|
||||
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.
|
||||
*
|
||||
* @param {Integer} number
|
||||
* @param {Boolean} animate
|
||||
* @return {jQuery.Deferred}
|
||||
* @param {number} number
|
||||
* @param {boolean} animate
|
||||
* @return {JQueryDeferred}
|
||||
*/
|
||||
scrollToNumber(number, animate) {
|
||||
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.
|
||||
*
|
||||
* @param {Integer} index
|
||||
* @param {Boolean} animate
|
||||
* @param {Boolean} reply Whether or not to scroll to the reply placeholder.
|
||||
* @return {jQuery.Deferred}
|
||||
* @param {number} index
|
||||
* @param {boolean} animate
|
||||
* @param {boolean} reply Whether or not to scroll to the reply placeholder.
|
||||
* @return {JQueryDeferred}
|
||||
*/
|
||||
scrollToIndex(index, animate, reply) {
|
||||
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.
|
||||
*
|
||||
* @param {jQuery} $item
|
||||
* @param {Boolean} animate
|
||||
* @param {Boolean} force Whether or not to force scrolling to the item, even
|
||||
* @param {JQuery} $item
|
||||
* @param {boolean} animate
|
||||
* @param {boolean} force Whether or not to force scrolling to the item, even
|
||||
* if it is already in the viewport.
|
||||
* @param {Boolean} reply Whether or not to scroll to the reply placeholder.
|
||||
* @return {jQuery.Deferred}
|
||||
* @param {boolean} reply Whether or not to scroll to the reply placeholder.
|
||||
* @return {JQueryDeferred}
|
||||
*/
|
||||
scrollToItem($item, animate, force, reply) {
|
||||
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.
|
||||
*
|
||||
* @param {jQuery} $item
|
||||
* @param {JQuery} $item
|
||||
*/
|
||||
flashItem($item) {
|
||||
// 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
|
||||
* index/visible properties.
|
||||
*
|
||||
* @param {Boolean} animate
|
||||
* @param {Partial<{fromScroll: boolean, forceHeightChange: boolean, animate: boolean}>} options
|
||||
*/
|
||||
updateScrubberValues(options = {}) {
|
||||
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
|
||||
* to each post.
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {{ index: number, visible: number }}
|
||||
* @property {Number} index The percent per post for posts on either side of
|
||||
* the visible part of the scrubber.
|
||||
* @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.
|
||||
*
|
||||
* @type {Integer}
|
||||
* @type {number}
|
||||
*/
|
||||
this.loadLimit = 20;
|
||||
|
||||
|
@ -100,8 +100,6 @@ export default class PostsUserPage extends UserPage {
|
|||
|
||||
/**
|
||||
* Clear and reload the user's activity feed.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
refresh() {
|
||||
this.loading = true;
|
||||
|
@ -115,8 +113,8 @@ export default class PostsUserPage extends UserPage {
|
|||
/**
|
||||
* Load a new page of the user's activity feed.
|
||||
*
|
||||
* @param {Integer} [offset] The position to start getting results from.
|
||||
* @return {Promise}
|
||||
* @param {number} [offset] The position to start getting results from.
|
||||
* @return {Promise<import('../../common/models/Post').default[]>}
|
||||
* @protected
|
||||
*/
|
||||
loadResults(offset) {
|
||||
|
@ -132,8 +130,6 @@ export default class PostsUserPage extends UserPage {
|
|||
|
||||
/**
|
||||
* Load the next page of results.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
loadMore() {
|
||||
this.loading = true;
|
||||
|
@ -143,13 +139,13 @@ export default class PostsUserPage extends UserPage {
|
|||
/**
|
||||
* Parse results and append them to the activity feed.
|
||||
*
|
||||
* @param {Post[]} results
|
||||
* @return {Post[]}
|
||||
* @param {import('../../common/models/Post').default[]} results
|
||||
* @return {import('../../common/models/Post').default[]}
|
||||
*/
|
||||
parseResults(results) {
|
||||
this.loading = false;
|
||||
|
||||
[].push.apply(this.posts, results);
|
||||
this.posts.push(results);
|
||||
|
||||
this.moreResults = results.length >= this.loadLimit;
|
||||
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.
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {Record<string, unknown>}
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -35,7 +35,7 @@ export default class SessionDropdown extends Dropdown {
|
|||
/**
|
||||
* Build an item list for the contents of the dropdown menu.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
|
|
@ -33,7 +33,7 @@ export default class SettingsPage extends UserPage {
|
|||
/**
|
||||
* Build an item list for the user's settings controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
settingsItems() {
|
||||
const items = new ItemList();
|
||||
|
@ -53,7 +53,7 @@ export default class SettingsPage extends UserPage {
|
|||
/**
|
||||
* Build an item list for the user's account settings.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
accountItems() {
|
||||
const items = new ItemList();
|
||||
|
@ -78,7 +78,7 @@ export default class SettingsPage extends UserPage {
|
|||
/**
|
||||
* Build an item list for the user's notification settings.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
notificationsItems() {
|
||||
const items = new ItemList();
|
||||
|
@ -91,7 +91,7 @@ export default class SettingsPage extends UserPage {
|
|||
/**
|
||||
* Build an item list for the user's privacy settings.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
privacyItems() {
|
||||
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
|
||||
* the user has entered one.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
logIn() {
|
||||
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.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
infoItems() {
|
||||
const items = new ItemList();
|
||||
|
|
|
@ -30,6 +30,11 @@ export default class UserPage extends Page {
|
|||
this.bodyClass = 'App--user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Base view template for the user page.
|
||||
*
|
||||
* @return {import('mithril').Children}
|
||||
*/
|
||||
view() {
|
||||
return (
|
||||
<div className="UserPage">
|
||||
|
@ -60,7 +65,7 @@ export default class UserPage extends Page {
|
|||
/**
|
||||
* Get the content to display in the user page.
|
||||
*
|
||||
* @return {VirtualElement}
|
||||
* @return {import('mithril').Children}
|
||||
*/
|
||||
content() {}
|
||||
|
||||
|
@ -68,7 +73,7 @@ export default class UserPage extends Page {
|
|||
* Initialize the component with a user, and trigger the loading of their
|
||||
* activity feed.
|
||||
*
|
||||
* @param {User} user
|
||||
* @param {import('../../common/models/User').default} user
|
||||
* @protected
|
||||
*/
|
||||
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
|
||||
* if we don't have it yet. Then initialize the profile page with that user.
|
||||
*
|
||||
* @param {String} username
|
||||
* @param {string} username
|
||||
*/
|
||||
loadUser(username) {
|
||||
const lowercaseUsername = username.toLowerCase();
|
||||
|
@ -110,7 +115,7 @@ export default class UserPage extends Page {
|
|||
/**
|
||||
* Build an item list for the content of the sidebar.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
sidebarItems() {
|
||||
const items = new ItemList();
|
||||
|
@ -128,7 +133,7 @@ export default class UserPage extends Page {
|
|||
/**
|
||||
* Build an item list for the navigation in the sidebar.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
navItems() {
|
||||
const items = new ItemList();
|
||||
|
|
|
@ -17,7 +17,7 @@ class ComposerState {
|
|||
* The composer's intended height, which can be modified by the user
|
||||
* (by dragging the composer handle).
|
||||
*
|
||||
* @type {Integer}
|
||||
* @type {number}
|
||||
*/
|
||||
this.height = null;
|
||||
|
||||
|
@ -41,8 +41,7 @@ class ComposerState {
|
|||
/**
|
||||
* Load a content component into the composer.
|
||||
*
|
||||
* @param {ComposerBody} componentClass
|
||||
* @public
|
||||
* @param {typeof import('../components/ComposerBody').default} componentClass
|
||||
*/
|
||||
load(componentClass, attrs) {
|
||||
const body = { componentClass, attrs };
|
||||
|
@ -82,8 +81,6 @@ class ComposerState {
|
|||
|
||||
/**
|
||||
* Show the composer.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
show() {
|
||||
if (this.position === ComposerState.Position.NORMAL || this.position === ComposerState.Position.FULLSCREEN) return;
|
||||
|
@ -94,8 +91,6 @@ class ComposerState {
|
|||
|
||||
/**
|
||||
* Close the composer.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
hide() {
|
||||
this.clear();
|
||||
|
@ -105,8 +100,6 @@ class ComposerState {
|
|||
/**
|
||||
* Confirm with the user so they don't lose their content, then close the
|
||||
* composer.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
close() {
|
||||
if (this.preventExit()) return;
|
||||
|
@ -116,8 +109,6 @@ class ComposerState {
|
|||
|
||||
/**
|
||||
* Minimize the composer. Has no effect if the composer is hidden.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
minimize() {
|
||||
if (!this.isVisible()) return;
|
||||
|
@ -129,8 +120,6 @@ class ComposerState {
|
|||
/**
|
||||
* Take the composer into fullscreen mode. Has no effect if the composer is
|
||||
* hidden.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
fullScreen() {
|
||||
if (!this.isVisible()) return;
|
||||
|
@ -141,8 +130,6 @@ class ComposerState {
|
|||
|
||||
/**
|
||||
* Exit fullscreen mode.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
exitFullScreen() {
|
||||
if (this.position !== ComposerState.Position.FULLSCREEN) return;
|
||||
|
@ -154,8 +141,7 @@ class ComposerState {
|
|||
/**
|
||||
* Determine whether the body matches the given component class and data.
|
||||
*
|
||||
* @param {object} type The component class to check against. Subclasses are
|
||||
* accepted as well.
|
||||
* @param {object} type The component class to check against. Subclasses are accepted as well.
|
||||
* @param {object} data
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
@ -186,8 +172,7 @@ class ComposerState {
|
|||
* 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..
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
* @return {boolean}
|
||||
*/
|
||||
isFullScreen() {
|
||||
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
|
||||
* discussion.
|
||||
*
|
||||
* @param {Discussion} discussion
|
||||
* @return {Boolean}
|
||||
* @param {import('../../common/models/Discussion').default} discussion
|
||||
* @return {boolean}
|
||||
*/
|
||||
composingReplyTo(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
|
||||
* content.
|
||||
*
|
||||
* @return {Boolean} Whether or not the exit was cancelled.
|
||||
* @return {boolean} Whether or not the exit was cancelled.
|
||||
*/
|
||||
preventExit() {
|
||||
if (!this.isVisible()) return;
|
||||
|
@ -226,8 +211,8 @@ class ComposerState {
|
|||
* confirmation is necessary. If the callback returns true at the time of
|
||||
* closing, the provided text will be shown in a standard confirmation dialog.
|
||||
*
|
||||
* @param {Function} callback
|
||||
* @param {String} message
|
||||
* @param {() => boolean} callback
|
||||
* @param {string} message
|
||||
*/
|
||||
preventClosingWhen(callback, message) {
|
||||
this.onExit = { callback, message };
|
||||
|
@ -235,7 +220,7 @@ class ComposerState {
|
|||
|
||||
/**
|
||||
* Minimum height of the Composer.
|
||||
* @returns {Integer}
|
||||
* @returns {number}
|
||||
*/
|
||||
minimumHeight() {
|
||||
return 200;
|
||||
|
@ -243,7 +228,7 @@ class ComposerState {
|
|||
|
||||
/**
|
||||
* Maxmimum height of the Composer.
|
||||
* @returns {Integer}
|
||||
* @returns {number}
|
||||
*/
|
||||
maximumHeight() {
|
||||
return $(window).height() - $('#header').outerHeight();
|
||||
|
@ -251,9 +236,9 @@ class ComposerState {
|
|||
|
||||
/**
|
||||
* 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.
|
||||
* @returns {Integer|String}
|
||||
* @returns {number | string}
|
||||
*/
|
||||
computedHeight() {
|
||||
// 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
|
||||
* discussion, if the end is being viewed.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
update() {
|
||||
if (!this.viewingEnd()) return Promise.resolve();
|
||||
|
@ -74,7 +72,7 @@ class PostStreamState {
|
|||
/**
|
||||
* Load and scroll up to the first post in the discussion.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
goToFirst() {
|
||||
return this.goToIndex(0);
|
||||
|
@ -83,7 +81,7 @@ class PostStreamState {
|
|||
/**
|
||||
* Load and scroll down to the last post in the discussion.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
goToLast() {
|
||||
return this.goToIndex(this.count() - 1, true);
|
||||
|
@ -92,10 +90,9 @@ class PostStreamState {
|
|||
/**
|
||||
* Load and scroll to a post with a certain number.
|
||||
*
|
||||
* @param {number|String} number The post number to go to. If 'reply', go to
|
||||
* the last post and scroll the reply preview into view.
|
||||
* @param {Boolean} noAnimation
|
||||
* @return {Promise}
|
||||
* @param {number | string} number The post number to go to. If 'reply', go to the last post and scroll the reply preview into view.
|
||||
* @param {boolean} [noAnimation]
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
goToNumber(number, noAnimation = false) {
|
||||
// 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.
|
||||
*
|
||||
* @param {number} index
|
||||
* @param {Boolean} noAnimation
|
||||
* @return {Promise}
|
||||
* @param {boolean} [noAnimation]
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
goToIndex(index, noAnimation = false) {
|
||||
this.paused = true;
|
||||
|
@ -151,7 +148,7 @@ class PostStreamState {
|
|||
* resolved immediately.
|
||||
*
|
||||
* @param {number} number
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
loadNearNumber(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.
|
||||
*
|
||||
* @param {number} index
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
loadNearIndex(index) {
|
||||
if (index >= this.visibleStart && index < this.visibleEnd) {
|
||||
|
@ -240,7 +237,7 @@ class PostStreamState {
|
|||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {Boolean} backwards
|
||||
* @param {boolean} backwards
|
||||
*/
|
||||
loadPage(start, end, backwards = false) {
|
||||
this.pagesLoading++;
|
||||
|
@ -271,7 +268,7 @@ class PostStreamState {
|
|||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
loadRange(start, end) {
|
||||
const loadIds = [];
|
||||
|
@ -302,7 +299,7 @@ class PostStreamState {
|
|||
/**
|
||||
* Set up the stream with the given array of posts.
|
||||
*
|
||||
* @param {Post[]} posts
|
||||
* @param {import('../../common/models/Post').default[]} posts
|
||||
*/
|
||||
show(posts) {
|
||||
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
|
||||
* posts are visible in the viewport.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
disabled() {
|
||||
return this.visible >= this.count();
|
||||
|
|
|
@ -16,11 +16,10 @@ export default {
|
|||
/**
|
||||
* Get a list of controls for a discussion.
|
||||
*
|
||||
* @param {Discussion} discussion
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @public
|
||||
* @param {import('../../common/models/Discussion').default} discussion
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
controls(discussion, context) {
|
||||
const items = new ItemList();
|
||||
|
@ -40,10 +39,10 @@ export default {
|
|||
* Get controls for a discussion pertaining to the current user (e.g. reply,
|
||||
* follow).
|
||||
*
|
||||
* @param {Discussion} discussion
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/Discussion').default} discussion
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
* @protected
|
||||
*/
|
||||
userControls(discussion, context) {
|
||||
|
@ -88,10 +87,10 @@ export default {
|
|||
/**
|
||||
* Get controls for a discussion pertaining to moderation (e.g. rename, lock).
|
||||
*
|
||||
* @param {Discussion} discussion
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/Discussion').default} discussion
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
* @protected
|
||||
*/
|
||||
moderationControls(discussion) {
|
||||
|
@ -116,10 +115,10 @@ export default {
|
|||
/**
|
||||
* Get controls for a discussion which are destructive (e.g. delete).
|
||||
*
|
||||
* @param {Discussion} discussion
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/Discussion').default} discussion
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
* @protected
|
||||
*/
|
||||
destructiveControls(discussion) {
|
||||
|
@ -175,11 +174,10 @@ export default {
|
|||
* logged in, they will be prompted. If they don't have permission to
|
||||
* reply, the promise will be rejected.
|
||||
*
|
||||
* @param {Boolean} goToLast Whether or not to scroll down to the last post if
|
||||
* 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.
|
||||
* @return {Promise}
|
||||
* @param {boolean} goToLast Whether or not to scroll down to the last post if 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.
|
||||
*
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
replyAction(goToLast, forceRefresh) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -212,7 +210,7 @@ export default {
|
|||
/**
|
||||
* Hide a discussion.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
hideAction() {
|
||||
this.pushAttributes({ hiddenAt: new Date(), hiddenUser: app.session.user });
|
||||
|
@ -223,7 +221,7 @@ export default {
|
|||
/**
|
||||
* Restore a discussion.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
restoreAction() {
|
||||
this.pushAttributes({ hiddenAt: null, hiddenUser: null });
|
||||
|
@ -234,7 +232,7 @@ export default {
|
|||
/**
|
||||
* Delete the discussion after confirming with the user.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
deleteAction() {
|
||||
if (confirm(extractText(app.translator.trans('core.forum.discussion_controls.delete_confirmation')))) {
|
||||
|
@ -250,8 +248,6 @@ export default {
|
|||
|
||||
/**
|
||||
* Rename the discussion.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
renameAction() {
|
||||
return app.modal.show(RenameDiscussionModal, {
|
||||
|
|
|
@ -52,8 +52,6 @@ export default class Pane {
|
|||
|
||||
/**
|
||||
* Enable the pane.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
enable() {
|
||||
this.active = true;
|
||||
|
@ -62,8 +60,6 @@ export default class Pane {
|
|||
|
||||
/**
|
||||
* Disable the pane.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
disable() {
|
||||
this.active = false;
|
||||
|
@ -73,8 +69,6 @@ export default class Pane {
|
|||
|
||||
/**
|
||||
* Show the pane.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
show() {
|
||||
clearTimeout(this.hideTimeout);
|
||||
|
@ -84,8 +78,6 @@ export default class Pane {
|
|||
|
||||
/**
|
||||
* Hide the pane.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
hide() {
|
||||
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
|
||||
* pane.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
onmouseleave() {
|
||||
this.hideTimeout = setTimeout(this.hide.bind(this), 250);
|
||||
|
@ -104,8 +94,6 @@ export default class Pane {
|
|||
|
||||
/**
|
||||
* Toggle whether or not the pane is pinned.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
togglePinned() {
|
||||
this.pinned = !this.pinned;
|
||||
|
|
|
@ -13,11 +13,10 @@ export default {
|
|||
/**
|
||||
* Get a list of controls for a post.
|
||||
*
|
||||
* @param {Post} post
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @public
|
||||
* @param {import('../../common/models/Post').default} post
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}')}
|
||||
*/
|
||||
controls(post, context) {
|
||||
const items = new ItemList();
|
||||
|
@ -36,10 +35,10 @@ export default {
|
|||
/**
|
||||
* Get controls for a post pertaining to the current user (e.g. report).
|
||||
*
|
||||
* @param {Post} post
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/Post').default} post
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}')}
|
||||
* @protected
|
||||
*/
|
||||
userControls(post, context) {
|
||||
|
@ -49,10 +48,10 @@ export default {
|
|||
/**
|
||||
* Get controls for a post pertaining to moderation (e.g. edit).
|
||||
*
|
||||
* @param {Post} post
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/Post').default} post
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}')}
|
||||
* @protected
|
||||
*/
|
||||
moderationControls(post, context) {
|
||||
|
@ -79,10 +78,10 @@ export default {
|
|||
/**
|
||||
* Get controls for a post that are destructive (e.g. delete).
|
||||
*
|
||||
* @param {Post} post
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/Post').default} post
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}')}
|
||||
* @protected
|
||||
*/
|
||||
destructiveControls(post, context) {
|
||||
|
@ -134,7 +133,7 @@ export default {
|
|||
/**
|
||||
* Open the composer to edit a post.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
editAction() {
|
||||
return new Promise((resolve) => {
|
||||
|
@ -148,7 +147,7 @@ export default {
|
|||
/**
|
||||
* Hide a post.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
hideAction() {
|
||||
if (!confirm(extractText(app.translator.trans('core.forum.post_controls.hide_confirmation')))) return;
|
||||
|
@ -160,7 +159,7 @@ export default {
|
|||
/**
|
||||
* Restore a post.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
restoreAction() {
|
||||
this.pushAttributes({ hiddenAt: null, hiddenUser: null });
|
||||
|
@ -171,7 +170,7 @@ export default {
|
|||
/**
|
||||
* Delete a post.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
deleteAction(context) {
|
||||
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.
|
||||
*
|
||||
* @param {User} user
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @public
|
||||
* @param {import('../../common/models/User').default} user
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
controls(user, context) {
|
||||
const items = new ItemList();
|
||||
|
@ -36,10 +35,10 @@ export default {
|
|||
/**
|
||||
* Get controls for a user pertaining to the current user (e.g. poke, follow).
|
||||
*
|
||||
* @param {User} user
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/User').default} user
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
* @protected
|
||||
*/
|
||||
userControls() {
|
||||
|
@ -49,10 +48,10 @@ export default {
|
|||
/**
|
||||
* Get controls for a user pertaining to moderation (e.g. suspend, edit).
|
||||
*
|
||||
* @param {User} user
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/User').default} user
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
* @protected
|
||||
*/
|
||||
moderationControls(user) {
|
||||
|
@ -73,10 +72,10 @@ export default {
|
|||
/**
|
||||
* Get controls for a user which are destructive (e.g. delete).
|
||||
*
|
||||
* @param {User} user
|
||||
* @param {*} context The parent component under which the controls menu will
|
||||
* be displayed.
|
||||
* @return {ItemList}
|
||||
* @param {import('../../common/models/User').default} user
|
||||
* @param {import('../../common/Component').default<any, any>} context The parent component under which the controls menu will be displayed.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
* @protected
|
||||
*/
|
||||
destructiveControls(user) {
|
||||
|
@ -97,7 +96,7 @@ export default {
|
|||
/**
|
||||
* Delete the user.
|
||||
*
|
||||
* @param {User} user
|
||||
* @param {import('../../common/models/User').default} user
|
||||
*/
|
||||
deleteAction(user) {
|
||||
if (!confirm(app.translator.trans('core.forum.user_controls.delete_confirmation'))) {
|
||||
|
@ -120,7 +119,7 @@ export default {
|
|||
/**
|
||||
* Show deletion alert of user.
|
||||
*
|
||||
* @param {User} user
|
||||
* @param {import('../../common/models/User').default} user
|
||||
* @param {string} type
|
||||
*/
|
||||
showDeletionAlert(user, type) {
|
||||
|
@ -141,7 +140,7 @@ export default {
|
|||
/**
|
||||
* Edit the user.
|
||||
*
|
||||
* @param {User} user
|
||||
* @param {import('../../common/models/User').default} user
|
||||
*/
|
||||
editAction(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.
|
||||
*
|
||||
* @param {ForumApplication} app
|
||||
* @param {import('../ForumApplication').default} app
|
||||
*/
|
||||
export default function alertEmailConfirmation(app) {
|
||||
const user = app.session.user;
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
* controls.
|
||||
*
|
||||
* It relies on the element having children with particular CSS classes.
|
||||
* TODO: document
|
||||
*
|
||||
* @param {DOMElement} element
|
||||
* @return {Object}
|
||||
* @property {function} reset Revert the slider to its original position. This
|
||||
* should be called, for example, when a controls dropdown is closed.
|
||||
* The function returns a record with a `reset` proeprty. This is a function
|
||||
* which reverts the slider to its original position. This should be called,
|
||||
* for example, when a controls dropdown is closed.
|
||||
*
|
||||
* @param {HTMLElement | SVGElement | Element} element
|
||||
* @return {{ reset : () => void }}
|
||||
*/
|
||||
export default function slidable(element) {
|
||||
const $element = $(element);
|
||||
|
@ -27,15 +28,15 @@ export default function slidable(element) {
|
|||
/**
|
||||
* Animate the slider to a new position.
|
||||
*
|
||||
* @param {Integer} newPos
|
||||
* @param {Object} [options]
|
||||
* @param {number} newPos
|
||||
* @param {Partial<JQueryAnimationOptions>} [options]
|
||||
*/
|
||||
const animatePos = (newPos, options = {}) => {
|
||||
// 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
|
||||
// will set the transform property, but then we animate an unused property
|
||||
// (background-position-x) with jQuery.
|
||||
options.duration = options.duration || 'fast';
|
||||
options.duration ||= 'fast';
|
||||
options.step = function (x) {
|
||||
$(this).css('transform', 'translate(' + x + 'px, 0)');
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user