diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 89cd112cd7..f305f0d3ce 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -331,6 +331,9 @@ func serve(cctx *cli.Context) error { e.GET("/starter-pack-short/:code", server.WebGeneric) e.GET("/start/:handleOrDID/:rkey", server.WebStarterPack) + // bookmarks + e.GET("/saved", server.WebGeneric) + // ipcc e.GET("/ipcc", server.WebIpCC) diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 7af38105be..cdc0fc220b 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -71,6 +71,7 @@ import {SupportScreen} from '#/view/screens/Support' import {TermsOfServiceScreen} from '#/view/screens/TermsOfService' import {BottomBar} from '#/view/shell/bottom-bar/BottomBar' import {createNativeStackNavigatorWithAuth} from '#/view/shell/createNativeStackNavigatorWithAuth' +import {BookmarksScreen} from '#/screens/Bookmarks' import {SharedPreferencesTesterScreen} from '#/screens/E2E/SharedPreferencesTesterScreen' import HashtagScreen from '#/screens/Hashtag' import {LogScreen} from '#/screens/Log' @@ -600,6 +601,14 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) { requireAuth: true, }} /> + BookmarksScreen} + options={{ + title: title(msg`Saved Posts`), + requireAuth: true, + }} + /> ) } diff --git a/src/lib/hooks/useNavigationTabState.ts b/src/lib/hooks/useNavigationTabState.ts index 2d15bce562..7fd76cb1b9 100644 --- a/src/lib/hooks/useNavigationTabState.ts +++ b/src/lib/hooks/useNavigationTabState.ts @@ -9,6 +9,7 @@ export function useNavigationTabState() { isAtSearch: getTabState(state, 'Search') !== TabState.Outside, // FeedsTab no longer exists, but this check works for `Feeds` screen as well isAtFeeds: getTabState(state, 'Feeds') !== TabState.Outside, + isAtBookmarks: getTabState(state, 'Bookmarks') !== TabState.Outside, isAtNotifications: getTabState(state, 'Notifications') !== TabState.Outside, isAtMyProfile: getTabState(state, 'MyProfile') !== TabState.Outside, diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 1725fdfb4c..4f7054cb3f 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -86,6 +86,7 @@ export type CommonNavigatorParams = { } StarterPackEdit: {rkey?: string} VideoFeed: VideoFeedSourceContext + Bookmarks: undefined } export type BottomTabNavigatorParams = CommonNavigatorParams & { diff --git a/src/routes.ts b/src/routes.ts index 7fc673e2be..1ed913bb29 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -90,4 +90,5 @@ export const router = new Router({ StarterPackShort: '/starter-pack-short/:code', StarterPackWizard: '/starter-pack/create', VideoFeed: '/video-feed', + Bookmarks: '/saved', }) diff --git a/src/screens/Bookmarks.tsx b/src/screens/Bookmarks.tsx new file mode 100644 index 0000000000..47ed719dec --- /dev/null +++ b/src/screens/Bookmarks.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import {Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useFocusEffect} from '@react-navigation/native' + +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {useSetMinimalShellMode} from '#/state/shell' +import * as Layout from '#/components/Layout' + +type Props = NativeStackScreenProps + +export function BookmarksScreen({}: Props) { + const {_} = useLingui() + const setMinimalShellMode = useSetMinimalShellMode() + + useFocusEffect( + React.useCallback(() => { + setMinimalShellMode(false) + }, [setMinimalShellMode]), + ) + + return ( + + + + + + Saved Posts + + + + + + ) +} diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index 79d8a21aef..10817407f2 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -30,6 +30,7 @@ import { Bell_Filled_Corner0_Rounded as BellFilled, Bell_Stroke2_Corner0_Rounded as Bell, } from '#/components/icons/Bell' +import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark' import {BulletList_Stroke2_Corner0_Rounded as List} from '#/components/icons/BulletList' import { Hashtag_Filled_Corner0_Rounded as HashtagFilled, @@ -150,6 +151,7 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { isAtHome, isAtSearch, isAtFeeds, + isAtBookmarks, isAtNotifications, isAtMyProfile, isAtMessages, @@ -231,6 +233,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { setDrawerOpen(false) }, [navigation, setDrawerOpen]) + const onPressBookmarks = React.useCallback(() => { + navigation.navigate('Bookmarks') + setDrawerOpen(false) + }, [navigation, setDrawerOpen]) + const onPressSettings = React.useCallback(() => { navigation.navigate('Settings') setDrawerOpen(false) @@ -292,6 +299,10 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => { /> + void}): React.ReactNode => { } ListsMenuItem = React.memo(ListsMenuItem) +let BookmarksMenuItem = ({ + isActive, + onPress, +}: { + isActive: boolean + onPress: () => void +}): React.ReactNode => { + const {_} = useLingui() + const t = useTheme() + + return ( + + ) : ( + + ) + } + label={_(msg`Saved`)} + onPress={onPress} + /> + ) +} +BookmarksMenuItem = React.memo(BookmarksMenuItem) + let ProfileMenuItem = ({ isActive, onPress, diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index cf1ff84251..c1e429c738 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -40,6 +40,7 @@ import { Bell_Filled_Corner0_Rounded as BellFilled, Bell_Stroke2_Corner0_Rounded as Bell, } from '#/components/icons/Bell' +import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark' import { BulletList_Filled_Corner0_Rounded as ListFilled, BulletList_Stroke2_Corner0_Rounded as List, @@ -743,6 +744,24 @@ export function DesktopLeftNav() { } label={_(msg`Lists`)} /> + + } + iconFilled={ + + } + label={_(msg`Saved`)} + />