New Idle timer; messages not marked read if user is idle
This commit is contained in:
parent
b77246a7e0
commit
8ccb89310b
11 changed files with 55 additions and 55 deletions
|
@ -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>
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
})();
|
|
@ -334,7 +334,7 @@
|
|||
this.id,
|
||||
[model.getReduxData()],
|
||||
isNewMessage,
|
||||
document.hasFocus()
|
||||
window.isActive()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -166,7 +166,7 @@
|
|||
if (!$.contains(this.el, this.inboxView.el)) {
|
||||
this.openView(this.inboxView);
|
||||
}
|
||||
window.focus(); // FIXME
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
onEmpty() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
Loading…
Reference in a new issue