[🐴] Adjust messages list styles (#3945)

* some initial tweaks

* tweaks

* more tweaks

* tweak chat header

* properly align placeholders

* tweak web header

* one more...

* remove extra loading states from chat

* limit line count for display name

* Tweaks styles (#3949)

* Adjust sizing

* Consistent font size

* Adjust header

* oops

* fix accessibility in list

* don't use `identifier` for notifications, use `dates` instead

---------

Co-authored-by: Eric Bailey <git@esb.lol>
zio/stable
Hailey 2024-05-10 08:09:00 -07:00 committed by GitHub
parent 1a90426026
commit e729647c02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 133 additions and 100 deletions

View File

@ -58,7 +58,7 @@ export function useNotificationsHandler() {
const closeAllActiveElements = useCloseAllActiveElements() const closeAllActiveElements = useCloseAllActiveElements()
// Safety to prevent double handling of the same notification // Safety to prevent double handling of the same notification
const prevIdentifier = React.useRef('') const prevDate = React.useRef(0)
React.useEffect(() => { React.useEffect(() => {
const handleNotification = (payload?: NotificationPayload) => { const handleNotification = (payload?: NotificationPayload) => {
@ -161,10 +161,10 @@ export function useNotificationsHandler() {
const responseReceivedListener = const responseReceivedListener =
Notifications.addNotificationResponseReceivedListener(e => { Notifications.addNotificationResponseReceivedListener(e => {
if (e.notification.request.identifier === prevIdentifier.current) { if (e.notification.date === prevDate.current) {
return return
} }
prevIdentifier.current = e.notification.request.identifier prevDate.current = e.notification.date
logger.debug( logger.debug(
'Notifications: response received', 'Notifications: response received',

View File

@ -160,7 +160,7 @@ let Header = ({
a.gap_lg, a.gap_lg,
a.pl_xl, a.pl_xl,
a.pr_lg, a.pr_lg,
a.py_sm, a.py_md,
]}> ]}>
{!gtTablet ? ( {!gtTablet ? (
<TouchableOpacity <TouchableOpacity
@ -188,13 +188,11 @@ let Header = ({
<View style={[a.align_center]}> <View style={[a.align_center]}>
<PreviewableUserAvatar size={32} profile={profile} /> <PreviewableUserAvatar size={32} profile={profile} />
<Text <Text
style={[a.text_lg, a.font_bold, isWeb ? a.mt_md : a.mt_sm]} style={[a.text_lg, a.font_bold, a.pt_sm, a.pb_2xs]}
numberOfLines={1}> numberOfLines={1}>
{profile.displayName} {profile.displayName}
</Text> </Text>
<Text <Text style={[t.atoms.text_contrast_medium]} numberOfLines={1}>
style={[t.atoms.text_contrast_medium, {fontSize: 15}]}
numberOfLines={1}>
@{profile.handle} @{profile.handle}
</Text> </Text>
</View> </View>

View File

@ -1,5 +1,3 @@
/* eslint-disable react/prop-types */
import React, {useCallback, useMemo, useState} from 'react' import React, {useCallback, useMemo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {ChatBskyConvoDefs} from '@atproto-labs/api' import {ChatBskyConvoDefs} from '@atproto-labs/api'
@ -40,6 +38,21 @@ import {ClipClopGate} from '../gate'
import {useDmServiceUrlStorage} from '../Temp/useDmServiceUrlStorage' import {useDmServiceUrlStorage} from '../Temp/useDmServiceUrlStorage'
type Props = NativeStackScreenProps<MessagesTabNavigatorParams, 'Messages'> type Props = NativeStackScreenProps<MessagesTabNavigatorParams, 'Messages'>
function renderItem({
item,
index,
}: {
item: ChatBskyConvoDefs.ConvoView
index: number
}) {
return <ChatListItem convo={item} index={index} />
}
function keyExtractor(item: ChatBskyConvoDefs.ConvoView) {
return item.id
}
export function MessagesScreen({navigation, route}: Props) { export function MessagesScreen({navigation, route}: Props) {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
@ -135,13 +148,6 @@ export function MessagesScreen({navigation, route}: Props) {
navigation.navigate('MessagesSettings') navigation.navigate('MessagesSettings')
}, [navigation]) }, [navigation])
const renderItem = useCallback(
({item}: {item: ChatBskyConvoDefs.ConvoView}) => {
return <ChatListItem key={item.id} convo={item} />
},
[],
)
const gate = useGate() const gate = useGate()
if (!gate('dms')) return <ClipClopGate /> if (!gate('dms')) return <ClipClopGate />
@ -213,7 +219,7 @@ export function MessagesScreen({navigation, route}: Props) {
<ViewHeader <ViewHeader
title={_(msg`Messages`)} title={_(msg`Messages`)}
renderButton={renderButton} renderButton={renderButton}
showBorder showBorder={false}
canGoBack={false} canGoBack={false}
/> />
)} )}
@ -221,7 +227,7 @@ export function MessagesScreen({navigation, route}: Props) {
<List <List
data={conversations} data={conversations}
renderItem={renderItem} renderItem={renderItem}
keyExtractor={item => item.id} keyExtractor={keyExtractor}
refreshing={isPTRing} refreshing={isPTRing}
onRefresh={onRefresh} onRefresh={onRefresh}
onEndReached={onEndReached} onEndReached={onEndReached}
@ -249,7 +255,13 @@ export function MessagesScreen({navigation, route}: Props) {
) )
} }
function ChatListItem({convo}: {convo: ChatBskyConvoDefs.ConvoView}) { function ChatListItem({
convo,
index,
}: {
convo: ChatBskyConvoDefs.ConvoView
index: number
}) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
const {currentAccount} = useSession() const {currentAccount} = useSession()
@ -301,95 +313,120 @@ function ChatListItem({convo}: {convo: ChatBskyConvoDefs.ConvoView}) {
} }
return ( return (
<Button <View
label={otherUser.displayName || otherUser.handle}
onPress={onPress}
style={a.flex_1}
onLongPress={isNative ? menuControl.open : undefined}
// @ts-expect-error web only // @ts-expect-error web only
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
onFocus={onFocus} onFocus={onFocus}
onBlur={onMouseLeave}> onBlur={onMouseLeave}>
{({hovered, pressed}) => ( <Button
<View label={otherUser.displayName || otherUser.handle}
style={[ onPress={onPress}
a.flex_row, style={a.flex_1}
a.flex_1, onLongPress={isNative ? menuControl.open : undefined}>
a.pl_md, {({hovered, pressed}) => (
a.py_sm, <View
a.gap_md, style={[
a.pr_xl, a.flex_row,
(hovered || pressed) && t.atoms.bg_contrast_25, a.flex_1,
]}> a.px_lg,
<View pointerEvents="none"> a.py_md,
<UserAvatar avatar={otherUser?.avatar} size={42} /> a.gap_md,
</View> (hovered || pressed) && t.atoms.bg_contrast_25,
<View style={[a.flex_1]}> index === 0 && [a.border_t, a.pt_lg],
<Text t.atoms.border_contrast_low,
numberOfLines={1} ]}>
style={[a.text_md, web([a.leading_normal, {marginTop: -4}])]}> <UserAvatar avatar={otherUser?.avatar} size={52} />
<Text <View style={[a.flex_1, a.flex_row, a.align_center]}>
style={[t.atoms.text, convo.unreadCount > 0 && a.font_bold]}> <View style={[a.flex_1]}>
{otherUser.displayName || otherUser.handle} <View
</Text>{' '} style={[
{lastMessageSentAt ? ( a.flex_1,
<TimeElapsed timestamp={lastMessageSentAt}> a.flex_row,
{({timeElapsed}) => ( a.align_end,
<Text style={t.atoms.text_contrast_medium}> a.pb_2xs,
@{otherUser.handle} &middot; {timeElapsed} web([{marginTop: -2}]),
]}>
<Text
numberOfLines={1}
style={[{maxWidth: '85%'}, web([a.leading_normal])]}>
<Text style={[a.text_md, t.atoms.text, a.font_bold]}>
{otherUser.displayName || otherUser.handle}
</Text> </Text>
</Text>
{lastMessageSentAt && (
<TimeElapsed timestamp={lastMessageSentAt}>
{({timeElapsed}) => (
<Text
style={[
a.text_sm,
web([a.leading_normal]),
t.atoms.text_contrast_medium,
]}>
{' '}
&middot; {timeElapsed}
</Text>
)}
</TimeElapsed>
)} )}
</TimeElapsed> </View>
) : ( <Text
<Text style={t.atoms.text_contrast_medium}> numberOfLines={1}
style={[a.text_sm, t.atoms.text_contrast_medium, a.pb_xs]}>
@{otherUser.handle} @{otherUser.handle}
</Text> </Text>
<Text
numberOfLines={2}
style={[
a.text_sm,
a.leading_snug,
convo.unreadCount > 0
? a.font_bold
: t.atoms.text_contrast_high,
]}>
{lastMessage}
</Text>
</View>
{convo.unreadCount > 0 && (
<View
style={[
a.absolute,
a.rounded_full,
{
backgroundColor: convo.muted
? t.palette.contrast_200
: t.palette.primary_500,
height: 7,
width: 7,
},
isNative
? {
top: 15,
right: 12,
}
: {
top: 0,
right: 0,
},
]}
/>
)} )}
</Text> <ConvoMenu
<Text convo={convo}
numberOfLines={2} profile={otherUser}
style={[ control={menuControl}
a.text_sm, currentScreen="list"
a.leading_snug, showMarkAsRead={convo.unreadCount > 0}
convo.unreadCount > 0 hideTrigger={isNative}
? a.font_bold triggerOpacity={
: t.atoms.text_contrast_medium, !gtMobile || showActions || menuControl.isOpen ? 1 : 0
]}> }
{lastMessage} />
</Text> </View>
</View> </View>
{convo.unreadCount > 0 && ( )}
<View </Button>
style={[ </View>
a.flex_0,
a.ml_md,
a.mt_sm,
a.rounded_full,
{
backgroundColor: convo.muted
? t.palette.contrast_200
: t.palette.primary_500,
height: 7,
width: 7,
},
]}
/>
)}
<ConvoMenu
convo={convo}
profile={otherUser}
control={menuControl}
currentScreen="list"
showMarkAsRead={convo.unreadCount > 0}
hideTrigger={isNative}
triggerOpacity={
!gtMobile || showActions || menuControl.isOpen ? 1 : 0
}
/>
</View>
)}
</Button>
) )
} }
@ -412,8 +449,6 @@ function DesktopHeader({
<View <View
style={[ style={[
t.atoms.bg, t.atoms.bg,
t.atoms.border_contrast_low,
a.border_b,
a.flex_row, a.flex_row,
a.align_center, a.align_center,
a.justify_between, a.justify_between,