[🏁 #3] Logged-in Starter Pack View (#4292)

* minimal landing page

add a minimal gate

remove overlap row

add bg to avatar set

implement basic mock up

add header of mock

add route

* add navigation

* create starter pack landing page

* add state reducer

* make value an array

* implement state w/ steps

* add back the flex

* view header back button

* add button text

* add button text

* add details screen

* add button states

* proper use of button

* oops

* rm extra state

* use keyboard aware scrollview

* rm log

* remove some stuff

* remove avatar from landing

* add list for profiles

* add profile card w/ button

* add feeds

* add processing state

* support editing a pack

* fix undefined error

* add logged-in view routes

* fix navigation link

* add dummy button to settings

* add starter pack screen

* update prop

* add a pager for starter packs

* add placeholder for feeds

* add share/edit buttons

* tsignore for now, fix merge issues
This commit is contained in:
Hailey
2024-06-02 18:41:41 -07:00
committed by GitHub
parent 30686702a6
commit 55c2ca5b92
10 changed files with 355 additions and 27 deletions
@@ -5,7 +5,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {CommonNavigatorParams} from 'lib/routes/types'
import {useGate} from 'lib/statsig/statsig'
import {useSetMinimalShellMode} from 'state/shell'
@@ -72,13 +71,12 @@ const PLACEHOLDER_USERS = [
},
]
export function Landing({
export function LandingScreen({
navigation,
}: NativeStackScreenProps<CommonNavigatorParams, 'StarterPackLanding'>) {
const {_} = useLingui()
const gate = useGate()
const t = useTheme()
const {isTabletOrDesktop} = useWebMediaQueries()
const setMinimalShellMode = useSetMinimalShellMode()
const gradient =
t.name === 'light'
@@ -93,7 +91,14 @@ export function Landing({
}, [])
React.useEffect(() => {
if (!gate('starter_packs_enabled')) {
// @ts-expect-error idk
navigation.replace('Home')
return
}
setMinimalShellMode(true)
return () => {
setMinimalShellMode(false)
}
@@ -120,21 +125,22 @@ export function Landing({
</View>
</LinearGradient>
<View style={[a.gap_md, a.mt_lg, a.mx_lg]}>
{/* TODO only display this when the total count is > 50 */}
<Text style={[a.text_md, a.text_center]}>186 joined this week!</Text>
<Text
style={[a.text_md, a.text_center, t.atoms.text_contrast_medium]}>
186 joined this week
</Text>
<Button
label={_(msg`Join Bluesky`)}
label={_(msg`Join Bluesky now`)}
onPress={() => {}}
variant="solid"
color="primary"
size="large"
style={[isTabletOrDesktop && {width: 200, alignSelf: 'center'}]}>
size="large">
<ButtonText style={[a.text_lg]}>
<Trans>Join Bluesky</Trans>
<Trans>Join Bluesky now</Trans>
</ButtonText>
</Button>
<View style={[a.gap_xl, a.mt_md]}>
<Text style={[a.text_md]}>
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
(This is the description) A collection of feeds and users to get
you started with the science community on Bluesky!
</Text>
@@ -162,7 +168,7 @@ export function Landing({
t.atoms.bg_contrast_25,
a.rounded_sm,
a.px_xs,
a.py_xl,
a.py_md,
a.gap_xl,
]}>
<UserSet users={userSets.first} />
@@ -0,0 +1,54 @@
import React, {useCallback} from 'react'
import {View} from 'react-native'
import {useBottomBarOffset} from 'lib/hooks/useBottomBarOffset'
import {isNative} from 'platform/detection'
import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard'
import {List, ListRef} from 'view/com/util/List'
import {SectionRef} from '#/screens/Profile/Sections/types'
function renderItem({item}: {item: string}) {
return <FeedSourceCard feedUri={item} />
}
function keyExtractor(item: string) {
return item
}
interface ProfilesListProps {
feeds: string[]
headerHeight: number
scrollElRef: ListRef
}
export const FeedsList = React.forwardRef<SectionRef, ProfilesListProps>(
function FeedsListImpl({feeds, headerHeight, scrollElRef}, ref) {
const [initialHeaderHeight] = React.useState(headerHeight)
const bottomBarOffset = useBottomBarOffset(20)
const onScrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({
animated: isNative,
offset: -headerHeight,
})
}, [scrollElRef, headerHeight])
React.useImperativeHandle(ref, () => ({
scrollToTop: onScrollToTop,
}))
return (
<List
data={feeds}
renderItem={renderItem}
keyExtractor={keyExtractor}
ref={scrollElRef}
headerOffset={headerHeight}
ListFooterComponent={
<View style={[{height: initialHeaderHeight + bottomBarOffset}]} />
}
showsVerticalScrollIndicator={false}
/>
)
},
)
@@ -0,0 +1,55 @@
import React, {useCallback} from 'react'
import {View} from 'react-native'
import {AppBskyActorDefs} from '@atproto/api'
import {useBottomBarOffset} from 'lib/hooks/useBottomBarOffset'
import {isNative} from 'platform/detection'
import {ProfileCardWithFollowBtn} from 'view/com/profile/ProfileCard'
import {List, ListRef} from 'view/com/util/List'
import {SectionRef} from '#/screens/Profile/Sections/types'
function renderItem({item}: {item: AppBskyActorDefs.ProfileViewBasic}) {
return <ProfileCardWithFollowBtn profile={item} />
}
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic, index: number) {
return `${item.did}-${index}`
}
interface ProfilesListProps {
profiles: AppBskyActorDefs.ProfileViewBasic[]
headerHeight: number
scrollElRef: ListRef
}
export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>(
function ProfilesListImpl({profiles, headerHeight, scrollElRef}, ref) {
const [initialHeaderHeight] = React.useState(headerHeight)
const bottomBarOffset = useBottomBarOffset(20)
const onScrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({
animated: isNative,
offset: -headerHeight,
})
}, [scrollElRef, headerHeight])
React.useImperativeHandle(ref, () => ({
scrollToTop: onScrollToTop,
}))
return (
<List
data={profiles}
renderItem={renderItem}
keyExtractor={keyExtractor}
ref={scrollElRef}
headerOffset={headerHeight}
ListFooterComponent={
<View style={[{height: initialHeaderHeight + bottomBarOffset}]} />
}
showsVerticalScrollIndicator={false}
/>
)
},
)
+183
View File
@@ -0,0 +1,183 @@
import React from 'react'
import {Text, View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {CommonNavigatorParams} from 'lib/routes/types'
import {isWeb} from 'platform/detection'
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
import {CenteredView} from 'view/com/util/Views'
import {FeedsList} from '#/screens/StarterPack/Main/FeedsList'
import {ProfilesList} from '#/screens/StarterPack/Main/ProfilesList'
import {atoms as a} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
/**
* TEMPORARY CONTENT, DO NOT TRANSLATE
*/
/**
* TEMPORARY CONTENT, DO NOT TRANSLATE
*/
/**
* TEMPORARY CONTENT, DO NOT TRANSLATE
*/
const PLACEHOLDER_USERS = [
{
did: 'did:plc:qjeavhlw222ppsre4rscd3n2',
handle: 'test.handle',
displayName: 'Bossett',
avatar:
'https://cdn.bsky.app/img/avatar/plain/did:plc:jfhpnnst6flqway4eaeqzj2a/bafkreid42e2su4sju7hl2nm4ouacw3icvvytf7r6gab3pvc2qxhqhc5ji4@jpeg',
},
{
did: 'did:plc:qjeavhlw222ppsre4rscd3n2',
handle: 'test.handle',
displayName: 'hailey',
avatar:
'https://cdn.bsky.app/img/avatar/plain/did:plc:oisofpd7lj26yvgiivf3lxsi/bafkreia4lpswywb5yx3gsezqb5io73kt7icw634ks4m4mmbfvp7e62cxku@jpeg',
},
{
did: 'did:plc:qjeavhlw222ppsre4rscd3n2',
handle: 'test.handle',
displayName: 'samuel',
avatar:
'https://cdn.bsky.app/img/avatar/plain/did:plc:p2cp5gopk7mgjegy6wadk3ep/bafkreias5vktko4jpg4mwp4audptcbdfkb4o3cfyzt4426mj2h7ewwvg7q@jpeg',
},
{
did: 'did:plc:qjeavhlw222ppsre4rscd3n2',
handle: 'test.handle',
displayName: 'Paul “Frazee” 🦋',
avatar:
'https://cdn.bsky.app/img/avatar/plain/did:plc:ragtjsm2j2vknwkz3zp4oxrd/bafkreihhpqdyntku66himwor5wlhtdo44hllmngj2ofmbqnm25bdm454wq@jpeg',
},
{
did: 'did:plc:qjeavhlw222ppsre4rscd3n2',
handle: 'test.handle',
displayName: 'Jay',
avatar:
'https://cdn.bsky.app/img/avatar/plain/did:plc:oky5czdrnfjpqslsw2a5iclo/bafkreihidru2xruxdxlvvcixc7lbgoudzicjbrdgacdhdhxyfw4yut4nfq@jpeg',
},
{
did: 'did:plc:qjeavhlw222ppsre4rscd3n2',
handle: 'test.handle',
displayName: 'Emily 🦋',
avatar:
'https://cdn.bsky.app/img/avatar/plain/did:plc:vjug55kidv6sye7ykr5faxxn/bafkreia63347vwjt4sgfkedgvytodt22ugfck5o36qotwaflsf3qq5xj3y@jpeg',
},
{
did: 'did:plc:qjeavhlw222ppsre4rscd3n2',
handle: 'test.handle',
displayName: 'Rose 🌹',
avatar:
'https://cdn.bsky.app/img/avatar/plain/did:plc:qjeavhlw222ppsre4rscd3n2/bafkreiehdi7v7bmz64anqam72ybtkkeodfe22sh7dcsh7m6wei2cf5liye@jpeg',
},
{
did: 'did:plc:qjeavhlw222ppsre4rscd3n2',
handle: 'test.handle',
displayName: 'dan',
avatar:
'https://cdn.bsky.app/img/avatar/plain/did:plc:fpruhuo22xkm5o7ttr2ktxdo/bafkreif65cz6sp4cvwue6dnsy7lhy7o7vdelknwd6hermcbzg2npealz5q@jpeg',
},
]
const PLACEHOLDER_FEEDS = [
'at://did:plc:jfhpnnst6flqway4eaeqzj2a/app.bsky.feed.generator/for-science',
'at://did:plc:upmfcx5muayjhkg5sltj625o/app.bsky.feed.generator/aaachrckxlsh2',
]
export function StarterPackScreen({}: NativeStackScreenProps<
CommonNavigatorParams,
'StarterPackLanding'
>) {
// const {id} = route.params
return (
<CenteredView style={a.flex_1}>
<StarterPackScreenInner />
</CenteredView>
)
}
function StarterPackScreenInner() {
const isOwn = true
return (
<View style={isWeb ? {minHeight: '100%'} : {height: '100%'}}>
<PagerWithHeader
items={['Profiles', 'Feeds']}
isHeaderReady={true}
renderHeader={() => <Header isOwn={isOwn} />}>
{({headerHeight, scrollElRef}) => (
<ProfilesList
key={0}
profiles={PLACEHOLDER_USERS}
headerHeight={headerHeight}
// @ts-expect-error
scrollElRef={scrollElRef}
/>
)}
{({headerHeight, scrollElRef}) => (
<FeedsList
key={1}
feeds={PLACEHOLDER_FEEDS}
headerHeight={headerHeight}
// @ts-expect-error
scrollElRef={scrollElRef}
/>
)}
</PagerWithHeader>
</View>
)
}
function Header({isOwn}: {isOwn: boolean}) {
const {_} = useLingui()
return (
<>
<ProfileSubpageHeader
isLoading={false}
href=""
title="Science"
isOwner={false}
avatar={undefined}
creator={undefined}
avatarType="starter-pack">
<View style={[a.flex_row, a.gap_sm]}>
<Button
label={_(msg`Share`)}
variant="solid"
color="primary"
size="small"
onPress={() => {}}>
<ButtonText>
<Trans>Share</Trans>
</ButtonText>
</Button>
{isOwn && (
<Button
label={_(msg`Edit`)}
variant="solid"
color="secondary"
size="small"
onPress={() => {}}>
<ButtonText>
<Trans>Edit</Trans>
</ButtonText>
</Button>
)}
</View>
</ProfileSubpageHeader>
<View style={[a.px_md, a.py_lg]}>
<Text style={[a.text_md]}>
(This is the description) A collection of feeds and users to get you
started with the science community on Bluesky!
</Text>
</View>
</>
)
}