diff options
author | Samuel Newman <mozzius@protonmail.com> | 2024-05-15 16:05:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-15 16:05:17 +0100 |
commit | ed8922281af46071375f47112fbb37ba5b7d578b (patch) | |
tree | e1165525e3271efa044e2d3f7ef622c6632e168a /src | |
parent | 2121b5f86f5229914256c7a818086aaaf4c3581a (diff) | |
download | voidsky-ed8922281af46071375f47112fbb37ba5b7d578b.tar.zst |
[🐴] Show if user can be messaged in new chat search (#4021)
* show if user can be messaged * allow 2 lines in handle field due to new text * cannot -> can't * rework canBeMessaged logic and move to new file --------- Co-authored-by: Eric Bailey <git@esb.lol>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/Lists.tsx | 4 | ||||
-rw-r--r-- | src/components/dialogs/GifSelect.tsx | 1 | ||||
-rw-r--r-- | src/components/dms/NewChat.tsx | 37 | ||||
-rw-r--r-- | src/components/dms/util.ts | 18 | ||||
-rw-r--r-- | src/screens/Messages/List/ChatListItem.tsx | 18 |
5 files changed, 61 insertions, 17 deletions
diff --git a/src/components/Lists.tsx b/src/components/Lists.tsx index 721e877be..8cbe2810e 100644 --- a/src/components/Lists.tsx +++ b/src/components/Lists.tsx @@ -136,6 +136,7 @@ let ListMaybePlaceholder = ({ onGoBack, hideBackButton, sideBorders, + topBorder = true, }: { isLoading: boolean noEmpty?: boolean @@ -149,6 +150,7 @@ let ListMaybePlaceholder = ({ onGoBack?: () => void hideBackButton?: boolean sideBorders?: boolean + topBorder?: boolean }): React.ReactNode => { const t = useTheme() const {_} = useLingui() @@ -165,7 +167,7 @@ let ListMaybePlaceholder = ({ {paddingTop: 175, paddingBottom: 110}, ]} sideBorders={sideBorders ?? gtMobile} - topBorder={!gtTablet}> + topBorder={topBorder && !gtTablet}> <View style={[a.w_full, a.align_center, {top: 100}]}> <Loader size="xl" /> </View> diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index 57389ba2b..4a3ce42aa 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -197,6 +197,7 @@ function GifList({ onGoBack={onGoBack} emptyType="results" sideBorders={false} + topBorder={false} errorTitle={_(msg`Failed to load GIFs`)} errorMessage={_(msg`There was an issue connecting to Tenor.`)} emptyMessage={ diff --git a/src/components/dms/NewChat.tsx b/src/components/dms/NewChat.tsx index 5dde8628c..3975c0c5d 100644 --- a/src/components/dms/NewChat.tsx +++ b/src/components/dms/NewChat.tsx @@ -10,6 +10,7 @@ import {sanitizeHandle} from '#/lib/strings/handles' import {isWeb} from '#/platform/detection' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' +import {useSession} from '#/state/session' import {useActorAutocompleteQuery} from 'state/queries/actor-autocomplete' import {FAB} from '#/view/com/util/fab/FAB' import * as Toast from '#/view/com/util/Toast' @@ -23,6 +24,7 @@ import {Button} from '../Button' import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '../icons/Envelope' import {ListMaybePlaceholder} from '../Lists' import {Text} from '../Typography' +import {canBeMessaged} from './util' export function NewChat({ control, @@ -82,6 +84,7 @@ function SearchablePeopleList({ const moderationOpts = useModerationOpts() const control = Dialog.useDialogContext() const listRef = useRef<BottomSheetFlatListMethods>(null) + const {currentAccount} = useSession() const [searchText, setSearchText] = useState('') @@ -95,12 +98,17 @@ function SearchablePeopleList({ const renderItem = useCallback( ({item: profile}: {item: AppBskyActorDefs.ProfileView}) => { if (!moderationOpts) return null + const moderation = moderateProfile(profile, moderationOpts) + + const disabled = !canBeMessaged(profile) + const handle = sanitizeHandle(profile.handle, '@') + return ( <Button label={profile.displayName || sanitizeHandle(profile.handle)} - onPress={() => onCreateChat(profile.did)}> - {({hovered, pressed}) => ( + onPress={() => !disabled && onCreateChat(profile.did)}> + {({hovered, pressed, focused}) => ( <View style={[ a.flex_1, @@ -110,7 +118,9 @@ function SearchablePeopleList({ a.align_center, a.flex_row, a.rounded_sm, - pressed + disabled + ? {opacity: 0.5} + : pressed || focused ? t.atoms.bg_contrast_25 : hovered ? t.atoms.bg_contrast_50 @@ -131,8 +141,12 @@ function SearchablePeopleList({ moderation.ui('displayName'), )} </Text> - <Text style={t.atoms.text_contrast_high} numberOfLines={1}> - {sanitizeHandle(profile.handle, '@')} + <Text style={t.atoms.text_contrast_high} numberOfLines={2}> + {disabled ? ( + <Trans>{handle} can't be messaged</Trans> + ) : ( + handle + )} </Text> </View> </View> @@ -166,7 +180,6 @@ function SearchablePeopleList({ t.atoms.bg, ]} /> - <Dialog.Close /> <Text style={[ a.text_2xl, @@ -201,14 +214,23 @@ function SearchablePeopleList({ autoFocus /> </TextField.Root> + <Dialog.Close /> </View> ) }, [t.atoms.bg, _, control, searchText]) + const dataWithoutSelf = useMemo(() => { + return ( + actorAutocompleteData?.filter( + profile => profile.did !== currentAccount?.did, + ) ?? [] + ) + }, [actorAutocompleteData, currentAccount?.did]) + return ( <Dialog.InnerFlatList ref={listRef} - data={actorAutocompleteData} + data={dataWithoutSelf} renderItem={renderItem} ListHeaderComponent={ <> @@ -235,6 +257,7 @@ function SearchablePeopleList({ hideBackButton={true} emptyType="results" sideBorders={false} + topBorder={false} emptyMessage={ isError ? _(msg`No search results found for "${searchText}".`) diff --git a/src/components/dms/util.ts b/src/components/dms/util.ts new file mode 100644 index 000000000..5952b9acf --- /dev/null +++ b/src/components/dms/util.ts @@ -0,0 +1,18 @@ +import {AppBskyActorDefs} from '@atproto/api' + +export function canBeMessaged(profile: AppBskyActorDefs.ProfileView) { + switch (profile.associated?.chat?.allowIncoming) { + case 'none': + return false + case 'all': + return true + // if unset, treat as following + case 'following': + case undefined: + return Boolean(profile.viewer?.followedBy) + // any other values are invalid according to the lexicon, so + // let's treat as false to be safe + default: + return false + } +} diff --git a/src/screens/Messages/List/ChatListItem.tsx b/src/screens/Messages/List/ChatListItem.tsx index aa47e9503..0a0f8c575 100644 --- a/src/screens/Messages/List/ChatListItem.tsx +++ b/src/screens/Messages/List/ChatListItem.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, {useCallback, useState} from 'react' import {View} from 'react-native' import { AppBskyActorDefs, @@ -88,22 +88,22 @@ function ChatListItemReady({ } const navigation = useNavigation<NavigationProp>() - const [showActions, setShowActions] = React.useState(false) + const [showActions, setShowActions] = useState(false) - const onMouseEnter = React.useCallback(() => { + const onMouseEnter = useCallback(() => { setShowActions(true) }, []) - const onMouseLeave = React.useCallback(() => { + const onMouseLeave = useCallback(() => { setShowActions(false) }, []) - const onFocus = React.useCallback<React.FocusEventHandler>(e => { + const onFocus = useCallback<React.FocusEventHandler>(e => { if (e.nativeEvent.relatedTarget == null) return setShowActions(true) }, []) - const onPress = React.useCallback(() => { + const onPress = useCallback(() => { navigation.push('MessagesConversation', { conversation: convo.id, }) @@ -119,9 +119,9 @@ function ChatListItemReady({ <Button label={profile.displayName || profile.handle} onPress={onPress} - style={a.flex_1} + style={[a.flex_1]} onLongPress={isNative ? menuControl.open : undefined}> - {({hovered, pressed}) => ( + {({hovered, pressed, focused}) => ( <View style={[ a.flex_row, @@ -129,7 +129,7 @@ function ChatListItemReady({ a.px_lg, a.py_md, a.gap_md, - (hovered || pressed) && t.atoms.bg_contrast_25, + (hovered || pressed || focused) && t.atoms.bg_contrast_25, t.atoms.border_contrast_low, ]}> <UserAvatar |