[🐴] 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>zio/stable
parent
2121b5f86f
commit
ed8922281a
|
@ -136,6 +136,7 @@ let ListMaybePlaceholder = ({
|
||||||
onGoBack,
|
onGoBack,
|
||||||
hideBackButton,
|
hideBackButton,
|
||||||
sideBorders,
|
sideBorders,
|
||||||
|
topBorder = true,
|
||||||
}: {
|
}: {
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
noEmpty?: boolean
|
noEmpty?: boolean
|
||||||
|
@ -149,6 +150,7 @@ let ListMaybePlaceholder = ({
|
||||||
onGoBack?: () => void
|
onGoBack?: () => void
|
||||||
hideBackButton?: boolean
|
hideBackButton?: boolean
|
||||||
sideBorders?: boolean
|
sideBorders?: boolean
|
||||||
|
topBorder?: boolean
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
@ -165,7 +167,7 @@ let ListMaybePlaceholder = ({
|
||||||
{paddingTop: 175, paddingBottom: 110},
|
{paddingTop: 175, paddingBottom: 110},
|
||||||
]}
|
]}
|
||||||
sideBorders={sideBorders ?? gtMobile}
|
sideBorders={sideBorders ?? gtMobile}
|
||||||
topBorder={!gtTablet}>
|
topBorder={topBorder && !gtTablet}>
|
||||||
<View style={[a.w_full, a.align_center, {top: 100}]}>
|
<View style={[a.w_full, a.align_center, {top: 100}]}>
|
||||||
<Loader size="xl" />
|
<Loader size="xl" />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -197,6 +197,7 @@ function GifList({
|
||||||
onGoBack={onGoBack}
|
onGoBack={onGoBack}
|
||||||
emptyType="results"
|
emptyType="results"
|
||||||
sideBorders={false}
|
sideBorders={false}
|
||||||
|
topBorder={false}
|
||||||
errorTitle={_(msg`Failed to load GIFs`)}
|
errorTitle={_(msg`Failed to load GIFs`)}
|
||||||
errorMessage={_(msg`There was an issue connecting to Tenor.`)}
|
errorMessage={_(msg`There was an issue connecting to Tenor.`)}
|
||||||
emptyMessage={
|
emptyMessage={
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members'
|
import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members'
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
import {useActorAutocompleteQuery} from 'state/queries/actor-autocomplete'
|
import {useActorAutocompleteQuery} from 'state/queries/actor-autocomplete'
|
||||||
import {FAB} from '#/view/com/util/fab/FAB'
|
import {FAB} from '#/view/com/util/fab/FAB'
|
||||||
import * as Toast from '#/view/com/util/Toast'
|
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 {Envelope_Stroke2_Corner0_Rounded as Envelope} from '../icons/Envelope'
|
||||||
import {ListMaybePlaceholder} from '../Lists'
|
import {ListMaybePlaceholder} from '../Lists'
|
||||||
import {Text} from '../Typography'
|
import {Text} from '../Typography'
|
||||||
|
import {canBeMessaged} from './util'
|
||||||
|
|
||||||
export function NewChat({
|
export function NewChat({
|
||||||
control,
|
control,
|
||||||
|
@ -82,6 +84,7 @@ function SearchablePeopleList({
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
const listRef = useRef<BottomSheetFlatListMethods>(null)
|
const listRef = useRef<BottomSheetFlatListMethods>(null)
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
const [searchText, setSearchText] = useState('')
|
const [searchText, setSearchText] = useState('')
|
||||||
|
|
||||||
|
@ -95,12 +98,17 @@ function SearchablePeopleList({
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
({item: profile}: {item: AppBskyActorDefs.ProfileView}) => {
|
({item: profile}: {item: AppBskyActorDefs.ProfileView}) => {
|
||||||
if (!moderationOpts) return null
|
if (!moderationOpts) return null
|
||||||
|
|
||||||
const moderation = moderateProfile(profile, moderationOpts)
|
const moderation = moderateProfile(profile, moderationOpts)
|
||||||
|
|
||||||
|
const disabled = !canBeMessaged(profile)
|
||||||
|
const handle = sanitizeHandle(profile.handle, '@')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
label={profile.displayName || sanitizeHandle(profile.handle)}
|
label={profile.displayName || sanitizeHandle(profile.handle)}
|
||||||
onPress={() => onCreateChat(profile.did)}>
|
onPress={() => !disabled && onCreateChat(profile.did)}>
|
||||||
{({hovered, pressed}) => (
|
{({hovered, pressed, focused}) => (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.flex_1,
|
a.flex_1,
|
||||||
|
@ -110,7 +118,9 @@ function SearchablePeopleList({
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.flex_row,
|
a.flex_row,
|
||||||
a.rounded_sm,
|
a.rounded_sm,
|
||||||
pressed
|
disabled
|
||||||
|
? {opacity: 0.5}
|
||||||
|
: pressed || focused
|
||||||
? t.atoms.bg_contrast_25
|
? t.atoms.bg_contrast_25
|
||||||
: hovered
|
: hovered
|
||||||
? t.atoms.bg_contrast_50
|
? t.atoms.bg_contrast_50
|
||||||
|
@ -131,8 +141,12 @@ function SearchablePeopleList({
|
||||||
moderation.ui('displayName'),
|
moderation.ui('displayName'),
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={t.atoms.text_contrast_high} numberOfLines={1}>
|
<Text style={t.atoms.text_contrast_high} numberOfLines={2}>
|
||||||
{sanitizeHandle(profile.handle, '@')}
|
{disabled ? (
|
||||||
|
<Trans>{handle} can't be messaged</Trans>
|
||||||
|
) : (
|
||||||
|
handle
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -166,7 +180,6 @@ function SearchablePeopleList({
|
||||||
t.atoms.bg,
|
t.atoms.bg,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Dialog.Close />
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.text_2xl,
|
a.text_2xl,
|
||||||
|
@ -201,14 +214,23 @@ function SearchablePeopleList({
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
|
<Dialog.Close />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}, [t.atoms.bg, _, control, searchText])
|
}, [t.atoms.bg, _, control, searchText])
|
||||||
|
|
||||||
|
const dataWithoutSelf = useMemo(() => {
|
||||||
|
return (
|
||||||
|
actorAutocompleteData?.filter(
|
||||||
|
profile => profile.did !== currentAccount?.did,
|
||||||
|
) ?? []
|
||||||
|
)
|
||||||
|
}, [actorAutocompleteData, currentAccount?.did])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.InnerFlatList
|
<Dialog.InnerFlatList
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
data={actorAutocompleteData}
|
data={dataWithoutSelf}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
ListHeaderComponent={
|
ListHeaderComponent={
|
||||||
<>
|
<>
|
||||||
|
@ -235,6 +257,7 @@ function SearchablePeopleList({
|
||||||
hideBackButton={true}
|
hideBackButton={true}
|
||||||
emptyType="results"
|
emptyType="results"
|
||||||
sideBorders={false}
|
sideBorders={false}
|
||||||
|
topBorder={false}
|
||||||
emptyMessage={
|
emptyMessage={
|
||||||
isError
|
isError
|
||||||
? _(msg`No search results found for "${searchText}".`)
|
? _(msg`No search results found for "${searchText}".`)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import React, {useCallback, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {
|
import {
|
||||||
AppBskyActorDefs,
|
AppBskyActorDefs,
|
||||||
|
@ -88,22 +88,22 @@ function ChatListItemReady({
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const [showActions, setShowActions] = React.useState(false)
|
const [showActions, setShowActions] = useState(false)
|
||||||
|
|
||||||
const onMouseEnter = React.useCallback(() => {
|
const onMouseEnter = useCallback(() => {
|
||||||
setShowActions(true)
|
setShowActions(true)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onMouseLeave = React.useCallback(() => {
|
const onMouseLeave = useCallback(() => {
|
||||||
setShowActions(false)
|
setShowActions(false)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onFocus = React.useCallback<React.FocusEventHandler>(e => {
|
const onFocus = useCallback<React.FocusEventHandler>(e => {
|
||||||
if (e.nativeEvent.relatedTarget == null) return
|
if (e.nativeEvent.relatedTarget == null) return
|
||||||
setShowActions(true)
|
setShowActions(true)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onPress = React.useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
navigation.push('MessagesConversation', {
|
navigation.push('MessagesConversation', {
|
||||||
conversation: convo.id,
|
conversation: convo.id,
|
||||||
})
|
})
|
||||||
|
@ -119,9 +119,9 @@ function ChatListItemReady({
|
||||||
<Button
|
<Button
|
||||||
label={profile.displayName || profile.handle}
|
label={profile.displayName || profile.handle}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={a.flex_1}
|
style={[a.flex_1]}
|
||||||
onLongPress={isNative ? menuControl.open : undefined}>
|
onLongPress={isNative ? menuControl.open : undefined}>
|
||||||
{({hovered, pressed}) => (
|
{({hovered, pressed, focused}) => (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.flex_row,
|
a.flex_row,
|
||||||
|
@ -129,7 +129,7 @@ function ChatListItemReady({
|
||||||
a.px_lg,
|
a.px_lg,
|
||||||
a.py_md,
|
a.py_md,
|
||||||
a.gap_md,
|
a.gap_md,
|
||||||
(hovered || pressed) && t.atoms.bg_contrast_25,
|
(hovered || pressed || focused) && t.atoms.bg_contrast_25,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
]}>
|
]}>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
|
|
Loading…
Reference in New Issue