Enable @typescript-eslint/explicit-function-return-type in /src (#9788)

* Enable `@typescript-eslint/explicit-member-accessibility` on /src

* Prettier

* Enable `@typescript-eslint/explicit-function-return-type` in /src

* Fix types

* tsc strict fixes

* Delint

* Fix test

* Fix bad merge
This commit is contained in:
Michael Telatynski 2023-01-12 13:25:14 +00:00 committed by GitHub
parent 7a36ba0fde
commit 030b7e90bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
683 changed files with 3459 additions and 3013 deletions

View file

@ -26,7 +26,7 @@ import { clamp } from "lodash";
* @param {number} amt the interpolant
* @returns
*/
export function lerp(start: number, end: number, amt: number) {
export function lerp(start: number, end: number, amt: number): number {
amt = clamp(amt, 0, 1);
return (1 - amt) * start + amt * end;
}

View file

@ -62,7 +62,7 @@ export default class DMRoomMap {
* Used by tests
* @param inst the new shared instance
*/
public static setShared(inst: DMRoomMap) {
public static setShared(inst: DMRoomMap): void {
DMRoomMap.sharedInstance = inst;
}
@ -75,16 +75,16 @@ export default class DMRoomMap {
return DMRoomMap.sharedInstance;
}
public start() {
public start(): void {
this.populateRoomToUser();
this.matrixClient.on(ClientEvent.AccountData, this.onAccountData);
}
public stop() {
public stop(): void {
this.matrixClient.removeListener(ClientEvent.AccountData, this.onAccountData);
}
private onAccountData = (ev: MatrixEvent) => {
private onAccountData = (ev: MatrixEvent): void => {
if (ev.getType() == EventType.Direct) {
this.mDirectEvent = { ...ev.getContent() }; // copy as we will mutate
this.userToRooms = null;
@ -97,7 +97,7 @@ export default class DMRoomMap {
* with ourself, not the other user. Fix it by guessing the other user and
* modifying userToRooms
*/
private patchUpSelfDMs(userToRooms: Record<string, string[]>) {
private patchUpSelfDMs(userToRooms: Record<string, string[]>): boolean {
const myUserId = this.matrixClient.getUserId();
const selfRoomIds = userToRooms[myUserId];
if (selfRoomIds) {
@ -215,7 +215,7 @@ export default class DMRoomMap {
return this.userToRooms;
}
private populateRoomToUser() {
private populateRoomToUser(): void {
this.roomToUser = {};
for (const user of Object.keys(this.getUserToRooms())) {
for (const roomId of this.userToRooms[user]) {

View file

@ -47,13 +47,13 @@ export class DialogOpener {
// We could do this in the constructor, but then we wouldn't have
// a function to call from Lifecycle to capture the class.
public prepare() {
public prepare(): void {
if (this.isRegistered) return;
defaultDispatcher.register(this.onDispatch);
this.isRegistered = true;
}
private onDispatch = (payload: ActionPayload) => {
private onDispatch = (payload: ActionPayload): void => {
switch (payload.action) {
case "open_room_settings":
Modal.createDialog(

View file

@ -30,7 +30,7 @@ export default class EditorStateTransfer {
public constructor(private readonly event: MatrixEvent) {}
public setEditorState(caret: DocumentOffset, serializedParts: SerializedPart[]) {
public setEditorState(caret: DocumentOffset, serializedParts: SerializedPart[]): void {
this.caret = caret;
this.serializedParts = serializedParts;
}

View file

@ -42,7 +42,7 @@ export function messageForResourceLimitError(
let errString = strings[limitType];
if (errString === undefined) errString = strings[""];
const linkSub = (sub) => {
const linkSub = (sub: string): ReactNode => {
if (adminContact) {
return (
<a href={adminContact} target="_blank" rel="noreferrer noopener">

View file

@ -88,7 +88,7 @@ export class FileDownloader {
return iframe;
}
public async download({ blob, name, autoDownload = true, opts = DEFAULT_STYLES }: DownloadOptions) {
public async download({ blob, name, autoDownload = true, opts = DEFAULT_STYLES }: DownloadOptions): Promise<void> {
const iframe = this.iframe; // get the iframe first just in case we need to await onload
if (this.onLoadPromise) await this.onLoadPromise;
iframe.contentWindow.postMessage(

View file

@ -43,7 +43,7 @@ export class FixedRollingArray<T> {
* Pushes a value to the array.
* @param value The value to push.
*/
public pushValue(value: T) {
public pushValue(value: T): void {
let swap = arrayFastClone(this.samples);
swap.splice(0, 0, value);
if (swap.length > this.width) {

View file

@ -35,14 +35,14 @@ export class MarkedExecution {
/**
* Resets the mark without calling the function.
*/
public reset() {
public reset(): void {
this.marked = false;
}
/**
* Marks the function to be called upon trigger().
*/
public mark() {
public mark(): void {
if (!this.marked) this.onMarkCallback?.();
this.marked = true;
}
@ -50,7 +50,7 @@ export class MarkedExecution {
/**
* If marked, the function will be called, otherwise this does nothing.
*/
public trigger() {
public trigger(): void {
if (!this.marked) return;
this.reset(); // reset first just in case the fn() causes a trigger()
this.fn();

View file

@ -54,14 +54,14 @@ export class MediaEventHelper implements IDestroyable {
);
}
public destroy() {
public destroy(): void {
if (this.media.isEncrypted) {
if (this.sourceUrl.present) URL.revokeObjectURL(this.sourceUrl.cachedValue);
if (this.thumbnailUrl.present) URL.revokeObjectURL(this.thumbnailUrl.cachedValue);
}
}
private prepareSourceUrl = async () => {
private prepareSourceUrl = async (): Promise<string> => {
if (this.media.isEncrypted) {
const blob = await this.sourceBlob.value;
return URL.createObjectURL(blob);
@ -70,7 +70,7 @@ export class MediaEventHelper implements IDestroyable {
}
};
private prepareThumbnailUrl = async () => {
private prepareThumbnailUrl = async (): Promise<string> => {
if (this.media.isEncrypted) {
const blob = await this.thumbnailBlob.value;
if (blob === null) return null;
@ -80,7 +80,7 @@ export class MediaEventHelper implements IDestroyable {
}
};
private fetchSource = () => {
private fetchSource = (): Promise<Blob> => {
if (this.media.isEncrypted) {
const content = this.event.getContent<IMediaEventContent>();
return decryptFile(content.file, content.info);
@ -88,7 +88,7 @@ export class MediaEventHelper implements IDestroyable {
return this.media.downloadSource().then((r) => r.blob());
};
private fetchThumbnail = () => {
private fetchThumbnail = (): Promise<Blob> => {
if (!this.media.hasThumbnail) return Promise.resolve(null);
if (this.media.isEncrypted) {

View file

@ -74,9 +74,16 @@ function wrapDeletion(child: Node): HTMLElement {
return wrapper;
}
function findRefNodes(root: Node, route: number[], isAddition = false) {
function findRefNodes(
root: Node,
route: number[],
isAddition = false,
): {
refNode: Node;
refParentNode?: Node;
} {
let refNode = root;
let refParentNode;
let refParentNode: Node | undefined;
const end = isAddition ? route.length - 1 : route.length;
for (let i = 0; i < end; ++i) {
refParentNode = refNode;
@ -85,8 +92,12 @@ function findRefNodes(root: Node, route: number[], isAddition = false) {
return { refNode, refParentNode };
}
function diffTreeToDOM(desc) {
if (desc.nodeName === "#text") {
function isTextNode(node: Text | HTMLElement): node is Text {
return node.nodeName === "#text";
}
function diffTreeToDOM(desc): Node {
if (isTextNode(desc)) {
return stringAsTextNode(desc.data);
} else {
const node = document.createElement(desc.nodeName);
@ -97,7 +108,7 @@ function diffTreeToDOM(desc) {
}
if (desc.childNodes) {
for (const childDesc of desc.childNodes) {
node.appendChild(diffTreeToDOM(childDesc));
node.appendChild(diffTreeToDOM(childDesc as Text | HTMLElement));
}
}
return node;
@ -152,25 +163,25 @@ function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatc
switch (diff.action) {
case "replaceElement": {
const container = document.createElement("span");
const delNode = wrapDeletion(diffTreeToDOM(diff.oldValue));
const insNode = wrapInsertion(diffTreeToDOM(diff.newValue));
const delNode = wrapDeletion(diffTreeToDOM(diff.oldValue as HTMLElement));
const insNode = wrapInsertion(diffTreeToDOM(diff.newValue as HTMLElement));
container.appendChild(delNode);
container.appendChild(insNode);
refNode.parentNode.replaceChild(container, refNode);
break;
}
case "removeTextElement": {
const delNode = wrapDeletion(stringAsTextNode(diff.value));
const delNode = wrapDeletion(stringAsTextNode(diff.value as string));
refNode.parentNode.replaceChild(delNode, refNode);
break;
}
case "removeElement": {
const delNode = wrapDeletion(diffTreeToDOM(diff.element));
const delNode = wrapDeletion(diffTreeToDOM(diff.element as HTMLElement));
refNode.parentNode.replaceChild(delNode, refNode);
break;
}
case "modifyTextElement": {
const textDiffs = diffMathPatch.diff_main(diff.oldValue, diff.newValue);
const textDiffs = diffMathPatch.diff_main(diff.oldValue as string, diff.newValue as string);
diffMathPatch.diff_cleanupSemantic(textDiffs);
const container = document.createElement("span");
for (const [modifier, text] of textDiffs) {
@ -186,7 +197,7 @@ function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatc
break;
}
case "addElement": {
const insNode = wrapInsertion(diffTreeToDOM(diff.element));
const insNode = wrapInsertion(diffTreeToDOM(diff.element as HTMLElement));
insertBefore(refParentNode, refNode, insNode);
break;
}
@ -194,7 +205,7 @@ function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatc
// XXX: sometimes diffDOM says insert a newline when there shouldn't be one
// but we must insert the node anyway so that we don't break the route child IDs.
// See https://github.com/fiduswriter/diffDOM/issues/100
const insNode = wrapInsertion(stringAsTextNode(diff.value !== "\n" ? diff.value : ""));
const insNode = wrapInsertion(stringAsTextNode(diff.value !== "\n" ? (diff.value as string) : ""));
insertBefore(refParentNode, refNode, insNode);
break;
}
@ -206,7 +217,7 @@ function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatc
const delNode = wrapDeletion(refNode.cloneNode(true));
const updatedNode = refNode.cloneNode(true) as HTMLElement;
if (diff.action === "addAttribute" || diff.action === "modifyAttribute") {
updatedNode.setAttribute(diff.name, diff.newValue);
updatedNode.setAttribute(diff.name, diff.newValue as string);
} else {
updatedNode.removeAttribute(diff.name);
}

View file

@ -68,7 +68,7 @@ export default class MultiInviter {
this.matrixClient = MatrixClientPeg.get();
}
public get fatal() {
public get fatal(): boolean {
return this._fatal;
}
@ -112,8 +112,8 @@ export default class MultiInviter {
return this.deferred.promise;
}
return this.deferred.promise.then(async (states) => {
const invitedUsers = [];
return this.deferred.promise.then(async (states): Promise<CompletionStates> => {
const invitedUsers: string[] = [];
for (const [addr, state] of Object.entries(states)) {
if (state === InviteState.Invited && getAddressType(addr) === AddressType.MatrixUserId) {
invitedUsers.push(addr);
@ -308,7 +308,7 @@ export default class MultiInviter {
);
if (unknownProfileUsers.length > 0) {
const inviteUnknowns = () => {
const inviteUnknowns = (): void => {
const promises = unknownProfileUsers.map((u) => this.doInvite(u, true));
Promise.all(promises).then(() => this.deferred.resolve(this.completionStates));
};

View file

@ -60,7 +60,7 @@ _td("Short keyboard patterns are easy to guess");
* @param {string} password Password to score
* @returns {object} Score result with `score` and `feedback` properties
*/
export function scorePassword(password: string) {
export function scorePassword(password: string): zxcvbn.ZXCVBNResult {
if (password.length === 0) return null;
const userInputs = ZXCVBN_USER_INPUTS.slice();

View file

@ -33,46 +33,46 @@ export default class ResizeNotifier extends EventEmitter {
// if there was another call in that timespan
private throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200);
public get isResizing() {
public get isResizing(): boolean {
return this._isResizing;
}
public startResizing() {
public startResizing(): void {
this._isResizing = true;
this.emit("isResizing", true);
}
public stopResizing() {
public stopResizing(): void {
this._isResizing = false;
this.emit("isResizing", false);
}
private noisyMiddlePanel() {
private noisyMiddlePanel(): void {
this.emit("middlePanelResizedNoisy");
}
private updateMiddlePanel() {
private updateMiddlePanel(): void {
this.throttledMiddlePanel();
this.noisyMiddlePanel();
}
// can be called in quick succession
public notifyLeftHandleResized() {
public notifyLeftHandleResized(): void {
// don't emit event for own region
this.updateMiddlePanel();
}
// can be called in quick succession
public notifyRightHandleResized() {
public notifyRightHandleResized(): void {
this.updateMiddlePanel();
}
public notifyTimelineHeightChanged() {
public notifyTimelineHeightChanged(): void {
this.updateMiddlePanel();
}
// can be called in quick succession
public notifyWindowResized() {
public notifyWindowResized(): void {
this.updateMiddlePanel();
}
}

View file

@ -43,7 +43,7 @@ export async function awaitRoomDownSync(cli: MatrixClient, roomId: string): Prom
// We have to wait for the js-sdk to give us the room back so
// we can more effectively abuse the MultiInviter behaviour
// which heavily relies on the Room object being available.
const checkForRoomFn = (room: Room) => {
const checkForRoomFn = (room: Room): void => {
if (room.roomId !== roomId) return;
resolve(room);
cli.off(ClientEvent.Room, checkForRoomFn);

View file

@ -64,14 +64,14 @@ export class Singleflight {
* Forgets all results for a given instance.
* @param {Object} instance The instance to forget about.
*/
public static forgetAllFor(instance: Object) {
public static forgetAllFor(instance: Object): void {
keyMap.delete(instance);
}
/**
* Forgets all cached results for all instances. Intended for use by tests.
*/
public static forgetAll() {
public static forgetAll(): void {
for (const k of keyMap.keys()) {
keyMap.remove(k);
}
@ -84,7 +84,7 @@ class SingleflightContext {
/**
* Forget this particular instance and key combination, discarding the result.
*/
public forget() {
public forget(): void {
const map = keyMap.get(this.instance);
if (!map) return;
map.remove(this.key);

View file

@ -29,7 +29,7 @@ export class SnakedObject<T = Record<string, any>> {
}
// Make JSON.stringify() pretend that everything is fine
public toJSON() {
public toJSON(): T {
return this.obj;
}
}

View file

@ -32,15 +32,15 @@ try {
const SYNC_STORE_NAME = "riot-web-sync";
const CRYPTO_STORE_NAME = "matrix-js-sdk:crypto";
function log(msg: string) {
function log(msg: string): void {
logger.log(`StorageManager: ${msg}`);
}
function error(msg: string, ...args: string[]) {
function error(msg: string, ...args: string[]): void {
logger.error(`StorageManager: ${msg}`, ...args);
}
export function tryPersistStorage() {
export function tryPersistStorage(): void {
if (navigator.storage && navigator.storage.persist) {
navigator.storage.persist().then((persistent) => {
logger.log("StorageManager: Persistent?", persistent);
@ -56,7 +56,12 @@ export function tryPersistStorage() {
}
}
export async function checkConsistency() {
export async function checkConsistency(): Promise<{
healthy: boolean;
cryptoInited: boolean;
dataInCryptoStore: boolean;
dataInLocalStorage: boolean;
}> {
log("Checking storage consistency");
log(`Local storage supported? ${!!localStorage}`);
log(`IndexedDB supported? ${!!indexedDB}`);
@ -121,7 +126,12 @@ export async function checkConsistency() {
};
}
async function checkSyncStore() {
interface StoreCheck {
exists: boolean;
healthy: boolean;
}
async function checkSyncStore(): Promise<StoreCheck> {
let exists = false;
try {
exists = await IndexedDBStore.exists(indexedDB, SYNC_STORE_NAME);
@ -134,7 +144,7 @@ async function checkSyncStore() {
return { exists, healthy: false };
}
async function checkCryptoStore() {
async function checkCryptoStore(): Promise<StoreCheck> {
let exists = false;
try {
exists = await IndexedDBCryptoStore.exists(indexedDB, CRYPTO_STORE_NAME);
@ -164,7 +174,7 @@ async function checkCryptoStore() {
*
* @param {boolean} cryptoInited True if crypto has been set up
*/
export function setCryptoInitialised(cryptoInited: boolean) {
export function setCryptoInitialised(cryptoInited: boolean): void {
localStorage.setItem("mx_crypto_initialised", String(cryptoInited));
}
@ -180,10 +190,10 @@ async function idbInit(): Promise<void> {
idb = await new Promise((resolve, reject) => {
const request = indexedDB.open("matrix-react-sdk", 1);
request.onerror = reject;
request.onsuccess = () => {
request.onsuccess = (): void => {
resolve(request.result);
};
request.onupgradeneeded = () => {
request.onupgradeneeded = (): void => {
const db = request.result;
db.createObjectStore("pickleKey");
db.createObjectStore("account");
@ -202,7 +212,7 @@ export async function idbLoad(table: string, key: string | string[]): Promise<an
const objectStore = txn.objectStore(table);
const request = objectStore.get(key);
request.onerror = reject;
request.onsuccess = (event) => {
request.onsuccess = (event): void => {
resolve(request.result);
};
});
@ -219,7 +229,7 @@ export async function idbSave(table: string, key: string | string[], data: any):
const objectStore = txn.objectStore(table);
const request = objectStore.put(data, key);
request.onerror = reject;
request.onsuccess = (event) => {
request.onsuccess = (event): void => {
resolve();
};
});
@ -236,7 +246,7 @@ export async function idbDelete(table: string, key: string | string[]): Promise<
const objectStore = txn.objectStore(table);
const request = objectStore.delete(key);
request.onerror = reject;
request.onsuccess = () => {
request.onsuccess = (): void => {
resolve();
};
});

View file

@ -36,7 +36,7 @@ export default class Timer {
this.setNotStarted();
}
private setNotStarted() {
private setNotStarted(): void {
this.timerHandle = null;
this.startTs = null;
this.promise = new Promise<void>((resolve, reject) => {
@ -47,7 +47,7 @@ export default class Timer {
});
}
private onTimeout = () => {
private onTimeout = (): void => {
const now = Date.now();
const elapsed = now - this.startTs;
if (elapsed >= this.timeout) {
@ -59,7 +59,7 @@ export default class Timer {
}
};
public changeTimeout(timeout: number) {
public changeTimeout(timeout: number): void {
if (timeout === this.timeout) {
return;
}
@ -75,7 +75,7 @@ export default class Timer {
* if not started before, starts the timer.
* @returns {Timer} the same timer
*/
public start() {
public start(): Timer {
if (!this.isRunning()) {
this.startTs = Date.now();
this.timerHandle = window.setTimeout(this.onTimeout, this.timeout);
@ -87,7 +87,7 @@ export default class Timer {
* (re)start the timer. If it's running, reset the timeout. If not, start it.
* @returns {Timer} the same timer
*/
public restart() {
public restart(): Timer {
if (this.isRunning()) {
// don't clearTimeout here as this method
// can be called in fast succession,
@ -105,7 +105,7 @@ export default class Timer {
* and reject the promise for this timer.
* @returns {Timer} the same timer
*/
public abort() {
public abort(): Timer {
if (this.isRunning()) {
clearTimeout(this.timerHandle);
this.reject(new Error("Timer was aborted."));
@ -119,11 +119,11 @@ export default class Timer {
*or is rejected when abort is called
*@return {Promise}
*/
public finished() {
public finished(): Promise<void> {
return this.promise;
}
public isRunning() {
public isRunning(): boolean {
return this.timerHandle !== null;
}
}

View file

@ -23,7 +23,7 @@ limitations under the License.
* @param {*} opts The options (properties) to set on the object.
* @returns {*} The created object.
*/
export function makeType(Type: any, opts: any) {
export function makeType<T>(Type: { new (): T }, opts: Partial<T>): T {
const c = new Type();
Object.assign(c, opts);
return c;

View file

@ -17,7 +17,7 @@ limitations under the License.
export class ValidatedServerConfig {
public hsUrl: string;
public hsName: string;
public hsNameIsDifferent: string;
public hsNameIsDifferent: boolean;
public isUrl: string;

View file

@ -68,7 +68,7 @@ export abstract class Whenable<T> implements IDestroyable {
* Notifies all the listeners of a given condition.
* @param condition The new condition that has been met.
*/
protected notifyCondition(condition: T) {
protected notifyCondition(condition: T): void {
const listeners = arrayFastClone(this.listeners); // clone just in case the handler modifies us
for (const listener of listeners) {
if (listener.condition === null || listener.condition === condition) {
@ -81,7 +81,7 @@ export abstract class Whenable<T> implements IDestroyable {
}
}
public destroy() {
public destroy(): void {
this.listeners = [];
}
}

View file

@ -143,7 +143,7 @@ export default class WidgetUtils {
return new Promise((resolve, reject) => {
// Tests an account data event, returning true if it's in the state
// we're waiting for it to be in
function eventInIntendedState(ev) {
function eventInIntendedState(ev): boolean {
if (!ev || !ev.getContent()) return false;
if (add) {
return ev.getContent()[widgetId] !== undefined;
@ -158,7 +158,7 @@ export default class WidgetUtils {
return;
}
function onAccountData(ev) {
function onAccountData(ev): void {
const currentAccountDataEvent = MatrixClientPeg.get().getAccountData("m.widgets");
if (eventInIntendedState(currentAccountDataEvent)) {
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, onAccountData);
@ -190,7 +190,7 @@ export default class WidgetUtils {
return new Promise((resolve, reject) => {
// Tests a list of state events, returning true if it's in the state
// we're waiting for it to be in
function eventsInIntendedState(evList) {
function eventsInIntendedState(evList: MatrixEvent[]): boolean {
const widgetPresent = evList.some((ev) => {
return ev.getContent() && ev.getContent()["id"] === widgetId;
});
@ -209,7 +209,7 @@ export default class WidgetUtils {
return;
}
function onRoomStateEvents(ev: MatrixEvent) {
function onRoomStateEvents(ev: MatrixEvent): void {
if (ev.getRoomId() !== roomId || ev.getType() !== "im.vector.modular.widgets") return;
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
@ -235,7 +235,7 @@ export default class WidgetUtils {
widgetUrl: string,
widgetName: string,
widgetData: IWidgetData,
) {
): Promise<void> {
const content = {
type: widgetType.preferred,
url: widgetUrl,
@ -288,10 +288,10 @@ export default class WidgetUtils {
widgetType?: WidgetType,
widgetUrl?: string,
widgetName?: string,
widgetData?: object,
widgetData?: IWidgetData,
widgetAvatarUrl?: string,
) {
let content;
): Promise<void> {
let content: Partial<IWidget> & { avatar_url?: string };
const addingWidget = Boolean(widgetUrl);
@ -309,10 +309,10 @@ export default class WidgetUtils {
content = {};
}
return WidgetUtils.setRoomWidgetContent(roomId, widgetId, content);
return WidgetUtils.setRoomWidgetContent(roomId, widgetId, content as IWidget);
}
public static setRoomWidgetContent(roomId: string, widgetId: string, content: IWidget) {
public static setRoomWidgetContent(roomId: string, widgetId: string, content: IWidget): Promise<void> {
const addingWidget = !!content.url;
WidgetEchoStore.setRoomWidgetEcho(roomId, widgetId, content);
@ -334,7 +334,7 @@ export default class WidgetUtils {
* @param {Room} room The room to get widgets force
* @return {[object]} Array containing current / active room widgets
*/
public static getRoomWidgets(room: Room) {
public static getRoomWidgets(room: Room): MatrixEvent[] {
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
const appsStateEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
if (!appsStateEvents) {
@ -500,7 +500,7 @@ export default class WidgetUtils {
return app as IApp;
}
public static getLocalJitsiWrapperUrl(opts: { forLocalRender?: boolean; auth?: string } = {}) {
public static getLocalJitsiWrapperUrl(opts: { forLocalRender?: boolean; auth?: string } = {}): string {
// NB. we can't just encodeURIComponent all of these because the $ signs need to be there
const queryStringParts = [
"conferenceDomain=$domain",
@ -557,7 +557,7 @@ export default class WidgetUtils {
.open(room, "type_" + app.type, app.id);
}
public static isManagedByManager(app) {
public static isManagedByManager(app: IApp): boolean {
if (WidgetUtils.isScalarUrl(app.url)) {
const managers = IntegrationManagers.sharedInstance();
if (managers.hasManager()) {

View file

@ -132,9 +132,9 @@ export const watchPosition = (
onWatchPositionError: (error: GeolocationError) => void,
): ClearWatchCallback => {
try {
const onError = (error) => onWatchPositionError(mapGeolocationError(error));
const onError = (error): void => onWatchPositionError(mapGeolocationError(error));
const watchId = getGeolocation().watchPosition(onWatchPosition, onError, GeolocationOptions);
const clearWatch = () => {
const clearWatch = (): void => {
getGeolocation().clearWatch(watchId);
};
return clearWatch;

View file

@ -69,7 +69,7 @@ export const useOwnLiveBeacons = (liveBeaconIds: BeaconIdentifier[]): LiveBeacon
.sort(sortBeaconsByLatestExpiry)
.shift();
const onStopSharing = async () => {
const onStopSharing = async (): Promise<void> => {
setStoppingInProgress(true);
try {
await Promise.all(liveBeaconIds.map((beaconId) => OwnBeaconStore.instance.stopBeacon(beaconId)));
@ -78,7 +78,7 @@ export const useOwnLiveBeacons = (liveBeaconIds: BeaconIdentifier[]): LiveBeacon
}
};
const onResetLocationPublishError = () => {
const onResetLocationPublishError = (): void => {
liveBeaconIds.forEach((beaconId) => {
OwnBeaconStore.instance.resetLocationPublishError(beaconId);
});

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { IEncryptedFile, MsgType } from "matrix-js-sdk/src/matrix";
import { IContent, IEncryptedFile, MsgType } from "matrix-js-sdk/src/matrix";
/**
* @param {string} mxc MXC URL of the file
@ -31,7 +31,7 @@ export const createVoiceMessageContent = (
size: number,
file?: IEncryptedFile,
waveform?: number[],
) => {
): IContent => {
return {
"body": "Voice message",
//"msgtype": "org.matrix.msc2516.voice",

View file

@ -19,7 +19,7 @@ import { logger } from "matrix-js-sdk/src/logger";
const SNOOZE_KEY = "mx_snooze_bulk_unverified_device_nag";
// one week
const snoozePeriod = 1000 * 60 * 60 * 24 * 7;
export const snoozeBulkUnverifiedDeviceReminder = () => {
export const snoozeBulkUnverifiedDeviceReminder = (): void => {
try {
localStorage.setItem(SNOOZE_KEY, String(Date.now()));
} catch (error) {
@ -27,7 +27,7 @@ export const snoozeBulkUnverifiedDeviceReminder = () => {
}
};
export const isBulkUnverifiedDeviceReminderSnoozed = () => {
export const isBulkUnverifiedDeviceReminderSnoozed = (): boolean => {
try {
const snoozedTimestamp = localStorage.getItem(SNOOZE_KEY);

View file

@ -117,7 +117,7 @@ export default abstract class Exporter {
this.cancelled = true;
}
protected downloadPlainText(fileName: string, text: string) {
protected downloadPlainText(fileName: string, text: string): void {
const content = new Blob([text], { type: "text/plain" });
saveAs(content, fileName);
}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { ReactNode } from "react";
import ReactDOM from "react-dom";
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
@ -65,7 +65,7 @@ export default class HTMLExporter extends Exporter {
this.threadsEnabled = SettingsStore.getValue("feature_threadenabled");
}
protected async getRoomAvatar() {
protected async getRoomAvatar(): Promise<ReactNode> {
let blob: Blob;
const avatarUrl = Avatar.avatarUrlForRoom(this.room, 32, 32, "crop");
const avatarPath = "room.png";
@ -92,7 +92,7 @@ export default class HTMLExporter extends Exporter {
return renderToStaticMarkup(avatar);
}
protected async wrapHTML(content: string) {
protected async wrapHTML(content: string): Promise<string> {
const roomAvatar = await this.getRoomAvatar();
const exportDate = formatFullDateNoDayNoTime(new Date());
const creator = this.room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
@ -224,7 +224,7 @@ export default class HTMLExporter extends Exporter {
);
}
protected async saveAvatarIfNeeded(event: MatrixEvent) {
protected async saveAvatarIfNeeded(event: MatrixEvent): Promise<void> {
const member = event.sender;
if (!this.avatars.has(member.userId)) {
try {
@ -239,7 +239,7 @@ export default class HTMLExporter extends Exporter {
}
}
protected getDateSeparator(event: MatrixEvent) {
protected async getDateSeparator(event: MatrixEvent): Promise<string> {
const ts = event.getTs();
const dateSeparator = (
<li key={ts}>
@ -249,12 +249,12 @@ export default class HTMLExporter extends Exporter {
return renderToStaticMarkup(dateSeparator);
}
protected needsDateSeparator(event: MatrixEvent, prevEvent: MatrixEvent) {
protected async needsDateSeparator(event: MatrixEvent, prevEvent: MatrixEvent): Promise<boolean> {
if (prevEvent == null) return true;
return wantsDateSeparator(prevEvent.getDate(), event.getDate());
}
public getEventTile(mxEv: MatrixEvent, continuation: boolean) {
public getEventTile(mxEv: MatrixEvent, continuation: boolean): JSX.Element {
return (
<div className="mx_Export_EventWrapper" id={mxEv.getId()}>
<MatrixClientContext.Provider value={this.client}>
@ -285,7 +285,7 @@ export default class HTMLExporter extends Exporter {
);
}
protected async getEventTileMarkup(mxEv: MatrixEvent, continuation: boolean, filePath?: string) {
protected async getEventTileMarkup(mxEv: MatrixEvent, continuation: boolean, filePath?: string): Promise<string> {
const hasAvatar = !!this.getAvatarURL(mxEv);
if (hasAvatar) await this.saveAvatarIfNeeded(mxEv);
const EventTile = this.getEventTile(mxEv, continuation);
@ -319,7 +319,7 @@ export default class HTMLExporter extends Exporter {
return eventTileMarkup;
}
protected createModifiedEvent(text: string, mxEv: MatrixEvent, italic = true) {
protected createModifiedEvent(text: string, mxEv: MatrixEvent, italic = true): MatrixEvent {
const modifiedContent = {
msgtype: "m.text",
body: `${text}`,
@ -338,7 +338,7 @@ export default class HTMLExporter extends Exporter {
return modifiedEvent;
}
protected async createMessageBody(mxEv: MatrixEvent, joined = false) {
protected async createMessageBody(mxEv: MatrixEvent, joined = false): Promise<string> {
let eventTile: string;
try {
if (this.isAttachment(mxEv)) {
@ -387,7 +387,7 @@ export default class HTMLExporter extends Exporter {
return eventTile;
}
protected async createHTML(events: MatrixEvent[], start: number) {
protected async createHTML(events: MatrixEvent[], start: number): Promise<string> {
let content = "";
let prevEvent = null;
for (let i = start; i < Math.min(start + 1000, events.length); i++) {
@ -415,7 +415,7 @@ export default class HTMLExporter extends Exporter {
return this.wrapHTML(content);
}
public async export() {
public async export(): Promise<void> {
this.updateProgress(_t("Starting export..."));
const fetchStart = performance.now();

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { IEvent, MatrixEvent } from "matrix-js-sdk/src/models/event";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { logger } from "matrix-js-sdk/src/logger";
@ -60,7 +60,7 @@ export default class JSONExporter extends Exporter {
return JSON.stringify(jsonObject, null, 2);
}
protected async getJSONString(mxEv: MatrixEvent) {
protected async getJSONString(mxEv: MatrixEvent): Promise<IEvent> {
if (this.exportOptions.attachmentsIncluded && this.isAttachment(mxEv)) {
try {
const blob = await this.getMediaBlob(mxEv);
@ -81,7 +81,7 @@ export default class JSONExporter extends Exporter {
return clearEvent;
}
protected async createOutput(events: MatrixEvent[]) {
protected async createOutput(events: MatrixEvent[]): Promise<string> {
for (let i = 0; i < events.length; i++) {
const event = events[i];
this.updateProgress(
@ -99,7 +99,7 @@ export default class JSONExporter extends Exporter {
return this.createJSONString();
}
public async export() {
public async export(): Promise<void> {
logger.info("Starting export process...");
logger.info("Fetching events...");

View file

@ -46,7 +46,7 @@ export default class PlainTextExporter extends Exporter {
return this.makeFileNameNoExtension() + ".txt";
}
public textForReplyEvent = (content: IContent) => {
public textForReplyEvent = (content: IContent): string => {
const REPLY_REGEX = /> <(.*?)>(.*?)\n\n(.*)/s;
const REPLY_SOURCE_MAX_LENGTH = 32;
@ -79,7 +79,7 @@ export default class PlainTextExporter extends Exporter {
return `<${rplName}${rplSource}> ${rplText}`;
};
protected plainTextForEvent = async (mxEv: MatrixEvent) => {
protected plainTextForEvent = async (mxEv: MatrixEvent): Promise<string> => {
const senderDisplayName = mxEv.sender && mxEv.sender.name ? mxEv.sender.name : mxEv.getSender();
let mediaText = "";
if (this.isAttachment(mxEv)) {
@ -107,7 +107,7 @@ export default class PlainTextExporter extends Exporter {
else return textForEvent(mxEv) + mediaText;
};
protected async createOutput(events: MatrixEvent[]) {
protected async createOutput(events: MatrixEvent[]): Promise<string> {
let content = "";
for (let i = 0; i < events.length; i++) {
const event = events[i];
@ -127,7 +127,7 @@ export default class PlainTextExporter extends Exporter {
return content;
}
public async export() {
public async export(): Promise<void> {
this.updateProgress(_t("Starting export process..."));
this.updateProgress(_t("Fetching events..."));

View file

@ -36,7 +36,7 @@ import { AfterLeaveRoomPayload } from "../dispatcher/payloads/AfterLeaveRoomPayl
import { bulkSpaceBehaviour } from "./space";
import { SdkContextClass } from "../contexts/SDKContext";
export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = true) {
export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = true): Promise<void> {
let spinnerModal: IHandle<any>;
if (spinner) {
spinnerModal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner");
@ -65,7 +65,7 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
.map(
(ev) =>
new Promise<void>((resolve, reject) => {
const handler = () => {
const handler = (): void => {
if (ev.status === EventStatus.NOT_SENT) {
spinnerModal?.close();
reject(ev.error);
@ -165,12 +165,12 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
}
}
export const leaveSpace = (space: Room) => {
export const leaveSpace = (space: Room): void => {
Modal.createDialog(
LeaveSpaceDialog,
{
space,
onFinished: async (leave: boolean, rooms: Room[]) => {
onFinished: async (leave: boolean, rooms: Room[]): Promise<void> => {
if (!leave) return;
await bulkSpaceBehaviour(space, rooms, (room) => leaveRoomBehaviour(room.roomId));

View file

@ -86,7 +86,7 @@ export async function waitForRoomReadyAndApplyAfterCreateCallbacks(
}
return new Promise((resolve) => {
const finish = () => {
const finish = (): void => {
if (checkRoomStateIntervalHandle) clearInterval(checkRoomStateIntervalHandle);
if (stopgapTimeoutHandle) clearTimeout(stopgapTimeoutHandle);
@ -97,7 +97,7 @@ export async function waitForRoomReadyAndApplyAfterCreateCallbacks(
});
};
const stopgapFinish = () => {
const stopgapFinish = (): void => {
logger.warn(`Assuming local room ${localRoom.roomId} is ready after hitting timeout`);
finish();
};

View file

@ -17,7 +17,8 @@ limitations under the License.
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { RoomState, RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
/**
* Approximation of a membership status for a given room.
@ -84,10 +85,15 @@ export function isJoinedOrNearlyJoined(membership: string): boolean {
* NOTE: this assumes you've just created the room and there's not been an opportunity
* for other code to run, so we shouldn't miss RoomState.newMember when it comes by.
*/
export async function waitForMember(client: MatrixClient, roomId: string, userId: string, opts = { timeout: 1500 }) {
export async function waitForMember(
client: MatrixClient,
roomId: string,
userId: string,
opts = { timeout: 1500 },
): Promise<boolean> {
const { timeout } = opts;
let handler;
return new Promise((resolve) => {
let handler: (event: MatrixEvent, state: RoomState, member: RoomMember) => void;
return new Promise<boolean>((resolve) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
handler = function (_, __, member: RoomMember) {
if (member.userId !== userId) return;

View file

@ -56,7 +56,7 @@ export default class ElementPermalinkConstructor extends PermalinkConstructor {
return testHost === (parsedUrl.host || parsedUrl.hostname); // one of the hosts should match
}
public encodeServerCandidates(candidates?: string[]) {
public encodeServerCandidates(candidates?: string[]): string {
if (!candidates || candidates.length === 0) return "";
return `?via=${candidates.map((c) => encodeURIComponent(c)).join("&via=")}`;
}

View file

@ -62,7 +62,7 @@ export default class MatrixSchemePermalinkConstructor extends PermalinkConstruct
return testHost === "";
}
public encodeServerCandidates(candidates: string[]) {
public encodeServerCandidates(candidates: string[]): string {
if (!candidates || candidates.length === 0) return "";
return `?via=${candidates.map((c) => encodeURIComponent(c)).join("&via=")}`;
}

View file

@ -47,7 +47,7 @@ export default class MatrixToPermalinkConstructor extends PermalinkConstructor {
return testHost === host;
}
public encodeServerCandidates(candidates: string[]) {
public encodeServerCandidates(candidates: string[]): string {
if (!candidates || candidates.length === 0) return "";
return `?via=${candidates.map((c) => encodeURIComponent(c)).join("&via=")}`;
}

View file

@ -109,7 +109,7 @@ export class RoomPermalinkCreator {
}
}
public load() {
public load(): void {
if (!this.room || !this.room.currentState) {
// Under rare and unknown circumstances it is possible to have a room with no
// currentState, at least potentially at the early stages of joining a room.
@ -121,22 +121,22 @@ export class RoomPermalinkCreator {
this.fullUpdate();
}
public start() {
public start(): void {
this.load();
this.room.currentState.on(RoomStateEvent.Update, this.onRoomStateUpdate);
this.started = true;
}
public stop() {
public stop(): void {
this.room.currentState.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate);
this.started = false;
}
public get serverCandidates() {
public get serverCandidates(): string[] {
return this._serverCandidates;
}
public isStarted() {
public isStarted(): boolean {
return this.started;
}
@ -159,11 +159,11 @@ export class RoomPermalinkCreator {
return getPermalinkConstructor().forRoom(this.roomId, this._serverCandidates);
}
private onRoomStateUpdate = () => {
private onRoomStateUpdate = (): void => {
this.fullUpdate();
};
private fullUpdate() {
private fullUpdate(): void {
// This updates the internal state of this object from the room state. It's broken
// down into separate functions, previously because we did some of these as incremental
// updates, but they were on member events which can be very numerous, so the incremental
@ -175,7 +175,7 @@ export class RoomPermalinkCreator {
this.updateServerCandidates();
}
private updateHighestPlUser() {
private updateHighestPlUser(): void {
const plEvent = this.room.currentState.getStateEvents("m.room.power_levels", "");
if (plEvent) {
const content = plEvent.getContent();
@ -215,13 +215,14 @@ export class RoomPermalinkCreator {
this.highestPlUserId = null;
}
private updateAllowedServers() {
private updateAllowedServers(): void {
const bannedHostsRegexps = [];
let allowedHostsRegexps = [ANY_REGEX]; // default allow everyone
if (this.room.currentState) {
const aclEvent = this.room.currentState.getStateEvents(EventType.RoomServerAcl, "");
if (aclEvent && aclEvent.getContent()) {
const getRegex = (hostname) => new RegExp("^" + utils.globToRegexp(hostname, false) + "$");
const getRegex = (hostname: string): RegExp =>
new RegExp("^" + utils.globToRegexp(hostname, false) + "$");
const denied = aclEvent.getContent().deny || [];
denied.forEach((h) => bannedHostsRegexps.push(getRegex(h)));
@ -235,7 +236,7 @@ export class RoomPermalinkCreator {
this.allowedHostsRegexps = allowedHostsRegexps;
}
private updatePopulationMap() {
private updatePopulationMap(): void {
const populationMap: { [server: string]: number } = {};
for (const member of this.room.getJoinedMembers()) {
const serverName = getServerName(member.userId);
@ -247,7 +248,7 @@ export class RoomPermalinkCreator {
this.populationMap = populationMap;
}
private updateServerCandidates = () => {
private updateServerCandidates = (): void => {
const candidates = new Set<string>();
if (this.highestPlUserId) {
candidates.add(getServerName(this.highestPlUserId));
@ -474,7 +475,7 @@ function isHostnameIpAddress(hostname: string): boolean {
return isIp(hostname);
}
export const calculateRoomVia = (room: Room) => {
export const calculateRoomVia = (room: Room): string[] => {
const permalinkCreator = new RoomPermalinkCreator(room);
permalinkCreator.load();
return permalinkCreator.serverCandidates;

View file

@ -37,7 +37,7 @@ import { parsePermalink } from "./permalinks/Permalinks";
* React components which have been mounted as part of this.
* The initial caller should pass in an empty array to seed the accumulator.
*/
export function pillifyLinks(nodes: ArrayLike<Element>, mxEvent: MatrixEvent, pills: Element[]) {
export function pillifyLinks(nodes: ArrayLike<Element>, mxEvent: MatrixEvent, pills: Element[]): void {
const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId());
const shouldShowPillAvatar = SettingsStore.getValue("Pill.shouldShowPillAvatar");
let node = nodes[0];
@ -148,7 +148,7 @@ export function pillifyLinks(nodes: ArrayLike<Element>, mxEvent: MatrixEvent, pi
* @param {Element[]} pills - array of pill containers whose React
* components should be unmounted.
*/
export function unmountPills(pills: Element[]) {
export function unmountPills(pills: Element[]): void {
for (const pillContainer of pills) {
ReactDOM.unmountComponentAtNode(pillContainer);
}

View file

@ -17,7 +17,7 @@ limitations under the License.
import { MatrixClientPeg } from "../MatrixClientPeg";
import SdkConfig from "../SdkConfig";
export function isPresenceEnabled() {
export function isPresenceEnabled(): boolean {
const hsUrl = MatrixClientPeg.get().baseUrl;
const urls = SdkConfig.get("enable_presence_by_hs_url");
if (!urls) return true;

View file

@ -18,6 +18,7 @@ import React from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomType, EventType } from "matrix-js-sdk/src/@types/event";
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
import { ICreateRoomStateEvent } from "matrix-js-sdk/src/matrix";
import { calculateRoomVia } from "./permalinks/Permalinks";
import Modal from "../Modal";
@ -39,7 +40,7 @@ import { OpenSpaceSettingsPayload } from "../dispatcher/payloads/OpenSpaceSettin
import { OpenAddExistingToSpaceDialogPayload } from "../dispatcher/payloads/OpenAddExistingToSpaceDialogPayload";
import { SdkContextClass } from "../contexts/SDKContext";
export const shouldShowSpaceSettings = (space: Room) => {
export const shouldShowSpaceSettings = (space: Room): boolean => {
const userId = space.client.getUserId();
return (
space.getMyMembership() === "join" &&
@ -50,7 +51,7 @@ export const shouldShowSpaceSettings = (space: Room) => {
);
};
export const makeSpaceParentEvent = (room: Room, canonical = false) => ({
export const makeSpaceParentEvent = (room: Room, canonical = false): ICreateRoomStateEvent => ({
type: EventType.SpaceParent,
content: {
via: calculateRoomVia(room),
@ -59,7 +60,7 @@ export const makeSpaceParentEvent = (room: Room, canonical = false) => ({
state_key: room.roomId,
});
export function showSpaceSettings(space: Room) {
export function showSpaceSettings(space: Room): void {
defaultDispatcher.dispatch<OpenSpaceSettingsPayload>({
action: Action.OpenSpaceSettings,
space,
@ -86,7 +87,7 @@ export const showCreateNewRoom = async (space: Room, type?: RoomType): Promise<b
return shouldCreate;
};
export const shouldShowSpaceInvite = (space: Room) =>
export const shouldShowSpaceInvite = (space: Room): boolean =>
((space?.getMyMembership() === "join" && space.canInvite(space.client.getUserId())) ||
space.getJoinRule() === JoinRule.Public) &&
shouldShowComponent(UIComponent.InviteUsers);
@ -159,7 +160,7 @@ export const bulkSpaceBehaviour = async (
}
};
export const showSpacePreferences = (space: Room, initialTabId?: SpacePreferenceTab) => {
export const showSpacePreferences = (space: Room, initialTabId?: SpacePreferenceTab): void => {
defaultDispatcher.dispatch<OpenSpacePreferencesPayload>({
action: Action.OpenSpacePreferences,
space,

View file

@ -55,7 +55,7 @@ export async function copyPlaintext(text: string): Promise<boolean> {
return false;
}
export function selectText(target: Element) {
export function selectText(target: Element): void {
const range = document.createRange();
range.selectNodeContents(target);

View file

@ -31,7 +31,7 @@ import LinkWithTooltip from "../components/views/elements/LinkWithTooltip";
* React components that have been mounted by this function. The initial caller
* should pass in an empty array to seed the accumulator.
*/
export function tooltipifyLinks(rootNodes: ArrayLike<Element>, ignoredNodes: Element[], containers: Element[]) {
export function tooltipifyLinks(rootNodes: ArrayLike<Element>, ignoredNodes: Element[], containers: Element[]): void {
if (!PlatformPeg.get()?.needsUrlTooltips()) {
return;
}
@ -83,7 +83,7 @@ export function tooltipifyLinks(rootNodes: ArrayLike<Element>, ignoredNodes: Ele
*
* @param {Element[]} containers - array of tooltip containers to unmount
*/
export function unmountTooltips(containers: Element[]) {
export function unmountTooltips(containers: Element[]): void {
for (const container of containers) {
ReactDOM.unmountComponentAtNode(container);
}

View file

@ -26,8 +26,8 @@ interface TooltipEvents {
export function useTooltip(props: ComponentProps<typeof Tooltip>): [TooltipEvents, JSX.Element | null] {
const [isVisible, setIsVisible] = useState(false);
const showTooltip = () => setIsVisible(true);
const hideTooltip = () => setIsVisible(false);
const showTooltip = (): void => setIsVisible(true);
const hideTooltip = (): void => setIsVisible(false);
// No need to fill up the DOM with hidden tooltip elements. Only add the
// tooltip when we're hovering over the item (performance)

View file

@ -19,6 +19,8 @@ limitations under the License.
* - a number
* - in a provided range (inclusive)
*/
export const validateNumberInRange = (min: number, max: number) => (value?: number) => {
return typeof value === "number" && !(isNaN(value) || min > value || value > max);
};
export const validateNumberInRange =
(min: number, max: number) =>
(value?: number): boolean => {
return typeof value === "number" && !(isNaN(value) || min > value || value > max);
};

View file

@ -17,5 +17,5 @@ limitations under the License.
import type { Room } from "matrix-js-sdk/src/models/room";
import SettingsStore from "../settings/SettingsStore";
export const isVideoRoom = (room: Room) =>
export const isVideoRoom = (room: Room): boolean =>
room.isElementVideoRoom() || (SettingsStore.getValue("feature_element_call_video_rooms") && room.isCallRoom());