Page MenuHomePhorge

D15528.1765026703.diff
No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None

D15528.1765026703.diff

diff --git a/lib/components/debug-logs-context.js b/lib/components/debug-logs-context.js
--- a/lib/components/debug-logs-context.js
+++ b/lib/components/debug-logs-context.js
@@ -130,6 +130,14 @@
+operation: 'uploadOneTimeKeys',
+success: boolean,
+resultDescription: string,
+ }
+ | {
+ +operation: 'resendPeerToPeerMessages',
+ +deviceID: string,
+ +newDeviceID?: ?string,
+ +messageCount: number,
+ +success: boolean,
+ +resultDescription: string,
};
function useOlmDebugLogs(): (olmLog: OlmDebugLog) => mixed {
diff --git a/lib/hooks/peer-list-hooks.js b/lib/hooks/peer-list-hooks.js
--- a/lib/hooks/peer-list-hooks.js
+++ b/lib/hooks/peer-list-hooks.js
@@ -4,6 +4,7 @@
import * as React from 'react';
import { setPeerDeviceListsActionType } from '../actions/aux-user-actions.js';
+import { usePeerOlmSessionsCreatorContext } from '../components/peer-olm-session-creator-provider.react.js';
import {
getAllPeerUserIDAndDeviceIDs,
getPeersPrimaryDeviceIDs,
@@ -11,6 +12,7 @@
import { IdentityClientContext } from '../shared/identity-client-context.js';
import { usePeerToPeerCommunication } from '../tunnelbroker/peer-to-peer-context.js';
import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js';
+import { useResendPeerToPeerMessages } from '../tunnelbroker/use-resend-peer-to-peer-messages.js';
import type {
UsersRawDeviceLists,
UsersDevicesPlatformDetails,
@@ -300,10 +302,58 @@
}, [getAuthMetadata, identityClient]);
}
+function useResetRatchetState(): (userID: ?string) => Promise<void> {
+ const { createOlmSessionsWithUser } = usePeerOlmSessionsCreatorContext();
+ const resendPeerToPeerMessages = useResendPeerToPeerMessages();
+ const getAndUpdateDeviceListsForUsers = useGetAndUpdateDeviceListsForUsers();
+
+ return React.useCallback(
+ async (userID: ?string) => {
+ if (!userID) {
+ return;
+ }
+
+ const deviceLists = await getAndUpdateDeviceListsForUsers([userID]);
+ const deviceList = deviceLists?.[userID];
+ const deviceIDs = deviceList?.devices;
+
+ if (!deviceIDs) {
+ return;
+ }
+
+ const sessionCreationPromises = deviceIDs.map(deviceID =>
+ createOlmSessionsWithUser(
+ userID,
+ [
+ {
+ deviceID,
+ sessionCreationOptions: { overwriteContentSession: true },
+ },
+ ],
+ 'session_reset',
+ ),
+ );
+
+ await Promise.all(sessionCreationPromises);
+
+ const messageResetPromises = deviceIDs.map(deviceID =>
+ resendPeerToPeerMessages(deviceID),
+ );
+ await Promise.all(messageResetPromises);
+ },
+ [
+ createOlmSessionsWithUser,
+ getAndUpdateDeviceListsForUsers,
+ resendPeerToPeerMessages,
+ ],
+ );
+}
+
export {
useGetDeviceListsForUsers,
useBroadcastDeviceListUpdates,
useGetAndUpdateDeviceListsForUsers,
useBroadcastAccountDeletion,
useCurrentIdentityUserState,
+ useResetRatchetState,
};
diff --git a/lib/tunnelbroker/use-resend-peer-to-peer-messages.js b/lib/tunnelbroker/use-resend-peer-to-peer-messages.js
--- a/lib/tunnelbroker/use-resend-peer-to-peer-messages.js
+++ b/lib/tunnelbroker/use-resend-peer-to-peer-messages.js
@@ -3,6 +3,7 @@
import * as React from 'react';
import { usePeerToPeerCommunication } from './peer-to-peer-context.js';
+import { useOlmDebugLogs } from '../components/debug-logs-context.js';
import type { PrimaryDeviceChange } from '../hooks/peer-list-hooks.js';
import { getConfig } from '../utils/config.js';
@@ -12,16 +13,39 @@
) => Promise<void> {
const { sqliteAPI } = getConfig();
const { processOutboundMessages } = usePeerToPeerCommunication();
+ const olmDebugLog = useOlmDebugLogs();
return React.useCallback(
async (deviceID: string, newDeviceID?: ?string) => {
- const messageIDs = await sqliteAPI.resetOutboundP2PMessagesForDevice(
- deviceID,
- newDeviceID,
- );
- processOutboundMessages(messageIDs);
+ try {
+ const messageIDs = await sqliteAPI.resetOutboundP2PMessagesForDevice(
+ deviceID,
+ newDeviceID,
+ );
+
+ olmDebugLog({
+ operation: 'resendPeerToPeerMessages',
+ deviceID,
+ newDeviceID,
+ messageCount: messageIDs.length,
+ success: true,
+ resultDescription: `Resent ${messageIDs.length} messages for device ${deviceID}`,
+ });
+
+ processOutboundMessages(messageIDs);
+ } catch (error) {
+ olmDebugLog({
+ operation: 'resendPeerToPeerMessages',
+ deviceID,
+ newDeviceID,
+ messageCount: 0,
+ success: false,
+ resultDescription: `Failed to resent messages for device ${deviceID}: ${error.message}`,
+ });
+ throw error;
+ }
},
- [processOutboundMessages, sqliteAPI],
+ [processOutboundMessages, sqliteAPI, olmDebugLog],
);
}
diff --git a/native/profile/user-relationship-tooltip-modal.react.js b/native/profile/user-relationship-tooltip-modal.react.js
--- a/native/profile/user-relationship-tooltip-modal.react.js
+++ b/native/profile/user-relationship-tooltip-modal.react.js
@@ -4,6 +4,7 @@
import { TouchableOpacity } from 'react-native';
import { updateRelationshipsActionTypes } from 'lib/actions/relationship-actions.js';
+import { useResetRatchetState } from 'lib/hooks/peer-list-hooks.js';
import { useUpdateRelationships } from 'lib/hooks/relationship-hooks.js';
import { stringForUser } from 'lib/shared/user-utils.js';
import type { RelativeUserInfo } from 'lib/types/user-types.js';
@@ -115,6 +116,12 @@
action: 'unblock',
});
+ const reset = useResetRatchetState();
+ const resetRatchetStateAction = React.useCallback(
+ () => reset(route.params.relativeUserInfo.id),
+ [reset, route.params.relativeUserInfo.id],
+ );
+
return (
<>
<TooltipItem
@@ -130,6 +137,12 @@
onPress={onUnblockUser}
key="unblock"
/>
+ <TooltipItem
+ id="reset-ratchet"
+ text="Reset ratchet"
+ onPress={resetRatchetStateAction}
+ key="reset"
+ />
</>
);
}
diff --git a/native/user-profile/user-profile-menu-button.react.js b/native/user-profile/user-profile-menu-button.react.js
--- a/native/user-profile/user-profile-menu-button.react.js
+++ b/native/user-profile/user-profile-menu-button.react.js
@@ -6,9 +6,11 @@
import { TouchableOpacity, View } from 'react-native';
import { useRelationshipPrompt } from 'lib/hooks/relationship-prompt.js';
+import { useIsCurrentUserStaff } from 'lib/shared/staff-utils.js';
import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
import { userRelationshipStatus } from 'lib/types/relationship-types.js';
import type { UserInfo } from 'lib/types/user-types';
+import { isDev } from 'lib/utils/dev-utils.js';
import { userProfileMenuButtonHeight } from './user-profile-constants.js';
import SWMansionIcon from '../components/swmansion-icon.react.js';
@@ -43,6 +45,8 @@
const menuButtonRef = React.useRef<?React.ElementRef<typeof View>>();
+ const isCurrentUserStaff = useIsCurrentUserStaff();
+
const visibleTooltipActionEntryIDs = React.useMemo(() => {
const result = [];
@@ -60,8 +64,12 @@
result.push('block');
}
+ if (isCurrentUserStaff || isDev) {
+ result.push('reset-ratchet');
+ }
+
return result;
- }, [otherUserInfo?.relationshipStatus]);
+ }, [isCurrentUserStaff, otherUserInfo?.relationshipStatus]);
const onPressMenuButton = React.useCallback(() => {
invariant(
diff --git a/web/modals/user-profile/user-profile-menu.react.js b/web/modals/user-profile/user-profile-menu.react.js
--- a/web/modals/user-profile/user-profile-menu.react.js
+++ b/web/modals/user-profile/user-profile-menu.react.js
@@ -1,13 +1,20 @@
// @flow
-import { faUserMinus, faUserShield } from '@fortawesome/free-solid-svg-icons';
+import {
+ faEraser,
+ faUserMinus,
+ faUserShield,
+} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as React from 'react';
import SWMansionIcon from 'lib/components/swmansion-icon.react.js';
+import { useResetRatchetState } from 'lib/hooks/peer-list-hooks.js';
import { useRelationshipPrompt } from 'lib/hooks/relationship-prompt.js';
+import { useIsCurrentUserStaff } from 'lib/shared/staff-utils.js';
import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
import { userRelationshipStatus } from 'lib/types/relationship-types.js';
+import { isDev } from 'lib/utils/dev-utils.js';
import MenuItem from '../../components/menu-item.react.js';
import Menu from '../../components/menu.react.js';
@@ -20,6 +27,8 @@
const unblockIcon = <FontAwesomeIcon icon={faUserShield} />;
+const resetIcon = <FontAwesomeIcon icon={faEraser} />;
+
type Props = {
+threadInfo: ThreadInfo,
};
@@ -27,6 +36,8 @@
function UserProfileMenu(props: Props): React.Node {
const { threadInfo } = props;
+ const isCurrentUserStaff = useIsCurrentUserStaff();
+
const {
otherUserInfo,
callbacks: { unfriendUser, blockUser, unblockUser },
@@ -69,6 +80,24 @@
[unblockUser],
);
+ const reset = useResetRatchetState();
+ const resetRatchetStateAction = React.useCallback(
+ () => reset(otherUserInfo?.id),
+ [otherUserInfo?.id, reset],
+ );
+ const resetRatchetState = React.useMemo(
+ () => (
+ <MenuItem
+ key="reset"
+ text="Reset ratchet"
+ dangerous
+ iconComponent={resetIcon}
+ onClick={resetRatchetStateAction}
+ />
+ ),
+ [resetRatchetStateAction],
+ );
+
const menuItems = React.useMemo(() => {
const items = [];
if (otherUserInfo?.relationshipStatus === userRelationshipStatus.FRIEND) {
@@ -85,10 +114,16 @@
items.push(blockMenuItem);
}
+ if (isCurrentUserStaff || isDev) {
+ items.push(resetRatchetState);
+ }
+
return items;
}, [
blockMenuItem,
+ isCurrentUserStaff,
otherUserInfo?.relationshipStatus,
+ resetRatchetState,
unblockMenuItem,
unfriendMenuIcon,
]);

File Metadata

Mime Type
text/plain
Expires
Sat, Dec 6, 1:11 PM (21 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5837867
Default Alt Text
D15528.1765026703.diff (10 KB)

Event Timeline