Skip to content

Commit

Permalink
user: Show custom profile fields in a user's profile
Browse files Browse the repository at this point in the history
Fixes: #2900
  • Loading branch information
gnprice committed Jun 3, 2022
1 parent 63439b4 commit 8dbacf0
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/account-info/AccountDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -96,6 +97,9 @@ export default function AccountDetails(props: Props): Node {
/>
)}
</View>
{/* 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 && (
<View>
<ActivityText style={styles.largerText} user={user} />
Expand All @@ -106,6 +110,7 @@ export default function AccountDetails(props: Props): Node {
<ZulipText style={styles.largerText} text={localTime} />
</View>
)}
<CustomProfileFields user={user} />
</ComponentList>
);
}
132 changes: 132 additions & 0 deletions src/account-info/CustomProfileFields.js
Original file line number Diff line number Diff line change
@@ -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 <ZulipTextIntl text="(unknown user)" />;
}

return (
<View style={styles.row}>
<UserAvatar size={24} avatarUrl={user.avatar_url} />
<ZulipText style={styles.text} text={user.full_name} />
</View>
);
}

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 = <ZulipText style={styles.valueText} text={value.text} />;
break;

case 'link':
valueElement = (
<View style={styles.valueView}>
{value.url ? (
<WebLink url={value.url} label={{ text: '{_}', values: { _: value.text } }} />
) : (
<ZulipText text={value.text} />
)}
</View>
);
break;

case 'users':
valueElement = (
<View style={styles.valueView}>
{value.userIds.map(userId => (
<CustomProfileFieldUser key={userId} userId={userId} />
))}
</View>
);
break;

default:
ensureUnreachable(value.displayType);
return null;
}

return (
<View style={styles.row}>
<ZulipText style={styles.label} text={name} />
{valueElement}
</View>
);
}

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 (
<View style={styles.outer}>
<View style={styles.inner}>
{fields.map((field, i) => (
<CustomProfileFieldRow
key={field.fieldId}
name={field.name}
value={field.value}
first={i === 0}
/>
))}
</View>
</View>
);
}
1 change: 1 addition & 0 deletions static/translations/messages_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 8dbacf0

Please sign in to comment.