diff --git a/native/chat/chat-thread-list-item.react.js b/native/chat/chat-thread-list-item.react.js
index cd7cafef1..ac3cfc446 100644
--- a/native/chat/chat-thread-list-item.react.js
+++ b/native/chat/chat-thread-list-item.react.js
@@ -1,232 +1,230 @@
// @flow
import * as React from 'react';
import { Text, View } from 'react-native';
import type { ChatThreadItem } from 'lib/selectors/chat-selectors.js';
-import { useGetAvatarForThread } from 'lib/shared/avatar-utils.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
import type { UserInfo } from 'lib/types/user-types.js';
import { shortAbsoluteDate } from 'lib/utils/date-utils.js';
import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js';
import ChatThreadListSeeMoreSidebars from './chat-thread-list-see-more-sidebars.react.js';
import ChatThreadListSidebar from './chat-thread-list-sidebar.react.js';
import MessagePreview from './message-preview.react.js';
import SwipeableThread from './swipeable-thread.react.js';
-import Avatar from '../components/avatar.react.js';
import Button from '../components/button.react.js';
import ColorSplotch from '../components/color-splotch.react.js';
import { SingleLine } from '../components/single-line.react.js';
import ThreadAncestorsLabel from '../components/thread-ancestors-label.react.js';
+import ThreadAvatar from '../components/thread-avatar.react.js';
import UnreadDot from '../components/unread-dot.react.js';
import { useColors, useStyles } from '../themes/colors.js';
import { useShouldRenderAvatars } from '../utils/avatar-utils.js';
type Props = {
+data: ChatThreadItem,
+onPressItem: (
threadInfo: ThreadInfo,
pendingPersonalThreadUserInfo?: UserInfo,
) => void,
+onPressSeeMoreSidebars: (threadInfo: ThreadInfo) => void,
+onSwipeableWillOpen: (threadInfo: ThreadInfo) => void,
+currentlyOpenedSwipeableId: string,
};
function ChatThreadListItem({
data,
onPressItem,
onPressSeeMoreSidebars,
onSwipeableWillOpen,
currentlyOpenedSwipeableId,
}: Props): React.Node {
const styles = useStyles(unboundStyles);
const colors = useColors();
const lastMessage = React.useMemo(() => {
const mostRecentMessageInfo = data.mostRecentMessageInfo;
if (!mostRecentMessageInfo) {
return (
No messages
);
}
return (
);
}, [data.mostRecentMessageInfo, data.threadInfo, styles]);
const numOfSidebarsWithExtendedArrow =
data.sidebars.filter(sidebarItem => sidebarItem.type === 'sidebar').length -
1;
const sidebars = data.sidebars.map((sidebarItem, index) => {
if (sidebarItem.type === 'sidebar') {
const { type, ...sidebarInfo } = sidebarItem;
return (
);
} else if (sidebarItem.type === 'seeMore') {
return (
);
} else {
return ;
}
});
const onPress = React.useCallback(() => {
onPressItem(data.threadInfo, data.pendingPersonalThreadUserInfo);
}, [onPressItem, data.threadInfo, data.pendingPersonalThreadUserInfo]);
const threadNameStyle = React.useMemo(() => {
if (!data.threadInfo.currentUser.unread) {
return styles.threadName;
}
return [styles.threadName, styles.unreadThreadName];
}, [
data.threadInfo.currentUser.unread,
styles.threadName,
styles.unreadThreadName,
]);
const lastActivity = shortAbsoluteDate(data.lastUpdatedTime);
const lastActivityStyle = React.useMemo(() => {
if (!data.threadInfo.currentUser.unread) {
return styles.lastActivity;
}
return [styles.lastActivity, styles.unreadLastActivity];
}, [
data.threadInfo.currentUser.unread,
styles.lastActivity,
styles.unreadLastActivity,
]);
const resolvedThreadInfo = useResolvedThreadInfo(data.threadInfo);
- const avatarInfo = useGetAvatarForThread(data.threadInfo);
const shouldRenderAvatars = useShouldRenderAvatars();
const avatar = React.useMemo(() => {
if (!shouldRenderAvatars) {
return ;
}
- return ;
- }, [avatarInfo, data.threadInfo.color, shouldRenderAvatars]);
+ return ;
+ }, [data.threadInfo, shouldRenderAvatars]);
return (
<>
{sidebars}
>
);
}
const chatThreadListItemHeight = 70;
const spacerHeight = 6;
const unboundStyles = {
container: {
height: chatThreadListItemHeight,
justifyContent: 'center',
backgroundColor: 'listBackground',
},
content: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
colorSplotch: {
marginLeft: 6,
marginBottom: 12,
},
threadDetails: {
paddingLeft: 12,
paddingRight: 18,
justifyContent: 'center',
flex: 1,
marginTop: 5,
},
lastActivity: {
color: 'listForegroundTertiaryLabel',
fontSize: 14,
marginLeft: 10,
},
unreadLastActivity: {
color: 'listForegroundLabel',
fontWeight: 'bold',
},
noMessages: {
color: 'listForegroundTertiaryLabel',
flex: 1,
fontSize: 14,
fontStyle: 'italic',
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
threadName: {
color: 'listForegroundSecondaryLabel',
flex: 1,
fontSize: 21,
},
unreadThreadName: {
color: 'listForegroundLabel',
fontWeight: '500',
},
spacer: {
height: spacerHeight,
},
};
export { ChatThreadListItem, chatThreadListItemHeight, spacerHeight };
diff --git a/native/chat/message-list-header-title.react.js b/native/chat/message-list-header-title.react.js
index 56758f6da..8ad30d1b0 100644
--- a/native/chat/message-list-header-title.react.js
+++ b/native/chat/message-list-header-title.react.js
@@ -1,125 +1,119 @@
// @flow
import {
HeaderTitle,
type HeaderTitleInputProps,
} from '@react-navigation/elements';
import * as React from 'react';
import { View } from 'react-native';
-import { useGetAvatarForThread } from 'lib/shared/avatar-utils.js';
-import type { ClientAvatar } from 'lib/types/avatar-types.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js';
import { firstLine } from 'lib/utils/string-utils.js';
import type { ChatNavigationProp } from './chat.react.js';
-import Avatar from '../components/avatar.react.js';
import Button from '../components/button.react.js';
+import ThreadAvatar from '../components/thread-avatar.react.js';
import { ThreadSettingsRouteName } from '../navigation/route-names.js';
import { useStyles } from '../themes/colors.js';
import { useShouldRenderAvatars } from '../utils/avatar-utils.js';
type BaseProps = {
+threadInfo: ThreadInfo,
+navigate: $PropertyType, 'navigate'>,
+isSearchEmpty: boolean,
+areSettingsEnabled: boolean,
...HeaderTitleInputProps,
};
type Props = {
...BaseProps,
+styles: typeof unboundStyles,
+title: string,
- +avatarInfo: ClientAvatar,
+shouldRenderAvatars: boolean,
};
class MessageListHeaderTitle extends React.PureComponent {
render() {
const {
threadInfo,
navigate,
isSearchEmpty,
areSettingsEnabled,
styles,
title,
- avatarInfo,
shouldRenderAvatars,
...rest
} = this.props;
let avatar;
if (!isSearchEmpty && shouldRenderAvatars) {
avatar = (
-
+
);
}
return (
);
}
onPress = () => {
const { threadInfo } = this.props;
this.props.navigate<'ThreadSettings'>({
name: ThreadSettingsRouteName,
params: { threadInfo },
key: `${ThreadSettingsRouteName}${threadInfo.id}`,
});
};
}
const unboundStyles = {
avatarContainer: {
marginRight: 8,
},
button: {
flex: 1,
},
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
};
const ConnectedMessageListHeaderTitle: React.ComponentType =
React.memo(function ConnectedMessageListHeaderTitle(
props: BaseProps,
) {
const styles = useStyles(unboundStyles);
const shouldRenderAvatars = useShouldRenderAvatars();
const { uiName } = useResolvedThreadInfo(props.threadInfo);
- const avatarInfo = useGetAvatarForThread(props.threadInfo);
const { isSearchEmpty } = props;
const title = isSearchEmpty ? 'New Message' : uiName;
return (
);
});
export default ConnectedMessageListHeaderTitle;
diff --git a/native/chat/settings/thread-settings-avatar.react.js b/native/chat/settings/thread-settings-avatar.react.js
index 50558fd1a..5e1804f02 100644
--- a/native/chat/settings/thread-settings-avatar.react.js
+++ b/native/chat/settings/thread-settings-avatar.react.js
@@ -1,38 +1,36 @@
// @flow
import * as React from 'react';
import { View } from 'react-native';
-import { useGetAvatarForThread } from 'lib/shared/avatar-utils.js';
import { type ResolvedThreadInfo } from 'lib/types/thread-types.js';
-import Avatar from '../../components/avatar.react.js';
+import ThreadAvatar from '../../components/thread-avatar.react.js';
import { useStyles } from '../../themes/colors.js';
type Props = {
+threadInfo: ResolvedThreadInfo,
};
function ThreadSettingsAvatar(props: Props): React.Node {
const styles = useStyles(unboundStyles);
- const avatarInfo = useGetAvatarForThread(props.threadInfo);
return (
-
+
);
}
const unboundStyles = {
container: {
alignItems: 'center',
backgroundColor: 'panelForeground',
flex: 1,
paddingVertical: 16,
},
};
const MemoizedThreadSettingsAvatar: React.ComponentType =
React.memo(ThreadSettingsAvatar);
export default MemoizedThreadSettingsAvatar;
diff --git a/native/chat/settings/thread-settings-child-thread.react.js b/native/chat/settings/thread-settings-child-thread.react.js
index 58762e28a..6ce6fec42 100644
--- a/native/chat/settings/thread-settings-child-thread.react.js
+++ b/native/chat/settings/thread-settings-child-thread.react.js
@@ -1,97 +1,95 @@
// @flow
import * as React from 'react';
import { View, Platform } from 'react-native';
-import { useGetAvatarForThread } from 'lib/shared/avatar-utils.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
-import Avatar from '../../components/avatar.react.js';
import Button from '../../components/button.react.js';
+import ThreadAvatar from '../../components/thread-avatar.react.js';
import ThreadIcon from '../../components/thread-icon.react.js';
import ThreadPill from '../../components/thread-pill.react.js';
import { useColors, useStyles } from '../../themes/colors.js';
import { useShouldRenderAvatars } from '../../utils/avatar-utils.js';
import { useNavigateToThread } from '../message-list-types.js';
type Props = {
+threadInfo: ThreadInfo,
+firstListItem: boolean,
+lastListItem: boolean,
};
function ThreadSettingsChildThread(props: Props): React.Node {
const { threadInfo } = props;
const navigateToThread = useNavigateToThread();
const onPress = React.useCallback(() => {
navigateToThread({ threadInfo });
}, [threadInfo, navigateToThread]);
const styles = useStyles(unboundStyles);
const colors = useColors();
- const avatarInfo = useGetAvatarForThread(threadInfo);
const shouldRenderAvatars = useShouldRenderAvatars();
const avatar = React.useMemo(() => {
if (!shouldRenderAvatars) {
return null;
}
return (
-
+
);
- }, [avatarInfo, shouldRenderAvatars, styles.avatarContainer]);
+ }, [shouldRenderAvatars, styles.avatarContainer, threadInfo]);
const firstItem = props.firstListItem ? null : styles.topBorder;
const lastItem = props.lastListItem ? styles.lastButton : null;
return (
);
}
const unboundStyles = {
avatarContainer: {
marginRight: 8,
},
button: {
flex: 1,
flexDirection: 'row',
paddingVertical: 8,
paddingLeft: 12,
paddingRight: 10,
alignItems: 'center',
},
topBorder: {
borderColor: 'panelForegroundBorder',
borderTopWidth: 1,
},
container: {
backgroundColor: 'panelForeground',
flex: 1,
paddingHorizontal: 12,
},
lastButton: {
paddingBottom: Platform.OS === 'ios' ? 12 : 10,
paddingTop: 8,
},
leftSide: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
};
export default ThreadSettingsChildThread;
diff --git a/native/chat/settings/thread-settings-parent.react.js b/native/chat/settings/thread-settings-parent.react.js
index f99af82f4..0516566ae 100644
--- a/native/chat/settings/thread-settings-parent.react.js
+++ b/native/chat/settings/thread-settings-parent.react.js
@@ -1,130 +1,128 @@
// @flow
import * as React from 'react';
import { Text, View } from 'react-native';
-import { useGetAvatarForThread } from 'lib/shared/avatar-utils.js';
import { type ThreadInfo } from 'lib/types/thread-types.js';
-import Avatar from '../../components/avatar.react.js';
import Button from '../../components/button.react.js';
+import ThreadAvatar from '../../components/thread-avatar.react.js';
import ThreadPill from '../../components/thread-pill.react.js';
import { useStyles } from '../../themes/colors.js';
import { useShouldRenderAvatars } from '../../utils/avatar-utils.js';
import { useNavigateToThread } from '../message-list-types.js';
type ParentButtonProps = {
+parentThreadInfo: ThreadInfo,
};
function ParentButton(props: ParentButtonProps): React.Node {
const styles = useStyles(unboundStyles);
const navigateToThread = useNavigateToThread();
const onPressParentThread = React.useCallback(() => {
navigateToThread({ threadInfo: props.parentThreadInfo });
}, [props.parentThreadInfo, navigateToThread]);
- const avatarInfo = useGetAvatarForThread(props.parentThreadInfo);
const shouldRenderAvatars = useShouldRenderAvatars();
const avatar = React.useMemo(() => {
if (!shouldRenderAvatars) {
return null;
}
return (
-
+
);
- }, [avatarInfo, shouldRenderAvatars, styles.avatarContainer]);
+ }, [props.parentThreadInfo, shouldRenderAvatars, styles.avatarContainer]);
return (
);
}
type ThreadSettingsParentProps = {
+threadInfo: ThreadInfo,
+parentThreadInfo: ?ThreadInfo,
};
function ThreadSettingsParent(props: ThreadSettingsParentProps): React.Node {
const { threadInfo, parentThreadInfo } = props;
const styles = useStyles(unboundStyles);
let parent;
if (parentThreadInfo) {
parent = ;
} else if (threadInfo.parentThreadID) {
parent = (
Secret parent
);
} else {
parent = (
No parent
);
}
return (
Parent
{parent}
);
}
const unboundStyles = {
avatarContainer: {
marginRight: 8,
},
currentValue: {
flex: 1,
},
currentValueText: {
color: 'panelForegroundSecondaryLabel',
fontFamily: 'Arial',
fontSize: 16,
margin: 0,
paddingRight: 0,
},
label: {
color: 'panelForegroundTertiaryLabel',
fontSize: 16,
width: 96,
},
noParent: {
fontStyle: 'italic',
paddingLeft: 2,
},
parentContainer: {
flexDirection: 'row',
},
row: {
backgroundColor: 'panelForeground',
flexDirection: 'row',
paddingHorizontal: 24,
paddingVertical: 4,
alignItems: 'center',
},
};
const ConnectedThreadSettingsParent: React.ComponentType =
React.memo(ThreadSettingsParent);
export default ConnectedThreadSettingsParent;
diff --git a/native/components/thread-list-thread.react.js b/native/components/thread-list-thread.react.js
index 1dcf6ee07..6b6c1a298 100644
--- a/native/components/thread-list-thread.react.js
+++ b/native/components/thread-list-thread.react.js
@@ -1,103 +1,98 @@
// @flow
import * as React from 'react';
-import { useGetAvatarForThread } from 'lib/shared/avatar-utils.js';
-import type { ClientAvatar } from 'lib/types/avatar-types.js';
import type { ThreadInfo, ResolvedThreadInfo } from 'lib/types/thread-types.js';
import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js';
-import Avatar from './avatar.react.js';
import Button from './button.react.js';
import ColorSplotch from './color-splotch.react.js';
import { SingleLine } from './single-line.react.js';
+import ThreadAvatar from './thread-avatar.react.js';
import { type Colors, useStyles, useColors } from '../themes/colors.js';
import type { ViewStyle, TextStyle } from '../types/styles.js';
import { useShouldRenderAvatars } from '../utils/avatar-utils.js';
type SharedProps = {
+onSelect: (threadID: string) => void,
+style?: ViewStyle,
+textStyle?: TextStyle,
};
type BaseProps = {
...SharedProps,
+threadInfo: ThreadInfo,
};
type Props = {
...SharedProps,
+threadInfo: ResolvedThreadInfo,
- +avatarInfo: ClientAvatar,
+shouldRenderAvatars: boolean,
+colors: Colors,
+styles: typeof unboundStyles,
};
class ThreadListThread extends React.PureComponent {
render() {
const { modalIosHighlightUnderlay: underlayColor } = this.props.colors;
let avatar;
if (this.props.shouldRenderAvatars) {
- avatar = ;
+ avatar = ;
} else {
avatar = ;
}
return (
);
}
onSelect = () => {
this.props.onSelect(this.props.threadInfo.id);
};
}
const unboundStyles = {
button: {
alignItems: 'center',
flexDirection: 'row',
paddingLeft: 13,
},
text: {
color: 'modalForegroundLabel',
fontSize: 16,
paddingLeft: 9,
paddingRight: 12,
paddingVertical: 6,
},
};
const ConnectedThreadListThread: React.ComponentType =
React.memo(function ConnectedThreadListThread(props: BaseProps) {
const { threadInfo, ...rest } = props;
const styles = useStyles(unboundStyles);
const colors = useColors();
const resolvedThreadInfo = useResolvedThreadInfo(threadInfo);
- const avatarInfo = useGetAvatarForThread(threadInfo);
const shouldRenderAvatars = useShouldRenderAvatars();
return (
);
});
export default ConnectedThreadListThread;
diff --git a/native/navigation/community-drawer-item.react.js b/native/navigation/community-drawer-item.react.js
index 908550561..5b21b07c2 100644
--- a/native/navigation/community-drawer-item.react.js
+++ b/native/navigation/community-drawer-item.react.js
@@ -1,171 +1,169 @@
// @flow
import * as React from 'react';
import { View, FlatList, TouchableOpacity } from 'react-native';
-import { useGetAvatarForThread } from 'lib/shared/avatar-utils.js';
import type { CommunityDrawerItemData } from 'lib/utils/drawer-utils.react.js';
import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js';
import { ExpandButton, ExpandButtonDisabled } from './expand-buttons.react.js';
import SubchannelsButton from './subchannels-button.react.js';
import type { MessageListParams } from '../chat/message-list-types.js';
-import Avatar from '../components/avatar.react.js';
import { SingleLine } from '../components/single-line.react.js';
+import ThreadAvatar from '../components/thread-avatar.react.js';
import { useStyles } from '../themes/colors.js';
import type { TextStyle } from '../types/styles.js';
import { useShouldRenderAvatars } from '../utils/avatar-utils.js';
export type DrawerItemProps = {
+itemData: CommunityDrawerItemData,
+toggleExpanded: (threadID: string) => void,
+expanded: boolean,
+navigateToThread: (params: MessageListParams) => void,
};
function CommunityDrawerItem(props: DrawerItemProps): React.Node {
const {
itemData: { threadInfo, itemChildren, labelStyle, hasSubchannelsButton },
navigateToThread,
expanded,
toggleExpanded,
} = props;
const styles = useStyles(unboundStyles);
const renderItem = React.useCallback(
({ item }) => (
),
[navigateToThread],
);
const children = React.useMemo(() => {
if (!expanded) {
return null;
}
if (hasSubchannelsButton) {
return (
);
}
return ;
}, [
expanded,
itemChildren,
renderItem,
hasSubchannelsButton,
styles.subchannelsButton,
threadInfo,
]);
const onExpandToggled = React.useCallback(() => {
toggleExpanded(threadInfo.id);
}, [toggleExpanded, threadInfo.id]);
const itemExpandButton = React.useMemo(() => {
if (!itemChildren?.length && !hasSubchannelsButton) {
return ;
}
return ;
}, [itemChildren?.length, hasSubchannelsButton, onExpandToggled, expanded]);
const onPress = React.useCallback(() => {
navigateToThread({ threadInfo });
}, [navigateToThread, threadInfo]);
const { uiName } = useResolvedThreadInfo(threadInfo);
- const avatarInfo = useGetAvatarForThread(threadInfo);
const shouldRenderAvatars = useShouldRenderAvatars();
const avatar = React.useMemo(() => {
if (!shouldRenderAvatars) {
return null;
}
return (
-
+
);
- }, [avatarInfo, shouldRenderAvatars, styles.avatarContainer]);
+ }, [shouldRenderAvatars, styles.avatarContainer, threadInfo]);
return (
{itemExpandButton}
{avatar}
{uiName}
{children}
);
}
const unboundStyles = {
avatarContainer: {
marginRight: 8,
},
chatView: {
marginLeft: 16,
},
threadEntry: {
flexDirection: 'row',
marginVertical: 6,
},
textTouchableWrapper: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
subchannelsButton: {
marginLeft: 24,
marginBottom: 6,
},
};
export type CommunityDrawerItemChatProps = {
+itemData: CommunityDrawerItemData,
+navigateToThread: (params: MessageListParams) => void,
};
function CommunityDrawerItemChat(
props: CommunityDrawerItemChatProps,
): React.Node {
const [expanded, setExpanded] = React.useState(false);
const styles = useStyles(unboundStyles);
const toggleExpanded = React.useCallback(() => {
setExpanded(isExpanded => !isExpanded);
}, []);
return (
);
}
const MemoizedCommunityDrawerItemChat: React.ComponentType =
React.memo(CommunityDrawerItemChat);
const MemoizedCommunityDrawerItem: React.ComponentType =
React.memo(CommunityDrawerItem);
export default MemoizedCommunityDrawerItem;