NotificationsScreen}
- options={{title: title('Notifications')}}
+ options={{title: title('Notifications'), requireAuth: true}}
/>
{commonScreens(Flat as typeof HomeTab, numUnread)}
diff --git a/src/state/shell/index.tsx b/src/state/shell/index.tsx
index 897a66020f..53f05055c3 100644
--- a/src/state/shell/index.tsx
+++ b/src/state/shell/index.tsx
@@ -7,7 +7,6 @@ import {Provider as ColorModeProvider} from './color-mode'
import {Provider as OnboardingProvider} from './onboarding'
import {Provider as ComposerProvider} from './composer'
import {Provider as TickEveryMinuteProvider} from './tick-every-minute'
-import {Provider as LoggedOutViewProvider} from './logged-out'
export {useIsDrawerOpen, useSetDrawerOpen} from './drawer-open'
export {
@@ -23,23 +22,19 @@ export {useTickEveryMinute} from './tick-every-minute'
export function Provider({children}: React.PropsWithChildren<{}>) {
return (
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
)
}
diff --git a/src/view/com/auth/withAuthRequired.tsx b/src/view/com/auth/withAuthRequired.tsx
deleted file mode 100644
index 7a91385451..0000000000
--- a/src/view/com/auth/withAuthRequired.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import React from 'react'
-import {
- ActivityIndicator,
- Linking,
- StyleSheet,
- TouchableOpacity,
-} from 'react-native'
-import {CenteredView} from '../util/Views'
-import {LoggedOut} from './LoggedOut'
-import {Onboarding} from './Onboarding'
-import {Text} from '../util/text/Text'
-import {usePalette} from 'lib/hooks/usePalette'
-import {STATUS_PAGE_URL} from 'lib/constants'
-import {useOnboardingState} from '#/state/shell'
-import {useSession} from '#/state/session'
-import {
- useLoggedOutView,
- useLoggedOutViewControls,
-} from '#/state/shell/logged-out'
-import {IS_PROD} from '#/env'
-
-export const withAuthRequired = (
- Component: React.ComponentType
,
- options: {
- isPublic?: boolean // TODO(pwi) need to enable in TF somehow
- } = {},
-): React.FC
=>
- function AuthRequired(props: P) {
- const {isInitialLoad, hasSession} = useSession()
- const onboardingState = useOnboardingState()
- const {showLoggedOut} = useLoggedOutView()
- const {setShowLoggedOut} = useLoggedOutViewControls()
-
- if (isInitialLoad) {
- return
- }
- if (!hasSession) {
- if (showLoggedOut) {
- return setShowLoggedOut(false)} />
- } else if (!options?.isPublic || IS_PROD) {
- return
- }
- }
- if (onboardingState.isActive) {
- return
- }
- return
- }
-
-function Loading() {
- const pal = usePalette('default')
-
- const [isTakingTooLong, setIsTakingTooLong] = React.useState(false)
- React.useEffect(() => {
- const t = setTimeout(() => setIsTakingTooLong(true), 15e3) // 15 seconds
- return () => clearTimeout(t)
- }, [setIsTakingTooLong])
-
- return (
-
-
-
- {isTakingTooLong
- ? "This is taking too long. There may be a problem with your internet or with the service, but we're going to try a couple more times..."
- : 'Connecting...'}
-
- {isTakingTooLong ? (
- {
- Linking.openURL(STATUS_PAGE_URL)
- }}
- accessibilityRole="button">
-
- Check Bluesky status page
-
-
- ) : null}
-
- )
-}
-
-const styles = StyleSheet.create({
- loading: {
- height: '100%',
- alignContent: 'center',
- justifyContent: 'center',
- paddingBottom: 100,
- },
- loadingText: {
- paddingVertical: 20,
- paddingHorizontal: 20,
- textAlign: 'center',
- },
-})
diff --git a/src/view/screens/AppPasswords.tsx b/src/view/screens/AppPasswords.tsx
index bc77a48cde..154035f228 100644
--- a/src/view/screens/AppPasswords.tsx
+++ b/src/view/screens/AppPasswords.tsx
@@ -12,7 +12,6 @@ import {Button} from '../com/util/forms/Button'
import * as Toast from '../com/util/Toast'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {CommonNavigatorParams} from 'lib/routes/types'
import {useAnalytics} from 'lib/analytics/analytics'
@@ -32,125 +31,111 @@ import {ErrorScreen} from '../com/util/error/ErrorScreen'
import {cleanError} from '#/lib/strings/errors'
type Props = NativeStackScreenProps
-export const AppPasswords = withAuthRequired(
- function AppPasswordsImpl({}: Props) {
- const pal = usePalette('default')
- const setMinimalShellMode = useSetMinimalShellMode()
- const {screen} = useAnalytics()
- const {isTabletOrDesktop} = useWebMediaQueries()
- const {openModal} = useModalControls()
- const {data: appPasswords, error} = useAppPasswordsQuery()
+export function AppPasswords({}: Props) {
+ const pal = usePalette('default')
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {screen} = useAnalytics()
+ const {isTabletOrDesktop} = useWebMediaQueries()
+ const {openModal} = useModalControls()
+ const {data: appPasswords, error} = useAppPasswordsQuery()
- useFocusEffect(
- React.useCallback(() => {
- screen('AppPasswords')
- setMinimalShellMode(false)
- }, [screen, setMinimalShellMode]),
+ useFocusEffect(
+ React.useCallback(() => {
+ screen('AppPasswords')
+ setMinimalShellMode(false)
+ }, [screen, setMinimalShellMode]),
+ )
+
+ const onAdd = React.useCallback(async () => {
+ openModal({name: 'add-app-password'})
+ }, [openModal])
+
+ if (error) {
+ return (
+
+
+
)
+ }
- const onAdd = React.useCallback(async () => {
- openModal({name: 'add-app-password'})
- }, [openModal])
-
- if (error) {
- return (
-
+
+
+
+
+ You have not created any app passwords yet. You can create one by
+ pressing the button below.
+
+
+
+ {!isTabletOrDesktop && }
+
-
+
-
- )
- }
+
+
+ )
+ }
- // no app passwords (empty) state
- if (appPasswords?.length === 0) {
- return (
-
+
+
-
-
-
-
- You have not created any app passwords yet. You can create one
- by pressing the button below.
-
-
-
- {!isTabletOrDesktop && }
-
-
-
- )
- }
-
- if (appPasswords?.length) {
- // has app passwords
- return (
-
-
-
- {appPasswords.map((password, i) => (
-
- ))}
- {isTabletOrDesktop && (
-
-
-
- )}
-
- {!isTabletOrDesktop && (
-
+ ))}
+ {isTabletOrDesktop && (
+
)}
-
- )
- }
-
- return (
-
-
+
+ {!isTabletOrDesktop && (
+
+
+
+ )}
)
- },
-)
+ }
+
+ return (
+
+
+
+ )
+}
function AppPasswordsHeader() {
const {isTabletOrDesktop} = useWebMediaQueries()
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
index f8fc0db178..ced8592c5c 100644
--- a/src/view/screens/Feeds.tsx
+++ b/src/view/screens/Feeds.tsx
@@ -2,7 +2,6 @@ import React from 'react'
import {ActivityIndicator, StyleSheet, View, RefreshControl} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from 'view/com/util/ViewHeader'
import {FAB} from 'view/com/util/fab/FAB'
import {Link} from 'view/com/util/Link'
@@ -88,437 +87,432 @@ type FlatlistSlice =
key: string
}
-export const FeedsScreen = withAuthRequired(
- function FeedsScreenImpl(_props: Props) {
- const pal = usePalette('default')
- const {openComposer} = useComposerControls()
- const {isMobile, isTabletOrDesktop} = useWebMediaQueries()
- const [query, setQuery] = React.useState('')
- const [isPTR, setIsPTR] = React.useState(false)
- const {
- data: preferences,
- isLoading: isPreferencesLoading,
- error: preferencesError,
- } = usePreferencesQuery()
- const {
- data: popularFeeds,
- isFetching: isPopularFeedsFetching,
- error: popularFeedsError,
- refetch: refetchPopularFeeds,
- fetchNextPage: fetchNextPopularFeedsPage,
- isFetchingNextPage: isPopularFeedsFetchingNextPage,
- hasNextPage: hasNextPopularFeedsPage,
- } = useGetPopularFeedsQuery()
- const {_} = useLingui()
- const setMinimalShellMode = useSetMinimalShellMode()
- const {
- data: searchResults,
- mutate: search,
- reset: resetSearch,
- isPending: isSearchPending,
- error: searchError,
- } = useSearchPopularFeedsMutation()
- const {hasSession} = useSession()
+export function FeedsScreen(_props: Props) {
+ const pal = usePalette('default')
+ const {openComposer} = useComposerControls()
+ const {isMobile, isTabletOrDesktop} = useWebMediaQueries()
+ const [query, setQuery] = React.useState('')
+ const [isPTR, setIsPTR] = React.useState(false)
+ const {
+ data: preferences,
+ isLoading: isPreferencesLoading,
+ error: preferencesError,
+ } = usePreferencesQuery()
+ const {
+ data: popularFeeds,
+ isFetching: isPopularFeedsFetching,
+ error: popularFeedsError,
+ refetch: refetchPopularFeeds,
+ fetchNextPage: fetchNextPopularFeedsPage,
+ isFetchingNextPage: isPopularFeedsFetchingNextPage,
+ hasNextPage: hasNextPopularFeedsPage,
+ } = useGetPopularFeedsQuery()
+ const {_} = useLingui()
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {
+ data: searchResults,
+ mutate: search,
+ reset: resetSearch,
+ isPending: isSearchPending,
+ error: searchError,
+ } = useSearchPopularFeedsMutation()
+ const {hasSession} = useSession()
- /**
- * A search query is present. We may not have search results yet.
- */
- const isUserSearching = query.length > 1
- const debouncedSearch = React.useMemo(
- () => debounce(q => search(q), 500), // debounce for 500ms
- [search],
+ /**
+ * A search query is present. We may not have search results yet.
+ */
+ const isUserSearching = query.length > 1
+ const debouncedSearch = React.useMemo(
+ () => debounce(q => search(q), 500), // debounce for 500ms
+ [search],
+ )
+ const onPressCompose = React.useCallback(() => {
+ openComposer({})
+ }, [openComposer])
+ const onChangeQuery = React.useCallback(
+ (text: string) => {
+ setQuery(text)
+ if (text.length > 1) {
+ debouncedSearch(text)
+ } else {
+ refetchPopularFeeds()
+ resetSearch()
+ }
+ },
+ [setQuery, refetchPopularFeeds, debouncedSearch, resetSearch],
+ )
+ const onPressCancelSearch = React.useCallback(() => {
+ setQuery('')
+ refetchPopularFeeds()
+ resetSearch()
+ }, [refetchPopularFeeds, setQuery, resetSearch])
+ const onSubmitQuery = React.useCallback(() => {
+ debouncedSearch(query)
+ }, [query, debouncedSearch])
+ const onPullToRefresh = React.useCallback(async () => {
+ setIsPTR(true)
+ await refetchPopularFeeds()
+ setIsPTR(false)
+ }, [setIsPTR, refetchPopularFeeds])
+ const onEndReached = React.useCallback(() => {
+ if (
+ isPopularFeedsFetching ||
+ isUserSearching ||
+ !hasNextPopularFeedsPage ||
+ popularFeedsError
)
- const onPressCompose = React.useCallback(() => {
- openComposer({})
- }, [openComposer])
- const onChangeQuery = React.useCallback(
- (text: string) => {
- setQuery(text)
- if (text.length > 1) {
- debouncedSearch(text)
- } else {
- refetchPopularFeeds()
- resetSearch()
- }
- },
- [setQuery, refetchPopularFeeds, debouncedSearch, resetSearch],
- )
- const onPressCancelSearch = React.useCallback(() => {
- setQuery('')
- refetchPopularFeeds()
- resetSearch()
- }, [refetchPopularFeeds, setQuery, resetSearch])
- const onSubmitQuery = React.useCallback(() => {
- debouncedSearch(query)
- }, [query, debouncedSearch])
- const onPullToRefresh = React.useCallback(async () => {
- setIsPTR(true)
- await refetchPopularFeeds()
- setIsPTR(false)
- }, [setIsPTR, refetchPopularFeeds])
- const onEndReached = React.useCallback(() => {
- if (
- isPopularFeedsFetching ||
- isUserSearching ||
- !hasNextPopularFeedsPage ||
- popularFeedsError
- )
- return
- fetchNextPopularFeedsPage()
- }, [
- isPopularFeedsFetching,
- isUserSearching,
- popularFeedsError,
- hasNextPopularFeedsPage,
- fetchNextPopularFeedsPage,
- ])
+ return
+ fetchNextPopularFeedsPage()
+ }, [
+ isPopularFeedsFetching,
+ isUserSearching,
+ popularFeedsError,
+ hasNextPopularFeedsPage,
+ fetchNextPopularFeedsPage,
+ ])
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
- const items = React.useMemo(() => {
- let slices: FlatlistSlice[] = []
+ const items = React.useMemo(() => {
+ let slices: FlatlistSlice[] = []
- if (hasSession) {
+ if (hasSession) {
+ slices.push({
+ key: 'savedFeedsHeader',
+ type: 'savedFeedsHeader',
+ })
+
+ if (preferencesError) {
slices.push({
- key: 'savedFeedsHeader',
- type: 'savedFeedsHeader',
+ key: 'savedFeedsError',
+ type: 'error',
+ error: cleanError(preferencesError.toString()),
})
-
- if (preferencesError) {
+ } else {
+ if (isPreferencesLoading || !preferences?.feeds?.saved) {
slices.push({
- key: 'savedFeedsError',
- type: 'error',
- error: cleanError(preferencesError.toString()),
+ key: 'savedFeedsLoading',
+ type: 'savedFeedsLoading',
+ // pendingItems: this.rootStore.preferences.savedFeeds.length || 3,
})
} else {
- if (isPreferencesLoading || !preferences?.feeds?.saved) {
+ if (preferences?.feeds?.saved.length === 0) {
slices.push({
- key: 'savedFeedsLoading',
- type: 'savedFeedsLoading',
- // pendingItems: this.rootStore.preferences.savedFeeds.length || 3,
+ key: 'savedFeedNoResults',
+ type: 'savedFeedNoResults',
})
} else {
- 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(
- pinned.map(uri => ({
+ slices = slices.concat(
+ pinned.map(uri => ({
+ key: `savedFeed:${uri}`,
+ type: 'savedFeed',
+ feedUri: uri,
+ })),
+ )
+
+ slices = slices.concat(
+ saved
+ .filter(uri => !pinned.includes(uri))
+ .map(uri => ({
key: `savedFeed:${uri}`,
type: 'savedFeed',
feedUri: uri,
})),
- )
+ )
+ }
+ }
+ }
+ }
+ slices.push({
+ key: 'popularFeedsHeader',
+ type: 'popularFeedsHeader',
+ })
+
+ if (popularFeedsError || searchError) {
+ slices.push({
+ key: 'popularFeedsError',
+ type: 'error',
+ error: cleanError(
+ popularFeedsError?.toString() ?? searchError?.toString() ?? '',
+ ),
+ })
+ } else {
+ if (isUserSearching) {
+ if (isSearchPending || !searchResults) {
+ slices.push({
+ key: 'popularFeedsLoading',
+ type: 'popularFeedsLoading',
+ })
+ } else {
+ if (!searchResults || searchResults?.length === 0) {
+ slices.push({
+ key: 'popularFeedsNoResults',
+ type: 'popularFeedsNoResults',
+ })
+ } else {
+ slices = slices.concat(
+ searchResults.map(feed => ({
+ key: `popularFeed:${feed.uri}`,
+ type: 'popularFeed',
+ feedUri: feed.uri,
+ })),
+ )
+ }
+ }
+ } else {
+ if (isPopularFeedsFetching && !popularFeeds?.pages) {
+ slices.push({
+ key: 'popularFeedsLoading',
+ type: 'popularFeedsLoading',
+ })
+ } else {
+ if (
+ !popularFeeds?.pages ||
+ popularFeeds?.pages[0]?.feeds?.length === 0
+ ) {
+ slices.push({
+ key: 'popularFeedsNoResults',
+ type: 'popularFeedsNoResults',
+ })
+ } else {
+ for (const page of popularFeeds.pages || []) {
slices = slices.concat(
- saved
- .filter(uri => !pinned.includes(uri))
- .map(uri => ({
- key: `savedFeed:${uri}`,
- type: 'savedFeed',
- feedUri: uri,
+ page.feeds
+ .filter(feed => !preferences?.feeds?.saved.includes(feed.uri))
+ .map(feed => ({
+ key: `popularFeed:${feed.uri}`,
+ type: 'popularFeed',
+ feedUri: feed.uri,
})),
)
}
- }
- }
- }
- slices.push({
- key: 'popularFeedsHeader',
- type: 'popularFeedsHeader',
- })
-
- if (popularFeedsError || searchError) {
- slices.push({
- key: 'popularFeedsError',
- type: 'error',
- error: cleanError(
- popularFeedsError?.toString() ?? searchError?.toString() ?? '',
- ),
- })
- } else {
- if (isUserSearching) {
- if (isSearchPending || !searchResults) {
- slices.push({
- key: 'popularFeedsLoading',
- type: 'popularFeedsLoading',
- })
- } else {
- if (!searchResults || searchResults?.length === 0) {
+ if (isPopularFeedsFetchingNextPage) {
slices.push({
- key: 'popularFeedsNoResults',
- type: 'popularFeedsNoResults',
+ key: 'popularFeedsLoadingMore',
+ type: 'popularFeedsLoadingMore',
})
- } else {
- slices = slices.concat(
- searchResults.map(feed => ({
- key: `popularFeed:${feed.uri}`,
- type: 'popularFeed',
- feedUri: feed.uri,
- })),
- )
- }
- }
- } else {
- if (isPopularFeedsFetching && !popularFeeds?.pages) {
- slices.push({
- key: 'popularFeedsLoading',
- type: 'popularFeedsLoading',
- })
- } else {
- if (
- !popularFeeds?.pages ||
- popularFeeds?.pages[0]?.feeds?.length === 0
- ) {
- slices.push({
- key: 'popularFeedsNoResults',
- type: 'popularFeedsNoResults',
- })
- } else {
- for (const page of popularFeeds.pages || []) {
- slices = slices.concat(
- page.feeds
- .filter(
- feed => !preferences?.feeds?.saved.includes(feed.uri),
- )
- .map(feed => ({
- key: `popularFeed:${feed.uri}`,
- type: 'popularFeed',
- feedUri: feed.uri,
- })),
- )
- }
-
- if (isPopularFeedsFetchingNextPage) {
- slices.push({
- key: 'popularFeedsLoadingMore',
- type: 'popularFeedsLoadingMore',
- })
- }
}
}
}
}
+ }
- return slices
- }, [
- hasSession,
- preferences,
- isPreferencesLoading,
- preferencesError,
- popularFeeds,
- isPopularFeedsFetching,
- popularFeedsError,
- isPopularFeedsFetchingNextPage,
- searchResults,
- isSearchPending,
- searchError,
- isUserSearching,
- ])
-
- const renderHeaderBtn = React.useCallback(() => {
- return (
-
-
-
- )
- }, [pal, _])
-
- const renderItem = React.useCallback(
- ({item}: {item: FlatlistSlice}) => {
- if (item.type === 'error') {
- return
- } else if (
- item.type === 'popularFeedsLoadingMore' ||
- item.type === 'savedFeedsLoading'
- ) {
- return (
-
-
-
- )
- } else if (item.type === 'savedFeedsHeader') {
- if (!isMobile) {
- return (
-
-
- My Feeds
-
-
-
-
-
- )
- }
- return
- } else if (item.type === 'savedFeedNoResults') {
- return (
-
-
- You don't have any saved feeds!
-
-
- )
- } else if (item.type === 'savedFeed') {
- return
- } else if (item.type === 'popularFeedsHeader') {
- return (
- <>
-
-
- Discover new feeds
-
-
- {!isMobile && (
-
- )}
-
-
- {isMobile && (
-
-
-
- )}
- >
- )
- } else if (item.type === 'popularFeedsLoading') {
- return
- } else if (item.type === 'popularFeed') {
- return (
-
- )
- } else if (item.type === 'popularFeedsNoResults') {
- return (
-
-
- No results found for "{query}"
-
-
- )
- }
- return null
- },
- [
- _,
- hasSession,
- isMobile,
- pal,
- query,
- onChangeQuery,
- onPressCancelSearch,
- onSubmitQuery,
- ],
- )
+ return slices
+ }, [
+ hasSession,
+ preferences,
+ isPreferencesLoading,
+ preferencesError,
+ popularFeeds,
+ isPopularFeedsFetching,
+ popularFeedsError,
+ isPopularFeedsFetchingNextPage,
+ searchResults,
+ isSearchPending,
+ searchError,
+ isUserSearching,
+ ])
+ const renderHeaderBtn = React.useCallback(() => {
return (
-
- {isMobile && (
-
- )}
-
- {preferences ? : }
-
- item.key}
- contentContainerStyle={styles.contentContainer}
- renderItem={renderItem}
- refreshControl={
-
- }
- initialNumToRender={10}
- onEndReached={onEndReached}
- // @ts-ignore our .web version only -prf
- desktopFixedHeight
- />
-
- {hasSession && (
- }
- accessibilityRole="button"
- accessibilityLabel={_(msg`New post`)}
- accessibilityHint=""
- />
- )}
-
+
+
+
)
- },
- {isPublic: true},
-)
+ }, [pal, _])
+
+ const renderItem = React.useCallback(
+ ({item}: {item: FlatlistSlice}) => {
+ if (item.type === 'error') {
+ return
+ } else if (
+ item.type === 'popularFeedsLoadingMore' ||
+ item.type === 'savedFeedsLoading'
+ ) {
+ return (
+
+
+
+ )
+ } else if (item.type === 'savedFeedsHeader') {
+ if (!isMobile) {
+ return (
+
+
+ My Feeds
+
+
+
+
+
+ )
+ }
+ return
+ } else if (item.type === 'savedFeedNoResults') {
+ return (
+
+
+ You don't have any saved feeds!
+
+
+ )
+ } else if (item.type === 'savedFeed') {
+ return
+ } else if (item.type === 'popularFeedsHeader') {
+ return (
+ <>
+
+
+ Discover new feeds
+
+
+ {!isMobile && (
+
+ )}
+
+
+ {isMobile && (
+
+
+
+ )}
+ >
+ )
+ } else if (item.type === 'popularFeedsLoading') {
+ return
+ } else if (item.type === 'popularFeed') {
+ return (
+
+ )
+ } else if (item.type === 'popularFeedsNoResults') {
+ return (
+
+
+ No results found for "{query}"
+
+
+ )
+ }
+ return null
+ },
+ [
+ _,
+ hasSession,
+ isMobile,
+ pal,
+ query,
+ onChangeQuery,
+ onPressCancelSearch,
+ onSubmitQuery,
+ ],
+ )
+
+ return (
+
+ {isMobile && (
+
+ )}
+
+ {preferences ? : }
+
+ item.key}
+ contentContainerStyle={styles.contentContainer}
+ renderItem={renderItem}
+ refreshControl={
+
+ }
+ initialNumToRender={10}
+ onEndReached={onEndReached}
+ // @ts-ignore our .web version only -prf
+ desktopFixedHeight
+ />
+
+ {hasSession && (
+ }
+ accessibilityRole="button"
+ accessibilityLabel={_(msg`New post`)}
+ accessibilityHint=""
+ />
+ )}
+
+ )
+}
function SavedFeed({feedUri}: {feedUri: string}) {
const pal = usePalette('default')
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 8df945cd28..015c436f41 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -3,7 +3,6 @@ import {View, ActivityIndicator, StyleSheet} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
@@ -17,29 +16,24 @@ import {emitSoftReset} from '#/state/events'
import {useSession} from '#/state/session'
type Props = NativeStackScreenProps
-export const HomeScreen = withAuthRequired(
- function HomeScreenImpl(props: Props) {
- const {hasSession} = useSession()
- const {data: preferences} = usePreferencesQuery()
+export function HomeScreen(props: Props) {
+ const {hasSession} = useSession()
+ const {data: preferences} = usePreferencesQuery()
- if (!hasSession) {
- return
- }
+ if (!hasSession) {
+ return
+ }
- if (preferences) {
- return
- } else {
- return (
-
-
-
- )
- }
- },
- {
- isPublic: true,
- },
-)
+ if (preferences) {
+ return
+ } else {
+ return (
+
+
+
+ )
+ }
+}
function HomeScreenPublic() {
const setMinimalShellMode = useSetMinimalShellMode()
diff --git a/src/view/screens/Lists.tsx b/src/view/screens/Lists.tsx
index c97be4a02c..d28db7c6c8 100644
--- a/src/view/screens/Lists.tsx
+++ b/src/view/screens/Lists.tsx
@@ -4,7 +4,6 @@ import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {AtUri} from '@atproto/api'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {MyLists} from '#/view/com/lists/MyLists'
import {Text} from 'view/com/util/text/Text'
import {Button} from 'view/com/util/forms/Button'
@@ -18,70 +17,68 @@ import {useModalControls} from '#/state/modals'
import {Trans} from '@lingui/macro'
type Props = NativeStackScreenProps
-export const ListsScreen = withAuthRequired(
- function ListsScreenImpl({}: Props) {
- const pal = usePalette('default')
- const setMinimalShellMode = useSetMinimalShellMode()
- const {isMobile} = useWebMediaQueries()
- const navigation = useNavigation()
- const {openModal} = useModalControls()
+export function ListsScreen({}: Props) {
+ const pal = usePalette('default')
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {isMobile} = useWebMediaQueries()
+ const navigation = useNavigation()
+ const {openModal} = useModalControls()
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
- const onPressNewList = React.useCallback(() => {
- openModal({
- name: 'create-or-edit-list',
- purpose: 'app.bsky.graph.defs#curatelist',
- onSave: (uri: string) => {
- try {
- const urip = new AtUri(uri)
- navigation.navigate('ProfileList', {
- name: urip.hostname,
- rkey: urip.rkey,
- })
- } catch {}
- },
- })
- }, [openModal, navigation])
+ const onPressNewList = React.useCallback(() => {
+ openModal({
+ name: 'create-or-edit-list',
+ purpose: 'app.bsky.graph.defs#curatelist',
+ onSave: (uri: string) => {
+ try {
+ const urip = new AtUri(uri)
+ navigation.navigate('ProfileList', {
+ name: urip.hostname,
+ rkey: urip.rkey,
+ })
+ } catch {}
+ },
+ })
+ }, [openModal, navigation])
- return (
-
-
-
-
- User Lists
+ return (
+
+
+
+
+ User Lists
+
+
+ Public, shareable lists which can drive feeds.
+
+
+
+
-
-
-
-
-
-
- )
- },
-)
+
+
+
+
+
+ )
+}
diff --git a/src/view/screens/Moderation.tsx b/src/view/screens/Moderation.tsx
index 10b72fe9ec..4d8d8cad73 100644
--- a/src/view/screens/Moderation.tsx
+++ b/src/view/screens/Moderation.tsx
@@ -6,7 +6,6 @@ import {
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {s} from 'lib/styles'
import {CenteredView} from '../com/util/Views'
import {ViewHeader} from '../com/util/ViewHeader'
@@ -21,100 +20,98 @@ import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
type Props = NativeStackScreenProps
-export const ModerationScreen = withAuthRequired(
- function Moderation({}: Props) {
- const pal = usePalette('default')
- const {_} = useLingui()
- const setMinimalShellMode = useSetMinimalShellMode()
- const {screen, track} = useAnalytics()
- const {isTabletOrDesktop} = useWebMediaQueries()
- const {openModal} = useModalControls()
+export function ModerationScreen({}: Props) {
+ const pal = usePalette('default')
+ const {_} = useLingui()
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {screen, track} = useAnalytics()
+ const {isTabletOrDesktop} = useWebMediaQueries()
+ const {openModal} = useModalControls()
- useFocusEffect(
- React.useCallback(() => {
- screen('Moderation')
- setMinimalShellMode(false)
- }, [screen, setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ screen('Moderation')
+ setMinimalShellMode(false)
+ }, [screen, setMinimalShellMode]),
+ )
- const onPressContentFiltering = React.useCallback(() => {
- track('Moderation:ContentfilteringButtonClicked')
- openModal({name: 'content-filtering-settings'})
- }, [track, openModal])
+ const onPressContentFiltering = React.useCallback(() => {
+ track('Moderation:ContentfilteringButtonClicked')
+ openModal({name: 'content-filtering-settings'})
+ }, [track, openModal])
- return (
-
-
-
-
-
-
-
-
- Content filtering
-
-
-
-
-
-
-
- Moderation lists
-
-
-
-
-
-
-
- Muted accounts
-
-
-
-
-
-
-
- Blocked accounts
-
-
-
- )
- },
-)
+ return (
+
+
+
+
+
+
+
+
+ Content filtering
+
+
+
+
+
+
+
+ Moderation lists
+
+
+
+
+
+
+
+ Muted accounts
+
+
+
+
+
+
+
+ Blocked accounts
+
+
+
+ )
+}
const styles = StyleSheet.create({
desktopContainer: {
diff --git a/src/view/screens/ModerationBlockedAccounts.tsx b/src/view/screens/ModerationBlockedAccounts.tsx
index c03275f5d8..8f6e2f7296 100644
--- a/src/view/screens/ModerationBlockedAccounts.tsx
+++ b/src/view/screens/ModerationBlockedAccounts.tsx
@@ -10,7 +10,6 @@ import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {Text} from '../com/util/text/Text'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {CommonNavigatorParams} from 'lib/routes/types'
import {useAnalytics} from 'lib/analytics/analytics'
@@ -30,146 +29,144 @@ type Props = NativeStackScreenProps<
CommonNavigatorParams,
'ModerationBlockedAccounts'
>
-export const ModerationBlockedAccounts = withAuthRequired(
- function ModerationBlockedAccountsImpl({}: Props) {
- const pal = usePalette('default')
- const {_} = useLingui()
- const setMinimalShellMode = useSetMinimalShellMode()
- const {isTabletOrDesktop} = useWebMediaQueries()
- const {screen} = useAnalytics()
- const [isPTRing, setIsPTRing] = React.useState(false)
- const {
- data,
- isFetching,
- isError,
- error,
- refetch,
- hasNextPage,
- fetchNextPage,
- isFetchingNextPage,
- } = useMyBlockedAccountsQuery()
- const isEmpty = !isFetching && !data?.pages[0]?.blocks.length
- const profiles = React.useMemo(() => {
- if (data?.pages) {
- return data.pages.flatMap(page => page.blocks)
- }
- return []
- }, [data])
+export function ModerationBlockedAccounts({}: Props) {
+ const pal = usePalette('default')
+ const {_} = useLingui()
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {isTabletOrDesktop} = useWebMediaQueries()
+ const {screen} = useAnalytics()
+ const [isPTRing, setIsPTRing] = React.useState(false)
+ const {
+ data,
+ isFetching,
+ isError,
+ error,
+ refetch,
+ hasNextPage,
+ fetchNextPage,
+ isFetchingNextPage,
+ } = useMyBlockedAccountsQuery()
+ const isEmpty = !isFetching && !data?.pages[0]?.blocks.length
+ const profiles = React.useMemo(() => {
+ if (data?.pages) {
+ return data.pages.flatMap(page => page.blocks)
+ }
+ return []
+ }, [data])
- useFocusEffect(
- React.useCallback(() => {
- screen('BlockedAccounts')
- setMinimalShellMode(false)
- }, [screen, setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ screen('BlockedAccounts')
+ setMinimalShellMode(false)
+ }, [screen, setMinimalShellMode]),
+ )
- const onRefresh = React.useCallback(async () => {
- setIsPTRing(true)
- try {
- await refetch()
- } catch (err) {
- logger.error('Failed to refresh my muted accounts', {error: err})
- }
- setIsPTRing(false)
- }, [refetch, setIsPTRing])
+ const onRefresh = React.useCallback(async () => {
+ setIsPTRing(true)
+ try {
+ await refetch()
+ } catch (err) {
+ logger.error('Failed to refresh my muted accounts', {error: err})
+ }
+ setIsPTRing(false)
+ }, [refetch, setIsPTRing])
- const onEndReached = React.useCallback(async () => {
- if (isFetching || !hasNextPage || isError) return
+ const onEndReached = React.useCallback(async () => {
+ if (isFetching || !hasNextPage || isError) return
- try {
- await fetchNextPage()
- } catch (err) {
- logger.error('Failed to load more of my muted accounts', {error: err})
- }
- }, [isFetching, hasNextPage, isError, fetchNextPage])
+ try {
+ await fetchNextPage()
+ } catch (err) {
+ logger.error('Failed to load more of my muted accounts', {error: err})
+ }
+ }, [isFetching, hasNextPage, isError, fetchNextPage])
- const renderItem = ({
- item,
- index,
- }: {
- item: ActorDefs.ProfileView
- index: number
- }) => (
-
- )
- return (
- (
+
+ )
+ return (
+
+
+
-
-
-
- Blocked accounts cannot reply in your threads, mention you, or
- otherwise interact with you. You will not see their content and they
- will be prevented from seeing yours.
-
-
- {isEmpty ? (
-
- {isError ? (
-
- ) : (
-
-
-
- You have not blocked any accounts yet. To block an account,
- go to their profile and selected "Block account" from the
- menu on their account.
-
-
-
- )}
-
- ) : (
- item.did}
- refreshControl={
-
- }
- onEndReached={onEndReached}
- renderItem={renderItem}
- initialNumToRender={15}
- // FIXME(dan)
+ styles.description,
+ pal.text,
+ isTabletOrDesktop && styles.descriptionDesktop,
+ ]}>
+
+ Blocked accounts cannot reply in your threads, mention you, or
+ otherwise interact with you. You will not see their content and they
+ will be prevented from seeing yours.
+
+
+ {isEmpty ? (
+
+ {isError ? (
+
+ ) : (
+
+
+
+ You have not blocked any accounts yet. To block an account, go
+ to their profile and selected "Block account" from the menu on
+ their account.
+
+
+
+ )}
+
+ ) : (
+ item.did}
+ refreshControl={
+
+ }
+ onEndReached={onEndReached}
+ renderItem={renderItem}
+ initialNumToRender={15}
+ // FIXME(dan)
- ListFooterComponent={() => (
-
- {(isFetching || isFetchingNextPage) && }
-
- )}
- // @ts-ignore our .web version only -prf
- desktopFixedHeight
- />
- )}
-
- )
- },
-)
+ ListFooterComponent={() => (
+
+ {(isFetching || isFetchingNextPage) && }
+
+ )}
+ // @ts-ignore our .web version only -prf
+ desktopFixedHeight
+ />
+ )}
+
+ )
+}
const styles = StyleSheet.create({
container: {
diff --git a/src/view/screens/ModerationModlists.tsx b/src/view/screens/ModerationModlists.tsx
index be0eb3850c..145b35a425 100644
--- a/src/view/screens/ModerationModlists.tsx
+++ b/src/view/screens/ModerationModlists.tsx
@@ -4,7 +4,6 @@ import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {AtUri} from '@atproto/api'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {MyLists} from '#/view/com/lists/MyLists'
import {Text} from 'view/com/util/text/Text'
import {Button} from 'view/com/util/forms/Button'
@@ -17,70 +16,68 @@ import {useSetMinimalShellMode} from '#/state/shell'
import {useModalControls} from '#/state/modals'
type Props = NativeStackScreenProps
-export const ModerationModlistsScreen = withAuthRequired(
- function ModerationModlistsScreenImpl({}: Props) {
- const pal = usePalette('default')
- const setMinimalShellMode = useSetMinimalShellMode()
- const {isMobile} = useWebMediaQueries()
- const navigation = useNavigation()
- const {openModal} = useModalControls()
+export function ModerationModlistsScreen({}: Props) {
+ const pal = usePalette('default')
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {isMobile} = useWebMediaQueries()
+ const navigation = useNavigation()
+ const {openModal} = useModalControls()
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
- const onPressNewList = React.useCallback(() => {
- openModal({
- name: 'create-or-edit-list',
- purpose: 'app.bsky.graph.defs#modlist',
- onSave: (uri: string) => {
- try {
- const urip = new AtUri(uri)
- navigation.navigate('ProfileList', {
- name: urip.hostname,
- rkey: urip.rkey,
- })
- } catch {}
- },
- })
- }, [openModal, navigation])
+ const onPressNewList = React.useCallback(() => {
+ openModal({
+ name: 'create-or-edit-list',
+ purpose: 'app.bsky.graph.defs#modlist',
+ onSave: (uri: string) => {
+ try {
+ const urip = new AtUri(uri)
+ navigation.navigate('ProfileList', {
+ name: urip.hostname,
+ rkey: urip.rkey,
+ })
+ } catch {}
+ },
+ })
+ }, [openModal, navigation])
- return (
-
-
-
-
- Moderation Lists
+ return (
+
+
+
+
+ Moderation Lists
+
+
+ Public, shareable lists of users to mute or block in bulk.
+
+
+
+
-
-
-
-
-
-
- )
- },
-)
+
+
+
+
+
+ )
+}
diff --git a/src/view/screens/ModerationMutedAccounts.tsx b/src/view/screens/ModerationMutedAccounts.tsx
index c0ff17eb46..41aee9f2ff 100644
--- a/src/view/screens/ModerationMutedAccounts.tsx
+++ b/src/view/screens/ModerationMutedAccounts.tsx
@@ -10,7 +10,6 @@ import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {Text} from '../com/util/text/Text'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {CommonNavigatorParams} from 'lib/routes/types'
import {useAnalytics} from 'lib/analytics/analytics'
@@ -30,145 +29,143 @@ type Props = NativeStackScreenProps<
CommonNavigatorParams,
'ModerationMutedAccounts'
>
-export const ModerationMutedAccounts = withAuthRequired(
- function ModerationMutedAccountsImpl({}: Props) {
- const pal = usePalette('default')
- const {_} = useLingui()
- const setMinimalShellMode = useSetMinimalShellMode()
- const {isTabletOrDesktop} = useWebMediaQueries()
- const {screen} = useAnalytics()
- const [isPTRing, setIsPTRing] = React.useState(false)
- const {
- data,
- isFetching,
- isError,
- error,
- refetch,
- hasNextPage,
- fetchNextPage,
- isFetchingNextPage,
- } = useMyMutedAccountsQuery()
- const isEmpty = !isFetching && !data?.pages[0]?.mutes.length
- const profiles = React.useMemo(() => {
- if (data?.pages) {
- return data.pages.flatMap(page => page.mutes)
- }
- return []
- }, [data])
+export function ModerationMutedAccounts({}: Props) {
+ const pal = usePalette('default')
+ const {_} = useLingui()
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {isTabletOrDesktop} = useWebMediaQueries()
+ const {screen} = useAnalytics()
+ const [isPTRing, setIsPTRing] = React.useState(false)
+ const {
+ data,
+ isFetching,
+ isError,
+ error,
+ refetch,
+ hasNextPage,
+ fetchNextPage,
+ isFetchingNextPage,
+ } = useMyMutedAccountsQuery()
+ const isEmpty = !isFetching && !data?.pages[0]?.mutes.length
+ const profiles = React.useMemo(() => {
+ if (data?.pages) {
+ return data.pages.flatMap(page => page.mutes)
+ }
+ return []
+ }, [data])
- useFocusEffect(
- React.useCallback(() => {
- screen('MutedAccounts')
- setMinimalShellMode(false)
- }, [screen, setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ screen('MutedAccounts')
+ setMinimalShellMode(false)
+ }, [screen, setMinimalShellMode]),
+ )
- const onRefresh = React.useCallback(async () => {
- setIsPTRing(true)
- try {
- await refetch()
- } catch (err) {
- logger.error('Failed to refresh my muted accounts', {error: err})
- }
- setIsPTRing(false)
- }, [refetch, setIsPTRing])
+ const onRefresh = React.useCallback(async () => {
+ setIsPTRing(true)
+ try {
+ await refetch()
+ } catch (err) {
+ logger.error('Failed to refresh my muted accounts', {error: err})
+ }
+ setIsPTRing(false)
+ }, [refetch, setIsPTRing])
- const onEndReached = React.useCallback(async () => {
- if (isFetching || !hasNextPage || isError) return
+ const onEndReached = React.useCallback(async () => {
+ if (isFetching || !hasNextPage || isError) return
- try {
- await fetchNextPage()
- } catch (err) {
- logger.error('Failed to load more of my muted accounts', {error: err})
- }
- }, [isFetching, hasNextPage, isError, fetchNextPage])
+ try {
+ await fetchNextPage()
+ } catch (err) {
+ logger.error('Failed to load more of my muted accounts', {error: err})
+ }
+ }, [isFetching, hasNextPage, isError, fetchNextPage])
- const renderItem = ({
- item,
- index,
- }: {
- item: ActorDefs.ProfileView
- index: number
- }) => (
-
- )
- return (
- (
+
+ )
+ return (
+
+
+
-
-
-
- Muted accounts have their posts removed from your feed and from your
- notifications. Mutes are completely private.
-
-
- {isEmpty ? (
-
- {isError ? (
-
- ) : (
-
-
-
- You have not muted any accounts yet. To mute an account, go
- to their profile and selected "Mute account" from the menu
- on their account.
-
-
-
- )}
-
- ) : (
- item.did}
- refreshControl={
-
- }
- onEndReached={onEndReached}
- renderItem={renderItem}
- initialNumToRender={15}
- // FIXME(dan)
+ styles.description,
+ pal.text,
+ isTabletOrDesktop && styles.descriptionDesktop,
+ ]}>
+
+ Muted accounts have their posts removed from your feed and from your
+ notifications. Mutes are completely private.
+
+
+ {isEmpty ? (
+
+ {isError ? (
+
+ ) : (
+
+
+
+ You have not muted any accounts yet. To mute an account, go to
+ their profile and selected "Mute account" from the menu on
+ their account.
+
+
+
+ )}
+
+ ) : (
+ item.did}
+ refreshControl={
+
+ }
+ onEndReached={onEndReached}
+ renderItem={renderItem}
+ initialNumToRender={15}
+ // FIXME(dan)
- ListFooterComponent={() => (
-
- {(isFetching || isFetchingNextPage) && }
-
- )}
- // @ts-ignore our .web version only -prf
- desktopFixedHeight
- />
- )}
-
- )
- },
-)
+ ListFooterComponent={() => (
+
+ {(isFetching || isFetchingNextPage) && }
+
+ )}
+ // @ts-ignore our .web version only -prf
+ desktopFixedHeight
+ />
+ )}
+
+ )
+}
const styles = StyleSheet.create({
container: {
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index 4ed9c7f74d..8516d1667f 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -6,7 +6,6 @@ import {
NativeStackScreenProps,
NotificationsTabNavigatorParams,
} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader'
import {Feed} from '../com/notifications/Feed'
import {TextLink} from 'view/com/util/Link'
@@ -28,102 +27,100 @@ type Props = NativeStackScreenProps<
NotificationsTabNavigatorParams,
'Notifications'
>
-export const NotificationsScreen = withAuthRequired(
- function NotificationsScreenImpl({}: Props) {
- const {_} = useLingui()
- const setMinimalShellMode = useSetMinimalShellMode()
- const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
- const scrollElRef = React.useRef(null)
- const {screen} = useAnalytics()
- const pal = usePalette('default')
- const {isDesktop} = useWebMediaQueries()
- const unreadNotifs = useUnreadNotifications()
- const queryClient = useQueryClient()
- const hasNew = !!unreadNotifs
+export function NotificationsScreen({}: Props) {
+ const {_} = useLingui()
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
+ const scrollElRef = React.useRef(null)
+ const {screen} = useAnalytics()
+ const pal = usePalette('default')
+ const {isDesktop} = useWebMediaQueries()
+ const unreadNotifs = useUnreadNotifications()
+ const queryClient = useQueryClient()
+ const hasNew = !!unreadNotifs
- // event handlers
- // =
- const scrollToTop = React.useCallback(() => {
- scrollElRef.current?.scrollToOffset({offset: 0})
- resetMainScroll()
- }, [scrollElRef, resetMainScroll])
+ // event handlers
+ // =
+ const scrollToTop = React.useCallback(() => {
+ scrollElRef.current?.scrollToOffset({offset: 0})
+ resetMainScroll()
+ }, [scrollElRef, resetMainScroll])
- const onPressLoadLatest = React.useCallback(() => {
- scrollToTop()
- queryClient.invalidateQueries({
- queryKey: NOTIFS_RQKEY(),
- })
- }, [scrollToTop, queryClient])
+ const onPressLoadLatest = React.useCallback(() => {
+ scrollToTop()
+ queryClient.invalidateQueries({
+ queryKey: NOTIFS_RQKEY(),
+ })
+ }, [scrollToTop, queryClient])
- // on-visible setup
- // =
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- logger.debug('NotificationsScreen: Updating feed')
- screen('Notifications')
- return listenSoftReset(onPressLoadLatest)
- }, [screen, onPressLoadLatest, setMinimalShellMode]),
- )
+ // on-visible setup
+ // =
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ logger.debug('NotificationsScreen: Updating feed')
+ screen('Notifications')
+ return listenSoftReset(onPressLoadLatest)
+ }, [screen, onPressLoadLatest, setMinimalShellMode]),
+ )
- const ListHeaderComponent = React.useCallback(() => {
- if (isDesktop) {
- return (
-
-
- Notifications{' '}
- {hasNew && (
-
- )}
- >
- }
- onPress={emitSoftReset}
- />
-
- )
- }
- return <>>
- }, [isDesktop, pal, hasNew])
-
- return (
-
-
-
- {(isScrolledDown || hasNew) && (
- {
+ if (isDesktop) {
+ return (
+
+
+ Notifications{' '}
+ {hasNew && (
+
+ )}
+ >
+ }
+ onPress={emitSoftReset}
/>
- )}
-
- )
- },
-)
+
+ )
+ }
+ return <>>
+ }, [isDesktop, pal, hasNew])
+
+ return (
+
+
+
+ {(isScrolledDown || hasNew) && (
+
+ )}
+
+ )
+}
diff --git a/src/view/screens/PostLikedBy.tsx b/src/view/screens/PostLikedBy.tsx
index 2209310d06..7cbb811024 100644
--- a/src/view/screens/PostLikedBy.tsx
+++ b/src/view/screens/PostLikedBy.tsx
@@ -2,7 +2,6 @@ import React from 'react'
import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader'
import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy'
import {makeRecordUri} from 'lib/strings/url-helpers'
@@ -11,25 +10,22 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
type Props = NativeStackScreenProps
-export const PostLikedByScreen = withAuthRequired(
- ({route}: Props) => {
- const setMinimalShellMode = useSetMinimalShellMode()
- const {name, rkey} = route.params
- const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
- const {_} = useLingui()
+export const PostLikedByScreen = ({route}: Props) => {
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {name, rkey} = route.params
+ const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
+ const {_} = useLingui()
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
- return (
-
-
-
-
- )
- },
- {isPublic: true},
-)
+ return (
+
+
+
+
+ )
+}
diff --git a/src/view/screens/PostRepostedBy.tsx b/src/view/screens/PostRepostedBy.tsx
index 5b3b5f8fae..de95f33bf9 100644
--- a/src/view/screens/PostRepostedBy.tsx
+++ b/src/view/screens/PostRepostedBy.tsx
@@ -1,7 +1,6 @@
import React from 'react'
import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {ViewHeader} from '../com/util/ViewHeader'
import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy'
@@ -11,25 +10,22 @@ import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
type Props = NativeStackScreenProps
-export const PostRepostedByScreen = withAuthRequired(
- ({route}: Props) => {
- const {name, rkey} = route.params
- const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
- const setMinimalShellMode = useSetMinimalShellMode()
- const {_} = useLingui()
+export const PostRepostedByScreen = ({route}: Props) => {
+ const {name, rkey} = route.params
+ const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {_} = useLingui()
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
- return (
-
-
-
-
- )
- },
- {isPublic: true},
-)
+ return (
+
+
+
+
+ )
+}
diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx
index 11574e2837..4b1f517487 100644
--- a/src/view/screens/PostThread.tsx
+++ b/src/view/screens/PostThread.tsx
@@ -5,7 +5,6 @@ import {useFocusEffect} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {makeRecordUri} from 'lib/strings/url-helpers'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader'
import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
import {ComposePrompt} from 'view/com/composer/Prompt'
@@ -27,85 +26,82 @@ import {CenteredView} from '../com/util/Views'
import {useComposerControls} from '#/state/shell/composer'
type Props = NativeStackScreenProps
-export const PostThreadScreen = withAuthRequired(
- function PostThreadScreenImpl({route}: Props) {
- const queryClient = useQueryClient()
- const {_} = useLingui()
- const {fabMinimalShellTransform} = useMinimalShellMode()
- const setMinimalShellMode = useSetMinimalShellMode()
- const {openComposer} = useComposerControls()
- const safeAreaInsets = useSafeAreaInsets()
- const {name, rkey} = route.params
- const {isMobile} = useWebMediaQueries()
- const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
- const {data: resolvedUri, error: uriError} = useResolveUriQuery(uri)
+export function PostThreadScreen({route}: Props) {
+ const queryClient = useQueryClient()
+ const {_} = useLingui()
+ const {fabMinimalShellTransform} = useMinimalShellMode()
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {openComposer} = useComposerControls()
+ const safeAreaInsets = useSafeAreaInsets()
+ const {name, rkey} = route.params
+ const {isMobile} = useWebMediaQueries()
+ const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
+ const {data: resolvedUri, error: uriError} = useResolveUriQuery(uri)
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
+
+ const onPressReply = React.useCallback(() => {
+ if (!resolvedUri) {
+ return
+ }
+ const thread = queryClient.getQueryData(
+ POST_THREAD_RQKEY(resolvedUri.uri),
)
-
- const onPressReply = React.useCallback(() => {
- if (!resolvedUri) {
- return
- }
- const thread = queryClient.getQueryData(
- POST_THREAD_RQKEY(resolvedUri.uri),
- )
- if (thread?.type !== 'post') {
- return
- }
- openComposer({
- replyTo: {
- uri: thread.post.uri,
- cid: thread.post.cid,
- text: thread.record.text,
- author: {
- handle: thread.post.author.handle,
- displayName: thread.post.author.displayName,
- avatar: thread.post.author.avatar,
- },
+ if (thread?.type !== 'post') {
+ return
+ }
+ openComposer({
+ replyTo: {
+ uri: thread.post.uri,
+ cid: thread.post.cid,
+ text: thread.record.text,
+ author: {
+ handle: thread.post.author.handle,
+ displayName: thread.post.author.displayName,
+ avatar: thread.post.author.avatar,
},
- onPost: () =>
- queryClient.invalidateQueries({
- queryKey: POST_THREAD_RQKEY(resolvedUri.uri || ''),
- }),
- })
- }, [openComposer, queryClient, resolvedUri])
+ },
+ onPost: () =>
+ queryClient.invalidateQueries({
+ queryKey: POST_THREAD_RQKEY(resolvedUri.uri || ''),
+ }),
+ })
+ }, [openComposer, queryClient, resolvedUri])
- return (
-
- {isMobile && }
-
- {uriError ? (
-
-
-
- ) : (
-
- )}
-
- {isMobile && (
-
-
-
+ return (
+
+ {isMobile && }
+
+ {uriError ? (
+
+
+
+ ) : (
+
)}
- )
- },
- {isPublic: true},
-)
+ {isMobile && (
+
+
+
+ )}
+
+ )
+}
const styles = StyleSheet.create({
prompt: {
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 48c914054e..8282b2105d 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -5,7 +5,6 @@ import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewSelectorHandle} from '../com/util/ViewSelector'
import {CenteredView, FlatList} from '../com/util/Views'
import {ScreenHider} from 'view/com/util/moderation/ScreenHider'
@@ -43,83 +42,78 @@ interface SectionRef {
}
type Props = NativeStackScreenProps
-export const ProfileScreen = withAuthRequired(
- function ProfileScreenImpl({route}: Props) {
- const {currentAccount} = useSession()
- const name =
- route.params.name === 'me' ? currentAccount?.did : route.params.name
- const moderationOpts = useModerationOpts()
- const {
- data: resolvedDid,
- error: resolveError,
- refetch: refetchDid,
- isFetching: isFetchingDid,
- } = useResolveDidQuery(name)
- const {
- data: profile,
- error: profileError,
- refetch: refetchProfile,
- isFetching: isFetchingProfile,
- } = useProfileQuery({
- did: resolvedDid?.did,
- })
+export function ProfileScreen({route}: Props) {
+ const {currentAccount} = useSession()
+ const name =
+ route.params.name === 'me' ? currentAccount?.did : route.params.name
+ const moderationOpts = useModerationOpts()
+ const {
+ data: resolvedDid,
+ error: resolveError,
+ refetch: refetchDid,
+ isFetching: isFetchingDid,
+ } = useResolveDidQuery(name)
+ const {
+ data: profile,
+ error: profileError,
+ refetch: refetchProfile,
+ isFetching: isFetchingProfile,
+ } = useProfileQuery({
+ did: resolvedDid?.did,
+ })
- const onPressTryAgain = React.useCallback(() => {
- if (resolveError) {
- refetchDid()
- } else {
- refetchProfile()
- }
- }, [resolveError, refetchDid, refetchProfile])
+ const onPressTryAgain = React.useCallback(() => {
+ if (resolveError) {
+ refetchDid()
+ } else {
+ refetchProfile()
+ }
+ }, [resolveError, refetchDid, refetchProfile])
- if (isFetchingDid || isFetchingProfile || !moderationOpts) {
- return (
-
-
-
- )
- }
- if (resolveError || profileError) {
- return (
-
-
-
- )
- }
- if (profile && moderationOpts) {
- return (
-
+
- )
- }
- // should never happen
+
+ )
+ }
+ if (resolveError || profileError) {
return (
)
- },
- {
- isPublic: true,
- },
-)
+ }
+ if (profile && moderationOpts) {
+ return (
+
+ )
+ }
+ // should never happen
+ return (
+
+
+
+ )
+}
function ProfileScreenLoaded({
profile: profileUnshadowed,
diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx
index 642793edac..da01cfca65 100644
--- a/src/view/screens/ProfileFeed.tsx
+++ b/src/view/screens/ProfileFeed.tsx
@@ -16,7 +16,6 @@ import {CommonNavigatorParams} from 'lib/routes/types'
import {makeRecordUri} from 'lib/strings/url-helpers'
import {colors, s} from 'lib/styles'
import {FeedDescriptor} from '#/state/queries/post-feed'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
import {Feed} from 'view/com/posts/Feed'
@@ -69,70 +68,65 @@ interface SectionRef {
}
type Props = NativeStackScreenProps
-export const ProfileFeedScreen = withAuthRequired(
- function ProfileFeedScreenImpl(props: Props) {
- const {rkey, name: handleOrDid} = props.route.params
+export function ProfileFeedScreen(props: Props) {
+ const {rkey, name: handleOrDid} = props.route.params
- const pal = usePalette('default')
- const {_} = useLingui()
- const navigation = useNavigation()
+ const pal = usePalette('default')
+ const {_} = useLingui()
+ const navigation = useNavigation()
- const uri = useMemo(
- () => makeRecordUri(handleOrDid, 'app.bsky.feed.generator', rkey),
- [rkey, handleOrDid],
- )
- const {error, data: resolvedUri} = useResolveUriQuery(uri)
+ const uri = useMemo(
+ () => makeRecordUri(handleOrDid, 'app.bsky.feed.generator', rkey),
+ [rkey, handleOrDid],
+ )
+ const {error, data: resolvedUri} = useResolveUriQuery(uri)
- const onPressBack = React.useCallback(() => {
- if (navigation.canGoBack()) {
- navigation.goBack()
- } else {
- navigation.navigate('Home')
- }
- }, [navigation])
-
- if (error) {
- return (
-
-
-
- Could not load feed
-
-
- {error.toString()}
-
-
-
-
-
-
-
- )
+ const onPressBack = React.useCallback(() => {
+ if (navigation.canGoBack()) {
+ navigation.goBack()
+ } else {
+ navigation.navigate('Home')
}
+ }, [navigation])
- return resolvedUri ? (
-
- ) : (
+ if (error) {
+ return (
-
-
+
+
+ Could not load feed
+
+
+ {error.toString()}
+
+
+
+
+
)
- },
- {
- isPublic: true,
- },
-)
+ }
+
+ return resolvedUri ? (
+
+ ) : (
+
+
+
+
+
+ )
+}
function ProfileFeedScreenIntermediate({feedUri}: {feedUri: string}) {
const {data: preferences} = usePreferencesQuery()
diff --git a/src/view/screens/ProfileFeedLikedBy.tsx b/src/view/screens/ProfileFeedLikedBy.tsx
index 6399c8a0b9..0460670e19 100644
--- a/src/view/screens/ProfileFeedLikedBy.tsx
+++ b/src/view/screens/ProfileFeedLikedBy.tsx
@@ -2,7 +2,6 @@ import React from 'react'
import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader'
import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy'
import {makeRecordUri} from 'lib/strings/url-helpers'
@@ -11,25 +10,22 @@ import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
type Props = NativeStackScreenProps
-export const ProfileFeedLikedByScreen = withAuthRequired(
- ({route}: Props) => {
- const setMinimalShellMode = useSetMinimalShellMode()
- const {name, rkey} = route.params
- const uri = makeRecordUri(name, 'app.bsky.feed.generator', rkey)
- const {_} = useLingui()
+export const ProfileFeedLikedByScreen = ({route}: Props) => {
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {name, rkey} = route.params
+ const uri = makeRecordUri(name, 'app.bsky.feed.generator', rkey)
+ const {_} = useLingui()
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
- return (
-
-
-
-
- )
- },
- {isPublic: true},
-)
+ return (
+
+
+
+
+ )
+}
diff --git a/src/view/screens/ProfileFollowers.tsx b/src/view/screens/ProfileFollowers.tsx
index 71c0e4a9cb..2cad08cb5d 100644
--- a/src/view/screens/ProfileFollowers.tsx
+++ b/src/view/screens/ProfileFollowers.tsx
@@ -2,7 +2,6 @@ import React from 'react'
import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader'
import {ProfileFollowers as ProfileFollowersComponent} from '../com/profile/ProfileFollowers'
import {useSetMinimalShellMode} from '#/state/shell'
@@ -10,24 +9,21 @@ import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
type Props = NativeStackScreenProps
-export const ProfileFollowersScreen = withAuthRequired(
- ({route}: Props) => {
- const {name} = route.params
- const setMinimalShellMode = useSetMinimalShellMode()
- const {_} = useLingui()
+export const ProfileFollowersScreen = ({route}: Props) => {
+ const {name} = route.params
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {_} = useLingui()
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
- return (
-
-
-
-
- )
- },
- {isPublic: true},
-)
+ return (
+
+
+
+
+ )
+}
diff --git a/src/view/screens/ProfileFollows.tsx b/src/view/screens/ProfileFollows.tsx
index bb3f2040f6..80502b98bc 100644
--- a/src/view/screens/ProfileFollows.tsx
+++ b/src/view/screens/ProfileFollows.tsx
@@ -2,7 +2,6 @@ import React from 'react'
import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader'
import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows'
import {useSetMinimalShellMode} from '#/state/shell'
@@ -10,24 +9,21 @@ import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
type Props = NativeStackScreenProps
-export const ProfileFollowsScreen = withAuthRequired(
- ({route}: Props) => {
- const {name} = route.params
- const setMinimalShellMode = useSetMinimalShellMode()
- const {_} = useLingui()
+export const ProfileFollowsScreen = ({route}: Props) => {
+ const {name} = route.params
+ const setMinimalShellMode = useSetMinimalShellMode()
+ const {_} = useLingui()
- useFocusEffect(
- React.useCallback(() => {
- setMinimalShellMode(false)
- }, [setMinimalShellMode]),
- )
+ useFocusEffect(
+ React.useCallback(() => {
+ setMinimalShellMode(false)
+ }, [setMinimalShellMode]),
+ )
- return (
-
-
-
-
- )
- },
- {isPublic: true},
-)
+ return (
+
+
+
+
+ )
+}
diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx
index a8c55250fc..cc6d85e6f5 100644
--- a/src/view/screens/ProfileList.tsx
+++ b/src/view/screens/ProfileList.tsx
@@ -12,7 +12,6 @@ import {useNavigation} from '@react-navigation/native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {AppBskyGraphDefs, AtUri, RichText as RichTextAPI} from '@atproto/api'
import {useQueryClient} from '@tanstack/react-query'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
import {Feed} from 'view/com/posts/Feed'
@@ -64,42 +63,40 @@ interface SectionRef {
}
type Props = NativeStackScreenProps
-export const ProfileListScreen = withAuthRequired(
- function ProfileListScreenImpl(props: Props) {
- const {name: handleOrDid, rkey} = props.route.params
- const {data: resolvedUri, error: resolveError} = useResolveUriQuery(
- AtUri.make(handleOrDid, 'app.bsky.graph.list', rkey).toString(),
- )
- const {data: list, error: listError} = useListQuery(resolvedUri?.uri)
+export function ProfileListScreen(props: Props) {
+ const {name: handleOrDid, rkey} = props.route.params
+ const {data: resolvedUri, error: resolveError} = useResolveUriQuery(
+ AtUri.make(handleOrDid, 'app.bsky.graph.list', rkey).toString(),
+ )
+ const {data: list, error: listError} = useListQuery(resolvedUri?.uri)
- if (resolveError) {
- return (
-
-
-
- )
- }
- if (listError) {
- return (
-
-
-
- )
- }
-
- return resolvedUri && list ? (
-
- ) : (
+ if (resolveError) {
+ return (
-
-
-
+
)
- },
-)
+ }
+ if (listError) {
+ return (
+
+
+
+ )
+ }
+
+ return resolvedUri && list ? (
+
+ ) : (
+
+
+
+
+
+ )
+}
function ProfileListScreenLoaded({
route,
diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx
index 4c13a2be17..640d76a5c5 100644
--- a/src/view/screens/SavedFeeds.tsx
+++ b/src/view/screens/SavedFeeds.tsx
@@ -14,7 +14,6 @@ import {useAnalytics} from 'lib/analytics/analytics'
import {usePalette} from 'lib/hooks/usePalette'
import {CommonNavigatorParams} from 'lib/routes/types'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from 'view/com/util/ViewHeader'
import {ScrollView, CenteredView} from 'view/com/util/Views'
import {Text} from 'view/com/util/text/Text'
@@ -51,7 +50,7 @@ const HITSLOP_BOTTOM = {
}
type Props = NativeStackScreenProps
-export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
+export function SavedFeeds({}: Props) {
const pal = usePalette('default')
const {_} = useLingui()
const {isMobile, isTabletOrDesktop} = useWebMediaQueries()
@@ -147,7 +146,7 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) {
)
-})
+}
function ListItem({
feedUri,
diff --git a/src/view/screens/Search/index.tsx b/src/view/screens/Search/index.tsx
index 69130ecfd6..a65149bf73 100644
--- a/src/view/screens/Search/index.tsx
+++ b/src/view/screens/Search/index.tsx
@@ -1,6 +1,3 @@
-import {withAuthRequired} from '#/view/com/auth/withAuthRequired'
import {SearchScreenMobile} from '#/view/screens/Search/Search'
-export const SearchScreen = withAuthRequired(SearchScreenMobile, {
- isPublic: true,
-})
+export const SearchScreen = SearchScreenMobile
diff --git a/src/view/screens/Search/index.web.tsx b/src/view/screens/Search/index.web.tsx
index 03fb7589c3..8e039e3cdf 100644
--- a/src/view/screens/Search/index.web.tsx
+++ b/src/view/screens/Search/index.web.tsx
@@ -1,6 +1,3 @@
-import {withAuthRequired} from '#/view/com/auth/withAuthRequired'
import {SearchScreenDesktop} from '#/view/screens/Search/Search'
-export const SearchScreen = withAuthRequired(SearchScreenDesktop, {
- isPublic: true,
-})
+export const SearchScreen = SearchScreenDesktop
diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx
index cc4348fff1..88cc2d5320 100644
--- a/src/view/screens/Settings.tsx
+++ b/src/view/screens/Settings.tsx
@@ -20,7 +20,6 @@ import {
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import * as AppInfo from 'lib/app-info'
import {s, colors} from 'lib/styles'
import {ScrollView} from '../com/util/Views'
@@ -141,7 +140,7 @@ function SettingsAccountCard({account}: {account: SessionAccount}) {
}
type Props = NativeStackScreenProps
-export const SettingsScreen = withAuthRequired(function Settings({}: Props) {
+export function SettingsScreen({}: Props) {
const queryClient = useQueryClient()
const colorMode = useColorMode()
const setColorMode = useSetColorMode()
@@ -731,7 +730,7 @@ export const SettingsScreen = withAuthRequired(function Settings({}: Props) {
)
-})
+}
function EmailConfirmationNotice() {
const pal = usePalette('default')
diff --git a/src/view/shell/createNativeStackNavigatorWithAuth.tsx b/src/view/shell/createNativeStackNavigatorWithAuth.tsx
new file mode 100644
index 0000000000..c7b5d1d2e3
--- /dev/null
+++ b/src/view/shell/createNativeStackNavigatorWithAuth.tsx
@@ -0,0 +1,150 @@
+import * as React from 'react'
+import {View} from 'react-native'
+
+// Based on @react-navigation/native-stack/src/createNativeStackNavigator.ts
+// MIT License
+// Copyright (c) 2017 React Navigation Contributors
+
+import {
+ createNavigatorFactory,
+ EventArg,
+ ParamListBase,
+ StackActionHelpers,
+ StackActions,
+ StackNavigationState,
+ StackRouter,
+ StackRouterOptions,
+ useNavigationBuilder,
+} from '@react-navigation/native'
+import type {
+ NativeStackNavigationEventMap,
+ NativeStackNavigationOptions,
+} from '@react-navigation/native-stack'
+import type {NativeStackNavigatorProps} from '@react-navigation/native-stack/src/types'
+import {NativeStackView} from '@react-navigation/native-stack'
+
+import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
+import {DesktopLeftNav} from './desktop/LeftNav'
+import {DesktopRightNav} from './desktop/RightNav'
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {useOnboardingState} from '#/state/shell'
+import {
+ useLoggedOutView,
+ useLoggedOutViewControls,
+} from '#/state/shell/logged-out'
+import {useSession} from '#/state/session'
+import {isWeb} from 'platform/detection'
+import {LoggedOut} from '../com/auth/LoggedOut'
+import {Onboarding} from '../com/auth/Onboarding'
+
+type NativeStackNavigationOptionsWithAuth = NativeStackNavigationOptions & {
+ requireAuth?: boolean
+}
+
+function NativeStackNavigator({
+ id,
+ initialRouteName,
+ children,
+ screenListeners,
+ screenOptions,
+ ...rest
+}: NativeStackNavigatorProps) {
+ // --- this is copy and pasted from the original native stack navigator ---
+ const {state, descriptors, navigation, NavigationContent} =
+ useNavigationBuilder<
+ StackNavigationState,
+ StackRouterOptions,
+ StackActionHelpers,
+ NativeStackNavigationOptionsWithAuth,
+ NativeStackNavigationEventMap
+ >(StackRouter, {
+ id,
+ initialRouteName,
+ children,
+ screenListeners,
+ screenOptions,
+ })
+ React.useEffect(
+ () =>
+ // @ts-expect-error: there may not be a tab navigator in parent
+ navigation?.addListener?.('tabPress', (e: any) => {
+ const isFocused = navigation.isFocused()
+
+ // Run the operation in the next frame so we're sure all listeners have been run
+ // This is necessary to know if preventDefault() has been called
+ requestAnimationFrame(() => {
+ if (
+ state.index > 0 &&
+ isFocused &&
+ !(e as EventArg<'tabPress', true>).defaultPrevented
+ ) {
+ // When user taps on already focused tab and we're inside the tab,
+ // reset the stack to replicate native behaviour
+ navigation.dispatch({
+ ...StackActions.popToTop(),
+ target: state.key,
+ })
+ }
+ })
+ }),
+ [navigation, state.index, state.key],
+ )
+
+ // --- our custom logic starts here ---
+ const {hasSession} = useSession()
+ const activeRoute = state.routes[state.index]
+ const activeDescriptor = descriptors[activeRoute.key]
+ const activeRouteRequiresAuth = activeDescriptor.options.requireAuth ?? false
+ const onboardingState = useOnboardingState()
+ const {showLoggedOut} = useLoggedOutView()
+ const {setShowLoggedOut} = useLoggedOutViewControls()
+ const {isMobile} = useWebMediaQueries()
+ if (activeRouteRequiresAuth && !hasSession) {
+ return
+ }
+ if (showLoggedOut) {
+ return setShowLoggedOut(false)} />
+ }
+ if (onboardingState.isActive) {
+ return
+ }
+ const newDescriptors: typeof descriptors = {}
+ for (let key in descriptors) {
+ const descriptor = descriptors[key]
+ const requireAuth = descriptor.options.requireAuth ?? false
+ newDescriptors[key] = {
+ ...descriptor,
+ render() {
+ if (requireAuth && !hasSession) {
+ return
+ } else {
+ return descriptor.render()
+ }
+ },
+ }
+ }
+ return (
+
+
+ {isWeb && isMobile && }
+ {isWeb && !isMobile && (
+ <>
+
+
+ >
+ )}
+
+ )
+}
+
+export const createNativeStackNavigatorWithAuth = createNavigatorFactory<
+ StackNavigationState,
+ NativeStackNavigationOptionsWithAuth,
+ NativeStackNavigationEventMap,
+ typeof NativeStackNavigator
+>(NativeStackNavigator)
diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx
index 74477243d6..38da860bdc 100644
--- a/src/view/shell/index.web.tsx
+++ b/src/view/shell/index.web.tsx
@@ -1,7 +1,5 @@
import React, {useEffect} from 'react'
import {View, StyleSheet, TouchableOpacity} from 'react-native'
-import {DesktopLeftNav} from './desktop/LeftNav'
-import {DesktopRightNav} from './desktop/RightNav'
import {ErrorBoundary} from '../com/util/ErrorBoundary'
import {Lightbox} from '../com/lightbox/Lightbox'
import {ModalsContainer} from '../com/modals/Modal'
@@ -11,27 +9,19 @@ import {s, colors} from 'lib/styles'
import {RoutesContainer, FlatNavigator} from '../../Navigation'
import {DrawerContent} from './Drawer'
import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries'
-import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from 'lib/routes/types'
import {useAuxClick} from 'lib/hooks/useAuxClick'
import {t} from '@lingui/macro'
-import {
- useIsDrawerOpen,
- useSetDrawerOpen,
- useOnboardingState,
-} from '#/state/shell'
+import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell'
import {useCloseAllActiveElements} from '#/state/util'
-import {useLoggedOutView} from '#/state/shell/logged-out'
function ShellInner() {
const isDrawerOpen = useIsDrawerOpen()
const setDrawerOpen = useSetDrawerOpen()
- const onboardingState = useOnboardingState()
- const {isDesktop, isMobile} = useWebMediaQueries()
+ const {isDesktop} = useWebMediaQueries()
const navigator = useNavigation()
const closeAllActiveElements = useCloseAllActiveElements()
- const {showLoggedOut} = useLoggedOutView()
useAuxClick()
@@ -42,8 +32,6 @@ function ShellInner() {
return unsubscribe
}, [navigator, closeAllActiveElements])
- const showBottomBar = isMobile && !onboardingState.isActive
- const showSideNavs = !isMobile && !onboardingState.isActive && !showLoggedOut
return (
@@ -51,22 +39,9 @@ function ShellInner() {
-
- {showSideNavs && (
- <>
-
-
- >
- )}
-
-
- {showBottomBar && }
-
-
-
{!isDesktop && isDrawerOpen && (
setDrawerOpen(false)}