New Idle timer; messages not marked read if user is idle

This commit is contained in:
Scott Nonnenberg 2019-09-19 15:16:46 -07:00
parent b77246a7e0
commit 8ccb89310b
11 changed files with 55 additions and 55 deletions

View file

@ -449,7 +449,6 @@
<script type='text/javascript' src='js/signal_protocol_store.js'></script>
<script type='text/javascript' src='js/libtextsecure.js'></script>
<script type='text/javascript' src='js/focus_listener.js'></script>
<script type='text/javascript' src='js/notifications.js'></script>
<script type='text/javascript' src='js/delivery_receipts.js'></script>
<script type='text/javascript' src='js/read_receipts.js'></script>

View file

@ -40,6 +40,43 @@
false
);
// Idle timer - you're active for ACTIVE_TIMEOUT after one of these events
const ACTIVE_TIMEOUT = 15 * 1000;
const ACTIVE_EVENTS = [
'click',
'keypress',
'mousedown',
'mousemove',
// 'scroll', // this is triggered by Timeline re-renders, can't use
'touchstart',
'wheel',
];
const LISTENER_DEBOUNCE = 5 * 1000;
let activeHandlers = [];
let activeTimestamp = Date.now();
window.resetActiveTimer = _.throttle(() => {
const previouslyActive = window.isActive();
activeTimestamp = Date.now();
if (!previouslyActive) {
activeHandlers.forEach(handler => handler());
}
}, LISTENER_DEBOUNCE);
ACTIVE_EVENTS.forEach(name => {
document.addEventListener(name, window.resetActiveTimer, true);
});
window.isActive = () => {
const now = Date.now();
return now <= activeTimestamp + ACTIVE_TIMEOUT;
};
window.registerForActive = handler => activeHandlers.push(handler);
window.unregisterForActive = handler => {
activeHandlers = activeHandlers.filter(item => item !== handler);
};
// Load these images now to ensure that they don't flicker on first use
window.Signal.EmojiLib.preloadImages();
const images = [];
@ -712,7 +749,7 @@
}
});
window.addEventListener('focus', () => Whisper.Notifications.clear());
window.registerForActive(() => Whisper.Notifications.clear());
window.addEventListener('unload', () => Whisper.Notifications.fastClear());
Whisper.events.on('showConversation', (id, messageId) => {

View file

@ -1,14 +0,0 @@
// eslint-disable-next-line func-names
(function() {
'use strict';
let windowFocused = false;
window.addEventListener('blur', () => {
windowFocused = false;
});
window.addEventListener('focus', () => {
windowFocused = true;
});
window.isFocused = () => windowFocused;
})();

View file

@ -334,7 +334,7 @@
this.id,
[model.getReduxData()],
isNewMessage,
document.hasFocus()
window.isActive()
);
}

View file

@ -3,7 +3,6 @@
/* global drawAttention: false */
/* global i18n: false */
/* global isFocused: false */
/* global Signal: false */
/* global storage: false */
/* global Whisper: false */
@ -54,7 +53,7 @@
}
const { isEnabled } = this;
const isAppFocused = isFocused();
const isAppFocused = window.isActive();
const isAudioNotificationEnabled =
storage.get('audio-notification') || false;
const isAudioNotificationSupported = Settings.isAudioNotificationSupported();

View file

@ -166,7 +166,7 @@
if (!$.contains(this.el, this.inboxView.el)) {
this.openView(this.inboxView);
}
window.focus(); // FIXME
return Promise.resolve();
},
onEmpty() {

View file

@ -442,7 +442,7 @@
id,
models.map(model => model.getReduxData()),
isNewMessage,
document.hasFocus()
window.isActive()
);
} catch (error) {
setMessagesLoading(conversationId, true);
@ -493,7 +493,7 @@
id,
models.map(model => model.getReduxData()),
isNewMessage,
document.hasFocus()
window.isActive()
);
} catch (error) {
setMessagesLoading(conversationId, false);
@ -502,10 +502,8 @@
finish();
}
};
const markMessageRead = async (messageId, forceFocus) => {
// We need a forceFocus parameter because the BrowserWindow focus event fires
// before the document realizes that it has focus.
if (!document.hasFocus() && !forceFocus) {
const markMessageRead = async messageId => {
if (!window.isActive()) {
return;
}

View file

@ -10,14 +10,6 @@ const { remote } = electron;
const { app } = remote;
const { systemPreferences } = remote.require('electron');
const browserWindow = remote.getCurrentWindow();
let focusHandlers = [];
browserWindow.on('focus', () => focusHandlers.forEach(handler => handler()));
window.registerForFocus = handler => focusHandlers.push(handler);
window.unregisterForFocus = handler => {
focusHandlers = focusHandlers.filter(item => item !== handler);
};
// Waiting for clients to implement changes on receive side
window.ENABLE_STICKER_SEND = true;
window.TIMESTAMP_VALIDATION = false;

View file

@ -476,7 +476,6 @@
<script type='text/javascript' src='../js/expiring_messages.js' data-cover></script>
<script type='text/javascript' src='../js/expiring_tap_to_view_messages.js' data-cover></script>
<script type='text/javascript' src='../js/notifications.js' data-cover></script>
<script type='text/javascript' src='../js/focus_listener.js'></script>
<script type="text/javascript" src="../js/chromium.js" data-cover></script>

View file

@ -61,7 +61,7 @@ type PropsActionsType = {
loadOlderMessages: (messageId: string) => unknown;
loadNewerMessages: (messageId: string) => unknown;
loadNewestMessages: (messageId: string) => unknown;
markMessageRead: (messageId: string, forceFocus?: boolean) => unknown;
markMessageRead: (messageId: string) => unknown;
} & MessageActionsType &
SafetyNumberActionsType;
@ -402,7 +402,7 @@ export class Timeline extends React.PureComponent<Props, State> {
// tslint:disable-next-line member-ordering cyclomatic-complexity
public updateWithVisibleRows = debounce(
(forceFocus?: boolean) => {
() => {
const {
unreadCount,
haveNewest,
@ -426,7 +426,7 @@ export class Timeline extends React.PureComponent<Props, State> {
return;
}
markMessageRead(newest.id, forceFocus);
markMessageRead(newest.id);
const rowCount = this.getRowCount();
@ -710,19 +710,14 @@ export class Timeline extends React.PureComponent<Props, State> {
public componentDidMount() {
this.updateWithVisibleRows();
// @ts-ignore
window.registerForFocus(this.forceFocusVisibleRowUpdate);
window.registerForActive(this.updateWithVisibleRows);
}
public componentWillUnmount() {
// @ts-ignore
window.unregisterForFocus(this.forceFocusVisibleRowUpdate);
window.unregisterForActive(this.updateWithVisibleRows);
}
public forceFocusVisibleRowUpdate = () => {
const forceFocus = true;
this.updateWithVisibleRows(forceFocus);
};
// tslint:disable-next-line cyclomatic-complexity max-func-body-length
public componentDidUpdate(prevProps: Props) {
const {

View file

@ -160,7 +160,7 @@ export type MessagesAddedActionType = {
conversationId: string;
messages: Array<MessageType>;
isNewMessage: boolean;
isFocused: boolean;
isActive: boolean;
};
};
export type MessagesResetActionType = {
@ -357,7 +357,7 @@ function messagesAdded(
conversationId: string,
messages: Array<MessageType>,
isNewMessage: boolean,
isFocused: boolean
isActive: boolean
): MessagesAddedActionType {
return {
type: 'MESSAGES_ADDED',
@ -365,7 +365,7 @@ function messagesAdded(
conversationId,
messages,
isNewMessage,
isFocused,
isActive,
},
};
}
@ -870,12 +870,7 @@ export function reducer(
};
}
if (action.type === 'MESSAGES_ADDED') {
const {
conversationId,
isFocused,
isNewMessage,
messages,
} = action.payload;
const { conversationId, isActive, isNewMessage, messages } = action.payload;
const { messagesByConversation, messagesLookup } = state;
const existingConversation = messagesByConversation[conversationId];
@ -937,7 +932,7 @@ export function reducer(
const newMessageIds = difference(newIds, existingConversation.messageIds);
const { isNearBottom } = existingConversation;
if ((!isNearBottom || !isFocused) && !oldestUnread) {
if ((!isNearBottom || !isActive) && !oldestUnread) {
const oldestId = newMessageIds.find(messageId => {
const message = lookup[messageId];