Add copy to feeds page (#2852)

* move `IconCircle` to `components` for reuse

* add copy to feeds page

* start of a header

* saveit

* add lg size

* add your feeds

* don't show Your Feeds if you don't have any

* Minor ui tweaks

* cleanup

* remove unused activity indicator

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
zio/stable
Hailey 2024-02-13 00:40:39 -08:00 committed by GitHub
parent 36e1da1006
commit d8245e96ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 129 additions and 97 deletions

View File

@ -30,8 +30,8 @@ export function IconCircle({
a.align_center, a.align_center,
a.rounded_full, a.rounded_full,
{ {
width: 64, width: size === 'lg' ? 52 : 64,
height: 64, height: size === 'lg' ? 52 : 64,
backgroundColor: backgroundColor:
t.name === 'light' ? t.palette.primary_50 : t.palette.primary_950, t.name === 'light' ? t.palette.primary_50 : t.palette.primary_950,
}, },

View File

@ -20,7 +20,7 @@ import {
OnboardingControls, OnboardingControls,
} from '#/screens/Onboarding/Layout' } from '#/screens/Onboarding/Layout'
import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard'
import {IconCircle} from '#/screens/Onboarding/IconCircle' import {IconCircle} from '#/components/IconCircle'
export type FeedConfig = { export type FeedConfig = {
default: boolean default: boolean

View File

@ -23,7 +23,7 @@ import {
Description, Description,
OnboardingControls, OnboardingControls,
} from '#/screens/Onboarding/Layout' } from '#/screens/Onboarding/Layout'
import {IconCircle} from '#/screens/Onboarding/IconCircle' import {IconCircle} from '#/components/IconCircle'
import { import {
bulkWriteFollows, bulkWriteFollows,
sortPrimaryAlgorithmFeeds, sortPrimaryAlgorithmFeeds,

View File

@ -22,7 +22,7 @@ import {
usePreferencesQuery, usePreferencesQuery,
useSetFeedViewPreferencesMutation, useSetFeedViewPreferencesMutation,
} from 'state/queries/preferences' } from 'state/queries/preferences'
import {IconCircle} from '#/screens/Onboarding/IconCircle' import {IconCircle} from '#/components/IconCircle'
export function StepFollowingFeed() { export function StepFollowingFeed() {
const {_} = useLingui() const {_} = useLingui()

View File

@ -26,7 +26,7 @@ import {
OnboardingControls, OnboardingControls,
} from '#/screens/Onboarding/Layout' } from '#/screens/Onboarding/Layout'
import {InterestButton} from '#/screens/Onboarding/StepInterests/InterestButton' import {InterestButton} from '#/screens/Onboarding/StepInterests/InterestButton'
import {IconCircle} from '#/screens/Onboarding/IconCircle' import {IconCircle} from '#/components/IconCircle'
export function StepInterests() { export function StepInterests() {
const {_} = useLingui() const {_} = useLingui()

View File

@ -26,7 +26,7 @@ import {
import {ModerationOption} from '#/screens/Onboarding/StepModeration/ModerationOption' import {ModerationOption} from '#/screens/Onboarding/StepModeration/ModerationOption'
import {AdultContentEnabledPref} from '#/screens/Onboarding/StepModeration/AdultContentEnabledPref' import {AdultContentEnabledPref} from '#/screens/Onboarding/StepModeration/AdultContentEnabledPref'
import {Context} from '#/screens/Onboarding/state' import {Context} from '#/screens/Onboarding/state'
import {IconCircle} from '#/screens/Onboarding/IconCircle' import {IconCircle} from '#/components/IconCircle'
function AnimatedDivider() { function AnimatedDivider() {
return ( return (

View File

@ -27,7 +27,7 @@ import {
SuggestedAccountCardPlaceholder, SuggestedAccountCardPlaceholder,
} from '#/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard' } from '#/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard'
import {aggregateInterestItems} from '#/screens/Onboarding/util' import {aggregateInterestItems} from '#/screens/Onboarding/util'
import {IconCircle} from '#/screens/Onboarding/IconCircle' import {IconCircle} from '#/components/IconCircle'
export function Inner({ export function Inner({
profiles, profiles,

View File

@ -21,7 +21,7 @@ import {
} from '#/screens/Onboarding/Layout' } from '#/screens/Onboarding/Layout'
import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard'
import {aggregateInterestItems} from '#/screens/Onboarding/util' import {aggregateInterestItems} from '#/screens/Onboarding/util'
import {IconCircle} from '#/screens/Onboarding/IconCircle' import {IconCircle} from '#/components/IconCircle'
export function StepTopicalFeeds() { export function StepTopicalFeeds() {
const {_} = useLingui() const {_} = useLingui()

View File

@ -16,6 +16,7 @@ import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {ComposeIcon2, CogIcon, MagnifyingGlassIcon2} from 'lib/icons' import {ComposeIcon2, CogIcon, MagnifyingGlassIcon2} from 'lib/icons'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {atoms as a, useTheme} from '#/alf'
import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput' import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput'
import {UserAvatar} from 'view/com/util/UserAvatar' import {UserAvatar} from 'view/com/util/UserAvatar'
import { import {
@ -41,8 +42,11 @@ import {
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
import {useComposerControls} from '#/state/shell/composer' import {useComposerControls} from '#/state/shell/composer'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {isNative} from '#/platform/detection' import {isNative, isWeb} from '#/platform/detection'
import {HITSLOP_10} from 'lib/constants' import {HITSLOP_10} from 'lib/constants'
import {IconCircle} from '#/components/IconCircle'
import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle'
import {ListMagnifyingGlass_Stroke2_Corner0_Rounded} from '#/components/icons/ListMagnifyingGlass'
type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'> type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'>
@ -215,12 +219,7 @@ export function FeedsScreen(_props: Props) {
// pendingItems: this.rootStore.preferences.savedFeeds.length || 3, // pendingItems: this.rootStore.preferences.savedFeeds.length || 3,
}) })
} else { } else {
if (preferences?.feeds?.saved.length === 0) { if (preferences?.feeds?.saved.length !== 0) {
slices.push({
key: 'savedFeedNoResults',
type: 'savedFeedNoResults',
})
} else {
const {saved, pinned} = preferences.feeds const {saved, pinned} = preferences.feeds
slices = slices.concat( slices = slices.concat(
@ -400,46 +399,48 @@ export function FeedsScreen(_props: Props) {
) { ) {
return ( return (
<View style={s.p10}> <View style={s.p10}>
<ActivityIndicator /> <ActivityIndicator size="large" />
</View> </View>
) )
} else if (item.type === 'savedFeedsHeader') { } else if (item.type === 'savedFeedsHeader') {
if (!isMobile) { return (
return ( <>
<View {!isMobile && (
style={[ <View
pal.view, style={[
styles.header, pal.view,
pal.border, styles.header,
{ pal.border,
borderBottomWidth: 1, {
}, borderBottomWidth: 1,
]}> },
<Text type="title-lg" style={[pal.text, s.bold]}> ]}>
<Trans>My Feeds</Trans> <Text type="title-lg" style={[pal.text, s.bold]}>
</Text> <Trans>Feeds</Trans>
<View style={styles.headerBtnGroup}> </Text>
<Pressable <View style={styles.headerBtnGroup}>
accessibilityRole="button" <Pressable
hitSlop={HITSLOP_10} accessibilityRole="button"
onPress={searchInputRef.current?.focus}> hitSlop={HITSLOP_10}
<MagnifyingGlassIcon2 onPress={searchInputRef.current?.focus}>
size={22} <MagnifyingGlassIcon2
strokeWidth={2} size={22}
style={pal.icon} strokeWidth={2}
/> style={pal.icon}
</Pressable> />
<Link </Pressable>
href="/settings/saved-feeds" <Link
accessibilityLabel={_(msg`Edit My Feeds`)} href="/settings/saved-feeds"
accessibilityHint=""> accessibilityLabel={_(msg`Edit My Feeds`)}
<CogIcon strokeWidth={1.5} style={pal.icon} size={28} /> accessibilityHint="">
</Link> <CogIcon strokeWidth={1.5} style={pal.icon} size={28} />
</Link>
</View>
</View> </View>
</View> )}
) {preferences?.feeds?.saved?.length !== 0 && <FeedsSavedHeader />}
} </>
return <View /> )
} else if (item.type === 'savedFeedNoResults') { } else if (item.type === 'savedFeedNoResults') {
return ( return (
<View <View
@ -457,47 +458,17 @@ export function FeedsScreen(_props: Props) {
} else if (item.type === 'popularFeedsHeader') { } else if (item.type === 'popularFeedsHeader') {
return ( return (
<> <>
<View <FeedsAboutHeader />
style={[ <View style={{paddingHorizontal: 12, paddingBottom: 12}}>
pal.view, <SearchInput
styles.header, ref={searchInputRef}
{ query={query}
// This is first in the flatlist without a session -esb onChangeQuery={onChangeQuery}
marginTop: hasSession ? 16 : 0, onPressCancelSearch={onPressCancelSearch}
paddingLeft: isMobile ? 12 : undefined, onSubmitQuery={onSubmitQuery}
paddingRight: 10, setIsInputFocused={onChangeSearchFocus}
paddingBottom: isMobile ? 6 : undefined, />
},
]}>
<Text type="title-lg" style={[pal.text, s.bold]}>
<Trans>Discover new feeds</Trans>
</Text>
{!isMobile && (
<SearchInput
ref={searchInputRef}
query={query}
onChangeQuery={onChangeQuery}
onPressCancelSearch={onPressCancelSearch}
onSubmitQuery={onSubmitQuery}
setIsInputFocused={onChangeSearchFocus}
style={{flex: 1, maxWidth: 250}}
/>
)}
</View> </View>
{isMobile && (
<View style={{paddingHorizontal: 8, paddingBottom: 10}}>
<SearchInput
ref={searchInputRef}
query={query}
onChangeQuery={onChangeQuery}
onPressCancelSearch={onPressCancelSearch}
onSubmitQuery={onSubmitQuery}
setIsInputFocused={onChangeSearchFocus}
/>
</View>
)}
</> </>
) )
} else if (item.type === 'popularFeedsLoading') { } else if (item.type === 'popularFeedsLoading') {
@ -529,15 +500,20 @@ export function FeedsScreen(_props: Props) {
return null return null
}, },
[ [
_,
hasSession,
isMobile, isMobile,
pal, pal.view,
pal.border,
pal.text,
pal.icon,
pal.textLight,
_,
preferences?.feeds?.saved?.length,
query, query,
onChangeQuery, onChangeQuery,
onPressCancelSearch, onPressCancelSearch,
onSubmitQuery, onSubmitQuery,
onChangeSearchFocus, onChangeSearchFocus,
hasSession,
], ],
) )
@ -552,8 +528,6 @@ export function FeedsScreen(_props: Props) {
/> />
)} )}
{preferences ? <View /> : <ActivityIndicator />}
<List <List
ref={listRef} ref={listRef}
style={[!isTabletOrDesktop && s.flex1, styles.list]} style={[!isTabletOrDesktop && s.flex1, styles.list]}
@ -660,6 +634,64 @@ function SavedFeedLoadingPlaceholder() {
) )
} }
function FeedsSavedHeader() {
const t = useTheme()
return (
<View
style={[
a.flex_row,
a.px_md,
a.pt_2xl,
a.gap_md,
isWeb ? a.pb_2xl : a.pb_xl,
a.border_b,
t.atoms.border_contrast_low,
]}>
<IconCircle icon={ListSparkle_Stroke2_Corner0_Rounded} size="lg" />
<View style={[a.flex_1, a.gap_sm]}>
<Text style={[a.flex_1, a.text_2xl, a.font_bold, t.atoms.text]}>
<Trans>My Feeds</Trans>
</Text>
<Text style={[t.atoms.text_contrast_high]}>
<Trans>All the feeds you've saved, right in one place.</Trans>
</Text>
</View>
</View>
)
}
function FeedsAboutHeader() {
const t = useTheme()
return (
<View
style={[
a.flex_row,
a.px_md,
a.pt_2xl,
a.gap_md,
isWeb ? a.pb_2xl : a.pb_xl,
]}>
<IconCircle
icon={ListMagnifyingGlass_Stroke2_Corner0_Rounded}
size="lg"
/>
<View style={[a.flex_1, a.gap_sm]}>
<Text style={[a.flex_1, a.text_2xl, a.font_bold, t.atoms.text]}>
<Trans>Discover New Feeds</Trans>
</Text>
<Text style={[t.atoms.text_contrast_high]}>
<Trans>
Custom feeds built by the community bring you new experiences and
help you find the content you love.
</Trans>
</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,