diff --git a/src/mobile/ios/ReadLess.xcodeproj/project.pbxproj b/src/mobile/ios/ReadLess.xcodeproj/project.pbxproj index ad09db1ae..6ae7a47d4 100644 --- a/src/mobile/ios/ReadLess.xcodeproj/project.pbxproj +++ b/src/mobile/ios/ReadLess.xcodeproj/project.pbxproj @@ -1722,7 +1722,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.16.3; + MARKETING_VERSION = 1.16.4; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1763,7 +1763,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.16.3; + MARKETING_VERSION = 1.16.4; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/src/mobile/ios/ReadLess.xcworkspace/contents.xcworkspacedata b/src/mobile/ios/ReadLess.xcworkspace/contents.xcworkspacedata index 2e19b8137..4aa62d129 100644 --- a/src/mobile/ios/ReadLess.xcworkspace/contents.xcworkspacedata +++ b/src/mobile/ios/ReadLess.xcworkspace/contents.xcworkspacedata @@ -1,9 +1,6 @@ - - diff --git a/src/mobile/src/components/common/RoutedScreen.tsx b/src/mobile/src/components/common/RoutedScreen.tsx index 443206b10..22a42ac7a 100644 --- a/src/mobile/src/components/common/RoutedScreen.tsx +++ b/src/mobile/src/components/common/RoutedScreen.tsx @@ -18,7 +18,7 @@ export function RoutedScreen({ ...props }: ScreenProps) { Linking.getInitialURL().then((url) => { if (url) { setPreference('loadedInitialUrl', true); - router({ navigator: navigation?.getParent('Stack'), url }); + router({ stackNav: navigation?.getParent('stackNav'), url }); } }); } diff --git a/src/mobile/src/components/common/Screen.tsx b/src/mobile/src/components/common/Screen.tsx index 173eaf273..bbf22b140 100644 --- a/src/mobile/src/components/common/Screen.tsx +++ b/src/mobile/src/components/common/Screen.tsx @@ -16,7 +16,7 @@ export function Screen({ children, safeArea = true, ...props -}: ScreenViewProps) { +}: ScreenProps) { const theme = useTheme(); return ( diff --git a/src/mobile/src/hooks/useNavigation.tsx b/src/mobile/src/hooks/useNavigation.tsx index 931891252..98fb4a7e2 100644 --- a/src/mobile/src/hooks/useNavigation.tsx +++ b/src/mobile/src/hooks/useNavigation.tsx @@ -9,48 +9,50 @@ import { ReadingFormat, } from '~/api'; import { SessionContext } from '~/contexts'; -import { RoutingParams } from '~/screens'; +import { NavigationID, RoutingParams } from '~/screens'; import { readingFormat, usePlatformTools } from '~/utils'; +export type Navigation = NativeStackNavigationProp; + export function useNavigation() { const { emitEvent } = usePlatformTools(); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const navigation = useRNNavigation>(); + const navigation = useRNNavigation(); const { preferredReadingFormat, setPreference } = React.useContext(SessionContext); - const navigate = React.useCallback((route: R, params?: RoutingParams[R], navigator?: any) => { + const navigate = React.useCallback((route: R, params?: RoutingParams[R], stackNav?: Navigation) => { emitEvent('navigate', route); // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (navigator?.push ?? (navigation as any).push ?? navigation.navigate)(route, params as RoutingParams[R]); + return (stackNav?.push ?? (navigation as any).push ?? navigation.navigate)(route, params as RoutingParams[R]); }, [emitEvent, navigation]); - const search = React.useCallback((params: RoutingParams['search'], navigator?: any) => { + const search = React.useCallback((params: RoutingParams['search'], stackNav?: Navigation) => { const prefilter = params.prefilter; if (!prefilter) { return; } setPreference('searchHistory', (prev) => Array.from(new Set([prefilter, ...(prev ?? [])])).slice(0, 10)); - navigate('search', params, navigator); + navigate('search', params, stackNav); }, [navigate, setPreference]); - const openSummary = React.useCallback((props: RoutingParams['summary'], navigator?: any) => { + const openSummary = React.useCallback((props: RoutingParams['summary'], stackNav?: Navigation) => { navigate('summary', { ...props, initialFormat: props.initialFormat ?? preferredReadingFormat ?? ReadingFormat.Bullets, - }, navigator); + }, stackNav); }, [navigate, preferredReadingFormat]); - const openPublisher = React.useCallback((publisher: PublicPublisherAttributes) => { - navigate('publisher', { publisher }); + const openPublisher = React.useCallback((publisher: PublicPublisherAttributes, stackNav?: Navigation) => { + navigate('publisher', { publisher }, stackNav); }, [navigate]); - const openCategory = React.useCallback((category: PublicCategoryAttributes) => { - navigate('category', { category }); + const openCategory = React.useCallback((category: PublicCategoryAttributes, stackNav?: Navigation) => { + navigate('category', { category }, stackNav); }, [navigate]); - const router = React.useCallback(({ url, navigator }: { url: string, navigator?: any }) => { + const router = React.useCallback(({ url, stackNav }: { url: string, stackNav?: Navigation }) => { // http://localhost:6969/read/?s=158&f=casual // https://dev.readless.ai/read/?s=158&f=casual // https://www.readless.ai/read/?s=4070&f=bullets @@ -71,7 +73,7 @@ export function useNavigation() { return; } const initialFormat = readingFormat(params['f']); - openSummary({ initialFormat, summary }, navigator); + openSummary({ initialFormat, summary }, stackNav); } else if (route === 'top') { navigate('topStories'); @@ -81,23 +83,25 @@ export function useNavigation() { if (!filter) { return; } - search({ prefilter: filter }, navigator); + search({ prefilter: filter }, stackNav); } else if (route === 'publisher') { const publisher = params['publisher']?.trim(); if (!publisher) { return; } - openPublisher({ name: publisher }); + openPublisher({ displayName: '', name: publisher }, stackNav); } else if (route === 'category') { const category = params['category']?.trim(); if (!category) { return; } - openCategory({ name: category }); + openCategory({ + displayName: '', icon: '', name: category, + }, stackNav); } - }, [navigate, navigation, search, openSummary, openPublisher, openCategory]); + }, [navigate, search, openSummary, openPublisher, openCategory]); return { navigate, diff --git a/src/mobile/src/screens/BookmarksScreen.tsx b/src/mobile/src/screens/BookmarksScreen.tsx index 64d7684f7..e470839a4 100644 --- a/src/mobile/src/screens/BookmarksScreen.tsx +++ b/src/mobile/src/screens/BookmarksScreen.tsx @@ -21,11 +21,11 @@ import { import { SessionContext } from '~/contexts'; import { useApiClient } from '~/hooks'; import { strings } from '~/locales'; -import { ScreenProps } from '~/screens'; +import { ScreenComponent } from '~/screens'; const pageSize = 10; -export function BookmarksScreen({ navigation }: ScreenProps<'bookmarks'>) { +export function BookmarksScreen({ navigation }: ScreenComponent<'bookmarks'>) { const { bookmarkedSummaries, @@ -58,7 +58,7 @@ export function BookmarksScreen({ navigation }: ScreenProps<'bookmarks'>) { headerRight: () => undefined, headerTitle: `${strings.bookmarks_header} (${bookmarkCount})`, }); - }, [bookmarkCount, navigation])); + }, [bookmarkCount, navigation, viewFeature])); return ( diff --git a/src/mobile/src/screens/CategoryScreen.tsx b/src/mobile/src/screens/CategoryScreen.tsx index 6733a9ef0..7684e2441 100644 --- a/src/mobile/src/screens/CategoryScreen.tsx +++ b/src/mobile/src/screens/CategoryScreen.tsx @@ -13,12 +13,12 @@ import { ChannelIcon } from '~/components/post/ChannelIcon'; import { SessionContext } from '~/contexts'; import { useApiClient } from '~/hooks'; import { strings } from '~/locales'; -import { ScreenProps } from '~/screens'; +import { ScreenComponent } from '~/screens'; export function CategoryScreen({ route, navigation, -}: ScreenProps<'category'>) { +}: ScreenComponent<'category'>) { const { getSummaries } = useApiClient(); diff --git a/src/mobile/src/screens/PublisherScreen.tsx b/src/mobile/src/screens/PublisherScreen.tsx index c03ea86ee..8ef3d4b35 100644 --- a/src/mobile/src/screens/PublisherScreen.tsx +++ b/src/mobile/src/screens/PublisherScreen.tsx @@ -13,12 +13,12 @@ import { ChannelIcon } from '~/components/post/ChannelIcon'; import { SessionContext } from '~/contexts'; import { useApiClient } from '~/hooks'; import { strings } from '~/locales'; -import { ScreenProps } from '~/screens'; +import { ScreenComponent } from '~/screens'; export function PublisherScreen({ route, navigation, -}: ScreenProps<'publisher'>) { +}: ScreenComponent<'publisher'>) { const { getSummaries } = useApiClient(); diff --git a/src/mobile/src/screens/RecapScreen.tsx b/src/mobile/src/screens/RecapScreen.tsx index fdeb472a2..be9b771da 100644 --- a/src/mobile/src/screens/RecapScreen.tsx +++ b/src/mobile/src/screens/RecapScreen.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Recap, RoutedScreen } from '~/components'; -import { ScreenProps } from '~/screens'; +import { ScreenComponent } from '~/screens'; -export function RecapScreen({ route }: ScreenProps<'recap'>) { +export function RecapScreen({ route }: ScreenComponent<'recap'>) { const recap = React.useMemo(() => route?.params?.recap, [route]); return ( diff --git a/src/mobile/src/screens/SearchScreen.tsx b/src/mobile/src/screens/SearchScreen.tsx index 3a1d0d191..01c6d143e 100644 --- a/src/mobile/src/screens/SearchScreen.tsx +++ b/src/mobile/src/screens/SearchScreen.tsx @@ -2,12 +2,12 @@ import React from 'react'; import { RoutedScreen, SummaryList } from '~/components'; import { useApiClient } from '~/hooks'; -import { ScreenProps } from '~/screens'; +import { ScreenComponent } from '~/screens'; export function SearchScreen({ route, navigation: _navigation, -}: ScreenProps<'search'>) { +}: ScreenComponent<'search'>) { const { getSummaries } = useApiClient(); return ( diff --git a/src/mobile/src/screens/StatsScreen.tsx b/src/mobile/src/screens/StatsScreen.tsx index e850be169..96016bce7 100644 --- a/src/mobile/src/screens/StatsScreen.tsx +++ b/src/mobile/src/screens/StatsScreen.tsx @@ -9,12 +9,12 @@ import { View, } from '~/components'; import { SessionContext } from '~/core'; -import { ScreenProps } from '~/screens'; +import { ScreenComponent } from '~/screens'; export function StatsScreen({ route: _route, navigation: _navigation, -}: ScreenProps<'stats'>) { +}: ScreenComponent<'stats'>) { const { setPreference } = React.useContext(SessionContext); return ( diff --git a/src/mobile/src/screens/SummaryScreen.tsx b/src/mobile/src/screens/SummaryScreen.tsx index 691bb3d4e..cb96fdb14 100644 --- a/src/mobile/src/screens/SummaryScreen.tsx +++ b/src/mobile/src/screens/SummaryScreen.tsx @@ -19,12 +19,12 @@ import { import { SessionContext } from '~/contexts'; import { useApiClient } from '~/hooks'; import { strings } from '~/locales'; -import { ScreenProps } from '~/screens'; +import { ScreenComponent } from '~/screens'; export function SummaryScreen({ route, navigation, -}: ScreenProps<'summary'>) { +}: ScreenComponent<'summary'>) { const { getSummary, interactWithSummary } = useApiClient(); const { preferredReadingFormat } = React.useContext(SessionContext); diff --git a/src/mobile/src/screens/home/HomeScreen.tsx b/src/mobile/src/screens/home/HomeScreen.tsx index c0f42d3a3..e1d77fafd 100644 --- a/src/mobile/src/screens/home/HomeScreen.tsx +++ b/src/mobile/src/screens/home/HomeScreen.tsx @@ -19,7 +19,7 @@ import { LiveFeedTab, OldNewsTab, RoutingParams, - ScreenProps, + ScreenComponent, TopStoriesTab, YourNewsTab, } from '~/screens'; @@ -29,7 +29,7 @@ const Tab = createMaterialTopTabNavigator(); export function HomeScreen({ route: _route, navigation: _navigation, -}: ScreenProps<'home'>) { +}: ScreenComponent<'home'>) { const theme = useTheme(); const { followCount } = React.useContext(SessionContext); diff --git a/src/mobile/src/screens/home/Tabs.tsx b/src/mobile/src/screens/home/Tabs.tsx index d032bc4c5..289926c35 100644 --- a/src/mobile/src/screens/home/Tabs.tsx +++ b/src/mobile/src/screens/home/Tabs.tsx @@ -11,12 +11,12 @@ import { } from '~/components'; import { SessionContext, useApiClient } from '~/core'; import { strings } from '~/locales'; -import { ScreenProps } from '~/screens'; +import { ScreenComponent } from '~/screens'; export function OldNewsTab({ route: _route, navigation: _navigation, -}: ScreenProps<'oldNews'>) { +}: ScreenComponent<'oldNews'>) { return ( ) { +}: ScreenComponent<'yourNews'>) { const { getSummaries } = useApiClient(); const { followFilter } = React.useContext(SessionContext); const [filter, setFilter] = React.useState(followFilter); @@ -53,7 +53,7 @@ export function YourNewsTab({ export function TopStoriesTab({ route: _route, navigation: _navigation, -}: ScreenProps<'topStories'>) { +}: ScreenComponent<'topStories'>) { const { getTopStories } = useApiClient(); return ( ) { +}: ScreenComponent<'liveFeed'>) { const { getSummaries } = useApiClient(); return ( ) { +export function SettingsScreen({ navigation }: ScreenComponent<'settings'>) { React.useEffect(() => { navigation?.setOptions({ headerRight: () => undefined }); }, [navigation]); diff --git a/src/mobile/src/screens/types.ts b/src/mobile/src/screens/types.ts index 121d395a1..c7ca4101b 100644 --- a/src/mobile/src/screens/types.ts +++ b/src/mobile/src/screens/types.ts @@ -1,10 +1,6 @@ import React from 'react'; -import { - LinkingOptions, - ParamListBase, - RouteProp, -} from '@react-navigation/native'; +import { LinkingOptions, RouteProp } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { @@ -15,6 +11,8 @@ import { RecapAttributes, } from '~/api'; +export type NavigationID = 'stackNav' | 'leftDrawerNav' | 'rightDrawerNav'; + export type RoutingParams = { // main default: undefined; @@ -82,19 +80,7 @@ export const NAVIGATION_LINKING_OPTIONS: LinkingOptions = { ], }; -export type ScreenComponentType< - ParamList extends ParamListBase, - RouteName extends keyof ParamList -> = - | React.ComponentType<{ - route: RouteProp; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - navigation: any; - }> - // eslint-disable-next-line @typescript-eslint/ban-types - | React.ComponentType<{}>; - -export type ScreenProps = { +export type ScreenComponent = { name?: Path; component?: C; icon?: string;