Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3007258
D13210.id43814.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
D13210.id43814.diff
View Options
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,
@@ -263,10 +263,14 @@
} = 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),
),
);
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 {
+ allUnreadCounts,
+ 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';
@@ -17,6 +23,9 @@
const connection = useSelector(allConnectionInfosSelector);
const unreadCount = useSelector(allUnreadCounts);
+ const { socketState: tunnelbrokerSocketState } = useTunnelbroker();
+ const currentUnreadThickThreadIDs = useSelector(unreadThickThreadIDsSelector);
+
React.useEffect(() => {
void (async () => {
const unreadCountUpdates: {
@@ -33,8 +42,23 @@
}
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),
]);
@@ -51,10 +75,21 @@
totalUnreadCount += queriedUnreadCounts[keyserverID];
}
+ totalUnreadCount += unreadThickThreadIDs.length;
document.title = getTitle(totalUnreadCount);
electron?.setBadge(totalUnreadCount === 0 ? null : totalUnreadCount);
})();
- }, [unreadCount, connection]);
+ }, [
+ tunnelbrokerSocketState,
+ currentUnreadThickThreadIDs,
+ unreadCount,
+ 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
Details
Attached
Mime Type
text/plain
Expires
Sat, Oct 19, 7:21 PM (21 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2324191
Default Alt Text
D13210.id43814.diff (11 KB)
Attached To
Mode
D13210: Update badge count on web and desktop taking into account thick threads
Attached
Detach File
Event Timeline
Log In to Comment