diff --git a/src/account-info/AccountDetails.js b/src/account-info/AccountDetails.js
index ba8ca126905..edf11a35565 100644
--- a/src/account-info/AccountDetails.js
+++ b/src/account-info/AccountDetails.js
@@ -16,6 +16,7 @@ import { getUserStatus } from '../user-statuses/userStatusesModel';
import PresenceStatusIndicator from '../common/PresenceStatusIndicator';
import ActivityText from '../title/ActivityText';
import { nowInTimeZone } from '../utils/date';
+import CustomProfileFields from './CustomProfileFields';
const componentStyles = createStyleSheet({
componentListItem: {
@@ -96,6 +97,9 @@ export default function AccountDetails(props: Props): Node {
/>
)}
+ {/* TODO: It's weird to omit fields on `isSelf` like this, so you can't
+ see how your profile looks to other people. Make this condition
+ its own prop, passed by ProfileScreen… or cut the condition entirely. */}
{!isSelf && (
@@ -106,6 +110,7 @@ export default function AccountDetails(props: Props): Node {
)}
+
);
}
diff --git a/src/account-info/CustomProfileFields.js b/src/account-info/CustomProfileFields.js
new file mode 100644
index 00000000000..6970300f12d
--- /dev/null
+++ b/src/account-info/CustomProfileFields.js
@@ -0,0 +1,132 @@
+// @flow strict-local
+import * as React from 'react';
+import { View } from 'react-native';
+
+import { type UserOrBot, type UserId } from '../api/modelTypes';
+import WebLink from '../common/WebLink';
+import ZulipText from '../common/ZulipText';
+import ZulipTextIntl from '../common/ZulipTextIntl';
+import { ensureUnreachable } from '../generics';
+import { useSelector } from '../react-redux';
+import { tryGetUserForId } from '../selectors';
+import {
+ type CustomProfileFieldValue,
+ getCustomProfileFieldsForUser,
+} from '../users/userSelectors';
+import UserAvatar from '../common/UserAvatar';
+
+type Props = {|
+ +user: UserOrBot,
+|};
+
+function CustomProfileFieldUser(props: {| +userId: UserId |}): React.Node {
+ const { userId } = props;
+ const user = useSelector(state => tryGetUserForId(state, userId));
+
+ const styles = React.useMemo(
+ () => ({
+ text: { marginStart: 8 },
+ row: { flexDirection: 'row', marginVertical: 4 },
+ }),
+ [],
+ );
+
+ if (!user) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ );
+}
+
+function CustomProfileFieldRow(props: {|
+ +name: string,
+ +value: CustomProfileFieldValue,
+ +first: boolean,
+|}): React.Node {
+ const { first, name, value } = props;
+
+ const styles = React.useMemo(
+ () => ({
+ row: { marginTop: first ? 0 : 8, flexDirection: 'row' },
+ label: { width: 96, marginEnd: 8, fontWeight: 'bold' },
+ valueView: { flex: 1 },
+ valueText: { flex: 1 },
+ }),
+ [first],
+ );
+
+ let valueElement = undefined;
+ switch (value.displayType) {
+ case 'text':
+ valueElement = ;
+ break;
+
+ case 'link':
+ valueElement = (
+
+ {value.url ? (
+
+ ) : (
+
+ )}
+
+ );
+ break;
+
+ case 'users':
+ valueElement = (
+
+ {value.userIds.map(userId => (
+
+ ))}
+
+ );
+ break;
+
+ default:
+ ensureUnreachable(value.displayType);
+ return null;
+ }
+
+ return (
+
+
+ {valueElement}
+
+ );
+}
+
+export default function CustomProfileFields(props: Props): React.Node {
+ const { user } = props;
+ const realm = useSelector(state => state.realm);
+
+ const fields = React.useMemo(() => getCustomProfileFieldsForUser(realm, user), [realm, user]);
+
+ const styles = React.useMemo(
+ () => ({
+ outer: { flexDirection: 'row', justifyContent: 'center' },
+ inner: { flexBasis: 400, flexShrink: 1 },
+ }),
+ [],
+ );
+
+ return (
+
+
+ {fields.map((field, i) => (
+
+ ))}
+
+
+ );
+}
diff --git a/static/translations/messages_en.json b/static/translations/messages_en.json
index f7529e1bb3d..96e4a585bf2 100644
--- a/static/translations/messages_en.json
+++ b/static/translations/messages_en.json
@@ -186,6 +186,7 @@
"Send private message": "Send private message",
"View private messages": "View private messages",
"(This user has been deactivated)": "(This user has been deactivated)",
+ "(unknown user)": "(unknown user)",
"Forgot password?": "Forgot password?",
"Members": "Members",
"Recipients": "Recipients",