Cleans up mute state after mute expires
This commit is contained in:
parent
a581f6ea81
commit
9510fd1eec
|
@ -121,6 +121,7 @@ const {
|
|||
} = require('../../ts/services/updateListener');
|
||||
const { notify } = require('../../ts/services/notify');
|
||||
const { calling } = require('../../ts/services/calling');
|
||||
const { onTimeout, removeTimeout } = require('../../ts/services/timers');
|
||||
const {
|
||||
enableStorageService,
|
||||
eraseAllStorageServiceState,
|
||||
|
@ -341,7 +342,9 @@ exports.setup = (options = {}) => {
|
|||
initializeGroupCredentialFetcher,
|
||||
initializeNetworkObserver,
|
||||
initializeUpdateListener,
|
||||
onTimeout,
|
||||
notify,
|
||||
removeTimeout,
|
||||
runStorageServiceSyncJob,
|
||||
storageServiceUploadJob,
|
||||
};
|
||||
|
|
|
@ -40,6 +40,9 @@ 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);
|
||||
});
|
||||
},
|
||||
addActive(model: ConversationModel) {
|
||||
if (model.get('active_at')) {
|
||||
|
@ -48,6 +51,22 @@ 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 {
|
||||
if (model.isMuted()) {
|
||||
window.Signal.Services.onTimeout(
|
||||
model.get('muteExpiresAt'),
|
||||
() => {
|
||||
model.set({ muteExpiresAt: undefined });
|
||||
},
|
||||
model.getMuteTimeoutId()
|
||||
);
|
||||
} else if (model.get('muteExpiresAt')) {
|
||||
model.set({ muteExpiresAt: undefined });
|
||||
}
|
||||
},
|
||||
updateUnreadCount() {
|
||||
const canCountMutedConversations = window.storage.get(
|
||||
'badge-count-muted-conversations'
|
||||
|
|
|
@ -206,7 +206,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
: null
|
||||
)}
|
||||
>
|
||||
{muteExpiresAt && (
|
||||
{muteExpiresAt && Date.now() < muteExpiresAt && (
|
||||
<span className="module-conversation-list-item__muted" />
|
||||
)}
|
||||
{!isAccepted ? (
|
||||
|
|
|
@ -3553,8 +3553,14 @@ export class ConversationModel extends window.Backbone.Model<
|
|||
}
|
||||
|
||||
isMuted(): boolean {
|
||||
return (this.get('muteExpiresAt') &&
|
||||
Date.now() < this.get('muteExpiresAt')) as boolean;
|
||||
return (
|
||||
Boolean(this.get('muteExpiresAt')) &&
|
||||
Date.now() < this.get('muteExpiresAt')
|
||||
);
|
||||
}
|
||||
|
||||
getMuteTimeoutId(): string {
|
||||
return `mute(${this.get('id')})`;
|
||||
}
|
||||
|
||||
async notify(message: WhatIsThis, reaction?: WhatIsThis): Promise<void> {
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import { v4 as getGuid } from 'uuid';
|
||||
|
||||
type TimeoutType = {
|
||||
timestamp: number;
|
||||
uuid: string;
|
||||
};
|
||||
|
||||
const timeoutStore: Map<string, () => void> = new Map();
|
||||
const allTimeouts: Set<TimeoutType> = 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)) {
|
||||
timeoutStore.delete(uuid);
|
||||
}
|
||||
|
||||
allTimeouts.forEach((timeout: TimeoutType) => {
|
||||
if (uuid === timeout.uuid) {
|
||||
allTimeouts.delete(timeout);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -395,7 +395,7 @@ Whisper.ConversationView = Whisper.View.extend({
|
|||
|
||||
getMuteExpirationLabel() {
|
||||
const muteExpiresAt = this.model.get('muteExpiresAt');
|
||||
if (!muteExpiresAt) {
|
||||
if (!this.model.isMuted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2614,10 +2614,29 @@ Whisper.ConversationView = Whisper.View.extend({
|
|||
}
|
||||
},
|
||||
|
||||
setMuteNotifications(ms: any) {
|
||||
this.model.set({
|
||||
muteExpiresAt: ms > 0 ? Date.now() + ms : undefined,
|
||||
});
|
||||
setMuteNotifications(ms: number) {
|
||||
const muteExpiresAt = ms > 0 ? Date.now() + ms : undefined;
|
||||
|
||||
if (muteExpiresAt) {
|
||||
// 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.model.getMuteTimeoutId();
|
||||
window.Signal.Services.removeTimeout(timeoutId);
|
||||
window.Signal.Services.onTimeout(
|
||||
muteExpiresAt,
|
||||
() => {
|
||||
this.setMuteNotifications(0);
|
||||
},
|
||||
timeoutId
|
||||
);
|
||||
}
|
||||
|
||||
this.model.set({ muteExpiresAt });
|
||||
this.saveModel();
|
||||
},
|
||||
|
||||
async destroyMessages() {
|
||||
|
|
|
@ -190,6 +190,8 @@ declare global {
|
|||
updates: WhatIsThis,
|
||||
events: WhatIsThis
|
||||
) => void;
|
||||
onTimeout: (timestamp: number, cb: () => void, id?: string) => string;
|
||||
removeTimeout: (uuid: string) => void;
|
||||
runStorageServiceSyncJob: () => Promise<void>;
|
||||
storageServiceUploadJob: () => void;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue