Page MenuHomePhabricator

D13210.diff
No OneTemporary

D13210.diff

diff --git a/lib/selectors/thread-selectors.js b/lib/selectors/thread-selectors.js
--- a/lib/selectors/thread-selectors.js
+++ b/lib/selectors/thread-selectors.js
@@ -18,7 +18,7 @@
} from './calendar-filter-selectors.js';
import { relativeMemberInfoSelectorForMembersOfThread } from './user-selectors.js';
import genesis from '../facts/genesis.js';
-import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
import {
getAvatarForThread,
getRandomDefaultEmojiAvatar,
@@ -258,15 +258,19 @@
).length,
);
-const allUnreadCounts: (state: BaseAppState<>) => {
+const thinThreadsUnreadCountSelector: (state: BaseAppState<>) => {
+[keyserverID: string]: number,
} = createSelector(
(state: BaseAppState<>) => state.threadStore.threadInfos,
(threadInfos: RawThreadInfos): { +[keyserverID: string]: number } => {
+ const thinThreadInfosList = values(threadInfos).filter(
+ threadInfo => !threadInfo.thick,
+ );
+
const keyserverToThreads = _groupBy(threadInfo =>
- extractKeyserverIDFromIDOptional(threadInfo.id),
+ extractKeyserverIDFromID(threadInfo.id),
)(
- values(threadInfos).filter(threadInfo =>
+ thinThreadInfosList.filter(threadInfo =>
threadInHomeChatList(threadInfo),
),
);
@@ -540,7 +544,7 @@
childThreadInfos,
containedThreadInfos,
unreadCount,
- allUnreadCounts,
+ thinThreadsUnreadCountSelector,
unreadBackgroundCount,
unreadCountSelectorForCommunity,
otherUsersButNoOtherAdmins,
diff --git a/native/push/push-handler.react.js b/native/push/push-handler.react.js
--- a/native/push/push-handler.react.js
+++ b/native/push/push-handler.react.js
@@ -27,7 +27,7 @@
} from 'lib/selectors/keyserver-selectors.js';
import {
threadInfoSelector,
- allUnreadCounts,
+ thinThreadsUnreadCountSelector,
unreadThickThreadIDsSelector,
} from 'lib/selectors/thread-selectors.js';
import { isLoggedIn } from 'lib/selectors/user-selectors.js';
@@ -108,7 +108,7 @@
// Navigation state
+activeThread: ?string,
// Redux state
- +unreadCount: { +[keyserverID: string]: number },
+ +thinThreadsUnreadCount: { +[keyserverID: string]: number },
+unreadThickThreadIDs: $ReadOnlyArray<string>,
+connection: { +[keyserverID: string]: ?ConnectionInfo },
+deviceTokens: {
@@ -326,7 +326,7 @@
}
async updateBadgeCount() {
- const curUnreadCounts = this.props.unreadCount;
+ const curThinUnreadCounts = this.props.thinThreadsUnreadCount;
const curConnections = this.props.connection;
const currentUnreadThickThreads = this.props.unreadThickThreadIDs;
@@ -339,7 +339,7 @@
}> = [];
const notifsStorageQueries: Array<string> = [];
- for (const keyserverID in curUnreadCounts) {
+ for (const keyserverID in curThinUnreadCounts) {
if (curConnections[keyserverID]?.status !== 'connected') {
notifsStorageQueries.push(keyserverID);
continue;
@@ -347,7 +347,7 @@
notifStorageUpdates.push({
id: keyserverID,
- unreadCount: curUnreadCounts[keyserverID],
+ unreadCount: curThinUnreadCounts[keyserverID],
});
}
@@ -401,7 +401,9 @@
}
async resetBadgeCount() {
- const keyserversDataToRemove = Object.keys(this.props.unreadCount);
+ const keyserversDataToRemove = Object.keys(
+ this.props.thinThreadsUnreadCount,
+ );
try {
await commCoreModule.removeKeyserverDataFromNotifStorage(
keyserversDataToRemove,
@@ -827,7 +829,7 @@
React.memo<BaseProps>(function ConnectedPushHandler(props: BaseProps) {
const navContext = React.useContext(NavContext);
const activeThread = activeMessageListSelector(navContext);
- const unreadCount = useSelector(allUnreadCounts);
+ const thinThreadsUnreadCount = useSelector(thinThreadsUnreadCountSelector);
const unreadThickThreadIDs = useSelector(unreadThickThreadIDsSelector);
const connection = useSelector(allConnectionInfosSelector);
const deviceTokens = useSelector(deviceTokensSelector);
@@ -852,7 +854,7 @@
<PushHandler
{...props}
activeThread={activeThread}
- unreadCount={unreadCount}
+ thinThreadsUnreadCount={thinThreadsUnreadCount}
unreadThickThreadIDs={unreadThickThreadIDs}
connection={connection}
deviceTokens={deviceTokens}
diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -69,7 +69,7 @@
import UpdateModalHandler from './modals/update-modal.react.js';
import SettingsSwitcher from './navigation-panels/settings-switcher.react.js';
import Topbar from './navigation-panels/topbar.react.js';
-import useBadgeHandler from './push-notif/badge-handler.react.js';
+import BadgeHandler from './push-notif/badge-handler.react.js';
import encryptedNotifUtilsAPI from './push-notif/encrypted-notif-utils-api.js';
import { PushNotificationsHandler } from './push-notif/push-notifs-handler.js';
import { updateNavInfoActionType } from './redux/action-types.js';
@@ -532,8 +532,6 @@
!!state.threadStore.threadInfos[activeChatThreadID]?.currentUser.unread,
);
- useBadgeHandler();
-
const dispatch = useDispatch();
const modalContext = useModalContext();
const modals = React.useMemo(
@@ -561,6 +559,7 @@
onClose={releaseLockOrAbortRequest}
secondaryTunnelbrokerConnection={secondaryTunnelbrokerConnection}
>
+ <BadgeHandler />
<IdentitySearchProvider>
<QRAuthProvider
parseTunnelbrokerQRAuthMessage={parseTunnelbrokerQRAuthMessage}
diff --git a/web/push-notif/badge-handler.react.js b/web/push-notif/badge-handler.react.js
--- a/web/push-notif/badge-handler.react.js
+++ b/web/push-notif/badge-handler.react.js
@@ -3,11 +3,17 @@
import * as React from 'react';
import { allConnectionInfosSelector } from 'lib/selectors/keyserver-selectors.js';
-import { allUnreadCounts } from 'lib/selectors/thread-selectors.js';
+import {
+ thinThreadsUnreadCountSelector,
+ unreadThickThreadIDsSelector,
+} from 'lib/selectors/thread-selectors.js';
+import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js';
import {
updateNotifsUnreadCountStorage,
queryNotifsUnreadCountStorage,
+ getNotifsUnreadThickThreadIDs,
+ updateNotifsUnreadThickThreadIDsStorage,
} from './notif-crypto-utils.js';
import electron from '../electron.js';
import { useSelector } from '../redux/redux-utils.js';
@@ -15,7 +21,10 @@
function useBadgeHandler() {
const connection = useSelector(allConnectionInfosSelector);
- const unreadCount = useSelector(allUnreadCounts);
+ const thinThreadsUnreadCount = useSelector(thinThreadsUnreadCountSelector);
+
+ const { socketState: tunnelbrokerSocketState } = useTunnelbroker();
+ const currentUnreadThickThreadIDs = useSelector(unreadThickThreadIDsSelector);
React.useEffect(() => {
void (async () => {
@@ -24,17 +33,32 @@
} = {};
const unreadCountQueries: Array<string> = [];
- for (const keyserverID in unreadCount) {
+ for (const keyserverID in thinThreadsUnreadCount) {
if (connection[keyserverID]?.status !== 'connected') {
unreadCountQueries.push(keyserverID);
continue;
}
- unreadCountUpdates[keyserverID] = unreadCount[keyserverID];
+ unreadCountUpdates[keyserverID] = thinThreadsUnreadCount[keyserverID];
}
let queriedUnreadCounts: { +[keyserverID: string]: ?number } = {};
- [queriedUnreadCounts] = await Promise.all([
+ let unreadThickThreadIDs: $ReadOnlyArray<string> = [];
+
+ const handleUnreadThickThreadIDsInNotifsStoragePromise = (async () => {
+ if (tunnelbrokerSocketState.connected) {
+ await updateNotifsUnreadThickThreadIDsStorage({
+ type: 'set',
+ threadIDs: currentUnreadThickThreadIDs,
+ forceWrite: true,
+ });
+ return currentUnreadThickThreadIDs;
+ }
+ return getNotifsUnreadThickThreadIDs();
+ })();
+
+ [queriedUnreadCounts, unreadThickThreadIDs] = await Promise.all([
queryNotifsUnreadCountStorage(unreadCountQueries),
+ handleUnreadThickThreadIDsInNotifsStoragePromise,
updateNotifsUnreadCountStorage(unreadCountUpdates),
]);
@@ -45,16 +69,27 @@
for (const keyserverID in queriedUnreadCounts) {
if (!queriedUnreadCounts[keyserverID]) {
- totalUnreadCount += unreadCount[keyserverID];
+ totalUnreadCount += thinThreadsUnreadCount[keyserverID];
continue;
}
totalUnreadCount += queriedUnreadCounts[keyserverID];
}
+ totalUnreadCount += unreadThickThreadIDs.length;
document.title = getTitle(totalUnreadCount);
electron?.setBadge(totalUnreadCount === 0 ? null : totalUnreadCount);
})();
- }, [unreadCount, connection]);
+ }, [
+ tunnelbrokerSocketState,
+ currentUnreadThickThreadIDs,
+ thinThreadsUnreadCount,
+ connection,
+ ]);
+}
+
+function BadgeHandler(): React.Node {
+ useBadgeHandler();
+ return null;
}
-export default useBadgeHandler;
+export default BadgeHandler;
diff --git a/web/push-notif/notif-crypto-utils.js b/web/push-notif/notif-crypto-utils.js
--- a/web/push-notif/notif-crypto-utils.js
+++ b/web/push-notif/notif-crypto-utils.js
@@ -88,6 +88,12 @@
const INDEXED_DB_NOTIFS_ACCOUNT_ENCRYPTION_KEY_DB_LABEL =
'notificationAccountEncryptionKey';
+// thick threads unread count
+const INDEXED_DB_UNREAD_THICK_THREAD_IDS = 'unreadThickThreadIDs';
+const INDEXED_DB_UNREAD_THICK_THREAD_IDS_ENCRYPTION_KEY_DB_LABEL =
+ 'unreadThickThreadIDsEncryptionKey';
+const INDEXED_DB_UNREAD_THICK_THREADS_SYNC_KEY = 'unreadThickThreadIDsSyncKey';
+
async function deserializeEncryptedData<T>(
encryptedData: EncryptedData,
encryptionKey: CryptoKey,
@@ -456,16 +462,25 @@
updatedOlmData = resultUpdatedOlmData;
updatedNotifsAccount = resultUpdatedNotifsAccount;
- await persistNotifsAccountWithOlmData({
- accountWithPicklingKey: updatedNotifsAccount,
- accountEncryptionKey,
- encryptionKey,
- olmData: updatedOlmData,
- olmDataKey,
- olmEncryptionKeyDBLabel,
- synchronizationValue,
- forceWrite: false,
- });
+ const { threadID } = decryptedNotification;
+
+ await Promise.all([
+ persistNotifsAccountWithOlmData({
+ accountWithPicklingKey: updatedNotifsAccount,
+ accountEncryptionKey,
+ encryptionKey,
+ olmData: updatedOlmData,
+ olmDataKey,
+ olmEncryptionKeyDBLabel,
+ synchronizationValue,
+ forceWrite: false,
+ }),
+ updateNotifsUnreadThickThreadIDsStorage({
+ type: 'add',
+ threadIDs: [threadID],
+ forceWrite: false,
+ }),
+ ]);
return { id, ...decryptedNotification };
}
@@ -585,16 +600,26 @@
encryptedPayload,
);
- await persistNotifsAccountWithOlmData({
- accountWithPicklingKey: updatedNotifsAccount,
- accountEncryptionKey,
- encryptionKey,
- olmData: updatedOlmData,
- olmDataKey,
- olmEncryptionKeyDBLabel,
- synchronizationValue,
- forceWrite: false,
- });
+ const { threadID } = decryptedNotification;
+ invariant(typeof threadID === 'string', 'threadID should be string');
+
+ await Promise.all([
+ persistNotifsAccountWithOlmData({
+ accountWithPicklingKey: updatedNotifsAccount,
+ accountEncryptionKey,
+ encryptionKey,
+ olmData: updatedOlmData,
+ olmDataKey,
+ olmEncryptionKeyDBLabel,
+ synchronizationValue,
+ forceWrite: false,
+ }),
+ updateNotifsUnreadThickThreadIDsStorage({
+ type: 'add',
+ threadIDs: [threadID],
+ forceWrite: false,
+ }),
+ ]);
return decryptedNotification;
}
@@ -1253,6 +1278,118 @@
return Object.fromEntries(queriedUnreadCounts);
}
+async function updateNotifsUnreadThickThreadIDsStorage(input: {
+ +type: 'add' | 'remove' | 'set',
+ +threadIDs: $ReadOnlyArray<string>,
+ +forceWrite: boolean,
+}): Promise<void> {
+ const { type, threadIDs, forceWrite } = input;
+
+ const {
+ values: {
+ [INDEXED_DB_UNREAD_THICK_THREAD_IDS]: encryptedData,
+ [INDEXED_DB_UNREAD_THICK_THREAD_IDS_ENCRYPTION_KEY_DB_LABEL]:
+ encryptionKey,
+ },
+ synchronizationValue,
+ } = await localforage.getMultipleItems<{
+ unreadThickThreadIDs: ?EncryptedData,
+ unreadThickThreadIDsEncryptionKey: ?(CryptoKey | SubtleCrypto$JsonWebKey),
+ }>(
+ [
+ INDEXED_DB_UNREAD_THICK_THREAD_IDS,
+ INDEXED_DB_UNREAD_THICK_THREAD_IDS_ENCRYPTION_KEY_DB_LABEL,
+ ],
+ INDEXED_DB_UNREAD_THICK_THREADS_SYNC_KEY,
+ );
+
+ let unreadThickThreadIDs;
+ let unreadThickThreadIDsEncryptionKey;
+
+ if (encryptedData && encryptionKey) {
+ unreadThickThreadIDsEncryptionKey = await validateCryptoKey(encryptionKey);
+ unreadThickThreadIDs = new Set(
+ await deserializeEncryptedData<Array<string>>(
+ encryptedData,
+ unreadThickThreadIDsEncryptionKey,
+ ),
+ );
+ } else {
+ unreadThickThreadIDs = new Set<string>();
+ unreadThickThreadIDsEncryptionKey = await generateCryptoKey({
+ extractable: isDesktopSafari,
+ });
+ }
+
+ if (type === 'add') {
+ for (const threadID of threadIDs) {
+ unreadThickThreadIDs.add(threadID);
+ }
+ } else if (type === 'remove') {
+ for (const threadID of threadIDs) {
+ unreadThickThreadIDs.delete(threadID);
+ }
+ } else {
+ unreadThickThreadIDs = new Set(threadIDs);
+ }
+
+ const [encryptionKeyPersistentForm, updatedEncryptedData] = await Promise.all(
+ [
+ getCryptoKeyPersistentForm(unreadThickThreadIDsEncryptionKey),
+ serializeUnencryptedData(
+ [...unreadThickThreadIDs],
+ unreadThickThreadIDsEncryptionKey,
+ ),
+ ],
+ );
+
+ const newSynchronizationValue = uuid.v4();
+ await localforage.setMultipleItems(
+ {
+ [INDEXED_DB_UNREAD_THICK_THREAD_IDS]: updatedEncryptedData,
+ [INDEXED_DB_UNREAD_THICK_THREAD_IDS_ENCRYPTION_KEY_DB_LABEL]:
+ encryptionKeyPersistentForm,
+ },
+ INDEXED_DB_UNREAD_THICK_THREADS_SYNC_KEY,
+ synchronizationValue,
+ newSynchronizationValue,
+ forceWrite,
+ );
+}
+
+async function getNotifsUnreadThickThreadIDs(): Promise<
+ $ReadOnlyArray<string>,
+> {
+ const {
+ values: {
+ [INDEXED_DB_UNREAD_THICK_THREAD_IDS]: encryptedData,
+ [INDEXED_DB_UNREAD_THICK_THREAD_IDS_ENCRYPTION_KEY_DB_LABEL]:
+ encryptionKey,
+ },
+ } = await localforage.getMultipleItems<{
+ unreadThickThreadIDs: ?EncryptedData,
+ unreadThickThreadIDsEncryptionKey: ?(CryptoKey | SubtleCrypto$JsonWebKey),
+ }>(
+ [
+ INDEXED_DB_UNREAD_THICK_THREAD_IDS,
+ INDEXED_DB_UNREAD_THICK_THREAD_IDS_ENCRYPTION_KEY_DB_LABEL,
+ ],
+ INDEXED_DB_UNREAD_THICK_THREADS_SYNC_KEY,
+ );
+
+ if (!encryptionKey || !encryptedData) {
+ return [];
+ }
+
+ const unreadThickThreadIDsEncryptionKey =
+ await validateCryptoKey(encryptionKey);
+
+ return await deserializeEncryptedData<Array<string>>(
+ encryptedData,
+ unreadThickThreadIDsEncryptionKey,
+ );
+}
+
export {
decryptWebNotification,
decryptDesktopNotification,
@@ -1268,4 +1405,6 @@
persistEncryptionKey,
retrieveEncryptionKey,
persistNotifsAccountWithOlmData,
+ updateNotifsUnreadThickThreadIDsStorage,
+ getNotifsUnreadThickThreadIDs,
};

File Metadata

Mime Type
text/plain
Expires
Sat, Oct 19, 5:15 PM (21 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2324131
Default Alt Text
D13210.diff (15 KB)

Event Timeline