Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3332948
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
28 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js
index 6c2a92484..d83856b52 100644
--- a/native/navigation/route-names.js
+++ b/native/navigation/route-names.js
@@ -1,399 +1,400 @@
// @flow
import type { RouteProp } from '@react-navigation/core';
import type { ActionResultModalParams } from './action-result-modal.react.js';
import type { InviteLinkModalParams } from './invite-link-modal.react';
import type { AvatarSelectionParams } from '../account/registration/avatar-selection.react.js';
import type { ConnectEthereumParams } from '../account/registration/connect-ethereum.react.js';
import type { ConnectFarcasterParams } from '../account/registration/connect-farcaster.react.js';
import type { EmojiAvatarSelectionParams } from '../account/registration/emoji-avatar-selection.react.js';
import type { ExistingEthereumAccountParams } from '../account/registration/existing-ethereum-account.react.js';
import type { KeyserverSelectionParams } from '../account/registration/keyserver-selection.react.js';
import type { PasswordSelectionParams } from '../account/registration/password-selection.react.js';
import type { RegistrationTermsParams } from '../account/registration/registration-terms.react.js';
import type { CreateSIWEBackupMessageParams } from '../account/registration/siwe-backup-message-creation.react.js';
import type { UsernameSelectionParams } from '../account/registration/username-selection.react.js';
import type { TermsAndPrivacyModalParams } from '../account/terms-and-privacy-modal.react.js';
import type { RestoreSIWEBackupParams } from '../backup/restore-siwe-backup.react.js';
import type { ThreadPickerModalParams } from '../calendar/thread-picker-modal.react.js';
import type { ComposeSubchannelParams } from '../chat/compose-subchannel.react.js';
import type { FullScreenThreadMediaGalleryParams } from '../chat/fullscreen-thread-media-gallery.react.js';
import type { ImagePasteModalParams } from '../chat/image-paste-modal.react.js';
import type { MessageListParams } from '../chat/message-list-types.js';
import type { MessageReactionsModalParams } from '../chat/message-reactions-modal.react.js';
import type { MultimediaMessageTooltipModalParams } from '../chat/multimedia-message-tooltip-modal.react.js';
import type { PinnedMessagesScreenParams } from '../chat/pinned-messages-screen.react.js';
import type { RobotextMessageTooltipModalParams } from '../chat/robotext-message-tooltip-modal.react.js';
import type { AddUsersModalParams } from '../chat/settings/add-users-modal.react.js';
import type { ColorSelectorModalParams } from '../chat/settings/color-selector-modal.react.js';
import type { ComposeSubchannelModalParams } from '../chat/settings/compose-subchannel-modal.react.js';
import type { DeleteThreadParams } from '../chat/settings/delete-thread.react.js';
import type { EmojiThreadAvatarCreationParams } from '../chat/settings/emoji-thread-avatar-creation.react.js';
import type { ThreadSettingsMemberTooltipModalParams } from '../chat/settings/thread-settings-member-tooltip-modal.react.js';
import type { ThreadSettingsNotificationsParams } from '../chat/settings/thread-settings-notifications.react.js';
import type { ThreadSettingsParams } from '../chat/settings/thread-settings.react.js';
import type { SidebarListModalParams } from '../chat/sidebar-list-modal.react.js';
import type { SubchannelListModalParams } from '../chat/subchannels-list-modal.react.js';
import type { TextMessageTooltipModalParams } from '../chat/text-message-tooltip-modal.react.js';
import type { TogglePinModalParams } from '../chat/toggle-pin-modal.react.js';
import type { TagFarcasterChannelByNameParams } from '../community-settings/tag-farcaster-channel/tag-farcaster-channel-by-name.react.js';
import type { TagFarcasterChannelParams } from '../community-settings/tag-farcaster-channel/tag-farcaster-channel.react.js';
import type { InviteLinksNavigatorParams } from '../invite-links/invite-links-navigator.react.js';
import type { ManagePublicLinkScreenParams } from '../invite-links/manage-public-link-screen.react.js';
import type { ViewInviteLinksScreenParams } from '../invite-links/view-invite-links-screen.react.js';
import type { ChatCameraModalParams } from '../media/chat-camera-modal.react.js';
import type { ImageModalParams } from '../media/image-modal.react.js';
import type { ThreadAvatarCameraModalParams } from '../media/thread-avatar-camera-modal.react.js';
import type { VideoPlaybackModalParams } from '../media/video-playback-modal.react.js';
import type { CustomServerModalParams } from '../profile/custom-server-modal.react.js';
import type { KeyserverSelectionBottomSheetParams } from '../profile/keyserver-selection-bottom-sheet.react.js';
+import type { LinkedDevicesBottomSheetParams } from '../profile/linked-devices-bottom-sheet.react.js';
import type { UserRelationshipTooltipModalParams } from '../profile/user-relationship-tooltip-modal.react.js';
import type { ChangeRolesScreenParams } from '../roles/change-roles-screen.react.js';
import type { CommunityRolesScreenParams } from '../roles/community-roles-screen.react.js';
import type { CreateRolesScreenParams } from '../roles/create-roles-screen.react.js';
import type { MessageSearchParams } from '../search/message-search.react.js';
import type { NUXTipsOverlayParams } from '../tooltip/nux-tips-overlay.react.js';
import type { UserProfileAvatarModalParams } from '../user-profile/user-profile-avatar-modal.react.js';
import type { UserProfileBottomSheetParams } from '../user-profile/user-profile-bottom-sheet.react.js';
export const ActionResultModalRouteName = 'ActionResultModal';
export const AddUsersModalRouteName = 'AddUsersModal';
export const AppearancePreferencesRouteName = 'AppearancePreferences';
export const AppRouteName = 'App';
export const AppsRouteName = 'Apps';
export const BackgroundChatThreadListRouteName = 'BackgroundChatThreadList';
export const BackupMenuRouteName = 'BackupMenu';
export const BlockListRouteName = 'BlockList';
export const BuildInfoRouteName = 'BuildInfo';
export const CalendarRouteName = 'Calendar';
export const CalendarScreenRouteName = 'CalendarScreen';
export const ChangeRolesScreenRouteName = 'ChangeRolesScreen';
export const ChatCameraModalRouteName = 'ChatCameraModal';
export const ChatRouteName = 'Chat';
export const ChatThreadListRouteName = 'ChatThreadList';
export const ColorSelectorModalRouteName = 'ColorSelectorModal';
export const ComposeSubchannelModalRouteName = 'ComposeSubchannelModal';
export const ComposeSubchannelRouteName = 'ComposeSubchannel';
export const CommunityDrawerNavigatorRouteName = 'CommunityDrawerNavigator';
export const CustomServerModalRouteName = 'CustomServerModal';
export const DefaultNotificationsPreferencesRouteName = 'DefaultNotifications';
export const DeleteAccountRouteName = 'DeleteAccount';
export const DeleteThreadRouteName = 'DeleteThread';
export const DevToolsRouteName = 'DevTools';
export const EditPasswordRouteName = 'EditPassword';
export const EmojiThreadAvatarCreationRouteName = 'EmojiThreadAvatarCreation';
export const EmojiUserAvatarCreationRouteName = 'EmojiUserAvatarCreation';
export const FriendListRouteName = 'FriendList';
export const FullScreenThreadMediaGalleryRouteName =
'FullScreenThreadMediaGallery';
export const HomeChatThreadListRouteName = 'HomeChatThreadList';
export const ImageModalRouteName = 'ImageModal';
export const ImagePasteModalRouteName = 'ImagePasteModal';
export const InviteLinkModalRouteName = 'InviteLinkModal';
export const InviteLinkNavigatorRouteName = 'InviteLinkNavigator';
export const LinkedDevicesRouteName = 'LinkedDevices';
export const LinkedDevicesBottomSheetRouteName = 'LinkedDevicesBottomSheet';
export const LoggedOutModalRouteName = 'LoggedOutModal';
export const ManagePublicLinkRouteName = 'ManagePublicLink';
export const MessageListRouteName = 'MessageList';
export const MessageReactionsModalRouteName = 'MessageReactionsModal';
export const PinnedMessagesScreenRouteName = 'PinnedMessagesScreen';
export const MultimediaMessageTooltipModalRouteName =
'MultimediaMessageTooltipModal';
export const PrivacyPreferencesRouteName = 'PrivacyPreferences';
export const ProfileRouteName = 'Profile';
export const ProfileScreenRouteName = 'ProfileScreen';
export const UserRelationshipTooltipModalRouteName =
'UserRelationshipTooltipModal';
export const RobotextMessageTooltipModalRouteName =
'RobotextMessageTooltipModal';
export const SecondaryDeviceQRCodeScannerRouteName =
'SecondaryDeviceQRCodeScanner';
export const SidebarListModalRouteName = 'SidebarListModal';
export const SubchannelsListModalRouteName = 'SubchannelsListModal';
export const TabNavigatorRouteName = 'TabNavigator';
export const TextMessageTooltipModalRouteName = 'TextMessageTooltipModal';
export const ThreadAvatarCameraModalRouteName = 'ThreadAvatarCameraModal';
export const ThreadPickerModalRouteName = 'ThreadPickerModal';
export const ThreadSettingsMemberTooltipModalRouteName =
'ThreadSettingsMemberTooltipModal';
export const ThreadSettingsRouteName = 'ThreadSettings';
export const TunnelbrokerMenuRouteName = 'TunnelbrokerMenu';
export const UserAvatarCameraModalRouteName = 'UserAvatarCameraModal';
export const TogglePinModalRouteName = 'TogglePinModal';
export const VideoPlaybackModalRouteName = 'VideoPlaybackModal';
export const ViewInviteLinksRouteName = 'ViewInviteLinks';
export const TermsAndPrivacyRouteName = 'TermsAndPrivacyModal';
export const RegistrationRouteName = 'Registration';
export const KeyserverSelectionRouteName = 'KeyserverSelection';
export const CoolOrNerdModeSelectionRouteName = 'CoolOrNerdModeSelection';
export const ConnectEthereumRouteName = 'ConnectEthereum';
export const CreateSIWEBackupMessageRouteName = 'CreateSIWEBackupMessage';
export const CreateMissingSIWEBackupMessageRouteName =
'CreateMissingSIWEBackupMessage';
export const RestoreSIWEBackupRouteName = 'RestoreSIWEBackup';
export const ExistingEthereumAccountRouteName = 'ExistingEthereumAccount';
export const ConnectFarcasterRouteName = 'ConnectFarcaster';
export const UsernameSelectionRouteName = 'UsernameSelection';
export const CommunityCreationRouteName = 'CommunityCreation';
export const CommunityConfigurationRouteName = 'CommunityConfiguration';
export const MessageSearchRouteName = 'MessageSearch';
export const PasswordSelectionRouteName = 'PasswordSelection';
export const AvatarSelectionRouteName = 'AvatarSelection';
export const EmojiAvatarSelectionRouteName = 'EmojiAvatarSelection';
export const RegistrationUserAvatarCameraModalRouteName =
'RegistrationUserAvatarCameraModal';
export const RegistrationTermsRouteName = 'RegistrationTerms';
export const RolesNavigatorRouteName = 'RolesNavigator';
export const CommunityRolesScreenRouteName = 'CommunityRolesScreen';
export const CreateRolesScreenRouteName = 'CreateRolesScreen';
export const QRCodeSignInNavigatorRouteName = 'QRCodeSignInNavigator';
export const QRCodeScreenRouteName = 'QRCodeScreen';
export const UserProfileBottomSheetNavigatorRouteName =
'UserProfileBottomSheetNavigator';
export const UserProfileBottomSheetRouteName = 'UserProfileBottomSheet';
export const UserProfileAvatarModalRouteName = 'UserProfileAvatarModal';
export const KeyserverSelectionListRouteName = 'KeyserverSelectionList';
export const AddKeyserverRouteName = 'AddKeyserver';
export const KeyserverSelectionBottomSheetRouteName =
'KeyserverSelectionBottomSheet';
export const AccountDoesNotExistRouteName = 'AccountDoesNotExist';
export const FarcasterAccountSettingsRouteName = 'FarcasterAccountSettings';
export const ConnectFarcasterBottomSheetRouteName =
'ConnectFarcasterBottomSheet';
export const TagFarcasterChannelNavigatorRouteName =
'TagFarcasterChannelNavigator';
export const TagFarcasterChannelRouteName = 'TagFarcasterChannel';
export const TagFarcasterChannelByNameRouteName = 'TagFarcasterChannelByName';
export const ThreadSettingsNotificationsRouteName =
'ThreadSettingsNotifications';
export const IntroTipRouteName = 'IntroTip';
export const CommunityDrawerTipRouteName = 'CommunityDrawerTip';
export const HomeTabTipRouteName = 'HomeTabTip';
export const MutedTabTipRouteName = 'MutedTabTip';
export const NUXTipOverlayBackdropRouteName = 'NUXTipOverlayBackdrop';
export type RootParamList = {
+LoggedOutModal: void,
+App: void,
+ThreadPickerModal: ThreadPickerModalParams,
+AddUsersModal: AddUsersModalParams,
+CustomServerModal: CustomServerModalParams,
+ColorSelectorModal: ColorSelectorModalParams,
+ComposeSubchannelModal: ComposeSubchannelModalParams,
+SidebarListModal: SidebarListModalParams,
+ImagePasteModal: ImagePasteModalParams,
+TermsAndPrivacyModal: TermsAndPrivacyModalParams,
+SubchannelsListModal: SubchannelListModalParams,
+MessageReactionsModal: MessageReactionsModalParams,
+Registration: void,
+CommunityCreation: void,
+InviteLinkModal: InviteLinkModalParams,
+InviteLinkNavigator: InviteLinksNavigatorParams,
+RolesNavigator: void,
+QRCodeSignInNavigator: void,
+UserProfileBottomSheetNavigator: void,
+TunnelbrokerMenu: void,
+KeyserverSelectionBottomSheet: KeyserverSelectionBottomSheetParams,
- +LinkedDevicesBottomSheet: void,
+ +LinkedDevicesBottomSheet: LinkedDevicesBottomSheetParams,
+ConnectFarcasterBottomSheet: void,
+TagFarcasterChannelNavigator: void,
+CreateMissingSIWEBackupMessage: void,
+RestoreSIWEBackup: RestoreSIWEBackupParams,
};
export type NUXTipRouteNames =
| typeof IntroTipRouteName
| typeof CommunityDrawerTipRouteName
| typeof HomeTabTipRouteName
| typeof MutedTabTipRouteName;
export type MessageTooltipRouteNames =
| typeof RobotextMessageTooltipModalRouteName
| typeof MultimediaMessageTooltipModalRouteName
| typeof TextMessageTooltipModalRouteName;
export const PinnableMessageTooltipRouteNames = [
TextMessageTooltipModalRouteName,
MultimediaMessageTooltipModalRouteName,
];
export type TooltipModalParamList = {
+MultimediaMessageTooltipModal: MultimediaMessageTooltipModalParams,
+TextMessageTooltipModal: TextMessageTooltipModalParams,
+ThreadSettingsMemberTooltipModal: ThreadSettingsMemberTooltipModalParams,
+UserRelationshipTooltipModal: UserRelationshipTooltipModalParams,
+RobotextMessageTooltipModal: RobotextMessageTooltipModalParams,
};
export type OverlayParamList = {
+CommunityDrawerNavigator: void,
+ImageModal: ImageModalParams,
+ActionResultModal: ActionResultModalParams,
+ChatCameraModal: ChatCameraModalParams,
+UserAvatarCameraModal: void,
+ThreadAvatarCameraModal: ThreadAvatarCameraModalParams,
+VideoPlaybackModal: VideoPlaybackModalParams,
+TogglePinModal: TogglePinModalParams,
+IntroTip: NUXTipsOverlayParams,
+CommunityDrawerTip: NUXTipsOverlayParams,
+HomeTabTip: NUXTipsOverlayParams,
+MutedTabTip: NUXTipsOverlayParams,
+NUXTipOverlayBackdrop: void,
...TooltipModalParamList,
};
export type TabParamList = {
+Calendar: void,
+Chat: void,
+Profile: void,
+Apps: void,
};
export type ChatParamList = {
+ChatThreadList: void,
+MessageList: MessageListParams,
+ComposeSubchannel: ComposeSubchannelParams,
+ThreadSettings: ThreadSettingsParams,
+EmojiThreadAvatarCreation: EmojiThreadAvatarCreationParams,
+DeleteThread: DeleteThreadParams,
+FullScreenThreadMediaGallery: FullScreenThreadMediaGalleryParams,
+PinnedMessagesScreen: PinnedMessagesScreenParams,
+MessageSearch: MessageSearchParams,
+ChangeRolesScreen: ChangeRolesScreenParams,
+ThreadSettingsNotifications: ThreadSettingsNotificationsParams,
};
export type ChatTopTabsParamList = {
+HomeChatThreadList: void,
+BackgroundChatThreadList: void,
};
export type ProfileParamList = {
+ProfileScreen: void,
+EmojiUserAvatarCreation: void,
+EditPassword: void,
+DeleteAccount: void,
+BuildInfo: void,
+DevTools: void,
+AppearancePreferences: void,
+PrivacyPreferences: void,
+DefaultNotifications: void,
+FriendList: void,
+BlockList: void,
+LinkedDevices: void,
+SecondaryDeviceQRCodeScanner: void,
+BackupMenu: void,
+TunnelbrokerMenu: void,
+KeyserverSelectionList: void,
+AddKeyserver: void,
+FarcasterAccountSettings: void,
};
export type CalendarParamList = {
+CalendarScreen: void,
};
export type CommunityDrawerParamList = { +TabNavigator: void };
export type RegistrationParamList = {
+CoolOrNerdModeSelection: void,
+KeyserverSelection: KeyserverSelectionParams,
+ConnectEthereum: ConnectEthereumParams,
+ExistingEthereumAccount: ExistingEthereumAccountParams,
+ConnectFarcaster: ConnectFarcasterParams,
+CreateSIWEBackupMessage: CreateSIWEBackupMessageParams,
+UsernameSelection: UsernameSelectionParams,
+PasswordSelection: PasswordSelectionParams,
+AvatarSelection: AvatarSelectionParams,
+EmojiAvatarSelection: EmojiAvatarSelectionParams,
+RegistrationUserAvatarCameraModal: void,
+RegistrationTerms: RegistrationTermsParams,
+AccountDoesNotExist: void,
};
export type InviteLinkParamList = {
+ViewInviteLinks: ViewInviteLinksScreenParams,
+ManagePublicLink: ManagePublicLinkScreenParams,
};
export type CommunityCreationParamList = {
+CommunityConfiguration: void,
};
export type RolesParamList = {
+CommunityRolesScreen: CommunityRolesScreenParams,
+CreateRolesScreen: CreateRolesScreenParams,
};
export type TagFarcasterChannelParamList = {
+TagFarcasterChannel: TagFarcasterChannelParams,
+TagFarcasterChannelByName: TagFarcasterChannelByNameParams,
};
export type QRCodeSignInParamList = {
+QRCodeScreen: void,
};
export type UserProfileBottomSheetParamList = {
+UserProfileBottomSheet: UserProfileBottomSheetParams,
+UserProfileAvatarModal: UserProfileAvatarModalParams,
+UserRelationshipTooltipModal: UserRelationshipTooltipModalParams,
};
export type ScreenParamList = {
...RootParamList,
...OverlayParamList,
...TabParamList,
...ChatParamList,
...ChatTopTabsParamList,
...ProfileParamList,
...CalendarParamList,
...CommunityDrawerParamList,
...RegistrationParamList,
...InviteLinkParamList,
...CommunityCreationParamList,
...RolesParamList,
...QRCodeSignInParamList,
...UserProfileBottomSheetParamList,
...TagFarcasterChannelParamList,
};
export type NavigationRoute<RouteName: string = $Keys<ScreenParamList>> =
RouteProp<ScreenParamList, RouteName>;
export const accountModals = [
LoggedOutModalRouteName,
RegistrationRouteName,
QRCodeSignInNavigatorRouteName,
];
export const scrollBlockingModals = [
ImageModalRouteName,
MultimediaMessageTooltipModalRouteName,
TextMessageTooltipModalRouteName,
ThreadSettingsMemberTooltipModalRouteName,
UserRelationshipTooltipModalRouteName,
RobotextMessageTooltipModalRouteName,
VideoPlaybackModalRouteName,
];
export const chatRootModals = [
AddUsersModalRouteName,
ColorSelectorModalRouteName,
ComposeSubchannelModalRouteName,
];
export const threadRoutes = [
MessageListRouteName,
ThreadSettingsRouteName,
DeleteThreadRouteName,
ComposeSubchannelRouteName,
FullScreenThreadMediaGalleryRouteName,
PinnedMessagesScreenRouteName,
MessageSearchRouteName,
EmojiThreadAvatarCreationRouteName,
CommunityRolesScreenRouteName,
ThreadSettingsNotificationsRouteName,
];
diff --git a/native/profile/linked-devices-bottom-sheet.react.js b/native/profile/linked-devices-bottom-sheet.react.js
index 68929a193..c645304c0 100644
--- a/native/profile/linked-devices-bottom-sheet.react.js
+++ b/native/profile/linked-devices-bottom-sheet.react.js
@@ -1,39 +1,170 @@
// @flow
+import invariant from 'invariant';
import * as React from 'react';
-import { View } from 'react-native';
+import { View, Text } from 'react-native';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+import { useBroadcastDeviceListUpdates } from 'lib/hooks/peer-list-hooks.js';
+import { getAllPeerDevices } from 'lib/selectors/user-selectors.js';
+import { removeDeviceFromDeviceList } from 'lib/shared/device-list-utils.js';
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
+import { usePeerToPeerCommunication } from 'lib/tunnelbroker/peer-to-peer-context.js';
+import {
+ userActionsP2PMessageTypes,
+ type DeviceLogoutP2PMessage,
+} from 'lib/types/tunnelbroker/user-actions-peer-to-peer-message-types.js';
+import { useSelector } from 'lib/utils/redux-utils.js';
+
+import { BottomSheetContext } from '../bottom-sheet/bottom-sheet-provider.react.js';
import BottomSheet from '../bottom-sheet/bottom-sheet.react.js';
+import Button from '../components/button.react.js';
import type { RootNavigationProp } from '../navigation/root-navigator.react.js';
import type { NavigationRoute } from '../navigation/route-names.js';
import { useStyles } from '../themes/colors.js';
import type { BottomSheetRef } from '../types/bottom-sheet.js';
+import Alert from '../utils/alert.js';
+
+export type LinkedDevicesBottomSheetParams = {
+ +deviceID: string,
+};
type Props = {
+navigation: RootNavigationProp<'LinkedDevicesBottomSheet'>,
+route: NavigationRoute<'LinkedDevicesBottomSheet'>,
};
function LinkedDevicesBottomSheet(props: Props): React.Node {
- const { navigation } = props;
+ const {
+ navigation,
+ route: {
+ params: { deviceID },
+ },
+ } = props;
const { goBack } = navigation;
+ const identityContext = React.useContext(IdentityClientContext);
+ invariant(identityContext, 'identity context not set');
+ const { identityClient, getAuthMetadata } = identityContext;
+
+ const broadcastDeviceListUpdates = useBroadcastDeviceListUpdates();
+ const { broadcastEphemeralMessage } = usePeerToPeerCommunication();
+ const allPeerDevices = useSelector(getAllPeerDevices);
+
+ const bottomSheetContext = React.useContext(BottomSheetContext);
+ invariant(bottomSheetContext, 'bottomSheetContext should be set');
+ const { setContentHeight } = bottomSheetContext;
+
const bottomSheetRef = React.useRef<?BottomSheetRef>();
+ const removeDeviceContainerRef =
+ React.useRef<?React.ElementRef<typeof View>>();
const styles = useStyles(unboundStyles);
+ const insets = useSafeAreaInsets();
+
+ const handleDeviceRemoval = React.useCallback(async () => {
+ const authMetadata = await getAuthMetadata();
+ const { userID } = authMetadata;
+ if (!userID) {
+ throw new Error('No user ID');
+ }
+
+ try {
+ await removeDeviceFromDeviceList(identityClient, userID, deviceID);
+ } catch (err) {
+ console.log('Primary device error:', err);
+ Alert.alert(
+ 'Removing device failed',
+ 'Failed to update the device list',
+ [{ text: 'OK' }],
+ );
+ bottomSheetRef.current?.close();
+ }
+
+ const messageContents: DeviceLogoutP2PMessage = {
+ type: userActionsP2PMessageTypes.LOG_OUT_DEVICE,
+ };
+
+ const sendLogoutMessagePromise = broadcastEphemeralMessage(
+ JSON.stringify(messageContents),
+ [{ userID, deviceID }],
+ authMetadata,
+ );
+ const broadcastUpdatePromise = broadcastDeviceListUpdates(
+ allPeerDevices.filter(peerDeviceID => deviceID !== peerDeviceID),
+ );
+ await Promise.all([sendLogoutMessagePromise, broadcastUpdatePromise]);
+ bottomSheetRef.current?.close();
+ }, [
+ broadcastDeviceListUpdates,
+ broadcastEphemeralMessage,
+ deviceID,
+ allPeerDevices,
+ getAuthMetadata,
+ identityClient,
+ ]);
+
+ const confirmDeviceRemoval = () => {
+ Alert.alert(
+ 'Remove device',
+ 'Are you sure you want to remove this device?',
+ [
+ { text: 'Cancel', style: 'cancel' },
+ { text: 'Remove', style: 'destructive', onPress: handleDeviceRemoval },
+ ],
+ { cancelable: true },
+ );
+ };
+
+ const onLayout = React.useCallback(() => {
+ removeDeviceContainerRef.current?.measure(
+ (x, y, width, height, pageX, pageY) => {
+ if (
+ height === null ||
+ height === undefined ||
+ pageY === null ||
+ pageY === undefined
+ ) {
+ return;
+ }
+
+ setContentHeight(height + insets.bottom);
+ },
+ );
+ }, [insets.bottom, setContentHeight]);
return (
<BottomSheet ref={bottomSheetRef} onClosed={goBack}>
- <View style={styles.container}></View>
+ <View
+ style={styles.container}
+ ref={removeDeviceContainerRef}
+ onLayout={onLayout}
+ >
+ <Button
+ style={styles.removeButtonContainer}
+ onPress={confirmDeviceRemoval}
+ >
+ <Text style={styles.removeButtonText}>Remove device</Text>
+ </Button>
+ </View>
</BottomSheet>
);
}
const unboundStyles = {
container: {
paddingHorizontal: 16,
},
+ removeButtonContainer: {
+ backgroundColor: 'vibrantRedButton',
+ paddingVertical: 12,
+ borderRadius: 8,
+ alignItems: 'center',
+ },
+ removeButtonText: {
+ color: 'floatingButtonLabel',
+ },
};
export default LinkedDevicesBottomSheet;
diff --git a/native/profile/linked-devices-list-item.react.js b/native/profile/linked-devices-list-item.react.js
index 6e64719fa..8461b4203 100644
--- a/native/profile/linked-devices-list-item.react.js
+++ b/native/profile/linked-devices-list-item.react.js
@@ -1,113 +1,116 @@
// @flow
import { useNavigation } from '@react-navigation/native';
import * as React from 'react';
import { View, TouchableOpacity } from 'react-native';
import {
type IdentityPlatformDetails,
identityDeviceTypes,
} from 'lib/types/identity-service-types.js';
import Pill from '../components/pill.react.js';
import SWMIcon from '../components/swmansion-icon.react.js';
import { LinkedDevicesBottomSheetRouteName } from '../navigation/route-names.js';
import { useStyles, useColors } from '../themes/colors.js';
type Props = {
+deviceID: string,
+platformDetails: ?IdentityPlatformDetails,
+isPrimary: boolean,
+isThisDevice: boolean,
};
function LinkedDevicesListItem(props: Props): React.Node {
const { deviceID, platformDetails, isPrimary, isThisDevice } = props;
const styles = useStyles(unboundStyles);
const colors = useColors();
const { navigate } = useNavigation();
const onPress = React.useCallback(() => {
navigate<'LinkedDevicesBottomSheet'>({
name: LinkedDevicesBottomSheetRouteName,
+ params: {
+ deviceID,
+ },
});
- }, [navigate]);
+ }, [deviceID, navigate]);
const deviceType = platformDetails?.deviceType;
const deviceIcon = React.useMemo(() => {
let name;
if (
deviceType === identityDeviceTypes.IOS ||
deviceType === identityDeviceTypes.ANDROID
) {
name = 'phone';
} else if (deviceType === identityDeviceTypes.KEYSERVER) {
name = 'cloud';
} else if (deviceType === identityDeviceTypes.WEB) {
name = 'globe-1';
} else {
name = 'question';
}
return (
<SWMIcon name={name} size={12} color={colors.panelForegroundLabel} />
);
}, [deviceType, colors.panelForegroundLabel]);
const label = React.useMemo(() => {
const baseLabel = deviceID.substr(0, 7);
let finalLabel = baseLabel;
if (isPrimary) {
finalLabel += ' (primary)';
}
if (isThisDevice) {
finalLabel += ' (this device)';
}
return finalLabel;
}, [deviceID, isPrimary, isThisDevice]);
const deviceListItem = React.useMemo(
() => (
<TouchableOpacity style={styles.listItemContainer} onPress={onPress}>
<View style={styles.pillContainer}>
<Pill
label={label}
backgroundColor={colors.codeBackground}
icon={deviceIcon}
/>
</View>
</TouchableOpacity>
),
[
styles.listItemContainer,
styles.pillContainer,
onPress,
label,
colors.codeBackground,
deviceIcon,
],
);
return deviceListItem;
}
const unboundStyles = {
listItemContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 24,
paddingVertical: 10,
},
pillContainer: {
flex: 1,
alignItems: 'baseline',
},
};
export default LinkedDevicesListItem;
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 2:48 AM (23 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2559429
Default Alt Text
(28 KB)
Attached To
Mode
rCOMM Comm
Attached
Detach File
Event Timeline
Log In to Comment