Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/__tests__/lib/exampleData.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
User,
UserGroup,
UserId,
UserStatus,
} from '../../api/modelTypes';
import { makeUserId } from '../../api/idTypes';
import type { InitialData } from '../../api/apiTypes';
Expand Down Expand Up @@ -184,6 +185,24 @@ export const makeCrossRealmBot = (
is_bot: true,
});

export const userStatusEmojiUnicode: $PropertyType<UserStatus, 'status_emoji'> = deepFreeze({
reaction_type: 'unicode_emoji',
emoji_code: '1f44d',
emoji_name: 'thumbs_up',
});

export const userStatusEmojiZulipExtra: $PropertyType<UserStatus, 'status_emoji'> = deepFreeze({
reaction_type: 'zulip_extra_emoji',
emoji_code: 'zulip',
emoji_name: 'zulip',
});

export const userStatusEmojiRealm: $PropertyType<UserStatus, 'status_emoji'> = deepFreeze({
reaction_type: 'realm_emoji',
emoji_code: '80',
emoji_name: 'github_parrot',
});

export const realm: URL = new URL('https://zulip.example.org');

export const zulipVersion: ZulipVersion = new ZulipVersion('2.1.0-234-g7c3acf4');
Expand Down
20 changes: 17 additions & 3 deletions src/emoji/Emoji.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* @flow strict-local */
import React, { useMemo } from 'react';
import React, { useContext, useMemo } from 'react';
import type { Node } from 'react';
import { Image } from 'react-native';
import { createIconSet } from 'react-native-vector-icons';

import type { EmojiType } from '../types';
import { createStyleSheet } from '../styles';
import { createStyleSheet, ThemeContext } from '../styles';
import { useSelector } from '../react-redux';
import { getAllImageEmojiByCode } from './emojiSelectors';
import { codeToEmojiMap } from './data';
Expand All @@ -23,6 +23,7 @@ type Props = $ReadOnly<{|

export default function Emoji(props: Props): Node {
const { code, size = 20 } = props;
const { color } = useContext(ThemeContext);
const imageEmoji = useSelector(state =>
props.type === 'image' ? getAllImageEmojiByCode(state)[props.code] : undefined,
);
Expand All @@ -33,5 +34,18 @@ export default function Emoji(props: Props): Node {
if (imageEmoji) {
return <Image style={componentStyles.image} source={{ uri: imageEmoji.source_url }} />;
}
return <UnicodeEmoji name={code} size={size} />;
return (
<UnicodeEmoji
// Set `color` just to remove some transparency or darkening that's
// somehow getting applied, at least on Android, making emojis
// noticeably faded; not sure how. See a screenshot of the faded
// appearance at
// https://github.com/zulip/zulip-mobile/pull/5277#issuecomment-1062504604
// and the doc for this property at
// https://github.com/oblador/react-native-vector-icons#properties
color={color}
name={code}
size={size}
/>
);
}
17 changes: 17 additions & 0 deletions src/user-statuses/userStatusesCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,20 @@ export const kUserStatusZero: UserStatus = {
* implicitly by having no record in this map.
*/
export type UserStatusesState = Immutable.Map<UserId, UserStatus>;

//
//
// Getters.
//

/**
* The `UserStatus` object for the given UserId, from `UserStatusesState`.
*
* Use this instead of reading `UserStatusesState` directly, because it
* takes care of this fact about UserStatusesState (from jsdoc):
*
* Users who have the "zero" status, `kUserStatusZero`, may be
* represented implicitly by having no record in this map.
*/
export const getUserStatusFromModel = (state: UserStatusesState, userId: UserId): UserStatus =>
state.get(userId, kUserStatusZero);
10 changes: 8 additions & 2 deletions src/user-statuses/userStatusesModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ import { kUserStatusZero } from './userStatusesCore';
// Selectors.
//

const getUserStatuses = (state: PerAccountState): UserStatusesState => state.userStatuses;
/**
* The `UserStatusesState`.
*
* Don't read from `UserStatusesState` directly; see `userStatusesCore` for
* a getter that takes a `UserStatusesState`.
*/
export const getUserStatuses = (state: PerAccountState): UserStatusesState => state.userStatuses;

/**
* The `UserStatus` object for the given UserId.
* The `UserStatus` object for the given UserId, from `PerAccountState`.
*/
export const getUserStatus = (state: PerAccountState, userId: UserId): UserStatus =>
getUserStatuses(state).get(userId, kUserStatusZero);
Expand Down
18 changes: 14 additions & 4 deletions src/users/UserItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import styles, { createStyleSheet, BRAND_COLOR } from '../styles';
import { useSelector } from '../react-redux';
import { getUserForId } from './userSelectors';
import { getMutedUsers } from '../selectors';
import { getUserStatus } from '../user-statuses/userStatusesModel';
import { emojiTypeFromReactionType } from '../emoji/data';
import Emoji from '../emoji/Emoji';

const componentStyles = createStyleSheet({
selectedRow: {
Expand All @@ -28,9 +31,6 @@ const componentStyles = createStyleSheet({
fontSize: 10,
color: 'hsl(0, 0%, 60%)',
},
textWrapper: {
flex: 1,
},
});

type Props<UserT> = $ReadOnly<{|
Expand All @@ -57,6 +57,7 @@ export function UserItemRaw<
const { user, isSelected = false, onPress, unreadCount, showEmail = false } = props;
const _ = useContext(TranslationContext);
const isMuted = useSelector(getMutedUsers).has(user.user_id);
const userStatusEmoji = useSelector(state => getUserStatus(state, user.user_id)).status_emoji;

const handlePress = useCallback(() => {
if (onPress) {
Expand All @@ -73,7 +74,7 @@ export function UserItemRaw<
isMuted={isMuted}
onPress={onPress && handlePress}
/>
<View style={componentStyles.textWrapper}>
<View>
<ZulipText
style={[componentStyles.text, isSelected && componentStyles.selectedText]}
text={isMuted ? _('Muted user') : user.full_name}
Expand All @@ -93,6 +94,15 @@ export function UserItemRaw<
/>
)}
</View>
{userStatusEmoji && (
<View style={{ marginLeft: 4 }}>
<Emoji
code={userStatusEmoji.emoji_code}
type={emojiTypeFromReactionType(userStatusEmoji.reaction_type)}
size={24}
/>
</View>
)}
<UnreadCount count={unreadCount} inverse={isSelected} />
</View>
</Touchable>
Expand Down
Loading