Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3507418
D13210.id43839.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D13210.id43839.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,
@@ -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
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 21, 8:45 PM (20 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2689021
Default Alt Text
D13210.id43839.diff (15 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