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;