migrate people lists off actor-autocomplete
This commit is contained in:
@@ -13,12 +13,12 @@ import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
|||||||
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
|
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
|
||||||
import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
|
import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
|
||||||
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {type ListMethods} from '#/view/com/util/List'
|
import {type ListMethods} from '#/view/com/util/List'
|
||||||
import {android, atoms as a, native, useTheme, web} from '#/alf'
|
import {android, atoms as a, native, useTheme, web} from '#/alf'
|
||||||
|
import {useAutocomplete} from '#/components/Autocomplete'
|
||||||
import {Button, ButtonIcon} from '#/components/Button'
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {
|
import {
|
||||||
@@ -106,10 +106,17 @@ export function SearchablePeopleList({
|
|||||||
const [searchText, setSearchText] = useState('')
|
const [searchText, setSearchText] = useState('')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: results,
|
items: autocompleteItems,
|
||||||
isError,
|
isError,
|
||||||
isFetching,
|
isFetching,
|
||||||
} = useActorAutocompleteQuery(searchText, true, 12)
|
} = useAutocomplete({
|
||||||
|
type: 'profile',
|
||||||
|
query: searchText,
|
||||||
|
limit: 12,
|
||||||
|
})
|
||||||
|
const results = autocompleteItems
|
||||||
|
.filter(item => item.type === 'profile')
|
||||||
|
.map(item => item.profile)
|
||||||
const {data: follows} = useProfileFollowsQuery(currentAccount?.did)
|
const {data: follows} = useProfileFollowsQuery(currentAccount?.did)
|
||||||
const {data: convos} = useListConvosQuery({
|
const {data: convos} = useListConvosQuery({
|
||||||
enabled: showRecentConvos,
|
enabled: showRecentConvos,
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import {LayoutAnimation, type TextInput, View} from 'react-native'
|
|||||||
import {Trans, useLingui} from '@lingui/react/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
|
||||||
import {useListConvoMembersQuery} from '#/state/queries/messages/list-convo-members'
|
import {useListConvoMembersQuery} from '#/state/queries/messages/list-convo-members'
|
||||||
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {type ListMethods} from '#/view/com/util/List'
|
import {type ListMethods} from '#/view/com/util/List'
|
||||||
import {android, atoms as a, native, useTheme, web} from '#/alf'
|
import {android, atoms as a, native, useTheme, web} from '#/alf'
|
||||||
|
import {useAutocomplete} from '#/components/Autocomplete'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {canBeAddedToGroup, type ConvoWithDetails} from '#/components/dms/util'
|
import {canBeAddedToGroup, type ConvoWithDetails} from '#/components/dms/util'
|
||||||
@@ -25,6 +25,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Ti
|
|||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {IS_NATIVE, IS_WEB} from '#/env'
|
import {IS_NATIVE, IS_WEB} from '#/env'
|
||||||
|
import {useFollowsSource} from '#/features/autocomplete/useFollowsSource'
|
||||||
import type * as bsky from '#/types/bsky'
|
import type * as bsky from '#/types/bsky'
|
||||||
import {ChatProfileTabs} from './ChatProfileTabs'
|
import {ChatProfileTabs} from './ChatProfileTabs'
|
||||||
import {EmptyMemberList} from './components/EmptyMemberList'
|
import {EmptyMemberList} from './components/EmptyMemberList'
|
||||||
@@ -145,11 +146,20 @@ export function AddMembersFlow({
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const followsSource = useFollowsSource()
|
||||||
const {
|
const {
|
||||||
data: autocompleteResults,
|
items: autocompleteItems,
|
||||||
isError,
|
isError,
|
||||||
isFetching: isAutocompleteFetching,
|
isFetching: isAutocompleteFetching,
|
||||||
} = useActorAutocompleteQuery(searchText, true, 12)
|
} = useAutocomplete({
|
||||||
|
type: 'profile',
|
||||||
|
query: searchText,
|
||||||
|
limit: 12,
|
||||||
|
sources: [followsSource],
|
||||||
|
})
|
||||||
|
const autocompleteResults = autocompleteItems
|
||||||
|
.filter(item => item.type === 'profile')
|
||||||
|
.map(item => item.profile)
|
||||||
const {data: follows} = useProfileFollowsQuery(currentAccount?.did)
|
const {data: follows} = useProfileFollowsQuery(currentAccount?.did)
|
||||||
const {data: memberListData = [], isPending: isMemberListPending} =
|
const {data: memberListData = [], isPending: isMemberListPending} =
|
||||||
useListConvoMembersQuery({
|
useListConvoMembersQuery({
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
|||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {isOverMaxGraphemeCount} from '#/lib/strings/helpers'
|
import {isOverMaxGraphemeCount} from '#/lib/strings/helpers'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
|
||||||
import {useChatActorStatusQuery} from '#/state/queries/messages/get-status'
|
import {useChatActorStatusQuery} from '#/state/queries/messages/get-status'
|
||||||
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {type ListMethods} from '#/view/com/util/List'
|
import {type ListMethods} from '#/view/com/util/List'
|
||||||
import {android, atoms as a, native, useTheme, web} from '#/alf'
|
import {android, atoms as a, native, useTheme, web} from '#/alf'
|
||||||
|
import {useAutocomplete} from '#/components/Autocomplete'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {canBeAddedToGroup, canBeMessaged} from '#/components/dms/util'
|
import {canBeAddedToGroup, canBeMessaged} from '#/components/dms/util'
|
||||||
@@ -38,6 +38,7 @@ import * as Prompt from '#/components/Prompt'
|
|||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAgeAssurance} from '#/ageAssurance'
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
import {IS_NATIVE, IS_WEB} from '#/env'
|
import {IS_NATIVE, IS_WEB} from '#/env'
|
||||||
|
import {useFollowsSource} from '#/features/autocomplete/useFollowsSource'
|
||||||
import type * as bsky from '#/types/bsky'
|
import type * as bsky from '#/types/bsky'
|
||||||
import {ChatProfileTabs} from './ChatProfileTabs'
|
import {ChatProfileTabs} from './ChatProfileTabs'
|
||||||
import {EmptyMemberList} from './components/EmptyMemberList'
|
import {EmptyMemberList} from './components/EmptyMemberList'
|
||||||
@@ -253,11 +254,20 @@ export function InitiateChatFlow({
|
|||||||
searchText: '',
|
searchText: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const followsSource = useFollowsSource()
|
||||||
const {
|
const {
|
||||||
data: results,
|
items: autocompleteItems,
|
||||||
isError,
|
isError,
|
||||||
isFetching,
|
isFetching,
|
||||||
} = useActorAutocompleteQuery(searchText, true, 12)
|
} = useAutocomplete({
|
||||||
|
type: 'profile',
|
||||||
|
query: searchText,
|
||||||
|
limit: 12,
|
||||||
|
sources: [followsSource],
|
||||||
|
})
|
||||||
|
const results = autocompleteItems
|
||||||
|
.filter(item => item.type === 'profile')
|
||||||
|
.map(item => item.profile)
|
||||||
const {data: follows} = useProfileFollowsQuery(currentAccount?.did)
|
const {data: follows} = useProfileFollowsQuery(currentAccount?.did)
|
||||||
|
|
||||||
const newGroupChatTitle = l`New group chat`
|
const newGroupChatTitle = l`New group chat`
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import {type ModerationOpts} from '@atproto/api'
|
|||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useA11y} from '#/state/a11y'
|
import {useA11y} from '#/state/a11y'
|
||||||
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
|
||||||
import {useActorSearch} from '#/state/queries/actor-search'
|
import {useActorSearch} from '#/state/queries/actor-search'
|
||||||
import {List} from '#/view/com/util/List'
|
import {List} from '#/view/com/util/List'
|
||||||
import {useWizardState} from '#/screens/StarterPack/Wizard/State'
|
import {useWizardState} from '#/screens/StarterPack/Wizard/State'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
import {useAutocomplete} from '#/components/Autocomplete'
|
||||||
import {SearchInput} from '#/components/forms/SearchInput'
|
import {SearchInput} from '#/components/forms/SearchInput'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {ScreenTransition} from '#/components/ScreenTransition'
|
import {ScreenTransition} from '#/components/ScreenTransition'
|
||||||
@@ -43,9 +43,16 @@ export function StepProfiles({
|
|||||||
.flatMap(p => p.actors)
|
.flatMap(p => p.actors)
|
||||||
.filter(p => !p.associated?.labeler)
|
.filter(p => !p.associated?.labeler)
|
||||||
|
|
||||||
const {data: resultsUnfiltered, isFetching: isFetchingResults} =
|
const {items: autocompleteItems, isFetching: isFetchingResults} =
|
||||||
useActorAutocompleteQuery(query, true, 12)
|
useAutocomplete({
|
||||||
const results = resultsUnfiltered?.filter(p => !p.associated?.labeler)
|
type: 'profile',
|
||||||
|
query,
|
||||||
|
limit: 12,
|
||||||
|
})
|
||||||
|
const results = autocompleteItems
|
||||||
|
.filter(item => item.type === 'profile')
|
||||||
|
.map(item => item.profile)
|
||||||
|
.filter(profile => !profile.associated?.labeler)
|
||||||
|
|
||||||
const isLoading = isLoadingTopPages || isFetchingResults
|
const isLoading = isLoadingTopPages || isFetchingResults
|
||||||
|
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
import {useCallback} from 'react'
|
|
||||||
import {
|
|
||||||
type AppBskyActorDefs,
|
|
||||||
moderateProfile,
|
|
||||||
type ModerationOpts,
|
|
||||||
} from '@atproto/api'
|
|
||||||
import {keepPreviousData, useQuery, useQueryClient} from '@tanstack/react-query'
|
|
||||||
|
|
||||||
import {isJustAMute, moduiContainsHideableOffense} from '#/lib/moderation'
|
|
||||||
import {logger} from '#/logger'
|
|
||||||
import {STALE} from '#/state/queries'
|
|
||||||
import {useAgent} from '#/state/session'
|
|
||||||
import {useModerationOpts} from '../preferences/moderation-opts'
|
|
||||||
import {DEFAULT_LOGGED_OUT_PREFERENCES} from './preferences'
|
|
||||||
|
|
||||||
const DEFAULT_MOD_OPTS = {
|
|
||||||
userDid: undefined,
|
|
||||||
prefs: DEFAULT_LOGGED_OUT_PREFERENCES.moderationPrefs,
|
|
||||||
}
|
|
||||||
|
|
||||||
const RQKEY_ROOT = 'actor-autocomplete'
|
|
||||||
export const RQKEY = (prefix: string) => [RQKEY_ROOT, prefix]
|
|
||||||
|
|
||||||
export function useActorAutocompleteQuery(
|
|
||||||
prefix: string,
|
|
||||||
maintainData?: boolean,
|
|
||||||
limit?: number,
|
|
||||||
) {
|
|
||||||
const moderationOpts = useModerationOpts()
|
|
||||||
const agent = useAgent()
|
|
||||||
|
|
||||||
prefix = prefix.toLowerCase().trim()
|
|
||||||
if (prefix.endsWith('.')) {
|
|
||||||
// Going from "foo" to "foo." should not clear matches.
|
|
||||||
prefix = prefix.slice(0, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return useQuery<AppBskyActorDefs.ProfileViewBasic[]>({
|
|
||||||
staleTime: STALE.MINUTES.ONE,
|
|
||||||
queryKey: RQKEY(prefix || ''),
|
|
||||||
async queryFn() {
|
|
||||||
const res = prefix
|
|
||||||
? await agent.searchActorsTypeahead({
|
|
||||||
q: prefix,
|
|
||||||
limit: limit || 8,
|
|
||||||
})
|
|
||||||
: undefined
|
|
||||||
return res?.data.actors || []
|
|
||||||
},
|
|
||||||
select: useCallback(
|
|
||||||
(data: AppBskyActorDefs.ProfileViewBasic[]) => {
|
|
||||||
return computeSuggestions({
|
|
||||||
q: prefix,
|
|
||||||
searched: data,
|
|
||||||
moderationOpts: moderationOpts || DEFAULT_MOD_OPTS,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[prefix, moderationOpts],
|
|
||||||
),
|
|
||||||
placeholderData: maintainData ? keepPreviousData : undefined,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ActorAutocompleteFn = ReturnType<typeof useActorAutocompleteFn>
|
|
||||||
export function useActorAutocompleteFn() {
|
|
||||||
const queryClient = useQueryClient()
|
|
||||||
const moderationOpts = useModerationOpts()
|
|
||||||
const agent = useAgent()
|
|
||||||
|
|
||||||
return useCallback(
|
|
||||||
async ({query, limit = 8}: {query: string; limit?: number}) => {
|
|
||||||
query = query.toLowerCase()
|
|
||||||
let res
|
|
||||||
if (query) {
|
|
||||||
try {
|
|
||||||
res = await queryClient.fetchQuery({
|
|
||||||
staleTime: STALE.MINUTES.ONE,
|
|
||||||
queryKey: RQKEY(query || ''),
|
|
||||||
queryFn: () =>
|
|
||||||
agent.searchActorsTypeahead({
|
|
||||||
q: query,
|
|
||||||
limit,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
logger.error('useActorSearch: searchActorsTypeahead failed', {
|
|
||||||
message: e,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return computeSuggestions({
|
|
||||||
q: query,
|
|
||||||
searched: res?.data.actors,
|
|
||||||
moderationOpts: moderationOpts || DEFAULT_MOD_OPTS,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[queryClient, moderationOpts, agent],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function computeSuggestions({
|
|
||||||
q,
|
|
||||||
searched = [],
|
|
||||||
moderationOpts,
|
|
||||||
}: {
|
|
||||||
q?: string
|
|
||||||
searched?: AppBskyActorDefs.ProfileViewBasic[]
|
|
||||||
moderationOpts: ModerationOpts
|
|
||||||
}) {
|
|
||||||
let items: AppBskyActorDefs.ProfileViewBasic[] = []
|
|
||||||
for (const item of searched) {
|
|
||||||
if (!items.find(item2 => item2.handle === item.handle)) {
|
|
||||||
items.push(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return items.filter(profile => {
|
|
||||||
const modui = moderateProfile(profile, moderationOpts).ui('profileList')
|
|
||||||
const isExactMatch = q && profile.handle.toLowerCase() === q
|
|
||||||
return (
|
|
||||||
(isExactMatch && !moduiContainsHideableOffense(modui)) ||
|
|
||||||
!modui.filter ||
|
|
||||||
isJustAMute(modui)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user