From d268894e61b29dcc1b6f1919a2c919b08a758a8c Mon Sep 17 00:00:00 2001 From: David Wheatley Date: Mon, 27 Dec 2021 19:58:18 +0100 Subject: [PATCH] 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> --- js/src/admin/components/AdminNav.js | 2 +- js/src/admin/components/BasicsPage.js | 3 +- js/src/admin/components/DashboardWidget.js | 6 +- js/src/admin/components/HeaderPrimary.js | 2 +- js/src/admin/components/HeaderSecondary.js | 4 +- js/src/admin/components/UploadImageButton.js | 4 +- js/src/common/Application.tsx | 3 - js/src/common/Fragment.ts | 4 +- js/src/common/components/Checkbox.js | 4 +- js/src/common/components/Dropdown.js | 4 +- js/src/common/components/LinkButton.js | 4 +- js/src/common/components/Navigation.js | 6 +- js/src/common/components/SplitDropdown.js | 3 +- js/src/common/components/TextEditor.js | 6 +- js/src/common/extend.ts | 4 +- js/src/common/helpers/punctuateSeries.js | 4 +- js/src/common/states/AlertManagerState.ts | 2 +- js/src/common/states/ModalManagerState.ts | 2 +- js/src/common/states/PageState.js | 5 +- js/src/common/utils/Drawer.js | 5 -- js/src/common/utils/ItemList.ts | 2 +- js/src/common/utils/ScrollListener.js | 17 +---- js/src/common/utils/anchorScroll.js | 4 +- js/src/common/utils/computed.ts | 5 +- js/src/common/utils/evented.js | 35 ++++++---- js/src/common/utils/mixin.js | 6 +- js/src/forum/components/AvatarEditor.js | 14 ++-- js/src/forum/components/CommentPost.js | 2 +- js/src/forum/components/Composer.js | 8 +-- js/src/forum/components/ComposerBody.js | 4 +- js/src/forum/components/DiscussionComposer.js | 4 +- js/src/forum/components/DiscussionHero.js | 2 +- js/src/forum/components/DiscussionListItem.js | 8 +-- js/src/forum/components/DiscussionPage.tsx | 66 ++++++++----------- js/src/forum/components/EditPostComposer.js | 2 +- js/src/forum/components/EventPost.js | 10 +-- js/src/forum/components/HeaderPrimary.js | 2 +- js/src/forum/components/IndexPage.js | 14 ++-- js/src/forum/components/LogInButtons.js | 3 +- js/src/forum/components/Notification.js | 8 +-- js/src/forum/components/NotificationGrid.js | 22 +++---- js/src/forum/components/Post.js | 14 ++-- js/src/forum/components/PostMeta.js | 4 +- js/src/forum/components/PostStream.js | 32 ++++----- js/src/forum/components/PostStreamScrubber.js | 4 +- js/src/forum/components/PostsUserPage.js | 16 ++--- js/src/forum/components/ReplyComposer.js | 2 +- js/src/forum/components/SessionDropdown.js | 2 +- js/src/forum/components/SettingsPage.js | 8 +-- js/src/forum/components/SignUpModal.tsx | 2 - js/src/forum/components/UserCard.js | 2 +- js/src/forum/components/UserPage.js | 15 +++-- js/src/forum/states/ComposerState.js | 41 ++++-------- js/src/forum/states/PostStreamState.js | 29 ++++---- js/src/forum/utils/DiscussionControls.js | 50 +++++++------- js/src/forum/utils/Pane.js | 12 ---- js/src/forum/utils/PostControls.js | 41 ++++++------ js/src/forum/utils/UserControls.js | 39 ++++++----- js/src/forum/utils/alertEmailConfirmation.js | 2 +- js/src/forum/utils/slidable.js | 17 ++--- 60 files changed, 292 insertions(+), 355 deletions(-) diff --git a/js/src/admin/components/AdminNav.js b/js/src/admin/components/AdminNav.js index 36f7cd906..506334a0a 100644 --- a/js/src/admin/components/AdminNav.js +++ b/js/src/admin/components/AdminNav.js @@ -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} */ items() { const items = new ItemList(); diff --git a/js/src/admin/components/BasicsPage.js b/js/src/admin/components/BasicsPage.js index d2e70a99d..658eaca68 100644 --- a/js/src/admin/components/BasicsPage.js +++ b/js/src/admin/components/BasicsPage.js @@ -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(); diff --git a/js/src/admin/components/DashboardWidget.js b/js/src/admin/components/DashboardWidget.js index 68bff531b..1e42cc8fb 100644 --- a/js/src/admin/components/DashboardWidget.js +++ b/js/src/admin/components/DashboardWidget.js @@ -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; } } diff --git a/js/src/admin/components/HeaderPrimary.js b/js/src/admin/components/HeaderPrimary.js index 5a87ff6e0..8246761a3 100644 --- a/js/src/admin/components/HeaderPrimary.js +++ b/js/src/admin/components/HeaderPrimary.js @@ -21,7 +21,7 @@ export default class HeaderPrimary extends Component { /** * Build an item list for the controls. * - * @return {ItemList} + * @return {ItemList} */ items() { return new ItemList(); diff --git a/js/src/admin/components/HeaderSecondary.js b/js/src/admin/components/HeaderSecondary.js index d218bbf91..255815be2 100644 --- a/js/src/admin/components/HeaderSecondary.js +++ b/js/src/admin/components/HeaderSecondary.js @@ -16,7 +16,7 @@ export default class HeaderSecondary extends Component { /** * Build an item list for the controls. * - * @return {ItemList} + * @return {ItemList} */ items() { const items = new ItemList(); @@ -28,7 +28,7 @@ export default class HeaderSecondary extends Component { ); - items.add('session', SessionDropdown.component()); + items.add('session', ); return items; } diff --git a/js/src/admin/components/UploadImageButton.js b/js/src/admin/components/UploadImageButton.js index 1af51aec6..e62454078 100644 --- a/js/src/admin/components/UploadImageButton.js +++ b/js/src/admin/components/UploadImageButton.js @@ -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) { diff --git a/js/src/common/Application.tsx b/js/src/common/Application.tsx index 3dc7b8259..cfcda3c89 100644 --- a/js/src/common/Application.tsx +++ b/js/src/common/Application.tsx @@ -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(originalOptions: FlarumRequestOptions): Promise { const options = this.transformRequestOptions(originalOptions); diff --git a/js/src/common/Fragment.ts b/js/src/common/Fragment.ts index a3886db39..4d0f5416c 100644 --- a/js/src/common/Fragment.ts +++ b/js/src/common/Fragment.ts @@ -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 { diff --git a/js/src/common/components/Checkbox.js b/js/src/common/components/Checkbox.js index 5c86ba270..bc7ed5db1 100644 --- a/js/src/common/components/Checkbox.js +++ b/js/src/common/components/Checkbox.js @@ -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) { diff --git a/js/src/common/components/Dropdown.js b/js/src/common/components/Dropdown.js index 6ed232c94..47c63b3a3 100644 --- a/js/src/common/components/Dropdown.js +++ b/js/src/common/components/Dropdown.js @@ -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) { diff --git a/js/src/common/components/LinkButton.js b/js/src/common/components/LinkButton.js index 1abebac1e..1ce560693 100644 --- a/js/src/common/components/LinkButton.js +++ b/js/src/common/components/LinkButton.js @@ -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; diff --git a/js/src/common/components/Navigation.js b/js/src/common/components/Navigation.js index 140ca43d8..157d07538 100644 --- a/js/src/common/components/Navigation.js +++ b/js/src/common/components/Navigation.js @@ -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() { diff --git a/js/src/common/components/SplitDropdown.js b/js/src/common/components/SplitDropdown.js index e898c95ad..fea79e6af 100644 --- a/js/src/common/components/SplitDropdown.js +++ b/js/src/common/components/SplitDropdown.js @@ -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) { diff --git a/js/src/common/components/TextEditor.js b/js/src/common/components/TextEditor.js index a1d30f354..c4608ace3 100644 --- a/js/src/common/components/TextEditor.js +++ b/js/src/common/components/TextEditor.js @@ -90,7 +90,7 @@ export default class TextEditor extends Component { /** * Build an item list for the text editor controls. * - * @return {ItemList} + * @return {ItemList} */ 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} */ 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; diff --git a/js/src/common/extend.ts b/js/src/common/extend.ts index a9b8b97b5..f3eb79df7 100644 --- a/js/src/common/extend.ts +++ b/js/src/common/extend.ts @@ -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>( +export function extend, K extends KeyOfType>( object: T, methods: K | K[], callback: (this: T, val: ReturnType, ...args: Parameters) => void @@ -72,7 +72,7 @@ export function extend>( * @param methods The name or names of the method(s) to override * @param newMethod The method to replace it with */ -export function override>( +export function override, K extends KeyOfType>( object: T, methods: K | K[], newMethod: (this: T, orig: T[K], ...args: Parameters) => void diff --git a/js/src/common/helpers/punctuateSeries.js b/js/src/common/helpers/punctuateSeries.js index 45610c8e2..ad8c70592 100644 --- a/js/src/common/helpers/punctuateSeries.js +++ b/js/src/common/helpers/punctuateSeries.js @@ -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) { diff --git a/js/src/common/states/AlertManagerState.ts b/js/src/common/states/AlertManagerState.ts index a5e479546..f254ea502 100644 --- a/js/src/common/states/AlertManagerState.ts +++ b/js/src/common/states/AlertManagerState.ts @@ -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; diff --git a/js/src/common/states/ModalManagerState.ts b/js/src/common/states/ModalManagerState.ts index 425de5150..445d002b1 100644 --- a/js/src/common/states/ModalManagerState.ts +++ b/js/src/common/states/ModalManagerState.ts @@ -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; diff --git a/js/src/common/states/PageState.js b/js/src/common/states/PageState.js index f8d01301b..1ff8cbbbd 100644 --- a/js/src/common/states/PageState.js +++ b/js/src/common/states/PageState.js @@ -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} data * @return {boolean} */ matches(type, data = {}) { diff --git a/js/src/common/utils/Drawer.js b/js/src/common/utils/Drawer.js index c0003dd1e..323e62fd0 100644 --- a/js/src/common/utils/Drawer.js +++ b/js/src/common/utils/Drawer.js @@ -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'); diff --git a/js/src/common/utils/ItemList.ts b/js/src/common/utils/ItemList.ts index 3f7857601..79b58a5bb 100644 --- a/js/src/common/utils/ItemList.ts +++ b/js/src/common/utils/ItemList.ts @@ -303,7 +303,7 @@ export default class ItemList { * * @param content The item's content (objects only) * @param key The item's key - * @returns Proxied content + * @return Proxied content * * @internal */ diff --git a/js/src/common/utils/ScrollListener.js b/js/src/common/utils/ScrollListener.js index 432fd553b..dba493bab 100644 --- a/js/src/common/utils/ScrollListener.js +++ b/js/src/common/utils/ScrollListener.js @@ -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); diff --git a/js/src/common/utils/anchorScroll.js b/js/src/common/utils/anchorScroll.js index 7e836cb90..0bd0b8756 100644 --- a/js/src/common/utils/anchorScroll.js +++ b/js/src/common/utils/anchorScroll.js @@ -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); diff --git a/js/src/common/utils/computed.ts b/js/src/common/utils/computed.ts index 8bf1cb3f4..a7a1892d6 100644 --- a/js/src/common/utils/computed.ts +++ b/js/src/common/utils/computed.ts @@ -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(...args: [...string[], (this: M, ...args: unknown[]) => T]): () => T { const keys = args.slice(0, -1) as string[]; diff --git a/js/src/common/utils/evented.js b/js/src/common/utils/evented.js index 9c345d1c2..1aa9e359c 100644 --- a/js/src/common/utils/evented.js +++ b/js/src/common/utils/evented.js @@ -13,17 +13,21 @@ export default { /** * Arrays of registered event handlers, grouped by the event name. * - * @type {Object} + * @type {Record} * @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); diff --git a/js/src/common/utils/mixin.js b/js/src/common/utils/mixin.js index da73f0116..ba9b2b535 100644 --- a/js/src/common/utils/mixin.js +++ b/js/src/common/utils/mixin.js @@ -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[]} 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 {} diff --git a/js/src/forum/components/AvatarEditor.js b/js/src/forum/components/AvatarEditor.js index f48d4c0d6..fa2ff1266 100644 --- a/js/src/forum/components/AvatarEditor.js +++ b/js/src/forum/components/AvatarEditor.js @@ -69,7 +69,7 @@ export default class AvatarEditor extends Component { /** * Get the items in the edit avatar dropdown menu. * - * @return {ItemList} + * @return {ItemList} */ 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) { diff --git a/js/src/forum/components/CommentPost.js b/js/src/forum/components/CommentPost.js index 730a54969..8c334d93e 100644 --- a/js/src/forum/components/CommentPost.js +++ b/js/src/forum/components/CommentPost.js @@ -120,7 +120,7 @@ export default class CommentPost extends Post { /** * Build an item list for the post's header. * - * @return {ItemList} + * @return {ItemList} */ headerItems() { const items = new ItemList(); diff --git a/js/src/forum/components/Composer.js b/js/src/forum/components/Composer.js index 549263534..71a178976 100644 --- a/js/src/forum/components/Composer.js +++ b/js/src/forum/components/Composer.js @@ -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} */ 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; diff --git a/js/src/forum/components/ComposerBody.js b/js/src/forum/components/ComposerBody.js index 03e827864..58df9580e 100644 --- a/js/src/forum/components/ComposerBody.js +++ b/js/src/forum/components/ComposerBody.js @@ -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} */ headerItems() { return new ItemList(); diff --git a/js/src/forum/components/DiscussionComposer.js b/js/src/forum/components/DiscussionComposer.js index cbb6dafb1..d517be92a 100644 --- a/js/src/forum/components/DiscussionComposer.js +++ b/js/src/forum/components/DiscussionComposer.js @@ -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} */ data() { return { diff --git a/js/src/forum/components/DiscussionHero.js b/js/src/forum/components/DiscussionHero.js index 6af6fba4a..659cf1a71 100644 --- a/js/src/forum/components/DiscussionHero.js +++ b/js/src/forum/components/DiscussionHero.js @@ -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} */ items() { const items = new ItemList(); diff --git a/js/src/forum/components/DiscussionListItem.js b/js/src/forum/components/DiscussionListItem.js index 67d1cd950..337681b94 100644 --- a/js/src/forum/components/DiscussionListItem.js +++ b/js/src/forum/components/DiscussionListItem.js @@ -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} */ infoItems() { const items = new ItemList(); diff --git a/js/src/forum/components/DiscussionPage.tsx b/js/src/forum/components/DiscussionPage.tsx index 0e99a517c..b8dfeaa57 100644 --- a/js/src/forum/components/DiscussionPage.tsx +++ b/js/src/forum/components/DiscussionPage.tsx @@ -70,7 +70,7 @@ export default class DiscussionPage { + const items = new ItemList(); items.add('spinner', , 100); @@ -101,10 +99,8 @@ export default class DiscussionPage
    {listItems(this.sidebarItems().toArray())}
@@ -114,20 +110,16 @@ export default class DiscussionPage; } /** * List of items rendered as the main page content. - * - * @returns {ItemList} */ - pageContent() { - const items = new ItemList(); + pageContent(): ItemList { + const items = new ItemList(); items.add('hero', this.hero(), 100); items.add('main',
{this.mainContent().toArray()}
, 10); @@ -137,11 +129,9 @@ export default class DiscussionPage { + const items = new ItemList(); items.add('sidebar', this.sidebar(), 100); @@ -163,7 +153,7 @@ export default class DiscussionPage(); if (preloadedDiscussion) { // We must wrap this in a setTimeout because if we are mounting this @@ -183,10 +173,8 @@ export default class DiscussionPage { return { bySlug: true, page: { near: this.near }, @@ -196,7 +184,7 @@ export default class DiscussionPage) { + show(discussion: ApiResponseSingle): void { app.history.push('discussion', discussion.title()); app.setTitle(discussion.title()); app.setTitleCount(0); @@ -242,21 +230,23 @@ export default class DiscussionPage(); + const items = new ItemList(); - 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', diff --git a/js/src/forum/components/EditPostComposer.js b/js/src/forum/components/EditPostComposer.js index 49e84a182..eac95a368 100644 --- a/js/src/forum/components/EditPostComposer.js +++ b/js/src/forum/components/EditPostComposer.js @@ -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} */ data() { return { diff --git a/js/src/forum/components/EventPost.js b/js/src/forum/components/EventPost.js index 57335ef38..e39984c7b 100644 --- a/js/src/forum/components/EventPost.js +++ b/js/src/forum/components/EventPost.js @@ -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} 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} */ descriptionData() { return {}; diff --git a/js/src/forum/components/HeaderPrimary.js b/js/src/forum/components/HeaderPrimary.js index db6f41eec..55282fd22 100644 --- a/js/src/forum/components/HeaderPrimary.js +++ b/js/src/forum/components/HeaderPrimary.js @@ -14,7 +14,7 @@ export default class HeaderPrimary extends Component { /** * Build an item list for the controls. * - * @return {ItemList} + * @return {ItemList} */ items() { return new ItemList(); diff --git a/js/src/forum/components/IndexPage.js b/js/src/forum/components/IndexPage.js index 157242016..3d79eb5ed 100644 --- a/js/src/forum/components/IndexPage.js +++ b/js/src/forum/components/IndexPage.js @@ -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} */ 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} */ 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} */ 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} */ 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} */ 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')); diff --git a/js/src/forum/components/LogInButtons.js b/js/src/forum/components/LogInButtons.js index 60f4b4d17..692fd852c 100644 --- a/js/src/forum/components/LogInButtons.js +++ b/js/src/forum/components/LogInButtons.js @@ -12,8 +12,7 @@ export default class LogInButtons extends Component { /** * Build a list of LogInButton components. * - * @return {ItemList} - * @public + * @return {ItemList} */ items() { return new ItemList(); diff --git a/js/src/forum/components/Notification.js b/js/src/forum/components/Notification.js index 6adaac9e1..91ac05daa 100644 --- a/js/src/forum/components/Notification.js +++ b/js/src/forum/components/Notification.js @@ -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() {} diff --git a/js/src/forum/components/NotificationGrid.js b/js/src/forum/components/NotificationGrid.js index 38e044cb5..368829873 100644 --- a/js/src/forum/components/NotificationGrid.js +++ b/js/src/forum/components/NotificationGrid.js @@ -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} */ 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(); diff --git a/js/src/forum/components/Post.js b/js/src/forum/components/Post.js index b36b75c7f..93f6141b7 100644 --- a/js/src/forum/components/Post.js +++ b/js/src/forum/components/Post.js @@ -102,7 +102,7 @@ export default class Post extends Component { /** * Get attributes for the post element. * - * @return {Object} + * @return {Record} */ 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} */ 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} */ footerItems() { return new ItemList(); diff --git a/js/src/forum/components/PostMeta.js b/js/src/forum/components/PostMeta.js index 7e7430644..d85d84e33 100644 --- a/js/src/forum/components/PostMeta.js +++ b/js/src/forum/components/PostMeta.js @@ -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); diff --git a/js/src/forum/components/PostStream.js b/js/src/forum/components/PostStream.js index 769f2f995..c3ff521ce 100644 --- a/js/src/forum/components/PostStream.js +++ b/js/src/forum/components/PostStream.js @@ -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 diff --git a/js/src/forum/components/PostStreamScrubber.js b/js/src/forum/components/PostStreamScrubber.js index b6f8a3888..cbdfe6060 100644 --- a/js/src/forum/components/PostStreamScrubber.js +++ b/js/src/forum/components/PostStreamScrubber.js @@ -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 diff --git a/js/src/forum/components/PostsUserPage.js b/js/src/forum/components/PostsUserPage.js index 2433e911a..50fbaf6ce 100644 --- a/js/src/forum/components/PostsUserPage.js +++ b/js/src/forum/components/PostsUserPage.js @@ -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} * @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(); diff --git a/js/src/forum/components/ReplyComposer.js b/js/src/forum/components/ReplyComposer.js index 554064de0..dfe5795d4 100644 --- a/js/src/forum/components/ReplyComposer.js +++ b/js/src/forum/components/ReplyComposer.js @@ -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} */ data() { return { diff --git a/js/src/forum/components/SessionDropdown.js b/js/src/forum/components/SessionDropdown.js index a2fb658b3..456069cd2 100644 --- a/js/src/forum/components/SessionDropdown.js +++ b/js/src/forum/components/SessionDropdown.js @@ -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} */ items() { const items = new ItemList(); diff --git a/js/src/forum/components/SettingsPage.js b/js/src/forum/components/SettingsPage.js index 0140946d8..40f82e20b 100644 --- a/js/src/forum/components/SettingsPage.js +++ b/js/src/forum/components/SettingsPage.js @@ -33,7 +33,7 @@ export default class SettingsPage extends UserPage { /** * Build an item list for the user's settings controls. * - * @return {ItemList} + * @return {ItemList} */ 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} */ 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} */ 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} */ privacyItems() { const items = new ItemList(); diff --git a/js/src/forum/components/SignUpModal.tsx b/js/src/forum/components/SignUpModal.tsx index 02524afbf..779ac1bd6 100644 --- a/js/src/forum/components/SignUpModal.tsx +++ b/js/src/forum/components/SignUpModal.tsx @@ -145,8 +145,6 @@ export default class SignUpModal} */ infoItems() { const items = new ItemList(); diff --git a/js/src/forum/components/UserPage.js b/js/src/forum/components/UserPage.js index df5a8ebf1..3b4b1863d 100644 --- a/js/src/forum/components/UserPage.js +++ b/js/src/forum/components/UserPage.js @@ -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 (
@@ -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} */ 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} */ navItems() { const items = new ItemList(); diff --git a/js/src/forum/states/ComposerState.js b/js/src/forum/states/ComposerState.js index afa46f714..d2a0549b7 100644 --- a/js/src/forum/states/ComposerState.js +++ b/js/src/forum/states/ComposerState.js @@ -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 diff --git a/js/src/forum/states/PostStreamState.js b/js/src/forum/states/PostStreamState.js index f39330945..1cb7920cb 100644 --- a/js/src/forum/states/PostStreamState.js +++ b/js/src/forum/states/PostStreamState.js @@ -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} */ 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} */ 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} */ 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} */ goToIndex(index, noAnimation = false) { this.paused = true; @@ -151,7 +148,7 @@ class PostStreamState { * resolved immediately. * * @param {number} number - * @return {Promise} + * @return {Promise} */ 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} */ 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} */ 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(); diff --git a/js/src/forum/utils/DiscussionControls.js b/js/src/forum/utils/DiscussionControls.js index fa169f2a6..cb81fffbe 100644 --- a/js/src/forum/utils/DiscussionControls.js +++ b/js/src/forum/utils/DiscussionControls.js @@ -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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList} */ 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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList} * @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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList} * @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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList} * @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} */ replyAction(goToLast, forceRefresh) { return new Promise((resolve, reject) => { @@ -212,7 +210,7 @@ export default { /** * Hide a discussion. * - * @return {Promise} + * @return {Promise} */ hideAction() { this.pushAttributes({ hiddenAt: new Date(), hiddenUser: app.session.user }); @@ -223,7 +221,7 @@ export default { /** * Restore a discussion. * - * @return {Promise} + * @return {Promise} */ 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} */ 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, { diff --git a/js/src/forum/utils/Pane.js b/js/src/forum/utils/Pane.js index 34ab447d7..ecd7750a6 100644 --- a/js/src/forum/utils/Pane.js +++ b/js/src/forum/utils/Pane.js @@ -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; diff --git a/js/src/forum/utils/PostControls.js b/js/src/forum/utils/PostControls.js index 17eebc45b..bf00e1dce 100644 --- a/js/src/forum/utils/PostControls.js +++ b/js/src/forum/utils/PostControls.js @@ -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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList}')} */ 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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList}')} * @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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList}')} * @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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList}')} * @protected */ destructiveControls(post, context) { @@ -134,7 +133,7 @@ export default { /** * Open the composer to edit a post. * - * @return {Promise} + * @return {Promise} */ editAction() { return new Promise((resolve) => { @@ -148,7 +147,7 @@ export default { /** * Hide a post. * - * @return {Promise} + * @return {Promise} */ 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} */ restoreAction() { this.pushAttributes({ hiddenAt: null, hiddenUser: null }); @@ -171,7 +170,7 @@ export default { /** * Delete a post. * - * @return {Promise} + * @return {Promise} */ deleteAction(context) { if (!confirm(extractText(app.translator.trans('core.forum.post_controls.delete_confirmation')))) return; diff --git a/js/src/forum/utils/UserControls.js b/js/src/forum/utils/UserControls.js index 675a1291b..6d40fdc89 100644 --- a/js/src/forum/utils/UserControls.js +++ b/js/src/forum/utils/UserControls.js @@ -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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList} */ 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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList} * @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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList} * @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} context The parent component under which the controls menu will be displayed. + * + * @return {ItemList} * @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 }); diff --git a/js/src/forum/utils/alertEmailConfirmation.js b/js/src/forum/utils/alertEmailConfirmation.js index db5047e00..54257e343 100644 --- a/js/src/forum/utils/alertEmailConfirmation.js +++ b/js/src/forum/utils/alertEmailConfirmation.js @@ -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; diff --git a/js/src/forum/utils/slidable.js b/js/src/forum/utils/slidable.js index cb4ccc953..5a292fe12 100644 --- a/js/src/forum/utils/slidable.js +++ b/js/src/forum/utils/slidable.js @@ -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} [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)'); };