diff --git a/lib/hooks/flag-hooks.js b/lib/hooks/flag-hooks.js new file mode 100644 --- /dev/null +++ b/lib/hooks/flag-hooks.js @@ -0,0 +1,14 @@ +// @flow + +import { useIsCurrentUserStaff } from '../shared/staff-utils.js'; +import { isDev } from '../utils/dev-utils.js'; + +// If this returns true, then DM creation will use E2EE DMs encrypted via Olm +// and brokered by Tunnelbroker, instead of creating chats under GENESIS on the +// authoritative keyserver. +function useAllowOlmViaTunnelbrokerForDMs(): boolean { + const isCurrentUserStaff = useIsCurrentUserStaff(); + return isDev || isCurrentUserStaff; +} + +export { useAllowOlmViaTunnelbrokerForDMs }; diff --git a/lib/shared/thread-actions-utils.js b/lib/shared/thread-actions-utils.js --- a/lib/shared/thread-actions-utils.js +++ b/lib/shared/thread-actions-utils.js @@ -59,6 +59,7 @@ +viewerID: ?string, +handleError?: () => mixed, +calendarQuery: CalendarQuery, + +usingOlmViaTunnelbrokerForDMs: boolean, }; async function createRealThreadFromPendingThread({ @@ -69,6 +70,7 @@ sourceMessageID, viewerID, calendarQuery, + usingOlmViaTunnelbrokerForDMs, }: CreateRealThreadParameters): Promise { if (!threadIsPending(threadInfo.id)) { return threadInfo.id; @@ -125,7 +127,11 @@ ); if (threadTypeIsThick(threadInfo.type)) { const type = assertThickThreadType( - pendingThreadType(otherMemberIDs.length, 'thick'), + pendingThreadType( + otherMemberIDs.length, + 'thick', + usingOlmViaTunnelbrokerForDMs, + ), ); invariant( @@ -140,7 +146,11 @@ }); } else { const type = assertThinThreadType( - pendingThreadType(otherMemberIDs.length, 'thin'), + pendingThreadType( + otherMemberIDs.length, + 'thin', + usingOlmViaTunnelbrokerForDMs, + ), ); invariant( diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js --- a/lib/shared/thread-utils.js +++ b/lib/shared/thread-utils.js @@ -15,6 +15,7 @@ import ashoat from '../facts/ashoat.js'; import genesis from '../facts/genesis.js'; import { useLoggedInUserInfo } from '../hooks/account-hooks.js'; +import { useAllowOlmViaTunnelbrokerForDMs } from '../hooks/flag-hooks.js'; import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js'; import { hasPermission, @@ -109,7 +110,6 @@ import { entries, values } from '../utils/objects.js'; import { useSelector } from '../utils/redux-utils.js'; import { userSurfacedPermissionsFromRolePermissions } from '../utils/role-utils.js'; -import { usingOlmViaTunnelbrokerForDMs } from '../utils/services-utils.js'; import { firstLine } from '../utils/string-utils.js'; import { pendingThreadIDRegex, @@ -601,13 +601,14 @@ loggedInUserInfo: LoggedInUserInfo, userID: string, username: ?string, + allowOlmViaTunnelbrokerForDMs: boolean, ): PendingPersonalThread { const pendingPersonalThreadUserInfo = { id: userID, username: username, }; - const threadType = usingOlmViaTunnelbrokerForDMs + const threadType = allowOlmViaTunnelbrokerForDMs ? threadTypes.PERSONAL : threadTypes.GENESIS_PERSONAL; @@ -623,9 +624,15 @@ function createPendingThreadItem( loggedInUserInfo: LoggedInUserInfo, user: UserIDAndUsername, + allowOlmViaTunnelbrokerForDMs: boolean, ): ChatThreadItem { const { threadInfo, pendingPersonalThreadUserInfo } = - createPendingPersonalThread(loggedInUserInfo, user.id, user.username); + createPendingPersonalThread( + loggedInUserInfo, + user.id, + user.username, + allowOlmViaTunnelbrokerForDMs, + ); return { type: 'chatThreadItem', @@ -694,6 +701,7 @@ function pendingThreadType( numberOfOtherMembers: number, thickOrThin: 'thick' | 'thin', + usingOlmViaTunnelbrokerForDMs: boolean, ): 4 | 6 | 7 | 13 | 14 | 15 { if (usingOlmViaTunnelbrokerForDMs && thickOrThin === 'thick') { if (numberOfOtherMembers === 0) { @@ -1244,6 +1252,7 @@ const pendingToRealizedThreadIDs = useSelector(state => pendingToRealizedThreadIDsSelector(state.threadStore.threadInfos), ); + const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); return React.useCallback( (params: ExistingThreadInfoFinderParams): ?ThreadInfo => { if (!baseThreadInfo) { @@ -1272,7 +1281,11 @@ if (searching) { const pendingThinThreadID = getPendingThreadID( - pendingThreadType(userInfoInputArray.length, 'thin'), + pendingThreadType( + userInfoInputArray.length, + 'thin', + usingOlmViaTunnelbrokerForDMs, + ), [...userInfoInputArray.map(user => user.id), viewerID], sourceMessageID, ); @@ -1282,7 +1295,11 @@ return threadInfos[realizedThinThreadID]; } const pendingThickThreadID = getPendingThreadID( - pendingThreadType(userInfoInputArray.length, 'thick'), + pendingThreadType( + userInfoInputArray.length, + 'thick', + usingOlmViaTunnelbrokerForDMs, + ), [...userInfoInputArray.map(user => user.id), viewerID], sourceMessageID, ); @@ -1307,14 +1324,24 @@ const updatedThread = searching ? createPendingThread({ viewerID, - threadType: pendingThreadType(userInfoInputArray.length, 'thick'), + threadType: pendingThreadType( + userInfoInputArray.length, + 'thick', + usingOlmViaTunnelbrokerForDMs, + ), members: [loggedInUserInfo, ...userInfoInputArray], }) : baseThreadInfo; return updatedThread; }, - [baseThreadInfo, threadInfos, loggedInUserInfo, pendingToRealizedThreadIDs], + [ + baseThreadInfo, + threadInfos, + loggedInUserInfo, + usingOlmViaTunnelbrokerForDMs, + pendingToRealizedThreadIDs, + ], ); } @@ -1412,6 +1439,7 @@ threadSearchResults: $ReadOnlySet, usersSearchResults: $ReadOnlyArray, loggedInUserInfo: ?LoggedInUserInfo, + allowOlmViaTunnelbrokerForDMs: boolean, ): $ReadOnlyArray { if (!searchText) { return chatListData.filter( @@ -1444,7 +1472,11 @@ if (loggedInUserInfo) { chatItems.push( ...usersSearchResults.map(user => - createPendingThreadItem(loggedInUserInfo, user), + createPendingThreadItem( + loggedInUserInfo, + user, + allowOlmViaTunnelbrokerForDMs, + ), ), ); } @@ -1677,6 +1709,8 @@ const usersWithPersonalThread = useSelector(usersWithPersonalThreadSelector); + const allowOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); + return React.useMemo(() => { if (!loggedInUserInfo || !userID || !username) { return null; @@ -1701,10 +1735,12 @@ loggedInUserInfo, userID, username, + allowOlmViaTunnelbrokerForDMs, ); return pendingPersonalThreadInfo; }, [ + allowOlmViaTunnelbrokerForDMs, isViewerProfile, loggedInUserInfo, personalThreadInfos, diff --git a/lib/utils/services-utils.js b/lib/utils/services-utils.js --- a/lib/utils/services-utils.js +++ b/lib/utils/services-utils.js @@ -18,11 +18,6 @@ // an authoritative keyserver for things like DMs. const relyingOnAuthoritativeKeyserver = true; -// If this is true, then DM creation will use E2EE DMs encrypted via Olm and -// brokered by Tunnelbroker, instead of creating chats under GENESIS on the -// authoritative keyserver. -const usingOlmViaTunnelbrokerForDMs = false; - function handleHTTPResponseError(response: Response): void { if (!response.ok) { const { status, statusText } = response; @@ -52,7 +47,6 @@ usingCommServicesAccessToken, supportingMultipleKeyservers, relyingOnAuthoritativeKeyserver, - usingOlmViaTunnelbrokerForDMs, createHTTPAuthorizationHeader, createDefaultHTTPRequestHeaders, }; diff --git a/native/chat/chat-thread-list.react.js b/native/chat/chat-thread-list.react.js --- a/native/chat/chat-thread-list.react.js +++ b/native/chat/chat-thread-list.react.js @@ -23,6 +23,7 @@ import { useSharedValue } from 'react-native-reanimated'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; +import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js'; import { useThreadListSearch } from 'lib/hooks/thread-search-hooks.js'; import { type ChatThreadItem, @@ -35,7 +36,6 @@ import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import { threadTypes } from 'lib/types/thread-types-enum.js'; import type { UserInfo } from 'lib/types/user-types.js'; -import { usingOlmViaTunnelbrokerForDMs } from 'lib/utils/services-utils.js'; import { ChatThreadListItem } from './chat-thread-list-item.react.js'; import ChatThreadListSearch from './chat-thread-list-search.react.js'; @@ -132,6 +132,8 @@ [], ); + const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); + const composeThread = React.useCallback(() => { if (!loggedInUserInfo) { return; @@ -145,7 +147,7 @@ members: [loggedInUserInfo], }); navigateToThread({ threadInfo, searching: true }); - }, [loggedInUserInfo, navigateToThread]); + }, [loggedInUserInfo, navigateToThread, usingOlmViaTunnelbrokerForDMs]); const onSearchFocus = React.useCallback(() => { if (searchStatus !== 'inactive') { @@ -277,6 +279,8 @@ ], ); + const allowOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); + const listData: $ReadOnlyArray = React.useMemo(() => { const chatThreadItems = getThreadListSearchResults( boundChatListData, @@ -285,6 +289,7 @@ threadSearchResults, usersSearchResults, loggedInUserInfo, + allowOlmViaTunnelbrokerForDMs, ); const chatItems: Item[] = [...chatThreadItems]; @@ -299,6 +304,7 @@ return chatItems; }, [ + allowOlmViaTunnelbrokerForDMs, boundChatListData, emptyItem, filterThreads, diff --git a/native/chat/compose-thread-button.react.js b/native/chat/compose-thread-button.react.js --- a/native/chat/compose-thread-button.react.js +++ b/native/chat/compose-thread-button.react.js @@ -4,9 +4,9 @@ import { StyleSheet } from 'react-native'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; +import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js'; import { createPendingThread } from 'lib/shared/thread-utils.js'; import { threadTypes } from 'lib/types/thread-types-enum.js'; -import { usingOlmViaTunnelbrokerForDMs } from 'lib/utils/services-utils.js'; import type { ChatNavigationProp } from './chat.react.js'; import Button from '../components/button.react.js'; @@ -20,6 +20,7 @@ function ComposeThreadButton(props: Props) { const { navigate } = props; const loggedInUserInfo = useLoggedInUserInfo(); + const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); const onPress = React.useCallback(() => { if (!loggedInUserInfo) { return; @@ -38,7 +39,7 @@ searching: true, }, }); - }, [navigate, loggedInUserInfo]); + }, [loggedInUserInfo, usingOlmViaTunnelbrokerForDMs, navigate]); const { listForegroundSecondaryLabel } = useColors(); return ( diff --git a/native/chat/message-list-container.react.js b/native/chat/message-list-container.react.js --- a/native/chat/message-list-container.react.js +++ b/native/chat/message-list-container.react.js @@ -7,6 +7,7 @@ import { Text, View } from 'react-native'; import genesis from 'lib/facts/genesis.js'; +import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { userInfoSelectorForPotentialMembers } from 'lib/selectors/user-selectors.js'; import { @@ -95,6 +96,7 @@ // withOverlayContext +overlayContext: ?OverlayContextType, +measureMessages: MessagesMeasurer, + +usingOlmViaTunnelbrokerForDMs: boolean, }; type State = { +listDataWithHeights: ?$ReadOnlyArray, @@ -170,6 +172,7 @@ childThreadType={pendingThreadType( userInfoInputArray.length, 'thick', + this.props.usingOlmViaTunnelbrokerForDMs, )} /> ); @@ -180,6 +183,7 @@ childThreadType={pendingThreadType( userInfoInputArray.length, 'thin', + this.props.usingOlmViaTunnelbrokerForDMs, )} /> ); @@ -417,6 +421,8 @@ colors.panelBackgroundLabel, ]); + const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); + return ( {pinnedCountBanner} @@ -435,6 +441,7 @@ styles={styles} overlayContext={overlayContext} measureMessages={measureMessages} + usingOlmViaTunnelbrokerForDMs={usingOlmViaTunnelbrokerForDMs} /> ); diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js --- a/native/input/input-state-container.react.js +++ b/native/input/input-state-container.react.js @@ -30,6 +30,7 @@ useBlobServiceUpload, } from 'lib/actions/upload-actions.js'; import commStaffCommunity from 'lib/facts/comm-staff-community.js'; +import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js'; import { useNewThickThread } from 'lib/hooks/thread-hooks.js'; import type { CallSingleKeyserverEndpointOptions, @@ -175,6 +176,7 @@ ) => Promise, +newThickThread: (request: NewThickThreadRequest) => Promise, +textMessageCreationSideEffectsFunc: CreationSideEffectsFunc, + +usingOlmViaTunnelbrokerForDMs: boolean, }; type State = { +pendingUploads: PendingMultimediaUploads, @@ -611,6 +613,7 @@ sourceMessageID: threadInfo.sourceMessageID, viewerID: this.props.viewerID, calendarQuery, + usingOlmViaTunnelbrokerForDMs: this.props.usingOlmViaTunnelbrokerForDMs, }); this.pendingThreadCreations.set(threadInfo.id, threadCreationPromise); } @@ -1819,6 +1822,7 @@ const textMessageCreationSideEffectsFunc = useMessageCreationSideEffectsFunc(messageTypes.TEXT); const processAndSendDMOperation = useProcessAndSendDMOperation(); + const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); return ( ); }); diff --git a/web/chat/thread-list-provider.js b/web/chat/thread-list-provider.js --- a/web/chat/thread-list-provider.js +++ b/web/chat/thread-list-provider.js @@ -4,6 +4,7 @@ import * as React from 'react'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; +import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js'; import { useThreadListSearch } from 'lib/hooks/thread-search-hooks.js'; import { type ChatThreadItem, @@ -179,6 +180,7 @@ ); const threadFilter = activeTab === 'Muted' ? threadInBackgroundChatList : threadInHomeChatList; + const allowOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); const chatListDataWithoutFilter = getThreadListSearchResults( chatListData, searchText, @@ -186,6 +188,7 @@ threadSearchResults, usersSearchResults, loggedInUserInfo, + allowOlmViaTunnelbrokerForDMs, ); const activeTopLevelChatThreadItem = useChatThreadItem( activeTopLevelThreadInfo, diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js --- a/web/input/input-state-container.react.js +++ b/web/input/input-state-container.react.js @@ -40,6 +40,7 @@ } from 'lib/components/modal-provider.react.js'; import blobService from 'lib/facts/blob-service.js'; import commStaffCommunity from 'lib/facts/comm-staff-community.js'; +import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js'; import { useNewThickThread } from 'lib/hooks/thread-hooks.js'; import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js'; import { getNextLocalUploadID } from 'lib/media/media-utils.js'; @@ -177,6 +178,7 @@ +unregisterSendCallback: (() => mixed) => void, +textMessageCreationSideEffectsFunc: CreationSideEffectsFunc, +identityContext: ?IdentityClientContextType, + +usingOlmViaTunnelbrokerForDMs: boolean, }; type WritableState = { pendingUploads: { @@ -610,6 +612,7 @@ sourceMessageID: threadInfo.sourceMessageID, viewerID: this.props.viewerID, calendarQuery, + usingOlmViaTunnelbrokerForDMs: this.props.usingOlmViaTunnelbrokerForDMs, }); this.pendingThreadCreations.set(threadInfo.id, threadCreationPromise); } @@ -1771,6 +1774,7 @@ ); const textMessageCreationSideEffectsFunc = useMessageCreationSideEffectsFunc(messageTypes.TEXT); + const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); return ( ); }); diff --git a/web/utils/thread-utils.js b/web/utils/thread-utils.js --- a/web/utils/thread-utils.js +++ b/web/utils/thread-utils.js @@ -4,6 +4,7 @@ import * as React from 'react'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; +import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { userInfoSelectorForPotentialMembers } from 'lib/selectors/user-selectors.js'; import { @@ -13,7 +14,6 @@ import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import { threadTypes } from 'lib/types/thread-types-enum.js'; import type { AccountUserInfo } from 'lib/types/user-types.js'; -import { usingOlmViaTunnelbrokerForDMs } from 'lib/utils/services-utils.js'; import { useSelector } from '../redux/redux-utils.js'; @@ -54,6 +54,8 @@ }), ); + const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs(); + const threadType = usingOlmViaTunnelbrokerForDMs ? threadTypes.PRIVATE : threadTypes.GENESIS_PRIVATE;