diff --git a/native/chat/chat-thread-list-item.react.js b/native/chat/chat-thread-list-item.react.js index c95bf8082..7ed332645 100644 --- a/native/chat/chat-thread-list-item.react.js +++ b/native/chat/chat-thread-list-item.react.js @@ -1,328 +1,308 @@ // @flow import Icon from '@expo/vector-icons/FontAwesome.js'; import * as React from 'react'; import { Text, View } from 'react-native'; import type { ChatThreadItem } from 'lib/selectors/chat-selectors.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import { threadTypeIsThick } from 'lib/types/thread-types-enum.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 ThreadAvatar from '../avatars/thread-avatar.react.js'; import Button from '../components/button.react.js'; import SingleLine from '../components/single-line.react.js'; import ThreadAncestorsLabel from '../components/thread-ancestors-label.react.js'; import UnreadDot from '../components/unread-dot.react.js'; import { useColors, useStyles } from '../themes/colors.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 = React.useMemo( () => data.sidebars.map((sidebarItem, index) => { if (sidebarItem.type === 'sidebar') { const { type, ...sidebarInfo } = sidebarItem; return ( ); } else if (sidebarItem.type === 'seeMore') { return ( ); } else { return ; } }), [ currentlyOpenedSwipeableId, data.sidebars, data.threadInfo, numOfSidebarsWithExtendedArrow, onPressItem, onPressSeeMoreSidebars, onSwipeableWillOpen, styles.spacer, ], ); 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 unreadDot = React.useMemo( () => ( ), [data.threadInfo.currentUser.unread, styles.avatarContainer], ); const threadAvatar = React.useMemo( () => ( ), [data.threadInfo, styles.avatarContainer], ); const isThick = threadTypeIsThick(data.threadInfo.type); const iconStyle = data.threadInfo.currentUser.unread ? styles.iconUnread : styles.iconRead; const iconName = isThick ? 'lock' : 'server'; const threadDetails = React.useMemo( () => ( {resolvedThreadInfo.uiName} - {lastMessage} + {lastActivity} ), [ iconStyle, data.threadInfo, iconName, lastActivity, lastActivityStyle, - lastMessage, resolvedThreadInfo.uiName, styles.header, styles.iconContainer, styles.row, styles.threadDetails, threadNameStyle, + data.mostRecentMessageInfo, ], ); const swipeableThreadContent = React.useMemo( () => ( ), [ colors.listIosHighlightUnderlay, onPress, styles.container, styles.content, threadAvatar, threadDetails, unreadDot, ], ); const swipeableThread = React.useMemo( () => ( {swipeableThreadContent} ), [ currentlyOpenedSwipeableId, data.mostRecentNonLocalMessage, data.threadInfo, onSwipeableWillOpen, swipeableThreadContent, ], ); const chatThreadListItem = React.useMemo( () => ( <> {swipeableThread} {sidebars} ), [sidebars, swipeableThread], ); return chatThreadListItem; } const chatThreadListItemHeight = 70; const spacerHeight = 6; const unboundStyles = { container: { height: chatThreadListItemHeight, justifyContent: 'center', backgroundColor: 'listBackground', }, content: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, avatarContainer: { 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, }, header: { flexDirection: 'row', alignItems: 'center', }, iconContainer: { marginRight: 6, }, iconRead: { color: 'listForegroundTertiaryLabel', }, iconUnread: { color: 'listForegroundLabel', }, }; export { ChatThreadListItem, chatThreadListItemHeight, spacerHeight }; diff --git a/native/chat/message-preview.react.js b/native/chat/message-preview.react.js index bd37943e6..3469c3fa2 100644 --- a/native/chat/message-preview.react.js +++ b/native/chat/message-preview.react.js @@ -1,93 +1,102 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { Text } from 'react-native'; import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { useMessagePreview } from 'lib/shared/message-utils.js'; import { type MessageInfo } from 'lib/types/message-types.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import SingleLine from '../components/single-line.react.js'; import { getDefaultTextMessageRules } from '../markdown/rules.react.js'; import { useStyles } from '../themes/colors.js'; type Props = { - +messageInfo: MessageInfo, + +messageInfo: ?MessageInfo, +threadInfo: ThreadInfo, }; function MessagePreview(props: Props): React.Node { const { messageInfo, threadInfo } = props; const chatMentionCandidates = useThreadChatMentionCandidates(threadInfo); const messagePreviewResult = useMessagePreview( messageInfo, threadInfo, getDefaultTextMessageRules(chatMentionCandidates).simpleMarkdownRules, ); - invariant( - messagePreviewResult, - 'useMessagePreview should only return falsey if pass null or undefined', - ); + const styles = useStyles(unboundStyles); + if (!messagePreviewResult) { + return ( + + No messages + + ); + } const { message, username } = messagePreviewResult; let messageStyle; - const styles = useStyles(unboundStyles); if (message.style === 'unread') { messageStyle = styles.unread; } else if (message.style === 'primary') { messageStyle = styles.primary; } else if (message.style === 'secondary') { messageStyle = styles.secondary; } invariant( messageStyle, `MessagePreview doesn't support ${message.style} style for message, ` + 'only unread, primary, and secondary', ); if (!username) { return ( {message.text} ); } let usernameStyle; if (username.style === 'unread') { usernameStyle = styles.unread; } else if (username.style === 'secondary') { usernameStyle = styles.secondary; } invariant( usernameStyle, `MessagePreview doesn't support ${username.style} style for username, ` + 'only unread and secondary', ); return ( {`${username.text}: `} {message.text} ); } const unboundStyles = { lastMessage: { flex: 1, fontSize: 14, }, primary: { color: 'listForegroundTertiaryLabel', }, secondary: { color: 'listForegroundTertiaryLabel', }, unread: { color: 'listForegroundLabel', }, + noMessages: { + color: 'listForegroundTertiaryLabel', + flex: 1, + fontSize: 14, + fontStyle: 'italic', + }, }; export default MessagePreview; diff --git a/native/chat/subchannel-item.react.js b/native/chat/subchannel-item.react.js index f25727da1..c2c5e8cbd 100644 --- a/native/chat/subchannel-item.react.js +++ b/native/chat/subchannel-item.react.js @@ -1,109 +1,90 @@ // @flow import * as React from 'react'; import { Text, View } from 'react-native'; import type { ChatThreadItem } from 'lib/selectors/chat-selectors.js'; import { shortAbsoluteDate } from 'lib/utils/date-utils.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; import MessagePreview from './message-preview.react.js'; import SingleLine from '../components/single-line.react.js'; import SWMansionIcon from '../components/swmansion-icon.react.js'; import { useStyles } from '../themes/colors.js'; type Props = { +subchannelInfo: ChatThreadItem, }; function SubchannelItem(props: Props): React.Node { const { lastUpdatedTime, threadInfo, mostRecentMessageInfo } = props.subchannelInfo; const { uiName } = useResolvedThreadInfo(threadInfo); const lastActivity = shortAbsoluteDate(lastUpdatedTime); const styles = useStyles(unboundStyles); const unreadStyle = threadInfo.currentUser.unread ? styles.unread : null; - const lastMessage = React.useMemo(() => { - if (!mostRecentMessageInfo) { - return ( - - No messages - - ); - } - return ( - - ); - }, [mostRecentMessageInfo, threadInfo, styles]); - return ( {uiName} - {lastMessage} + {lastActivity} ); } const fontSize = 14; const unboundStyles = { outerView: { flex: 1, flexDirection: 'column', justifyContent: 'center', paddingVertical: 8, paddingHorizontal: 8, height: 60, }, itemRowContainer: { flexDirection: 'row', alignItems: 'center', }, unread: { color: 'listForegroundLabel', fontWeight: 'bold', }, name: { color: 'listForegroundSecondaryLabel', flex: 1, fontSize: 16, paddingBottom: 8, }, lastActivity: { color: 'listForegroundTertiaryLabel', fontSize, marginLeft: 10, }, iconWrapper: { marginRight: 8, alignItems: 'center', }, icon: { fontSize: 22, color: 'listForegroundSecondaryLabel', alignItems: 'center', height: 24, }, - noMessages: { - color: 'listForegroundTertiaryLabel', - flex: 1, - fontSize, - fontStyle: 'italic', - }, }; export default SubchannelItem;