diff --git a/background.html b/background.html
index 6d9188870..9255fdd79 100644
--- a/background.html
+++ b/background.html
@@ -39,7 +39,6 @@
-
@@ -49,9 +48,6 @@
-
-
-
diff --git a/js/background.js b/js/background.js
index 541c9645c..e9cfde8e0 100644
--- a/js/background.js
+++ b/js/background.js
@@ -228,7 +228,8 @@
el: $('body'),
});
- Whisper.WallClockListener.init(Whisper.events);
+ // FIXME audric2 ExpiringMessagesListener Whisper.Expi
+ throw new Error('plop');
Whisper.ExpiringMessagesListener.init(Whisper.events);
if (Whisper.Registration.isDone() && !window.textsecure.storage.user.isSignInByLinking()) {
diff --git a/js/expiring_messages.js b/js/expiring_messages.js
deleted file mode 100644
index 10678c43c..000000000
--- a/js/expiring_messages.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/* global
- _,
- Backbone,
- i18n,
- moment,
- Whisper
-*/
-
-// eslint-disable-next-line func-names
-(function() {
- 'use strict';
-
- window.Whisper = window.Whisper || {};
-
- async function destroyExpiredMessages() {
- try {
- window.log.info('destroyExpiredMessages: Loading messages...');
- const messages = await window.Signal.Data.getExpiredMessages();
-
- await Promise.all(
- messages.map(async message => {
- window.log.info('Message expired', {
- sentAt: message.get('sent_at'),
- });
-
- // We delete after the trigger to allow the conversation time to process
- // the expiration before the message is removed from the database.
- await window.Signal.Data.removeMessage(message.id);
-
- Whisper.events.trigger('messageExpired', {
- conversationKey: message.attributes.conversationId,
- messageId: message.id,
- });
-
- const conversation = message.getConversation();
- if (conversation) {
- conversation.onExpired(message);
- }
- })
- );
- } catch (error) {
- window.log.error(
- 'destroyExpiredMessages: Error deleting expired messages',
- error && error.stack ? error.stack : error
- );
- }
-
- window.log.info('destroyExpiredMessages: complete');
- checkExpiringMessages();
- }
-
- let timeout;
- async function checkExpiringMessages() {
- // Look up the next expiring message and set a timer to destroy it
- const messages = await window.Signal.Data.getNextExpiringMessage();
- const next = messages.at(0);
- if (!next) {
- return;
- }
-
- const expiresAt = next.get('expires_at');
- Whisper.ExpiringMessagesListener.nextExpiration = expiresAt;
- window.log.info('next message expires', new Date(expiresAt).toISOString());
- window.log.info('next message expires in ', (expiresAt - Date.now()) / 1000);
-
- let wait = expiresAt - Date.now();
-
- // In the past
- if (wait < 0) {
- wait = 0;
- }
-
- // Too far in the future, since it's limited to a 32-bit value
- if (wait > 2147483647) {
- wait = 2147483647;
- }
-
- clearTimeout(timeout);
- timeout = setTimeout(destroyExpiredMessages, wait);
- }
- const throttledCheckExpiringMessages = _.throttle(checkExpiringMessages, 1000);
-
- Whisper.ExpiringMessagesListener = {
- nextExpiration: null,
- init(events) {
- checkExpiringMessages();
- events.on('timetravel', throttledCheckExpiringMessages);
- },
- update: throttledCheckExpiringMessages,
- };
-
- const TimerOption = Backbone.Model.extend({
- getName() {
- return (
- i18n(['timerOption', this.get('time'), this.get('unit')].join('_')) ||
- moment.duration(this.get('time'), this.get('unit')).humanize()
- );
- },
- getAbbreviated() {
- return i18n(['timerOption', this.get('time'), this.get('unit'), 'abbreviated'].join('_'));
- },
- });
- Whisper.ExpirationTimerOptions = new (Backbone.Collection.extend({
- model: TimerOption,
- getName(seconds = 0) {
- const o = this.findWhere({ seconds });
- if (o) {
- return o.getName();
- }
- return [seconds, 'seconds'].join(' ');
- },
- getAbbreviated(seconds = 0) {
- const o = this.findWhere({ seconds });
- if (o) {
- return o.getAbbreviated();
- }
- return [seconds, 's'].join('');
- },
- }))(
- [
- [0, 'seconds'],
- [5, 'seconds'],
- [10, 'seconds'],
- [30, 'seconds'],
- [1, 'minute'],
- [5, 'minutes'],
- [30, 'minutes'],
- [1, 'hour'],
- [6, 'hours'],
- [12, 'hours'],
- [1, 'day'],
- [1, 'week'],
- ].map(o => {
- const duration = moment.duration(o[0], o[1]); // 5, 'seconds'
- return {
- time: o[0],
- unit: o[1],
- seconds: duration.asSeconds(),
- };
- })
- );
-})();
diff --git a/js/wall_clock_listener.js b/js/wall_clock_listener.js
deleted file mode 100644
index 43d52f4e3..000000000
--- a/js/wall_clock_listener.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/* global Whisper */
-
-// eslint-disable-next-line func-names
-(function() {
- 'use strict';
-
- window.Whisper = window.Whisper || {};
-
- let lastTime;
- const interval = 10000;
- let events;
- function checkTime() {
- const currentTime = Date.now();
- if (currentTime > lastTime + interval * 2) {
- events.trigger('timetravel');
- }
- lastTime = currentTime;
- }
-
- Whisper.WallClockListener = {
- init(_events) {
- events = _events;
- lastTime = Date.now();
- setInterval(checkTime, interval);
- },
- };
-})();
diff --git a/main.js b/main.js
index 19d9dda5d..827d97477 100644
--- a/main.js
+++ b/main.js
@@ -244,7 +244,7 @@ async function createWindow() {
webPreferences: {
nodeIntegration: false,
enableRemoteModule: true,
- nodeIntegrationInWorker: false,
+ nodeIntegrationInWorker: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js'),
nativeWindowOpen: true,
diff --git a/ts/components/SessionInboxView.tsx b/ts/components/SessionInboxView.tsx
index 6a58f07f2..5c7374e4d 100644
--- a/ts/components/SessionInboxView.tsx
+++ b/ts/components/SessionInboxView.tsx
@@ -1,6 +1,5 @@
import React from 'react';
import { Provider } from 'react-redux';
-import { bindActionCreators } from 'redux';
import { LeftPane } from './leftpane/LeftPane';
// tslint:disable-next-line: no-submodule-imports
@@ -10,7 +9,6 @@ import { getConversationController } from '../session/conversations';
import { UserUtils } from '../session/utils';
import { initialCallState } from '../state/ducks/call';
import {
- actions as conversationActions,
getEmptyConversationState,
openConversationWithMessages,
} from '../state/ducks/conversations';
@@ -27,6 +25,7 @@ import { StateType } from '../state/reducer';
import { makeLookup } from '../util';
import { SessionMainPanel } from './SessionMainPanel';
import { createStore } from '../state/createStore';
+import { ExpirationTimerOptions } from '../util/expiringMessages';
// Workaround: A react component's required properties are filtering up through connect()
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
@@ -80,12 +79,7 @@ export class SessionInboxView extends React.Component {
.getConversations()
.map(conversation => conversation.getConversationModelProps());
- const timerOptions: TimerOptionsArray = window.Whisper.ExpirationTimerOptions.map(
- (item: any) => ({
- name: item.getName(),
- value: item.get('seconds'),
- })
- );
+ const timerOptions: TimerOptionsArray = ExpirationTimerOptions.getTimerSecondsWithName();
const initialState: StateType = {
conversations: {
@@ -112,13 +106,8 @@ export class SessionInboxView extends React.Component {
this.store = createStore(initialState);
window.inboxStore = this.store;
- // Enables our redux store to be updated by backbone events in the outside world
- const { messageExpired } = bindActionCreators(conversationActions, this.store.dispatch);
window.openConversationWithMessages = openConversationWithMessages;
- // messageExpired is currently inboked fropm js. So we link it to Redux that way
- window.Whisper.events.on('messageExpired', messageExpired);
-
this.setState({ isInitialLoadComplete: true });
}
}
diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx
index 8abd34b82..67373404b 100644
--- a/ts/components/conversation/ConversationHeader.tsx
+++ b/ts/components/conversation/ConversationHeader.tsx
@@ -41,6 +41,7 @@ import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/S
import { SessionIconButton } from '../icon';
import { ConversationHeaderMenu } from '../menu/ConversationHeaderMenu';
import { Flex } from '../basic/Flex';
+import { ExpirationTimerOptions } from '../../util/expiringMessages';
export interface TimerOption {
name: string;
diff --git a/ts/data/data.ts b/ts/data/data.ts
index f27a727ff..733b1c64a 100644
--- a/ts/data/data.ts
+++ b/ts/data/data.ts
@@ -18,6 +18,7 @@ import { getSodium } from '../session/crypto';
import { PubKey } from '../session/types';
import { fromArrayBufferToBase64, fromBase64ToArrayBuffer } from '../session/utils/String';
import { ReduxConversationType } from '../state/ducks/conversations';
+import { ExpirationTimerOptions } from '../util/expiringMessages';
import { channels } from './channels';
import { channelsToMake as channelstoMakeOpenGroupV2 } from './opengroups';
@@ -648,7 +649,7 @@ export async function updateLastHash(data: {
export async function saveMessage(data: MessageAttributes): Promise {
const cleanedData = _cleanData(data);
const id = await channels.saveMessage(cleanedData);
- window.Whisper.ExpiringMessagesListener.update();
+ ExpirationTimerOptions.updateExpiringMessagesCheck();
return id;
}
diff --git a/ts/models/message.ts b/ts/models/message.ts
index fb189e6c8..f5a1c9cbe 100644
--- a/ts/models/message.ts
+++ b/ts/models/message.ts
@@ -60,6 +60,7 @@ import {
loadPreviewData,
loadQuoteData,
} from '../types/MessageAttachment';
+import { ExpirationTimerOptions } from '../util/expiringMessages';
// tslint:disable: cyclomatic-complexity
/**
@@ -214,10 +215,9 @@ export class MessageModel extends Backbone.Model {
return window.i18n('disappearingMessagesDisabled');
}
- return window.i18n(
- 'timerSetTo',
- window.Whisper.ExpirationTimerOptions.getAbbreviated(expireTimerUpdate.expireTimer || 0)
- );
+ return window.i18n('timerSetTo', [
+ ExpirationTimerOptions.getAbbreviated(expireTimerUpdate.expireTimer || 0),
+ ]);
}
return '';
@@ -241,7 +241,7 @@ export class MessageModel extends Backbone.Model {
}
const { expireTimer, fromSync, source } = timerUpdate;
- const timespan = window.Whisper.ExpirationTimerOptions.getName(expireTimer || 0) as string;
+ const timespan = ExpirationTimerOptions.getName(expireTimer || 0);
const disabled = !expireTimer;
const basicProps: PropsForExpirationTimer = {
diff --git a/ts/util/expiringMessages.ts b/ts/util/expiringMessages.ts
new file mode 100644
index 000000000..04b2a9f4f
--- /dev/null
+++ b/ts/util/expiringMessages.ts
@@ -0,0 +1,167 @@
+import _ from 'lodash';
+import moment from 'moment';
+import { MessageModel } from '../models/message';
+import { messageExpired } from '../state/ducks/conversations';
+import { TimerOptionsArray } from '../state/ducks/timerOptions';
+import { LocalizerKeys } from '../types/LocalizerKeys';
+import { initWallClockListener } from './wallClockListener';
+
+import * as Data from '../data/data';
+
+async function destroyExpiredMessages() {
+ try {
+ window.log.info('destroyExpiredMessages: Loading messages...');
+ const messages = await Data.getExpiredMessages();
+
+ await Promise.all(
+ messages.map(async (message: MessageModel) => {
+ window.log.info('Message expired', {
+ sentAt: message.get('sent_at'),
+ });
+
+ // We delete after the trigger to allow the conversation time to process
+ // the expiration before the message is removed from the database.
+ await Data.removeMessage(message.id);
+
+ // trigger the expiration of the message on the redux itself.
+ window.inboxStore?.dispatch(
+ messageExpired({
+ conversationKey: message.attributes.conversationId,
+ messageId: message.id,
+ })
+ );
+
+ const conversation = message.getConversation();
+ if (conversation) {
+ await conversation.onExpired(message);
+ }
+ })
+ );
+ } catch (error) {
+ window.log.error(
+ 'destroyExpiredMessages: Error deleting expired messages',
+ error && error.stack ? error.stack : error
+ );
+ }
+
+ window.log.info('destroyExpiredMessages: complete');
+ void checkExpiringMessages();
+}
+
+let timeout: NodeJS.Timeout | undefined;
+async function checkExpiringMessages() {
+ // Look up the next expiring message and set a timer to destroy it
+ const messages = await window.Signal.Data.getNextExpiringMessage();
+ const next = messages.at(0);
+ if (!next) {
+ return;
+ }
+
+ const expiresAt = next.get('expires_at');
+
+ window.log.info('next message expires', new Date(expiresAt).toISOString());
+ window.log.info('next message expires in ', (expiresAt - Date.now()) / 1000);
+
+ let wait = expiresAt - Date.now();
+
+ // In the past
+ if (wait < 0) {
+ wait = 0;
+ }
+
+ // Too far in the future, since it's limited to a 32-bit value
+ if (wait > 2147483647) {
+ wait = 2147483647;
+ }
+
+ if (timeout) {
+ global.clearTimeout(timeout);
+ }
+ timeout = global.setTimeout(destroyExpiredMessages, wait);
+}
+const throttledCheckExpiringMessages = _.throttle(checkExpiringMessages, 1000);
+
+let isInit = false;
+
+const initExpiringMessageListener = () => {
+ if (isInit) {
+ throw new Error('expiring messages listener is already init');
+ }
+
+ void checkExpiringMessages();
+
+ initWallClockListener(throttledCheckExpiringMessages);
+ isInit = true;
+};
+
+const updateExpiringMessagesCheck = () => {
+ void throttledCheckExpiringMessages();
+};
+
+function getTimerOptionName(time: number, unit: moment.DurationInputArg2) {
+ return (
+ window.i18n(['timerOption', time, unit].join('_') as LocalizerKeys) ||
+ moment.duration(time, unit).humanize()
+ );
+}
+function getTimerOptionAbbreviated(time: number, unit: string) {
+ return window.i18n(['timerOption', time, unit, 'abbreviated'].join('_') as LocalizerKeys);
+}
+
+const timerOptionsDurations: Array<{
+ time: number;
+ unit: moment.DurationInputArg2;
+ seconds: number;
+}> = [
+ { time: 0, unit: 'seconds' as moment.DurationInputArg2 },
+ { time: 5, unit: 'seconds' as moment.DurationInputArg2 },
+ { time: 10, unit: 'seconds' as moment.DurationInputArg2 },
+ { time: 30, unit: 'seconds' as moment.DurationInputArg2 },
+ { time: 1, unit: 'minute' as moment.DurationInputArg2 },
+ { time: 5, unit: 'minutes' as moment.DurationInputArg2 },
+ { time: 30, unit: 'minutes' as moment.DurationInputArg2 },
+ { time: 1, unit: 'hour' as moment.DurationInputArg2 },
+ { time: 6, unit: 'hours' as moment.DurationInputArg2 },
+ { time: 12, unit: 'hours' as moment.DurationInputArg2 },
+ { time: 1, unit: 'day' as moment.DurationInputArg2 },
+ { time: 1, unit: 'week' as moment.DurationInputArg2 },
+].map(o => {
+ const duration = moment.duration(o.time, o.unit); // 5, 'seconds'
+ return {
+ time: o.time,
+ unit: o.unit,
+ seconds: duration.asSeconds(),
+ };
+});
+
+function getName(seconds = 0) {
+ const o = timerOptionsDurations.find(m => m.seconds === seconds);
+
+ if (o) {
+ return getTimerOptionName(o.time, o.unit);
+ }
+ return [seconds, 'seconds'].join(' ');
+}
+function getAbbreviated(seconds = 0) {
+ const o = timerOptionsDurations.find(m => m.seconds === seconds);
+
+ if (o) {
+ return getTimerOptionAbbreviated(o.time, o.unit);
+ }
+
+ return [seconds, 's'].join('');
+}
+
+function getTimerSecondsWithName(): TimerOptionsArray {
+ return timerOptionsDurations.map(t => {
+ return { name: getName(t.seconds), value: t.seconds };
+ });
+}
+
+export const ExpirationTimerOptions = {
+ getName,
+ getAbbreviated,
+ updateExpiringMessagesCheck,
+ initExpiringMessageListener,
+ getTimerSecondsWithName,
+};
diff --git a/ts/util/wallClockListener.ts b/ts/util/wallClockListener.ts
new file mode 100644
index 000000000..66e7b6d40
--- /dev/null
+++ b/ts/util/wallClockListener.ts
@@ -0,0 +1,22 @@
+let lastTime = Date.now();
+const interval = 10 * 1000;
+let timeTravelListener: (() => void) | undefined;
+
+function checkTime() {
+ const currentTime = Date.now();
+ if (currentTime > lastTime + interval * 2) {
+ if (!timeTravelListener) {
+ throw new Error('timeTravelListener should have been set in initWallClockListener');
+ }
+ timeTravelListener();
+ }
+ lastTime = currentTime;
+}
+
+export const initWallClockListener = (onTimeTravelDetectedListener: () => void) => {
+ if (timeTravelListener) {
+ throw new Error('Wall clock listener already init');
+ }
+ timeTravelListener = onTimeTravelDetectedListener;
+ global.setInterval(checkTime, interval);
+};
diff --git a/ts/webworker/workers/auth.worker.ts b/ts/webworker/workers/auth.worker.ts
index 6c360655b..263064f7a 100644
--- a/ts/webworker/workers/auth.worker.ts
+++ b/ts/webworker/workers/auth.worker.ts
@@ -1,11 +1,11 @@
-const _ = require('lodash');
+import * as lodash from 'lodash';
// import * as _ from 'lodash';
const sleep = async (time: any) => new Promise(r => setTimeout(r, time));
~(async function main() {
while (true) {
- console.log('lodash map exists:', typeof _.map);
+ console.log('lodash map exists:', typeof lodash.map);
await sleep(1000);
}
})();