1
0
mirror of https://github.com/flarum/core.git synced 2025-08-06 08:27:42 +02:00

chore: handle deprecations from 1.x (#3909)

* chore: drop deprecated `post_number_index` column

* chore: remove deprecated `FlagsWillBeDeleted` event

* chore: `Migration::addSettings` can still be needed

* chore: `settings->get` default can still be needed

* chore: deprecated `$default` in `Settings::serializeToForum` extender

* chore: deprecated request `actor` attribute

* chore: already handled

* chore: remove `RecompileFrontendAssets::whenSettingsSaved`

* chore: remove `getReadIds`

* chore: `Model::dateAttribute` extender

* chore: `evented` js util

* chore: `WelcomeHero` js hidden prop

* chore: attributes pushData with relations

* chore: app request options `extract`

* chore: itemlist deprecations

* chore: `search` state

* chore: `getMentionText`

* chore: deprecated non-registered store type

* chore: `Button` title

* chore: `Modal` deprecations

* chore

* chore: deprecated `less` mixins

* Apply fixes from StyleCI

* fix

* fix: phpstan

* fix

* fix

---------

Co-authored-by: StyleCI Bot <bot@styleci.io>
This commit is contained in:
Sami Mazouz
2023-11-10 22:20:18 +01:00
committed by GitHub
parent d01c0e5210
commit e2281a2123
35 changed files with 48 additions and 633 deletions

View File

@@ -4,10 +4,9 @@ import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
export interface ILoadingModalAttrs extends IInternalModalAttrs {}
export default class LoadingModal<ModalAttrs extends ILoadingModalAttrs = ILoadingModalAttrs> extends Modal<ModalAttrs> {
/**
* @inheritdoc
*/
static readonly isDismissible: boolean = false;
protected static readonly isDismissibleViaCloseButton: boolean = false;
protected static readonly isDismissibleViaEscKey: boolean = false;
protected static readonly isDismissibleViaBackdropClick: boolean = false;
className() {
return 'LoadingModal Modal--small';

View File

@@ -45,13 +45,6 @@ export type FlarumGenericRoute = RouteItem<any, any, any>;
export interface FlarumRequestOptions<ResponseType> extends Omit<Mithril.RequestOptions<ResponseType>, 'extract'> {
errorHandler?: (error: RequestError) => void;
url: string;
// TODO: [Flarum 2.0] Remove deprecated option
/**
* Manipulate the response text before it is parsed into JSON.
*
* @deprecated Please use `modifyText` instead.
*/
extract?: (responseText: string) => string;
/**
* Manipulate the response text before it is parsed into JSON.
*
@@ -434,7 +427,7 @@ export default class Application {
}
protected transformRequestOptions<ResponseType>(flarumOptions: FlarumRequestOptions<ResponseType>): InternalFlarumRequestOptions<ResponseType> {
const { background, deserialize, extract, modifyText, ...tmpOptions } = { ...flarumOptions };
const { background, deserialize, modifyText, ...tmpOptions } = { ...flarumOptions };
// Unless specified otherwise, requests should run asynchronously in the
// background, so that they don't prevent redraws from occurring.
@@ -446,11 +439,6 @@ export default class Application {
const defaultDeserialize = (response: string) => response as ResponseType;
// When extracting the data from the response, we can check the server
// response code and show an error message to the user if something's gone
// awry.
const originalExtract = modifyText || extract;
const options: InternalFlarumRequestOptions<ResponseType> = {
background: background ?? defaultBackground,
deserialize: deserialize ?? defaultDeserialize,
@@ -474,11 +462,14 @@ export default class Application {
options.method = 'POST';
}
// When extracting the data from the response, we can check the server
// response code and show an error message to the user if something's gone
// awry.
options.extract = (xhr: XMLHttpRequest) => {
let responseText;
if (originalExtract) {
responseText = originalExtract(xhr.responseText);
if (modifyText) {
responseText = modifyText(xhr.responseText);
} else {
responseText = xhr.responseText;
}

View File

@@ -1,6 +1,5 @@
import app from '../common/app';
import { FlarumRequestOptions } from './Application';
import { fireDeprecationWarning } from './helpers/fireDebugWarning';
import Store, { ApiPayloadSingle, ApiResponseSingle, MetaInformation } from './Store';
export interface ModelIdentifier {
@@ -113,15 +112,11 @@ export default abstract class Model {
if ('attributes' in data) {
this.data.attributes ||= {};
// @deprecated
// Filter out relationships that got in by accident.
for (const key in data.attributes) {
const val = data.attributes[key];
if (val && val instanceof Model) {
fireDeprecationWarning('Providing models as attributes to `Model.pushData()` or `Model.pushAttributes()` is deprecated.', '3249');
delete data.attributes[key];
data.relationships ||= {};
data.relationships[key] = { data: Model.getIdentifier(val) };
}
}

View File

@@ -1,6 +1,5 @@
import app from '../common/app';
import { FlarumRequestOptions } from './Application';
import { fireDeprecationWarning } from './helpers/fireDebugWarning';
import Model, { ModelData, SavedModelData } from './Model';
export interface MetaInformation {
@@ -100,7 +99,7 @@ export default class Store {
pushPayload<M extends Model | Model[]>(payload: ApiPayload): ApiResponse<FlatArray<M, 1>> {
if (payload.included) payload.included.map(this.pushObject.bind(this));
const models = payload.data instanceof Array ? payload.data.map((o) => this.pushObject(o, false)) : this.pushObject(payload.data, false);
const models = payload.data instanceof Array ? payload.data.map((o) => this.pushObject(o)) : this.pushObject(payload.data);
const result = models as ApiResponse<FlatArray<M, 1>>;
// Attach the original payload to the model that we give back. This is
@@ -120,14 +119,11 @@ export default class Store {
* registered for this resource type.
*/
pushObject<M extends Model>(data: SavedModelData): M | null;
pushObject<M extends Model>(data: SavedModelData, allowUnregistered: false): M;
pushObject<M extends Model>(data: SavedModelData, allowUnregistered = true): M | null {
pushObject<M extends Model>(data: SavedModelData): M | null {
if (!this.models[data.type]) {
if (!allowUnregistered) {
setTimeout(() =>
fireDeprecationWarning(`Pushing object of type \`${data.type}\` not allowed, as type not yet registered in the store.`, '3206')
);
}
setTimeout(() => {
throw new Error(`Pushing object of type \`${data.type}\` not allowed, as type not yet registered in the store.`);
});
return null;
}

View File

@@ -27,18 +27,6 @@ export interface IButtonAttrs extends ComponentAttrs {
* Default: `false`
*/
loading?: boolean;
/**
* **DEPRECATED:** Please use the `aria-label` attribute instead. For tooltips, use
* the `<Tooltip>` component.
*
* Accessible text for the button. This should always be present if the button only
* contains an icon.
*
* The textual content of this attribute is passed to the DOM element as `aria-label`.
*
* @deprecated
*/
title?: string | Mithril.ChildArray;
/**
* Accessible text for the button. This should always be present if the button only
* contains an icon.
@@ -68,14 +56,11 @@ export interface IButtonAttrs extends ComponentAttrs {
*/
export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> extends Component<CustomAttrs> {
view(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
let { type, title, 'aria-label': ariaLabel, icon: iconName, disabled, loading, className, class: _class, ...attrs } = this.attrs;
let { type, 'aria-label': ariaLabel, icon: iconName, disabled, loading, className, class: _class, ...attrs } = this.attrs;
// If no `type` attr provided, set to "button"
type ||= 'button';
// Use `title` attribute as `aria-label` if none provided
ariaLabel ||= title;
// If given a translation object, extract the text.
if (typeof ariaLabel === 'object') {
ariaLabel = extractText(ariaLabel);

View File

@@ -17,10 +17,6 @@ export interface IInternalModalAttrs {
}
export interface IDismissibleOptions {
/**
* @deprecated Check specific individual attributes instead. Will be removed in Flarum 2.0.
*/
isDismissible: boolean;
viaCloseButton: boolean;
viaEscKey: boolean;
viaBackdropClick: boolean;
@@ -34,14 +30,6 @@ export default abstract class Modal<ModalAttrs extends IInternalModalAttrs = IIn
ModalAttrs,
CustomState
> {
// TODO: [Flarum 2.0] remove `isDismissible` static attribute
/**
* Determine whether or not the modal should be dismissible via an 'x' button.
*
* @deprecated Use the individual `isDismissibleVia...` attributes instead and remove references to this.
*/
static readonly isDismissible: boolean = true;
/**
* Can the model be dismissed with a close button (X)?
*
@@ -58,18 +46,7 @@ export default abstract class Modal<ModalAttrs extends IInternalModalAttrs = IIn
protected static readonly isDismissibleViaBackdropClick: boolean = true;
static get dismissibleOptions(): IDismissibleOptions {
// If someone sets this to `false`, provide the same behaviour as previous versions of Flarum.
if (!this.isDismissible) {
return {
isDismissible: false,
viaCloseButton: false,
viaEscKey: false,
viaBackdropClick: false,
};
}
return {
isDismissible: true,
viaCloseButton: this.isDismissibleViaCloseButton,
viaEscKey: this.isDismissibleViaEscKey,
viaBackdropClick: this.isDismissibleViaBackdropClick,
@@ -83,31 +60,6 @@ export default abstract class Modal<ModalAttrs extends IInternalModalAttrs = IIn
*/
alertAttrs: AlertAttrs | null = null;
oninit(vnode: Mithril.Vnode<ModalAttrs, this>) {
super.oninit(vnode);
// TODO: [Flarum 2.0] Remove the code below.
// This code prevents extensions which do not implement all abstract methods of this class from breaking
// the forum frontend. Without it, function calls would would error rather than returning `undefined.`
const missingMethods: string[] = [];
['className', 'title', 'content', 'onsubmit'].forEach((method) => {
if (!(this as any)[method]) {
(this as any)[method] = function (): void {};
missingMethods.push(method);
}
});
if (missingMethods.length > 0) {
fireDebugWarning(
`Modal \`${this.constructor.name}\` does not implement all abstract methods of the Modal super class. Missing methods: ${missingMethods.join(
', '
)}.`
);
}
}
oncreate(vnode: Mithril.VnodeDOM<ModalAttrs, this>) {
super.oncreate(vnode);

View File

@@ -26,24 +26,6 @@ export default class ItemList<T> {
*/
protected _items: Record<string, Item<T>> = {};
// TODO: [Flarum 2.0] Remove `.items` getter.
/**
* A **read-only copy** of items in the list.
*
* We don't allow adding new items to the ItemList via setting new properties,
* nor do we allow modifying existing items directly.
*
* @deprecated Use {@link ItemList.toObject} instead.
*/
get items(): DeepReadonly<Record<string, Item<T>>> {
return new Proxy(this._items, {
set() {
console.warn('Modifying `ItemList.items` is not allowed.');
return false;
},
});
}
/**
* Check whether the list is empty.
*/
@@ -86,44 +68,6 @@ export default class ItemList<T> {
return this;
}
// TODO: [Flarum 2.0] Remove deprecated `.replace()` method.
/**
* Replace an item and/or priority in the list, only if it is already present.
*
* If `content` or `priority` are `null`, these values will not be replaced.
*
* If the provided `key` is not present, nothing will happen.
*
* @deprecated Please use the {@link ItemList.setContent} and {@link ItemList.setPriority}
* methods to replace items and their priorities. This method will be removed in Flarum 2.0.
*
* @param key The key of the item in the list
* @param content The item's new content
* @param priority The item's new priority
*
* @example <caption>Replace priority and not content.</caption>
* items.replace('myItem', null, 10);
*
* @example <caption>Replace content and not priority.</caption>
* items.replace('myItem', <p>My new value.</p>);
*
* @example <caption>Replace content and priority.</caption>
* items.replace('myItem', <p>My new value.</p>, 10);
*/
replace(key: string, content: T | null = null, priority: number | null = null): this {
if (!this.has(key)) return this;
if (content !== null) {
this._items[key].content = content;
}
if (priority !== null) {
this._items[key].priority = priority;
}
return this;
}
/**
* Replaces an item's content, if the provided item key exists.
*
@@ -147,8 +91,11 @@ export default class ItemList<T> {
throw new Error(`[ItemList] Cannot set content of Item. Key \`${key}\` is not present.`);
}
// Saves on bundle size to call the deprecated method internally
return this.replace(key, content);
if (content !== null) {
this._items[key].content = content;
}
return this;
}
/**

View File

@@ -1,109 +0,0 @@
import { fireDeprecationWarning } from '../helpers/fireDebugWarning';
const deprecatedNotice = 'The `evented` util is deprecated and no longer supported.';
const deprecationIssueId = '2547';
/**
* The `evented` mixin provides methods allowing an object to trigger events,
* running externally registered event handlers.
*
* @deprecated v1.2, to be removed in v2.0
*/
export default {
/**
* Arrays of registered event handlers, grouped by the event name.
*
* @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 {Function[]}
* @protected
*
* @deprecated
*/
getHandlers(event) {
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
this.handlers = this.handlers || {};
this.handlers[event] = this.handlers[event] || [];
return this.handlers[event];
},
/**
* Trigger an event.
*
* @param {string} event The name of the event.
* @param {any[]} args Arguments to pass to event handlers.
*
* @deprecated
*/
trigger(event, ...args) {
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
this.getHandlers(event).forEach((handler) => handler.apply(this, args));
},
/**
* Register an event handler.
*
* @param {string} event The name of the event.
* @param {Function} handler The function to handle the event.
*
* @deprecated
*/
on(event, handler) {
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
this.getHandlers(event).push(handler);
},
/**
* 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.
*
* @deprecated
*/
one(event, handler) {
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
const wrapper = function () {
handler.apply(this, arguments);
this.off(event, wrapper);
};
this.getHandlers(event).push(wrapper);
},
/**
* Unregister an event handler.
*
* @param {string} event The name of the event.
* @param {Function} handler The function that handles the event.
*
* @deprecated
*/
off(event, handler) {
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
const handlers = this.getHandlers(event);
const index = handlers.indexOf(handler);
if (index !== -1) {
handlers.splice(index, 1);
}
},
};

View File

@@ -8,7 +8,6 @@ import KeyboardNavigatable from '../../common/utils/KeyboardNavigatable';
import SearchState from '../states/SearchState';
import DiscussionsSearchSource from './DiscussionsSearchSource';
import UsersSearchSource from './UsersSearchSource';
import { fireDeprecationWarning } from '../../common/helpers/fireDebugWarning';
import type Mithril from 'mithril';
import Icon from '../../common/components/Icon';
@@ -64,24 +63,6 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
*/
protected searchState!: SearchState;
/**
* The instance of `SearchState` for this component.
*
* @deprecated Replace with`this.searchState` instead.
*/
// TODO: [Flarum 2.0] Remove this.
// @ts-expect-error This is a get accessor, while superclass defines this as a property. This is needed to prevent breaking changes, however.
get state() {
fireDeprecationWarning('`state` property of the Search component is deprecated', '3212');
return this.searchState;
}
set state(state: SearchState) {
// Workaround to prevent triggering deprecation warnings due to Mithril
// setting state to undefined when creating components
state !== undefined && fireDeprecationWarning('`state` property of the Search component is deprecated', '3212');
this.searchState = state;
}
/**
* Whether or not the search input has focus.
*/

View File

@@ -13,11 +13,6 @@ const LOCAL_STORAGE_KEY = 'welcomeHidden';
* forum.
*/
export default class WelcomeHero extends Component<IWelcomeHeroAttrs> {
/**
* @deprecated Extend the `isHidden` method instead.
*/
hidden: boolean = false;
oninit(vnode: Mithril.Vnode<IWelcomeHeroAttrs, this>) {
super.oninit(vnode);
}
@@ -60,7 +55,6 @@ export default class WelcomeHero extends Component<IWelcomeHeroAttrs> {
isHidden(): boolean {
if (!app.forum.attribute<string>('welcomeTitle')?.trim()) return true;
if (localStorage.getItem(LOCAL_STORAGE_KEY)) return true;
if (this.hidden) return true;
return false;
}