diff --git a/native/profile/keyserver-selection-list.react.js b/native/profile/keyserver-selection-list.react.js
index 8bd5c0fe0..1c6e6c283 100644
--- a/native/profile/keyserver-selection-list.react.js
+++ b/native/profile/keyserver-selection-list.react.js
@@ -1,132 +1,125 @@
// @flow
import * as React from 'react';
import { Text, View, FlatList } from 'react-native';
import { selectedKeyserversSelector } from 'lib/selectors/keyserver-selectors.js';
import type { SelectedKeyserverInfo } from 'lib/types/keyserver-types.js';
import KeyserverSelectionListItem from './keyserver-selection-list-item.react.js';
import type { ProfileNavigationProp } from './profile.react.js';
import type { NavigationRoute } from '../navigation/route-names.js';
import { useSelector } from '../redux/redux-utils.js';
import { useStyles } from '../themes/colors.js';
function keyExtractor(item: SelectedKeyserverInfo) {
return `${item.keyserverAdminUserInfo.id}${item.keyserverInfo.urlPrefix}`;
}
function renderKeyserverListItem({
item,
}: {
+item: SelectedKeyserverInfo,
...
}) {
return ;
}
type Props = {
+navigation: ProfileNavigationProp<'KeyserverSelectionList'>,
+route: NavigationRoute<'KeyserverSelectionList'>,
};
// eslint-disable-next-line no-unused-vars
function KeyserverSelectionList(props: Props): React.Node {
const styles = useStyles(unboundStyles);
const selectedKeyserverInfos: $ReadOnlyArray =
useSelector(selectedKeyserversSelector);
const keyserverListSeparatorComponent = React.useCallback(
() => ,
[styles.separator],
);
const keyserverSelectionList = React.useMemo(
() => (
CONNECTED KEYSERVERS
),
[
keyserverListSeparatorComponent,
selectedKeyserverInfos,
styles.container,
styles.header,
styles.keyserverListContentContainer,
],
);
return keyserverSelectionList;
}
const unboundStyles = {
container: {
flex: 1,
backgroundColor: 'panelBackground',
paddingTop: 24,
},
header: {
color: 'panelBackgroundLabel',
fontSize: 12,
fontWeight: '400',
paddingBottom: 3,
paddingHorizontal: 24,
},
keyserverListContentContainer: {
backgroundColor: 'panelForeground',
borderBottomWidth: 1,
borderColor: 'panelForegroundBorder',
borderTopWidth: 1,
marginBottom: 24,
paddingVertical: 2,
},
- keyserverListItemContainer: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- paddingHorizontal: 24,
- paddingVertical: 10,
- },
separator: {
backgroundColor: 'panelForegroundBorder',
height: 1,
marginHorizontal: 16,
},
onlineIndicatorOuter: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'greenIndicatorOuter',
width: 18,
height: 18,
borderRadius: 9,
},
onlineIndicatorInner: {
backgroundColor: 'greenIndicatorInner',
width: 9,
height: 9,
borderRadius: 4.5,
},
offlineIndicatorOuter: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'redIndicatorOuter',
width: 18,
height: 18,
borderRadius: 9,
},
offlineIndicatorInner: {
backgroundColor: 'redIndicatorInner',
width: 9,
height: 9,
borderRadius: 4.5,
},
};
export default KeyserverSelectionList;
diff --git a/native/profile/linked-devices-list-item.react.js b/native/profile/linked-devices-list-item.react.js
new file mode 100644
index 000000000..7ba0fa85c
--- /dev/null
+++ b/native/profile/linked-devices-list-item.react.js
@@ -0,0 +1,102 @@
+// @flow
+
+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 { 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 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 (
+
+ );
+ }, [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(
+ () => (
+
+
+
+
+
+ ),
+ [
+ styles.listItemContainer,
+ styles.pillContainer,
+ 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;
diff --git a/native/profile/linked-devices.react.js b/native/profile/linked-devices.react.js
index 4e43464da..c67b720ec 100644
--- a/native/profile/linked-devices.react.js
+++ b/native/profile/linked-devices.react.js
@@ -1,17 +1,125 @@
// @flow
+import invariant from 'invariant';
import * as React from 'react';
+import { Text, View, FlatList } from 'react-native';
+import {
+ getOwnPeerDevices,
+ type DeviceIDAndPlatformDetails,
+} from 'lib/selectors/user-selectors.js';
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
+
+import LinkedDevicesListItem from './linked-devices-list-item.react.js';
import type { ProfileNavigationProp } from './profile.react.js';
import type { NavigationRoute } from '../navigation/route-names.js';
+import { useSelector } from '../redux/redux-utils.js';
+import { useStyles } from '../themes/colors.js';
+
+function keyExtractor(item: DeviceIDAndPlatformDetails) {
+ return item.deviceID;
+}
+function renderDeviceListItem({
+ item,
+ index,
+ thisDeviceID,
+}: {
+ +item: DeviceIDAndPlatformDetails,
+ +index: number,
+ +thisDeviceID: ?string,
+ ...
+}) {
+ return (
+
+ );
+}
type Props = {
+navigation: ProfileNavigationProp<'LinkedDevices'>,
+route: NavigationRoute<'LinkedDevices'>,
};
// eslint-disable-next-line no-unused-vars
function LinkedDevices(props: Props): React.Node {
- return null;
+ const styles = useStyles(unboundStyles);
+
+ const userDevicesInfos: $ReadOnlyArray =
+ useSelector(getOwnPeerDevices);
+
+ const identityContext = React.useContext(IdentityClientContext);
+ invariant(identityContext, 'identity context not set');
+ const { getAuthMetadata } = identityContext;
+ const [thisDeviceID, setThisDeviceID] = React.useState(null);
+
+ React.useEffect(() => {
+ void (async () => {
+ const { deviceID } = await getAuthMetadata();
+ setThisDeviceID(deviceID);
+ })();
+ }, [getAuthMetadata]);
+
+ const separatorComponent = React.useCallback(
+ () => ,
+ [styles.separator],
+ );
+
+ const userDeviceList = React.useMemo(
+ () => (
+
+ USER DEVICES
+
+ renderDeviceListItem({ item, index, thisDeviceID })
+ }
+ keyExtractor={keyExtractor}
+ contentContainerStyle={styles.deviceListContentContainer}
+ ItemSeparatorComponent={separatorComponent}
+ />
+
+ ),
+ [
+ separatorComponent,
+ userDevicesInfos,
+ styles.container,
+ styles.header,
+ styles.deviceListContentContainer,
+ thisDeviceID,
+ ],
+ );
+
+ return userDeviceList;
}
+const unboundStyles = {
+ container: {
+ flex: 1,
+ backgroundColor: 'panelBackground',
+ paddingTop: 24,
+ },
+ header: {
+ color: 'panelBackgroundLabel',
+ fontSize: 12,
+ fontWeight: '400',
+ paddingBottom: 3,
+ paddingHorizontal: 24,
+ },
+ deviceListContentContainer: {
+ backgroundColor: 'panelForeground',
+ borderBottomWidth: 1,
+ borderColor: 'panelForegroundBorder',
+ borderTopWidth: 1,
+ marginBottom: 24,
+ paddingVertical: 2,
+ },
+ separator: {
+ backgroundColor: 'panelForegroundBorder',
+ height: 1,
+ marginHorizontal: 16,
+ },
+};
+
export default LinkedDevices;