start reworking ui
This commit is contained in:
@@ -29,8 +29,7 @@ export function WizardListEmpty({type}: {type: 'profiles' | 'feeds'}) {
|
|||||||
<Text style={[a.text_md, a.text_center, t.atoms.text_contrast_medium]}>
|
<Text style={[a.text_md, a.text_center, t.atoms.text_contrast_medium]}>
|
||||||
{type === 'profiles' ? (
|
{type === 'profiles' ? (
|
||||||
<Trans>
|
<Trans>
|
||||||
Add people to your starter pack that you think others will enjoy
|
Search for people that you want to suggest others to follow
|
||||||
following
|
|
||||||
</Trans>
|
</Trans>
|
||||||
) : (
|
) : (
|
||||||
<Trans>Add some cool feeds!</Trans>
|
<Trans>Add some cool feeds!</Trans>
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import React from 'react'
|
import React, {useState} from 'react'
|
||||||
import {ListRenderItemInfo} from 'react-native'
|
import {ListRenderItemInfo, View} from 'react-native'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
|
|
||||||
|
import {useActorAutocompleteQuery} from 'state/queries/actor-autocomplete'
|
||||||
|
import {useProfileFollowsQuery} from 'state/queries/profile-follows'
|
||||||
|
import {useSession} from 'state/session'
|
||||||
|
import {SearchInput} from 'view/com/util/forms/SearchInput'
|
||||||
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 {WizardListEmpty} from '#/components/StarterPack/Wizard/WizardListEmpty'
|
import {atoms as a} from '#/alf'
|
||||||
import {WizardProfileCard} from '#/components/StarterPack/Wizard/WizardProfileCard'
|
import {WizardProfileCard} from '#/components/StarterPack/Wizard/WizardProfileCard'
|
||||||
|
|
||||||
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
|
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
|
||||||
@@ -13,6 +17,19 @@ function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
|
|||||||
|
|
||||||
export function StepProfiles() {
|
export function StepProfiles() {
|
||||||
const [state, dispatch] = useWizardState()
|
const [state, dispatch] = useWizardState()
|
||||||
|
const [query, setQuery] = useState('')
|
||||||
|
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
const {data: followsPages, fetchNextPage} = useProfileFollowsQuery(
|
||||||
|
currentAccount?.did,
|
||||||
|
)
|
||||||
|
const follows = followsPages?.pages.flatMap(page => page.follows) || []
|
||||||
|
|
||||||
|
const {data: results} = useActorAutocompleteQuery(
|
||||||
|
query || encodeURIComponent('*'),
|
||||||
|
true,
|
||||||
|
12,
|
||||||
|
)
|
||||||
|
|
||||||
const renderItem = ({
|
const renderItem = ({
|
||||||
item,
|
item,
|
||||||
@@ -23,11 +40,21 @@ export function StepProfiles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List
|
<>
|
||||||
data={state.profiles}
|
<View style={[a.my_sm, a.px_md, {height: 40}]}>
|
||||||
renderItem={renderItem}
|
<SearchInput
|
||||||
keyExtractor={keyExtractor}
|
query={query}
|
||||||
ListEmptyComponent={<WizardListEmpty type="profiles" />}
|
onChangeQuery={setQuery}
|
||||||
/>
|
onPressCancelSearch={() => setQuery('')}
|
||||||
|
onSubmitQuery={() => {}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<List
|
||||||
|
data={query ? results : follows}
|
||||||
|
renderItem={renderItem}
|
||||||
|
keyExtractor={keyExtractor}
|
||||||
|
onEndReached={!query ? () => fetchNextPage() : undefined}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Keyboard, View} from 'react-native'
|
import {Keyboard, View} from 'react-native'
|
||||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
|
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api'
|
||||||
|
import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
|
||||||
|
import {msg, Plural, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||||
|
|
||||||
import {useBottomBarOffset} from 'lib/hooks/useBottomBarOffset'
|
|
||||||
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
|
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
|
||||||
|
import {enforceLen} from 'lib/strings/helpers'
|
||||||
import {isAndroid, isWeb} from 'platform/detection'
|
import {isAndroid, isWeb} from 'platform/detection'
|
||||||
import {useProfileQuery} from 'state/queries/profile'
|
import {useProfileQuery} from 'state/queries/profile'
|
||||||
import {useSession} from 'state/session'
|
import {useSession} from 'state/session'
|
||||||
|
import {useSetMinimalShellMode} from 'state/shell'
|
||||||
|
import {UserAvatar} from 'view/com/util/UserAvatar'
|
||||||
import {CenteredView} from 'view/com/util/Views'
|
import {CenteredView} from 'view/com/util/Views'
|
||||||
import {useWizardState, WizardStep} from '#/screens/StarterPack/Wizard/State'
|
import {useWizardState, WizardStep} from '#/screens/StarterPack/Wizard/State'
|
||||||
import {StepDetails} from '#/screens/StarterPack/Wizard/StepDetails'
|
import {StepDetails} from '#/screens/StarterPack/Wizard/StepDetails'
|
||||||
@@ -71,7 +75,6 @@ function WizardInner() {
|
|||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const bottomOffset = useBottomBarOffset()
|
|
||||||
const [state, dispatch] = useWizardState()
|
const [state, dispatch] = useWizardState()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {data: currentProfile} = useProfileQuery({
|
const {data: currentProfile} = useProfileQuery({
|
||||||
@@ -80,22 +83,20 @@ function WizardInner() {
|
|||||||
})
|
})
|
||||||
const addDialogControl = useDialogControl()
|
const addDialogControl = useDialogControl()
|
||||||
|
|
||||||
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
gestureEnabled: false,
|
gestureEnabled: false,
|
||||||
})
|
})
|
||||||
}, [navigation])
|
}, [navigation])
|
||||||
|
|
||||||
// TODO move to state if we keep this
|
useFocusEffect(
|
||||||
const continueIsCta = () => {
|
React.useCallback(() => {
|
||||||
if (state.currentStep === 'Details') {
|
setMinimalShellMode(true)
|
||||||
return true
|
return () => setMinimalShellMode(false)
|
||||||
} else if (state.currentStep === 'Profiles') {
|
}, [setMinimalShellMode]),
|
||||||
return state.profiles.length > 0
|
)
|
||||||
} else if (state.currentStep === 'Feeds') {
|
|
||||||
return state.feeds.length > 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const wizardUiStrings: Record<
|
const wizardUiStrings: Record<
|
||||||
WizardStep,
|
WizardStep,
|
||||||
@@ -145,9 +146,7 @@ function WizardInner() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CenteredView
|
<CenteredView style={[a.flex_1]} sideBorders>
|
||||||
style={[a.flex_1, {marginBottom: bottomOffset + 20}]}
|
|
||||||
sideBorders>
|
|
||||||
<View style={[]}>
|
<View style={[]}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -187,35 +186,19 @@ function WizardInner() {
|
|||||||
<StepView />
|
<StepView />
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
<View style={[a.gap_sm]}>
|
{state.currentStep === 'Details' && (
|
||||||
{state.currentStep !== 'Details' && (
|
|
||||||
<Button
|
|
||||||
label={_(msg`Cancel`)}
|
|
||||||
onPress={addDialogControl.open}
|
|
||||||
variant="ghost"
|
|
||||||
color="primary"
|
|
||||||
size="medium"
|
|
||||||
style={[a.mx_2xl]}>
|
|
||||||
<ButtonText>
|
|
||||||
{state.currentStep === 'Profiles' ? (
|
|
||||||
<Trans>Find People</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>Find Feeds</Trans>
|
|
||||||
)}
|
|
||||||
</ButtonText>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Cancel`)}
|
label={uiStrings.nextBtn}
|
||||||
onPress={onNext}
|
variant="solid"
|
||||||
variant={continueIsCta() ? 'solid' : 'ghost'}
|
color="primary"
|
||||||
color={continueIsCta() ? 'primary' : 'secondary'}
|
|
||||||
size="medium"
|
size="medium"
|
||||||
style={[a.mx_2xl]}>
|
style={[a.mx_xl, a.my_5xl]}
|
||||||
|
onPress={onNext}>
|
||||||
<ButtonText>{uiStrings.nextBtn}</ButtonText>
|
<ButtonText>{uiStrings.nextBtn}</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
)}
|
||||||
|
|
||||||
|
<Footer onNext={onNext} nextBtnText={uiStrings.nextBtn} />
|
||||||
|
|
||||||
{(state.currentStep === 'Profiles' || state.currentStep === 'Feeds') && (
|
{(state.currentStep === 'Profiles' || state.currentStep === 'Feeds') && (
|
||||||
<WizardAddDialog
|
<WizardAddDialog
|
||||||
@@ -256,3 +239,135 @@ function StepView() {
|
|||||||
return <StepFeeds />
|
return <StepFeeds />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Footer({
|
||||||
|
onNext,
|
||||||
|
nextBtnText,
|
||||||
|
}: {
|
||||||
|
onNext: () => void
|
||||||
|
nextBtnText: string
|
||||||
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const t = useTheme()
|
||||||
|
const [state] = useWizardState()
|
||||||
|
|
||||||
|
const items = state.currentStep === 'Profiles' ? state.profiles : state.feeds
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.border_t,
|
||||||
|
a.border_l,
|
||||||
|
a.border_r,
|
||||||
|
a.align_center,
|
||||||
|
a.px_md,
|
||||||
|
a.pt_lg,
|
||||||
|
a.pb_5xl,
|
||||||
|
a.gap_sm,
|
||||||
|
t.atoms.bg,
|
||||||
|
t.atoms.shadow_md,
|
||||||
|
t.atoms.border_contrast_medium,
|
||||||
|
{
|
||||||
|
height: 190,
|
||||||
|
borderTopLeftRadius: 14,
|
||||||
|
borderTopRightRadius: 14,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<View style={[a.flex_row, a.gap_xs]}>
|
||||||
|
{items.slice(0, 5).map(p => (
|
||||||
|
<UserAvatar
|
||||||
|
key={p.did}
|
||||||
|
avatar={p.avatar}
|
||||||
|
size={28}
|
||||||
|
type={state.currentStep === 'Profiles' ? 'user' : 'algo'}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{items.length === 0 ? (
|
||||||
|
<View style={[a.gap_sm]}>
|
||||||
|
<Text style={[a.font_bold, a.text_center]}>
|
||||||
|
{state.currentStep === 'Profiles' ? (
|
||||||
|
<Trans>You haven't added anyone to your starter pack yet!</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>You haven't added any suggested feeds yet!</Trans>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
<Text style={[a.text_center]}>
|
||||||
|
{state.currentStep === 'Profiles' ? (
|
||||||
|
<Trans>
|
||||||
|
Search for people that you want to suggest to others, or skip
|
||||||
|
for now and add some later.
|
||||||
|
</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>
|
||||||
|
Search for feeds that you want to suggest to others, or skip for
|
||||||
|
now and add some later.
|
||||||
|
</Trans>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<Text style={[a.text_center]}>
|
||||||
|
{items.length === 1 ? (
|
||||||
|
<Trans>
|
||||||
|
<Text style={[a.font_bold]}>{getName(items[0])}</Text> is included
|
||||||
|
in your starter pack
|
||||||
|
</Trans>
|
||||||
|
) : items.length === 2 ? (
|
||||||
|
<Trans>
|
||||||
|
<Text style={[a.font_bold]}>{getName(items[0])} </Text>
|
||||||
|
and
|
||||||
|
<Text> </Text>
|
||||||
|
<Text style={[a.font_bold]}>{getName(items[1])} </Text>
|
||||||
|
are included in your starter pack
|
||||||
|
</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>
|
||||||
|
<Text style={[a.font_bold]}>{getName(items[0])}, </Text>
|
||||||
|
<Text style={[a.font_bold]}>{getName(items[1])}, </Text>
|
||||||
|
and {state.profiles.length - 2}{' '}
|
||||||
|
<Plural
|
||||||
|
value={state.profiles.length - 2}
|
||||||
|
one="other"
|
||||||
|
other="others"
|
||||||
|
/>{' '}
|
||||||
|
are included in your starter pack
|
||||||
|
</Trans>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={[a.flex_row, a.w_full, a.justify_between, {marginTop: 'auto'}]}>
|
||||||
|
<Button
|
||||||
|
label={_(msg`Edit`)}
|
||||||
|
variant="ghost"
|
||||||
|
color="primary"
|
||||||
|
size="small">
|
||||||
|
<ButtonText>
|
||||||
|
<Trans>Edit</Trans>
|
||||||
|
</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
label={nextBtnText}
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
size="small"
|
||||||
|
onPress={onNext}>
|
||||||
|
<ButtonText>{nextBtnText}</ButtonText>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getName(item: AppBskyActorDefs.ProfileViewBasic | GeneratorView) {
|
||||||
|
if (AppBskyActorDefs.isProfileView(item)) {
|
||||||
|
return enforceLen(item.displayName || item.handle, 16)
|
||||||
|
}
|
||||||
|
if (AppBskyFeedDefs.isGeneratorView(item)) {
|
||||||
|
return enforceLen(item.displayName, 16)
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user