Skip to content

Commit

Permalink
Dedicated screen for hashtags, POC ALF list (#3047)
Browse files Browse the repository at this point in the history
* create dedicated hashtag "search" screen

clarify loading component name

more adjustments

rework `ViewHeader` to keep chevron centered w/ first line

adjustments

adjustments

use `author` instead of `handle` in route

add web route for url

add web route for url

Add desktop list header

support web

keep header lowercase

add optional subtitle to view header

correct isFetching logic

oops

use `isFetching` for clarity in footer

combine logic

update bskyweb

finish screen

style, add footer, add spinner, etc

add list

add header, params

create a screen

* add variable to server path

* localize `By`

* add empty state

* more adjustments

* sanitize author

* fix web

* add custom message for hashtag not found error

* ellipsis in middle

* fix

* fix trans

* account for multiple #

* encode #

* replaceall

* Use sanitized tag

* don't call function in lingui

* add share button

---------

Co-authored-by: Eric Bailey <[email protected]>
  • Loading branch information
haileyok and estrattonbailey authored Mar 1, 2024
1 parent 8900c67 commit cf8b038
Show file tree
Hide file tree
Showing 10 changed files with 502 additions and 81 deletions.
1 change: 1 addition & 0 deletions bskyweb/cmd/bskyweb/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ func serve(cctx *cli.Context) error {
e.GET("/", server.WebHome)

// generic routes
e.GET("/hashtag/:tag", server.WebGeneric)
e.GET("/search", server.WebGeneric)
e.GET("/feeds", server.WebGeneric)
e.GET("/notifications", server.WebGeneric)
Expand Down
6 changes: 6 additions & 0 deletions src/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbed
import {createNativeStackNavigatorWithAuth} from './view/shell/createNativeStackNavigatorWithAuth'
import {msg} from '@lingui/macro'
import {i18n, MessageDescriptor} from '@lingui/core'
import HashtagScreen from '#/screens/Hashtag'

const navigationRef = createNavigationContainerRef<AllNavigatorParams>()

Expand Down Expand Up @@ -262,6 +263,11 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
requireAuth: true,
}}
/>
<Stack.Screen
name="Hashtag"
getComponent={() => HashtagScreen}
options={{title: title(msg`Hashtag`)}}
/>
</>
)
}
Expand Down
228 changes: 228 additions & 0 deletions src/components/Lists.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import React from 'react'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {View} from 'react-native'
import {Loader} from '#/components/Loader'
import {Trans} from '@lingui/macro'
import {cleanError} from 'lib/strings/errors'
import {Button} from '#/components/Button'
import {Text} from '#/components/Typography'
import {StackActions} from '@react-navigation/native'
import {useNavigation} from '@react-navigation/core'
import {NavigationProp} from 'lib/routes/types'

export function ListFooter({
isFetching,
isError,
error,
onRetry,
}: {
isFetching: boolean
isError: boolean
error?: string
onRetry?: () => Promise<unknown>
}) {
const t = useTheme()

return (
<View
style={[
a.w_full,
a.align_center,
a.justify_center,
a.border_t,
t.atoms.border_contrast_low,
{height: 100},
]}>
{isFetching ? (
<Loader size="xl" />
) : (
<ListFooterMaybeError
isError={isError}
error={error}
onRetry={onRetry}
/>
)}
</View>
)
}

function ListFooterMaybeError({
isError,
error,
onRetry,
}: {
isError: boolean
error?: string
onRetry?: () => Promise<unknown>
}) {
const t = useTheme()

if (!isError) return null

return (
<View style={[a.w_full, a.px_lg]}>
<View
style={[
a.flex_row,
a.gap_md,
a.p_md,
a.rounded_sm,
a.align_center,
t.atoms.bg_contrast_25,
]}>
<Text
style={[a.flex_1, a.text_sm, t.atoms.text_contrast_medium]}
numberOfLines={2}>
{error ? (
cleanError(error)
) : (
<Trans>Oops, something went wrong!</Trans>
)}
</Text>
<Button
variant="gradient"
label="Press to retry"
style={[
a.align_center,
a.justify_center,
a.rounded_sm,
a.overflow_hidden,
a.px_md,
a.py_sm,
]}
onPress={onRetry}>
Retry
</Button>
</View>
</View>
)
}

export function ListHeaderDesktop({
title,
subtitle,
}: {
title: string
subtitle?: string
}) {
const {gtTablet} = useBreakpoints()
const t = useTheme()

if (!gtTablet) return null

return (
<View style={[a.w_full, a.py_lg, a.px_xl, a.gap_xs]}>
<Text style={[a.text_3xl, a.font_bold]}>{title}</Text>
{subtitle ? (
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
{subtitle}
</Text>
) : undefined}
</View>
)
}

export function ListMaybePlaceholder({
isLoading,
isEmpty,
isError,
empty,
error,
onRetry,
}: {
isLoading: boolean
isEmpty: boolean
isError: boolean
empty?: string
error?: string
onRetry?: () => Promise<unknown>
}) {
const navigation = useNavigation<NavigationProp>()
const t = useTheme()

const canGoBack = navigation.canGoBack()
const onGoBack = React.useCallback(() => {
if (canGoBack) {
navigation.goBack()
} else {
navigation.navigate('HomeTab')
navigation.dispatch(StackActions.popToTop())
}
}, [navigation, canGoBack])

if (!isEmpty) return null

return (
<View
style={[
a.flex_1,
a.align_center,
a.border_t,
a.justify_between,
t.atoms.border_contrast_low,
{paddingTop: 175, paddingBottom: 110},
]}>
{isLoading ? (
<View style={[a.w_full, a.align_center, {top: 100}]}>
<Loader size="xl" />
</View>
) : (
<>
<View style={[a.w_full, a.align_center, a.gap_lg]}>
<Text style={[a.font_bold, a.text_3xl]}>
{isError ? (
<Trans>Oops!</Trans>
) : isEmpty ? (
<Trans>Page not found</Trans>
) : undefined}
</Text>

{isError ? (
<Text
style={[a.text_md, a.text_center, t.atoms.text_contrast_high]}>
{error ? error : <Trans>Something went wrong!</Trans>}
</Text>
) : isEmpty ? (
<Text
style={[a.text_md, a.text_center, t.atoms.text_contrast_high]}>
{empty ? (
empty
) : (
<Trans>
We're sorry! We can't find the page you were looking for.
</Trans>
)}
</Text>
) : undefined}
</View>
<View style={[a.w_full, a.px_lg, a.gap_md]}>
{isError && onRetry && (
<Button
variant="solid"
color="primary"
label="Click here"
onPress={onRetry}
size="large"
style={[
a.rounded_sm,
a.overflow_hidden,
{paddingVertical: 10},
]}>
Retry
</Button>
)}
<Button
variant="solid"
color={isError && onRetry ? 'secondary' : 'primary'}
label="Click here"
onPress={onGoBack}
size="large"
style={[a.rounded_sm, a.overflow_hidden, {paddingVertical: 10}]}>
Go Back
</Button>
</View>
</>
)}
</View>
)
}
11 changes: 7 additions & 4 deletions src/components/RichText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export function RichText({
<RichTextTag
key={key}
text={segment.text}
tag={tag.tag}
style={styles}
selectable={selectable}
authorHandle={authorHandle}
Expand All @@ -145,12 +146,14 @@ export function RichText({
}

function RichTextTag({
text: tag,
text,
tag,
style,
selectable,
authorHandle,
}: {
text: string
tag: string
selectable?: boolean
authorHandle?: string
} & TextStyleProp) {
Expand Down Expand Up @@ -184,8 +187,8 @@ function RichTextTag({
<Text
selectable={selectable}
{...native({
accessibilityLabel: _(msg`Hashtag: ${tag}`),
accessibilityHint: _(msg`Click here to open tag menu for ${tag}`),
accessibilityLabel: _(msg`Hashtag: #${tag}`),
accessibilityHint: _(msg`Click here to open tag menu for #${tag}`),
accessibilityRole: isNative ? 'button' : undefined,
onPress: open,
onPressIn: onPressIn,
Expand Down Expand Up @@ -213,7 +216,7 @@ function RichTextTag({
textDecorationColor: t.palette.primary_500,
},
]}>
{tag}
{text}
</Text>
</TagMenu>
</React.Fragment>
Expand Down
Loading

0 comments on commit cf8b038

Please sign in to comment.