diff --git a/src/screens/StarterPack/Wizard/StepProfiles/WizardAddProfilesDialog.tsx b/src/components/StarterPack/Wizard/WizardAddDialog.tsx
similarity index 64%
rename from src/screens/StarterPack/Wizard/StepProfiles/WizardAddProfilesDialog.tsx
rename to src/components/StarterPack/Wizard/WizardAddDialog.tsx
index d74f24b05d..1dec85da28 100644
--- a/src/screens/StarterPack/Wizard/StepProfiles/WizardAddProfilesDialog.tsx
+++ b/src/components/StarterPack/Wizard/WizardAddDialog.tsx
@@ -2,45 +2,131 @@ import React, {useLayoutEffect, useRef, useState} from 'react'
import type {ListRenderItemInfo, TextInput as RNTextInput} from 'react-native'
import {View} from 'react-native'
import {AppBskyActorDefs} from '@atproto/api'
+import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
import {BottomSheetFlatListMethods} from '@discord/bottom-sheet'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
+import debounce from 'lodash.debounce'
-import {isWeb} from '#/platform/detection'
+import {isWeb} from 'platform/detection'
import {useActorAutocompleteQuery} from 'state/queries/actor-autocomplete'
+import {
+ useGetPopularFeedsQuery,
+ useSearchPopularFeedsMutation,
+} from 'state/queries/feed'
import {useProfileFollowsQuery} from 'state/queries/profile-follows'
import {useSession} from 'state/session'
import {WizardAction, WizardState} from '#/screens/StarterPack/Wizard/State'
-import {WizardProfileCard} from '#/screens/StarterPack/Wizard/StepProfiles/WizardProfileCard'
import {atoms as a, native, useTheme, web} from '#/alf'
import * as Dialog from '#/components/Dialog'
import {TextInput} from '#/components/dms/dialogs/TextInput'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2'
+import {WizardFeedCard} from '#/components/StarterPack/Wizard/WizardFeedCard'
+import {WizardProfileCard} from '#/components/StarterPack/Wizard/WizardProfileCard'
import {Text} from '#/components/Typography'
-function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
- return item.did
-}
-
-export function WizardAddProfilesDialog({
- control,
- state,
- dispatch,
-}: {
+interface Props {
control: Dialog.DialogControlProps
+ type: 'profiles' | 'feeds'
state: WizardState
dispatch: (action: WizardAction) => void
-}) {
+}
+
+function keyExtractor(
+ item: AppBskyActorDefs.ProfileViewBasic | GeneratorView,
+ index: number,
+) {
+ return `${item.did}-${index}`
+}
+
+export function WizardAddDialog(props: Props) {
+ if (props.type === 'profiles') {
+ return
+ }
+ return
+}
+
+function AddProfiles(props: Props) {
const [searchText, setSearchText] = useState('')
const {currentAccount} = useSession()
- const {data: results} = useActorAutocompleteQuery(searchText, true, 12)
const {data: followsPages, fetchNextPage} = useProfileFollowsQuery(
currentAccount?.did,
)
const follows = followsPages?.pages.flatMap(page => page.follows) || []
+ const {data: searchedProfiles} = useActorAutocompleteQuery(
+ searchText,
+ true,
+ 12,
+ )
+
+ return (
+ fetchNextPage()}
+ searchText={searchText}
+ setSearchText={setSearchText}
+ />
+ )
+}
+
+function AddFeeds(props: Props) {
+ const [searchText, setSearchText] = useState('')
+
+ const {data: popularFeedsPages, fetchNextPage} = useGetPopularFeedsQuery()
+
+ const popularFeeds =
+ popularFeedsPages?.pages.flatMap(page => page.feeds) || []
+
+ const {
+ data: searchedFeeds,
+ mutate: search,
+ reset: resetSearch,
+ } = useSearchPopularFeedsMutation()
+
+ const debouncedSearch = React.useMemo(
+ () => debounce(q => search(q), 500), // debounce for 500ms
+ [search],
+ )
+
+ const onChangeText = (text: string) => {
+ setSearchText(text)
+ if (text.length > 1) {
+ debouncedSearch(text)
+ } else {
+ resetSearch()
+ }
+ }
+
+ return (
+ fetchNextPage()}
+ searchText={searchText}
+ setSearchText={onChangeText}
+ />
+ )
+}
+
+function AddDialog({
+ type,
+ control,
+ state,
+ dispatch,
+ data,
+ onEndReached,
+ searchText,
+ setSearchText,
+}: Props & {
+ data?: AppBskyActorDefs.ProfileViewBasic[] | GeneratorView[]
+ onEndReached?: () => void
+ searchText: string
+ setSearchText: (text: string) => void
+}) {
const listRef = useRef(null)
const inputRef = useRef(null)
@@ -52,13 +138,12 @@ export function WizardAddProfilesDialog({
}
}, [])
- const renderItem = ({
- item,
- }: ListRenderItemInfo) => {
- return (
+ const renderItem = ({item}: ListRenderItemInfo) =>
+ type === 'profiles' ? (
+ ) : (
+
)
- }
return (
0 ? results : follows}
+ data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
ListHeaderComponent={
@@ -75,6 +160,7 @@ export function WizardAddProfilesDialog({
searchText={searchText}
setSearchText={setSearchText}
inputRef={inputRef}
+ type={type}
/>
}
stickyHeaderIndices={[0]}
@@ -91,7 +177,7 @@ export function WizardAddProfilesDialog({
]}
webInnerStyle={[a.py_0, {maxWidth: 500, minWidth: 200}]}
keyboardDismissMode="on-drag"
- onEndReached={() => fetchNextPage()}
+ onEndReached={onEndReached}
onEndReachedThreshold={2}
removeClippedSubviews={true}
/>
@@ -100,10 +186,12 @@ export function WizardAddProfilesDialog({
}
function ListHeader({
+ type,
searchText,
setSearchText,
inputRef,
}: {
+ type: 'profiles' | 'feeds'
searchText: string
setSearchText: (text: string) => void
inputRef: React.Ref
@@ -145,7 +233,11 @@ function ListHeader({
a.leading_tight,
t.atoms.text_contrast_high,
]}>
- Select profiles to add
+ {type === 'profiles' ? (
+ Select profiles to add
+ ) : (
+ Select feeds to add
+ )}
diff --git a/src/components/StarterPack/Wizard/WizardFeedCard.tsx b/src/components/StarterPack/Wizard/WizardFeedCard.tsx
new file mode 100644
index 0000000000..bf01b3bd4c
--- /dev/null
+++ b/src/components/StarterPack/Wizard/WizardFeedCard.tsx
@@ -0,0 +1,69 @@
+import React from 'react'
+import {View} from 'react-native'
+import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {UserAvatar} from 'view/com/util/UserAvatar'
+import {WizardAction, WizardState} from '#/screens/StarterPack/Wizard/State'
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import {Text} from '#/components/Typography'
+
+export function WizardFeedCard({
+ generator,
+ state,
+ dispatch,
+}: {
+ generator: GeneratorView
+ state: WizardState
+ dispatch: (action: WizardAction) => void
+}) {
+ const {_} = useLingui()
+ const t = useTheme()
+
+ const includesFeed = state.feeds.some(f => f.uri === generator.uri)
+ const onAdd = () => {
+ if (includesFeed) {
+ dispatch({type: 'RemoveFeed', feedUri: generator.uri})
+ } else {
+ dispatch({type: 'AddFeed', feed: generator})
+ }
+ }
+
+ return (
+
+
+
+
+ {generator.displayName}
+
+
+ {_(msg`Feed by @${generator.creator.handle}`)}
+
+
+
+
+ )
+}
diff --git a/src/components/StarterPack/Wizard/WizardListEmpty.tsx b/src/components/StarterPack/Wizard/WizardListEmpty.tsx
new file mode 100644
index 0000000000..332b89e132
--- /dev/null
+++ b/src/components/StarterPack/Wizard/WizardListEmpty.tsx
@@ -0,0 +1,44 @@
+import React from 'react'
+import {View} from 'react-native'
+import {Trans} from '@lingui/macro'
+
+import {atoms as a, useTheme} from '#/alf'
+import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag'
+import {UserCircle_Stroke2_Corner0_Rounded as UserCircle} from '#/components/icons/UserCircle'
+import {Text} from '#/components/Typography'
+
+export function WizardListEmpty({type}: {type: 'profiles' | 'feeds'}) {
+ const t = useTheme()
+
+ return (
+
+ {type === 'profiles' ? (
+
+ ) : (
+
+ )}
+
+ {type === 'profiles' ? (
+ Recommend people to follow!
+ ) : (
+ Add some cool feeds!
+ )}
+
+
+ )
+}
diff --git a/src/screens/StarterPack/Wizard/StepProfiles/WizardProfileCard.tsx b/src/components/StarterPack/Wizard/WizardProfileCard.tsx
similarity index 100%
rename from src/screens/StarterPack/Wizard/StepProfiles/WizardProfileCard.tsx
rename to src/components/StarterPack/Wizard/WizardProfileCard.tsx
diff --git a/src/screens/StarterPack/Wizard/State.tsx b/src/screens/StarterPack/Wizard/State.tsx
index d82254e43f..5468c07ead 100644
--- a/src/screens/StarterPack/Wizard/State.tsx
+++ b/src/screens/StarterPack/Wizard/State.tsx
@@ -1,5 +1,6 @@
import React from 'react'
import {AppBskyActorDefs} from '@atproto/api'
+import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
const steps = ['Landing', 'Details', 'Profiles', 'Feeds'] as const
type Step = (typeof steps)[number]
@@ -12,8 +13,8 @@ type Action =
| {type: 'SetDescription'; description: string}
| {type: 'AddProfile'; profile: AppBskyActorDefs.ProfileViewBasic}
| {type: 'RemoveProfile'; profileDid: string}
- | {type: 'AddFeed'; uri: string}
- | {type: 'RemoveFeed'; uri: string}
+ | {type: 'AddFeed'; feed: GeneratorView}
+ | {type: 'RemoveFeed'; feedUri: string}
| {type: 'SetProcessing'; processing: boolean}
interface State {
@@ -23,7 +24,7 @@ interface State {
description?: string
avatar?: string
profiles: AppBskyActorDefs.ProfileViewBasic[]
- feedUris: string[]
+ feeds: GeneratorView[]
processing: boolean
}
@@ -66,12 +67,12 @@ function reducer(state: State, action: Action): State {
}
break
case 'AddFeed':
- updatedState = {...state, feedUris: [...state.feedUris, action.uri]}
+ updatedState = {...state, feeds: [...state.feeds, action.feed]}
break
case 'RemoveFeed':
updatedState = {
...state,
- feedUris: state.feedUris.filter(uri => uri !== action.uri),
+ feeds: state.feeds.filter(f => f.uri !== action.feedUri),
}
break
case 'SetProcessing':
@@ -118,7 +119,7 @@ export function Provider({
canNext: true,
currentStep: initialStep,
profiles: [],
- feedUris: [],
+ feeds: [],
processing: false,
},
)
diff --git a/src/screens/StarterPack/Wizard/StepFeeds.tsx b/src/screens/StarterPack/Wizard/StepFeeds.tsx
index 2b889ae767..8acc8ca262 100644
--- a/src/screens/StarterPack/Wizard/StepFeeds.tsx
+++ b/src/screens/StarterPack/Wizard/StepFeeds.tsx
@@ -1,88 +1,33 @@
import React from 'react'
-import {ListRenderItemInfo, View} from 'react-native'
+import {ListRenderItemInfo} from 'react-native'
import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {useProfileFeedgensQuery} from 'state/queries/profile-feedgens'
-import {useSession} from 'state/session'
import {List} from 'view/com/util/List'
-import {UserAvatar} from 'view/com/util/UserAvatar'
import {useWizardState} from '#/screens/StarterPack/Wizard/State'
-import {atoms as a, useTheme} from '#/alf'
-import {Button, ButtonText} from '#/components/Button'
-import {Text} from '#/components/Typography'
-
-function renderItem({item}: ListRenderItemInfo) {
- return
-}
+import {atoms as a} from '#/alf'
+import {WizardFeedCard} from '#/components/StarterPack/Wizard/WizardFeedCard'
+import {WizardListEmpty} from '#/components/StarterPack/Wizard/WizardListEmpty'
function keyExtractor(item: GeneratorView) {
return item.uri
}
export function StepFeeds() {
- const {currentAccount} = useSession()
- const {data} = useProfileFeedgensQuery(currentAccount!.did)
- const feeds = data?.pages.flatMap(page => page.feeds) || []
-
- return (
-
- )
-}
-
-function FeedCard({generator}: {generator: GeneratorView}) {
- const {_} = useLingui()
- const t = useTheme()
const [state, dispatch] = useWizardState()
- const includesFeed = state.feedUris.includes(generator.uri)
- const onAdd = () => {
- if (includesFeed) {
- dispatch({type: 'RemoveFeed', uri: generator.uri})
- } else {
- dispatch({type: 'AddFeed', uri: generator.uri})
- }
+ console.log(state)
+
+ const renderItem = ({item}: ListRenderItemInfo) => {
+ return
}
return (
-
-
-
-
- {generator.displayName}
-
-
- {_(msg`Feed by @${generator.creator.handle}`)}
-
-
-
-
+
}
+ />
)
}
diff --git a/src/screens/StarterPack/Wizard/StepProfiles/index.tsx b/src/screens/StarterPack/Wizard/StepProfiles.tsx
similarity index 51%
rename from src/screens/StarterPack/Wizard/StepProfiles/index.tsx
rename to src/screens/StarterPack/Wizard/StepProfiles.tsx
index 84a138ee89..43211339cc 100644
--- a/src/screens/StarterPack/Wizard/StepProfiles/index.tsx
+++ b/src/screens/StarterPack/Wizard/StepProfiles.tsx
@@ -1,13 +1,12 @@
import React from 'react'
import {ListRenderItemInfo, View} from 'react-native'
import {AppBskyActorDefs} from '@atproto/api'
-import {Trans} from '@lingui/macro'
import {List} from 'view/com/util/List'
import {useWizardState} from '#/screens/StarterPack/Wizard/State'
-import {WizardProfileCard} from '#/screens/StarterPack/Wizard/StepProfiles/WizardProfileCard'
import {atoms as a} from '#/alf'
-import {Text} from '#/components/Typography'
+import {WizardListEmpty} from '#/components/StarterPack/Wizard/WizardListEmpty'
+import {WizardProfileCard} from '#/components/StarterPack/Wizard/WizardProfileCard'
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
return item.did
@@ -27,26 +26,13 @@ export function StepProfiles() {
return (
<>
- {state.profiles.length > 0 ? (
-
- ) : (
-
- )}
+
}
+ />
>
)
}
-
-function ListEmpty() {
- return (
-
-
- Add the people you recommend to your starter pack!
-
-
- )
-}
diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx
index 080a0cefd7..757277fcd9 100644
--- a/src/screens/StarterPack/Wizard/index.tsx
+++ b/src/screens/StarterPack/Wizard/index.tsx
@@ -20,11 +20,11 @@ import {StepDetails} from '#/screens/StarterPack/Wizard/StepDetails'
import {StepFeeds} from '#/screens/StarterPack/Wizard/StepFeeds'
import {StepLanding} from '#/screens/StarterPack/Wizard/StepLanding'
import {StepProfiles} from '#/screens/StarterPack/Wizard/StepProfiles'
-import {WizardAddProfilesDialog} from '#/screens/StarterPack/Wizard/StepProfiles/WizardAddProfilesDialog'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {Loader} from '#/components/Loader'
+import {WizardAddDialog} from '#/components/StarterPack/Wizard/WizardAddDialog'
import {Provider} from './State'
export function Wizard({
@@ -83,7 +83,7 @@ function WizardInner() {
staleTime: 0,
})
const bottomBarOffset = useBottomBarOffset()
- const addProfilesControl = useDialogControl()
+ const searchDialogControl = useDialogControl()
React.useEffect(() => {
navigation.setOptions({
@@ -95,15 +95,15 @@ function WizardInner() {
{
Landing: {
header: _(msg`Create a starter pack`),
- button: _(msg`Create`),
+ button: _(msg`Get started`),
},
Details: {
header: _(msg`Details`),
- button: _(msg`Add profiles`),
+ button: _(msg`Continue`),
},
Profiles: {
header: _(msg`Add profiles`),
- button: _(msg`Add feeds`),
+ button: _(msg`Continue`),
},
Feeds: {
header: _(msg`Add feeds`),
@@ -150,14 +150,14 @@ function WizardInner() {
showBorder={true}
showOnDesktop={true}
renderButton={
- state.currentStep === 'Profiles'
+ state.currentStep === 'Profiles' || state.currentStep === 'Feeds'
? () => (