* refactor name * refactor to support feeds search * more refactor * cleanup * allow searching for feeds * better empty states * move some stuff around * one more move * rename buttons * another move * clarify variable names
This commit is contained in:
+113
-21
@@ -2,45 +2,131 @@ import React, {useLayoutEffect, useRef, useState} from 'react'
|
|||||||
import type {ListRenderItemInfo, TextInput as RNTextInput} from 'react-native'
|
import type {ListRenderItemInfo, TextInput as RNTextInput} from 'react-native'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
|
import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
|
||||||
import {BottomSheetFlatListMethods} from '@discord/bottom-sheet'
|
import {BottomSheetFlatListMethods} from '@discord/bottom-sheet'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
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 {useActorAutocompleteQuery} from 'state/queries/actor-autocomplete'
|
||||||
|
import {
|
||||||
|
useGetPopularFeedsQuery,
|
||||||
|
useSearchPopularFeedsMutation,
|
||||||
|
} from 'state/queries/feed'
|
||||||
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 {WizardAction, WizardState} from '#/screens/StarterPack/Wizard/State'
|
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 {atoms as a, native, useTheme, web} from '#/alf'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {TextInput} from '#/components/dms/dialogs/TextInput'
|
import {TextInput} from '#/components/dms/dialogs/TextInput'
|
||||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||||
import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2'
|
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'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
|
interface Props {
|
||||||
return item.did
|
|
||||||
}
|
|
||||||
|
|
||||||
export function WizardAddProfilesDialog({
|
|
||||||
control,
|
|
||||||
state,
|
|
||||||
dispatch,
|
|
||||||
}: {
|
|
||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
|
type: 'profiles' | 'feeds'
|
||||||
state: WizardState
|
state: WizardState
|
||||||
dispatch: (action: WizardAction) => void
|
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 <AddProfiles {...props} />
|
||||||
|
}
|
||||||
|
return <AddFeeds {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddProfiles(props: Props) {
|
||||||
const [searchText, setSearchText] = useState('')
|
const [searchText, setSearchText] = useState('')
|
||||||
|
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {data: results} = useActorAutocompleteQuery(searchText, true, 12)
|
|
||||||
const {data: followsPages, fetchNextPage} = useProfileFollowsQuery(
|
const {data: followsPages, fetchNextPage} = useProfileFollowsQuery(
|
||||||
currentAccount?.did,
|
currentAccount?.did,
|
||||||
)
|
)
|
||||||
const follows = followsPages?.pages.flatMap(page => page.follows) || []
|
const follows = followsPages?.pages.flatMap(page => page.follows) || []
|
||||||
|
|
||||||
|
const {data: searchedProfiles} = useActorAutocompleteQuery(
|
||||||
|
searchText,
|
||||||
|
true,
|
||||||
|
12,
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AddDialog
|
||||||
|
{...props}
|
||||||
|
data={searchText ? searchedProfiles : follows}
|
||||||
|
onEndReached={searchText ? undefined : () => 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 (
|
||||||
|
<AddDialog
|
||||||
|
{...props}
|
||||||
|
data={searchText ? searchedFeeds : popularFeeds}
|
||||||
|
onEndReached={() => 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<BottomSheetFlatListMethods>(null)
|
const listRef = useRef<BottomSheetFlatListMethods>(null)
|
||||||
const inputRef = useRef<RNTextInput>(null)
|
const inputRef = useRef<RNTextInput>(null)
|
||||||
|
|
||||||
@@ -52,13 +138,12 @@ export function WizardAddProfilesDialog({
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const renderItem = ({
|
const renderItem = ({item}: ListRenderItemInfo<any>) =>
|
||||||
item,
|
type === 'profiles' ? (
|
||||||
}: ListRenderItemInfo<AppBskyActorDefs.ProfileViewBasic>) => {
|
|
||||||
return (
|
|
||||||
<WizardProfileCard profile={item} state={state} dispatch={dispatch} />
|
<WizardProfileCard profile={item} state={state} dispatch={dispatch} />
|
||||||
|
) : (
|
||||||
|
<WizardFeedCard generator={item} state={state} dispatch={dispatch} />
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer
|
<Dialog.Outer
|
||||||
@@ -67,7 +152,7 @@ export function WizardAddProfilesDialog({
|
|||||||
nativeOptions={{sheet: {snapPoints: ['100%']}}}>
|
nativeOptions={{sheet: {snapPoints: ['100%']}}}>
|
||||||
<Dialog.InnerFlatList
|
<Dialog.InnerFlatList
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
data={searchText.length > 0 ? results : follows}
|
data={data}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={keyExtractor}
|
keyExtractor={keyExtractor}
|
||||||
ListHeaderComponent={
|
ListHeaderComponent={
|
||||||
@@ -75,6 +160,7 @@ export function WizardAddProfilesDialog({
|
|||||||
searchText={searchText}
|
searchText={searchText}
|
||||||
setSearchText={setSearchText}
|
setSearchText={setSearchText}
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
|
type={type}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
stickyHeaderIndices={[0]}
|
stickyHeaderIndices={[0]}
|
||||||
@@ -91,7 +177,7 @@ export function WizardAddProfilesDialog({
|
|||||||
]}
|
]}
|
||||||
webInnerStyle={[a.py_0, {maxWidth: 500, minWidth: 200}]}
|
webInnerStyle={[a.py_0, {maxWidth: 500, minWidth: 200}]}
|
||||||
keyboardDismissMode="on-drag"
|
keyboardDismissMode="on-drag"
|
||||||
onEndReached={() => fetchNextPage()}
|
onEndReached={onEndReached}
|
||||||
onEndReachedThreshold={2}
|
onEndReachedThreshold={2}
|
||||||
removeClippedSubviews={true}
|
removeClippedSubviews={true}
|
||||||
/>
|
/>
|
||||||
@@ -100,10 +186,12 @@ export function WizardAddProfilesDialog({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListHeader({
|
function ListHeader({
|
||||||
|
type,
|
||||||
searchText,
|
searchText,
|
||||||
setSearchText,
|
setSearchText,
|
||||||
inputRef,
|
inputRef,
|
||||||
}: {
|
}: {
|
||||||
|
type: 'profiles' | 'feeds'
|
||||||
searchText: string
|
searchText: string
|
||||||
setSearchText: (text: string) => void
|
setSearchText: (text: string) => void
|
||||||
inputRef: React.Ref<RNTextInput>
|
inputRef: React.Ref<RNTextInput>
|
||||||
@@ -145,7 +233,11 @@ function ListHeader({
|
|||||||
a.leading_tight,
|
a.leading_tight,
|
||||||
t.atoms.text_contrast_high,
|
t.atoms.text_contrast_high,
|
||||||
]}>
|
]}>
|
||||||
<Trans>Select profiles to add</Trans>
|
{type === 'profiles' ? (
|
||||||
|
<Trans>Select profiles to add</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>Select feeds to add</Trans>
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -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 (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.px_md,
|
||||||
|
a.py_sm,
|
||||||
|
a.border_b,
|
||||||
|
a.gap_md,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
]}>
|
||||||
|
<UserAvatar type="algo" size={45} avatar={generator.avatar} />
|
||||||
|
<View style={[a.flex_1]}>
|
||||||
|
<Text style={[a.flex_1, a.font_bold, a.text_md]} numberOfLines={1}>
|
||||||
|
{generator.displayName}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[a.flex_1, t.atoms.text_contrast_medium]}
|
||||||
|
numberOfLines={1}>
|
||||||
|
{_(msg`Feed by @${generator.creator.handle}`)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Button
|
||||||
|
label={includesFeed ? _(msg`Remove`) : _(msg`Add`)}
|
||||||
|
variant="solid"
|
||||||
|
color={includesFeed ? 'secondary' : 'primary'}
|
||||||
|
size="small"
|
||||||
|
style={{paddingVertical: 6}}
|
||||||
|
onPress={onAdd}>
|
||||||
|
<ButtonText>
|
||||||
|
{includesFeed ? <Trans>Remove</Trans> : <Trans>Add</Trans>}
|
||||||
|
</ButtonText>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
|
<View
|
||||||
|
style={[a.flex_1, a.px_md, a.align_center, a.gap_md, {marginTop: 80}]}>
|
||||||
|
{type === 'profiles' ? (
|
||||||
|
<UserCircle
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
style={t.atoms.text_contrast_medium}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Hashtag
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
style={t.atoms.text_contrast_medium}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.font_bold,
|
||||||
|
a.text_xl,
|
||||||
|
a.text_center,
|
||||||
|
t.atoms.text_contrast_medium,
|
||||||
|
]}>
|
||||||
|
{type === 'profiles' ? (
|
||||||
|
<Trans>Recommend people to follow!</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>Add some cool feeds!</Trans>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
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
|
const steps = ['Landing', 'Details', 'Profiles', 'Feeds'] as const
|
||||||
type Step = (typeof steps)[number]
|
type Step = (typeof steps)[number]
|
||||||
@@ -12,8 +13,8 @@ type Action =
|
|||||||
| {type: 'SetDescription'; description: string}
|
| {type: 'SetDescription'; description: string}
|
||||||
| {type: 'AddProfile'; profile: AppBskyActorDefs.ProfileViewBasic}
|
| {type: 'AddProfile'; profile: AppBskyActorDefs.ProfileViewBasic}
|
||||||
| {type: 'RemoveProfile'; profileDid: string}
|
| {type: 'RemoveProfile'; profileDid: string}
|
||||||
| {type: 'AddFeed'; uri: string}
|
| {type: 'AddFeed'; feed: GeneratorView}
|
||||||
| {type: 'RemoveFeed'; uri: string}
|
| {type: 'RemoveFeed'; feedUri: string}
|
||||||
| {type: 'SetProcessing'; processing: boolean}
|
| {type: 'SetProcessing'; processing: boolean}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@@ -23,7 +24,7 @@ interface State {
|
|||||||
description?: string
|
description?: string
|
||||||
avatar?: string
|
avatar?: string
|
||||||
profiles: AppBskyActorDefs.ProfileViewBasic[]
|
profiles: AppBskyActorDefs.ProfileViewBasic[]
|
||||||
feedUris: string[]
|
feeds: GeneratorView[]
|
||||||
processing: boolean
|
processing: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,12 +67,12 @@ function reducer(state: State, action: Action): State {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'AddFeed':
|
case 'AddFeed':
|
||||||
updatedState = {...state, feedUris: [...state.feedUris, action.uri]}
|
updatedState = {...state, feeds: [...state.feeds, action.feed]}
|
||||||
break
|
break
|
||||||
case 'RemoveFeed':
|
case 'RemoveFeed':
|
||||||
updatedState = {
|
updatedState = {
|
||||||
...state,
|
...state,
|
||||||
feedUris: state.feedUris.filter(uri => uri !== action.uri),
|
feeds: state.feeds.filter(f => f.uri !== action.feedUri),
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'SetProcessing':
|
case 'SetProcessing':
|
||||||
@@ -118,7 +119,7 @@ export function Provider({
|
|||||||
canNext: true,
|
canNext: true,
|
||||||
currentStep: initialStep,
|
currentStep: initialStep,
|
||||||
profiles: [],
|
profiles: [],
|
||||||
feedUris: [],
|
feeds: [],
|
||||||
processing: false,
|
processing: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,88 +1,33 @@
|
|||||||
import React from 'react'
|
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 {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 {List} from 'view/com/util/List'
|
||||||
import {UserAvatar} from 'view/com/util/UserAvatar'
|
|
||||||
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} from '#/alf'
|
||||||
import {Button, ButtonText} from '#/components/Button'
|
import {WizardFeedCard} from '#/components/StarterPack/Wizard/WizardFeedCard'
|
||||||
import {Text} from '#/components/Typography'
|
import {WizardListEmpty} from '#/components/StarterPack/Wizard/WizardListEmpty'
|
||||||
|
|
||||||
function renderItem({item}: ListRenderItemInfo<GeneratorView>) {
|
|
||||||
return <FeedCard generator={item} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyExtractor(item: GeneratorView) {
|
function keyExtractor(item: GeneratorView) {
|
||||||
return item.uri
|
return item.uri
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StepFeeds() {
|
export function StepFeeds() {
|
||||||
const {currentAccount} = useSession()
|
|
||||||
const {data} = useProfileFeedgensQuery(currentAccount!.did)
|
|
||||||
const feeds = data?.pages.flatMap(page => page.feeds) || []
|
|
||||||
|
|
||||||
return (
|
|
||||||
<List
|
|
||||||
data={feeds}
|
|
||||||
renderItem={renderItem}
|
|
||||||
keyExtractor={keyExtractor}
|
|
||||||
style={[a.flex_1]}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FeedCard({generator}: {generator: GeneratorView}) {
|
|
||||||
const {_} = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
const [state, dispatch] = useWizardState()
|
const [state, dispatch] = useWizardState()
|
||||||
|
|
||||||
const includesFeed = state.feedUris.includes(generator.uri)
|
console.log(state)
|
||||||
const onAdd = () => {
|
|
||||||
if (includesFeed) {
|
const renderItem = ({item}: ListRenderItemInfo<GeneratorView>) => {
|
||||||
dispatch({type: 'RemoveFeed', uri: generator.uri})
|
return <WizardFeedCard generator={item} state={state} dispatch={dispatch} />
|
||||||
} else {
|
|
||||||
dispatch({type: 'AddFeed', uri: generator.uri})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<List
|
||||||
style={[
|
data={state.feeds}
|
||||||
a.flex_row,
|
renderItem={renderItem}
|
||||||
a.align_center,
|
keyExtractor={keyExtractor}
|
||||||
a.px_md,
|
style={[a.flex_1]}
|
||||||
a.py_sm,
|
ListEmptyComponent={<WizardListEmpty type="feeds" />}
|
||||||
a.border_b,
|
/>
|
||||||
a.gap_md,
|
|
||||||
t.atoms.border_contrast_low,
|
|
||||||
]}>
|
|
||||||
<UserAvatar type="algo" size={45} avatar={generator.avatar} />
|
|
||||||
<View style={[a.flex_1]}>
|
|
||||||
<Text style={[a.flex_1, a.font_bold, a.text_md]} numberOfLines={1}>
|
|
||||||
{generator.displayName}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
style={[a.flex_1, t.atoms.text_contrast_medium]}
|
|
||||||
numberOfLines={1}>
|
|
||||||
{_(msg`Feed by @${generator.creator.handle}`)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Button
|
|
||||||
label={includesFeed ? _(msg`Remove`) : _(msg`Add`)}
|
|
||||||
variant="solid"
|
|
||||||
color={includesFeed ? 'secondary' : 'primary'}
|
|
||||||
size="small"
|
|
||||||
style={{paddingVertical: 6}}
|
|
||||||
onPress={onAdd}>
|
|
||||||
<ButtonText>
|
|
||||||
{includesFeed ? <Trans>Remove</Trans> : <Trans>Add</Trans>}
|
|
||||||
</ButtonText>
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-22
@@ -1,13 +1,12 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {ListRenderItemInfo, View} from 'react-native'
|
import {ListRenderItemInfo, View} from 'react-native'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
import {Trans} from '@lingui/macro'
|
|
||||||
|
|
||||||
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 {WizardProfileCard} from '#/screens/StarterPack/Wizard/StepProfiles/WizardProfileCard'
|
|
||||||
import {atoms as a} from '#/alf'
|
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) {
|
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
|
||||||
return item.did
|
return item.did
|
||||||
@@ -27,26 +26,13 @@ export function StepProfiles() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
{state.profiles.length > 0 ? (
|
<List
|
||||||
<List
|
data={state.profiles}
|
||||||
data={state.profiles}
|
renderItem={renderItem}
|
||||||
renderItem={renderItem}
|
keyExtractor={keyExtractor}
|
||||||
keyExtractor={keyExtractor}
|
ListEmptyComponent={<WizardListEmpty type="profiles" />}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<ListEmpty />
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ListEmpty() {
|
|
||||||
return (
|
|
||||||
<View style={[a.flex_1, a.px_md, a.align_center]}>
|
|
||||||
<Text style={[a.font_bold, a.text_xl, a.text_center, {marginTop: 100}]}>
|
|
||||||
<Trans>Add the people you recommend to your starter pack!</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -20,11 +20,11 @@ import {StepDetails} from '#/screens/StarterPack/Wizard/StepDetails'
|
|||||||
import {StepFeeds} from '#/screens/StarterPack/Wizard/StepFeeds'
|
import {StepFeeds} from '#/screens/StarterPack/Wizard/StepFeeds'
|
||||||
import {StepLanding} from '#/screens/StarterPack/Wizard/StepLanding'
|
import {StepLanding} from '#/screens/StarterPack/Wizard/StepLanding'
|
||||||
import {StepProfiles} from '#/screens/StarterPack/Wizard/StepProfiles'
|
import {StepProfiles} from '#/screens/StarterPack/Wizard/StepProfiles'
|
||||||
import {WizardAddProfilesDialog} from '#/screens/StarterPack/Wizard/StepProfiles/WizardAddProfilesDialog'
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Button, ButtonText} from '#/components/Button'
|
import {Button, ButtonText} from '#/components/Button'
|
||||||
import {useDialogControl} from '#/components/Dialog'
|
import {useDialogControl} from '#/components/Dialog'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
|
import {WizardAddDialog} from '#/components/StarterPack/Wizard/WizardAddDialog'
|
||||||
import {Provider} from './State'
|
import {Provider} from './State'
|
||||||
|
|
||||||
export function Wizard({
|
export function Wizard({
|
||||||
@@ -83,7 +83,7 @@ function WizardInner() {
|
|||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
})
|
})
|
||||||
const bottomBarOffset = useBottomBarOffset()
|
const bottomBarOffset = useBottomBarOffset()
|
||||||
const addProfilesControl = useDialogControl()
|
const searchDialogControl = useDialogControl()
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
@@ -95,15 +95,15 @@ function WizardInner() {
|
|||||||
{
|
{
|
||||||
Landing: {
|
Landing: {
|
||||||
header: _(msg`Create a starter pack`),
|
header: _(msg`Create a starter pack`),
|
||||||
button: _(msg`Create`),
|
button: _(msg`Get started`),
|
||||||
},
|
},
|
||||||
Details: {
|
Details: {
|
||||||
header: _(msg`Details`),
|
header: _(msg`Details`),
|
||||||
button: _(msg`Add profiles`),
|
button: _(msg`Continue`),
|
||||||
},
|
},
|
||||||
Profiles: {
|
Profiles: {
|
||||||
header: _(msg`Add profiles`),
|
header: _(msg`Add profiles`),
|
||||||
button: _(msg`Add feeds`),
|
button: _(msg`Continue`),
|
||||||
},
|
},
|
||||||
Feeds: {
|
Feeds: {
|
||||||
header: _(msg`Add feeds`),
|
header: _(msg`Add feeds`),
|
||||||
@@ -150,14 +150,14 @@ function WizardInner() {
|
|||||||
showBorder={true}
|
showBorder={true}
|
||||||
showOnDesktop={true}
|
showOnDesktop={true}
|
||||||
renderButton={
|
renderButton={
|
||||||
state.currentStep === 'Profiles'
|
state.currentStep === 'Profiles' || state.currentStep === 'Feeds'
|
||||||
? () => (
|
? () => (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel`)}
|
label={_(msg`Cancel`)}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="xsmall"
|
size="xsmall"
|
||||||
onPress={addProfilesControl.open}
|
onPress={searchDialogControl.open}
|
||||||
style={{marginLeft: -15}}>
|
style={{marginLeft: -15}}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Add</Trans>
|
<Trans>Add</Trans>
|
||||||
@@ -187,11 +187,14 @@ function WizardInner() {
|
|||||||
</View>
|
</View>
|
||||||
</KeyboardStickyView>
|
</KeyboardStickyView>
|
||||||
|
|
||||||
<WizardAddProfilesDialog
|
{(state.currentStep === 'Profiles' || state.currentStep === 'Feeds') && (
|
||||||
control={addProfilesControl}
|
<WizardAddDialog
|
||||||
state={state}
|
control={searchDialogControl}
|
||||||
dispatch={dispatch}
|
state={state}
|
||||||
/>
|
dispatch={dispatch}
|
||||||
|
type={state.currentStep === 'Profiles' ? 'profiles' : 'feeds'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user