From a537204fc0e35187bc2143d56134a8ff96e6bdd2 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Wed, 6 Oct 2021 14:59:34 -0700 Subject: [PATCH] Remove ts/services/timers.ts --- js/modules/signal.js | 3 -- preload.js | 5 --- ts/ConversationController.ts | 24 ++--------- ts/components/Toast.tsx | 5 +-- ts/models/conversations.ts | 48 +++++++++++----------- ts/services/networkObserver.ts | 8 ++-- ts/services/timers.ts | 73 ---------------------------------- ts/state/ducks/updates.ts | 8 ++-- ts/util/postLinkExperience.ts | 6 +-- ts/window.d.ts | 2 - 10 files changed, 38 insertions(+), 144 deletions(-) delete mode 100644 ts/services/timers.ts diff --git a/js/modules/signal.js b/js/modules/signal.js index dc947d472..4c34b5be9 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -147,7 +147,6 @@ const { initializeUpdateListener, } = require('../../ts/services/updateListener'); const { calling } = require('../../ts/services/calling'); -const { onTimeout, removeTimeout } = require('../../ts/services/timers'); const { enableStorageService, eraseAllStorageServiceState, @@ -388,8 +387,6 @@ exports.setup = (options = {}) => { initializeGroupCredentialFetcher, initializeNetworkObserver, initializeUpdateListener, - onTimeout, - removeTimeout, runStorageServiceSyncJob, storageServiceUploadJob, }; diff --git a/preload.js b/preload.js index 799a4c847..3a676f308 100644 --- a/preload.js +++ b/preload.js @@ -383,11 +383,6 @@ try { version: config.version, }); - // Linux seems to periodically let the event loop stop, so this is a global workaround - setInterval(() => { - window.nodeSetImmediate(() => {}); - }, 1000); - const { imageToBlurHash } = require('./ts/util/imageToBlurHash'); const { isValidGuid } = require('./ts/util/isValidGuid'); const { ActiveWindowService } = require('./ts/services/ActiveWindowService'); diff --git a/ts/ConversationController.ts b/ts/ConversationController.ts index fa3329214..549e0f8b5 100644 --- a/ts/ConversationController.ts +++ b/ts/ConversationController.ts @@ -54,7 +54,10 @@ export function start(): void { this.on('add remove change:unreadCount', debouncedUpdateUnreadCount); window.Whisper.events.on('updateUnreadCount', debouncedUpdateUnreadCount); this.on('add', (model: ConversationModel): void => { - this.initMuteExpirationTimer(model); + // If the conversation is muted we set a timeout so when the mute expires + // we can reset the mute state on the model. If the mute has already expired + // then we reset the state right away. + model.startMuteTimer(); }); }, addActive(model: ConversationModel) { @@ -64,25 +67,6 @@ export function start(): void { this.remove(model); } }, - // If the conversation is muted we set a timeout so when the mute expires - // we can reset the mute state on the model. If the mute has already expired - // then we reset the state right away. - initMuteExpirationTimer(model: ConversationModel): void { - const muteExpiresAt = model.get('muteExpiresAt'); - // This check for `muteExpiresAt` is likely redundant, but is needed to appease - // TypeScript. - if (model.isMuted() && muteExpiresAt) { - window.Signal.Services.onTimeout( - muteExpiresAt, - () => { - model.set({ muteExpiresAt: undefined }); - }, - model.getMuteTimeoutId() - ); - } else if (muteExpiresAt) { - model.set({ muteExpiresAt: undefined }); - } - }, updateUnreadCount() { const canCountMutedConversations = window.storage.get( 'badge-count-muted-conversations' diff --git a/ts/components/Toast.tsx b/ts/components/Toast.tsx index 44f5859ed..3d78b013d 100644 --- a/ts/components/Toast.tsx +++ b/ts/components/Toast.tsx @@ -4,7 +4,6 @@ import React, { KeyboardEvent, MouseEvent, ReactNode, useEffect } from 'react'; import classNames from 'classnames'; import { createPortal } from 'react-dom'; -import { onTimeout, removeTimeout } from '../services/timers'; import { useRestoreFocus } from '../hooks/useRestoreFocus'; export type PropsType = { @@ -48,11 +47,11 @@ export const Toast = ({ return; } - const timeoutId = onTimeout(Date.now() + timeout, onClose); + const timeoutId = setTimeout(onClose, timeout); return () => { if (timeoutId) { - removeTimeout(timeoutId); + clearTimeout(timeoutId); } }; }, [autoDismissDisabled, onClose, root, timeout]); diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index c4c18743e..c3224bacc 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -3,7 +3,7 @@ /* eslint-disable class-methods-use-this */ /* eslint-disable camelcase */ -import { compact } from 'lodash'; +import { compact, isNumber } from 'lodash'; import { ConversationAttributesType, ConversationModelCollectionType, @@ -190,6 +190,8 @@ export class ConversationModel extends window.Backbone private lastIsTyping?: boolean; + private muteTimer?: NodeJS.Timer; + // eslint-disable-next-line class-methods-use-this defaults(): Partial { return { @@ -1593,7 +1595,7 @@ export class ConversationModel extends window.Backbone } const temporaryMemberCount = this.get('temporaryMemberCount'); - if (window._.isNumber(temporaryMemberCount)) { + if (isNumber(temporaryMemberCount)) { return temporaryMemberCount; } @@ -4826,6 +4828,24 @@ export class ConversationModel extends window.Backbone }); } + startMuteTimer(): void { + if (this.muteTimer !== undefined) { + clearTimeout(this.muteTimer); + this.muteTimer = undefined; + } + + const muteExpiresAt = this.get('muteExpiresAt'); + if (isNumber(muteExpiresAt) && muteExpiresAt < Number.MAX_SAFE_INTEGER) { + const delay = muteExpiresAt - Date.now(); + if (delay <= 0) { + this.setMuteExpiration(0); + return; + } + + this.muteTimer = setTimeout(() => this.setMuteExpiration(0), delay); + } + } + setMuteExpiration( muteExpiresAt = 0, { viaStorageServiceSync = false } = {} @@ -4836,26 +4856,8 @@ export class ConversationModel extends window.Backbone return; } - // we use a timeoutId here so that we can reference the mute that was - // potentially set in the ConversationController. Specifically for a - // scenario where a conversation is already muted and we boot up the app, - // a timeout will be already set. But if we change the mute to a later - // date a new timeout would need to be set and the old one cleared. With - // this ID we can reference the existing timeout. - const timeoutId = this.getMuteTimeoutId(); - window.Signal.Services.removeTimeout(timeoutId); - - if (muteExpiresAt && muteExpiresAt < Number.MAX_SAFE_INTEGER) { - window.Signal.Services.onTimeout( - muteExpiresAt, - () => { - this.setMuteExpiration(0); - }, - timeoutId - ); - } - this.set({ muteExpiresAt }); + this.startMuteTimer(); if (!viaStorageServiceSync) { this.captureChange('mutedUntilTimestamp'); } @@ -4866,10 +4868,6 @@ export class ConversationModel extends window.Backbone return isMuted(this.get('muteExpiresAt')); } - getMuteTimeoutId(): string { - return `mute(${this.get('id')})`; - } - async notify( message: Readonly, reaction?: WhatIsThis diff --git a/ts/services/networkObserver.ts b/ts/services/networkObserver.ts index f931ceb9a..b6023df19 100644 --- a/ts/services/networkObserver.ts +++ b/ts/services/networkObserver.ts @@ -7,18 +7,17 @@ import { } from '../state/ducks/network'; import { getSocketStatus } from '../shims/socketStatus'; import * as log from '../logging/log'; +import { SECOND } from '../util/durations'; type NetworkActions = { checkNetworkStatus: (x: CheckNetworkStatusPayloadType) => NetworkActionType; closeConnectingGracePeriod: () => NetworkActionType; }; -const REFRESH_INTERVAL = 5000; - export function initializeNetworkObserver( networkActions: NetworkActions ): void { - log.info(`Initializing network observer every ${REFRESH_INTERVAL}ms`); + log.info('Initializing network observer'); const refresh = () => { networkActions.checkNetworkStatus({ @@ -31,8 +30,7 @@ export function initializeNetworkObserver( window.addEventListener('online', refresh); window.addEventListener('offline', refresh); - window.setInterval(refresh, REFRESH_INTERVAL); window.setTimeout(() => { networkActions.closeConnectingGracePeriod(); - }, REFRESH_INTERVAL); + }, 5 * SECOND); } diff --git a/ts/services/timers.ts b/ts/services/timers.ts deleted file mode 100644 index 63a3298d7..000000000 --- a/ts/services/timers.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -import { v4 as getGuid } from 'uuid'; - -type TimeoutType = { - timestamp: number; - uuid: string; -}; - -const timeoutStore: Map void> = new Map(); -const allTimeouts: Set = new Set(); - -setInterval(() => { - if (!allTimeouts.size) { - return; - } - - const now = Date.now(); - - allTimeouts.forEach((timeout: TimeoutType) => { - const { timestamp, uuid } = timeout; - - if (now >= timestamp) { - if (timeoutStore.has(uuid)) { - const callback = timeoutStore.get(uuid); - if (callback) { - callback(); - } - timeoutStore.delete(uuid); - } - - allTimeouts.delete(timeout); - } - }); -}, 100); - -export function onTimeout( - timestamp: number, - callback: () => void, - id?: string -): string { - if (id && timeoutStore.has(id)) { - throw new ReferenceError(`onTimeout: ${id} already exists`); - } - - let uuid = id || getGuid(); - while (timeoutStore.has(uuid)) { - uuid = getGuid(); - } - - timeoutStore.set(uuid, callback); - allTimeouts.add({ - timestamp, - uuid, - }); - - return uuid; -} - -export function removeTimeout(uuid: string): void { - if (!timeoutStore.has(uuid)) { - return; - } - - timeoutStore.delete(uuid); - - allTimeouts.forEach((timeout: TimeoutType) => { - if (uuid === timeout.uuid) { - allTimeouts.delete(timeout); - } - }); -} diff --git a/ts/state/ducks/updates.ts b/ts/state/ducks/updates.ts index 46f7c15b0..416cc1c1b 100644 --- a/ts/state/ducks/updates.ts +++ b/ts/state/ducks/updates.ts @@ -4,8 +4,8 @@ import { ThunkAction } from 'redux-thunk'; import * as updateIpc from '../../shims/updateIpc'; import { DialogType } from '../../types/Dialogs'; +import { DAY } from '../../util/durations'; import { StateType as RootStateType } from '../reducer'; -import { onTimeout } from '../../services/timers'; // State @@ -85,8 +85,6 @@ function showUpdateDialog( }; } -const ONE_DAY = 24 * 60 * 60 * 1000; - function snoozeUpdate(): ThunkAction< void, RootStateType, @@ -95,12 +93,12 @@ function snoozeUpdate(): ThunkAction< > { return (dispatch, getState) => { const { dialogType } = getState().updates; - onTimeout(Date.now() + ONE_DAY, () => { + setTimeout(() => { dispatch({ type: UNSNOOZE_UPDATE, payload: dialogType, }); - }); + }, DAY); dispatch({ type: SNOOZE_UPDATE, diff --git a/ts/util/postLinkExperience.ts b/ts/util/postLinkExperience.ts index dde415757..966bed7e0 100644 --- a/ts/util/postLinkExperience.ts +++ b/ts/util/postLinkExperience.ts @@ -1,7 +1,7 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { onTimeout } from '../services/timers'; +import { MINUTE } from './durations'; class PostLinkExperience { private hasNotFinishedSync: boolean; @@ -15,9 +15,9 @@ class PostLinkExperience { // timeout "post link" after 10 minutes in case the syncs don't complete // in time or are never called. - onTimeout(Date.now() + 60 * 60 * 10 * 1000, () => { + setTimeout(() => { this.stop(); - }); + }, 10 * MINUTE); } stop() { diff --git a/ts/window.d.ts b/ts/window.d.ts index 9c9f821d7..0d49ece32 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -291,8 +291,6 @@ declare global { initializeGroupCredentialFetcher: () => void; initializeNetworkObserver: (network: ReduxActions['network']) => void; initializeUpdateListener: (updates: ReduxActions['updates']) => void; - onTimeout: (timestamp: number, cb: () => void, id?: string) => string; - removeTimeout: (uuid: string) => void; retryPlaceholders?: Util.RetryPlaceholders; lightSessionResetQueue?: PQueue; runStorageServiceSyncJob: () => Promise;