Revert "[iPad support] Custom tab navigator for native tablet, with side nav …"
This reverts commit 842f0c96a3.
This commit is contained in:
+94
-254
@@ -1,6 +1,5 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import {JSX} from 'react/jsx-runtime'
|
import {JSX} from 'react/jsx-runtime'
|
||||||
import {View} from 'react-native'
|
|
||||||
import {i18n, MessageDescriptor} from '@lingui/core'
|
import {i18n, MessageDescriptor} from '@lingui/core'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {
|
import {
|
||||||
@@ -16,12 +15,11 @@ import {
|
|||||||
StackActions,
|
StackActions,
|
||||||
} from '@react-navigation/native'
|
} from '@react-navigation/native'
|
||||||
|
|
||||||
import {init as initAnalytics} from '#/lib/analytics/analytics'
|
import {timeout} from 'lib/async/timeout'
|
||||||
import {timeout} from '#/lib/async/timeout'
|
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
||||||
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useWebScrollRestoration} from '#/lib/hooks/useWebScrollRestoration'
|
import {buildStateObject} from 'lib/routes/helpers'
|
||||||
import {buildStateObject} from '#/lib/routes/helpers'
|
import {
|
||||||
import type {
|
|
||||||
AllNavigatorParams,
|
AllNavigatorParams,
|
||||||
BottomTabNavigatorParams,
|
BottomTabNavigatorParams,
|
||||||
FeedsTabNavigatorParams,
|
FeedsTabNavigatorParams,
|
||||||
@@ -30,60 +28,59 @@ import type {
|
|||||||
MyProfileTabNavigatorParams,
|
MyProfileTabNavigatorParams,
|
||||||
NotificationsTabNavigatorParams,
|
NotificationsTabNavigatorParams,
|
||||||
SearchTabNavigatorParams,
|
SearchTabNavigatorParams,
|
||||||
SideTabNavigatorParams,
|
} from 'lib/routes/types'
|
||||||
} from '#/lib/routes/types'
|
import {RouteParams, State} from 'lib/routes/types'
|
||||||
import {RouteParams, State} from '#/lib/routes/types'
|
import {bskyTitle} from 'lib/strings/headings'
|
||||||
import {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig'
|
import {isAndroid, isNative} from 'platform/detection'
|
||||||
import {bskyTitle} from '#/lib/strings/headings'
|
|
||||||
import {isAndroid, isNative, isNativeTablet} from '#/platform/detection'
|
|
||||||
import {useModalControls} from '#/state/modals'
|
|
||||||
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {
|
|
||||||
setEmailConfirmationRequested,
|
|
||||||
shouldRequestEmailConfirmation,
|
|
||||||
} from '#/state/shell/reminders'
|
|
||||||
import {AppPasswords} from '#/view/screens/AppPasswords'
|
|
||||||
import {CommunityGuidelinesScreen} from '#/view/screens/CommunityGuidelines'
|
|
||||||
import {CopyrightPolicyScreen} from '#/view/screens/CopyrightPolicy'
|
|
||||||
import {DebugModScreen} from '#/view/screens/DebugMod'
|
|
||||||
import {FeedsScreen} from '#/view/screens/Feeds'
|
|
||||||
import {HomeScreen} from '#/view/screens/Home'
|
|
||||||
import {LanguageSettingsScreen} from '#/view/screens/LanguageSettings'
|
|
||||||
import {ListsScreen} from '#/view/screens/Lists'
|
|
||||||
import {LogScreen} from '#/view/screens/Log'
|
|
||||||
import {ModerationBlockedAccounts} from '#/view/screens/ModerationBlockedAccounts'
|
|
||||||
import {ModerationModlistsScreen} from '#/view/screens/ModerationModlists'
|
|
||||||
import {ModerationMutedAccounts} from '#/view/screens/ModerationMutedAccounts'
|
|
||||||
import {NotFoundScreen} from '#/view/screens/NotFound'
|
|
||||||
import {NotificationsScreen} from '#/view/screens/Notifications'
|
|
||||||
import {PostLikedByScreen} from '#/view/screens/PostLikedBy'
|
|
||||||
import {PostRepostedByScreen} from '#/view/screens/PostRepostedBy'
|
|
||||||
import {PostThreadScreen} from '#/view/screens/PostThread'
|
|
||||||
import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbeds'
|
import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbeds'
|
||||||
import {PreferencesFollowingFeed} from '#/view/screens/PreferencesFollowingFeed'
|
import {AppPasswords} from 'view/screens/AppPasswords'
|
||||||
import {PreferencesThreads} from '#/view/screens/PreferencesThreads'
|
import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts'
|
||||||
import {PrivacyPolicyScreen} from '#/view/screens/PrivacyPolicy'
|
import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts'
|
||||||
import {ProfileScreen} from '#/view/screens/Profile'
|
import {PreferencesFollowingFeed} from 'view/screens/PreferencesFollowingFeed'
|
||||||
import {ProfileFeedScreen} from '#/view/screens/ProfileFeed'
|
import {PreferencesThreads} from 'view/screens/PreferencesThreads'
|
||||||
import {ProfileFeedLikedByScreen} from '#/view/screens/ProfileFeedLikedBy'
|
import {SavedFeeds} from 'view/screens/SavedFeeds'
|
||||||
import {ProfileFollowersScreen} from '#/view/screens/ProfileFollowers'
|
|
||||||
import {ProfileFollowsScreen} from '#/view/screens/ProfileFollows'
|
|
||||||
import {ProfileListScreen} from '#/view/screens/ProfileList'
|
|
||||||
import {SavedFeeds} from '#/view/screens/SavedFeeds'
|
|
||||||
import {SearchScreen} from '#/view/screens/Search'
|
|
||||||
import {SettingsScreen} from '#/view/screens/Settings'
|
|
||||||
import {Storybook} from '#/view/screens/Storybook'
|
|
||||||
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 {LeftNav} from '#/view/shell/desktop/LeftNav'
|
|
||||||
import HashtagScreen from '#/screens/Hashtag'
|
import HashtagScreen from '#/screens/Hashtag'
|
||||||
import {ModerationScreen} from '#/screens/Moderation'
|
import {ModerationScreen} from '#/screens/Moderation'
|
||||||
import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy'
|
import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {init as initAnalytics} from './lib/analytics/analytics'
|
||||||
import {router} from '#/routes'
|
import {useWebScrollRestoration} from './lib/hooks/useWebScrollRestoration'
|
||||||
|
import {attachRouteToLogEvents, logEvent} from './lib/statsig/statsig'
|
||||||
|
import {router} from './routes'
|
||||||
|
import {useModalControls} from './state/modals'
|
||||||
|
import {useUnreadNotifications} from './state/queries/notifications/unread'
|
||||||
|
import {useSession} from './state/session'
|
||||||
|
import {
|
||||||
|
setEmailConfirmationRequested,
|
||||||
|
shouldRequestEmailConfirmation,
|
||||||
|
} from './state/shell/reminders'
|
||||||
|
import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines'
|
||||||
|
import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy'
|
||||||
|
import {DebugModScreen} from './view/screens/DebugMod'
|
||||||
|
import {FeedsScreen} from './view/screens/Feeds'
|
||||||
|
import {HomeScreen} from './view/screens/Home'
|
||||||
|
import {LanguageSettingsScreen} from './view/screens/LanguageSettings'
|
||||||
|
import {ListsScreen} from './view/screens/Lists'
|
||||||
|
import {LogScreen} from './view/screens/Log'
|
||||||
|
import {ModerationModlistsScreen} from './view/screens/ModerationModlists'
|
||||||
|
import {NotFoundScreen} from './view/screens/NotFound'
|
||||||
|
import {NotificationsScreen} from './view/screens/Notifications'
|
||||||
|
import {PostLikedByScreen} from './view/screens/PostLikedBy'
|
||||||
|
import {PostRepostedByScreen} from './view/screens/PostRepostedBy'
|
||||||
|
import {PostThreadScreen} from './view/screens/PostThread'
|
||||||
|
import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy'
|
||||||
|
import {ProfileScreen} from './view/screens/Profile'
|
||||||
|
import {ProfileFeedScreen} from './view/screens/ProfileFeed'
|
||||||
|
import {ProfileFeedLikedByScreen} from './view/screens/ProfileFeedLikedBy'
|
||||||
|
import {ProfileFollowersScreen} from './view/screens/ProfileFollowers'
|
||||||
|
import {ProfileFollowsScreen} from './view/screens/ProfileFollows'
|
||||||
|
import {ProfileListScreen} from './view/screens/ProfileList'
|
||||||
|
import {SearchScreen} from './view/screens/Search'
|
||||||
|
import {SettingsScreen} from './view/screens/Settings'
|
||||||
|
import {Storybook} from './view/screens/Storybook'
|
||||||
|
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'
|
||||||
|
|
||||||
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
||||||
|
|
||||||
@@ -94,23 +91,15 @@ const NotificationsTab =
|
|||||||
createNativeStackNavigatorWithAuth<NotificationsTabNavigatorParams>()
|
createNativeStackNavigatorWithAuth<NotificationsTabNavigatorParams>()
|
||||||
const MyProfileTab =
|
const MyProfileTab =
|
||||||
createNativeStackNavigatorWithAuth<MyProfileTabNavigatorParams>()
|
createNativeStackNavigatorWithAuth<MyProfileTabNavigatorParams>()
|
||||||
const ListsTab = createNativeStackNavigatorWithAuth<FlatNavigatorParams>()
|
|
||||||
const ModerationTab = createNativeStackNavigatorWithAuth<FlatNavigatorParams>()
|
|
||||||
const SettingsTab = createNativeStackNavigatorWithAuth<FlatNavigatorParams>()
|
|
||||||
const Flat = createNativeStackNavigatorWithAuth<FlatNavigatorParams>()
|
const Flat = createNativeStackNavigatorWithAuth<FlatNavigatorParams>()
|
||||||
|
const Tab = createBottomTabNavigator<BottomTabNavigatorParams>()
|
||||||
const MobileTab = createBottomTabNavigator<BottomTabNavigatorParams>()
|
|
||||||
const TabletTab = createBottomTabNavigator<SideTabNavigatorParams>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These "common screens" are reused across stacks.
|
* These "common screens" are reused across stacks.
|
||||||
*/
|
*/
|
||||||
function commonScreens(
|
function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
||||||
Stack: typeof HomeTab,
|
|
||||||
opts?: {unreadCountLabel?: string},
|
|
||||||
) {
|
|
||||||
const title = (page: MessageDescriptor) =>
|
const title = (page: MessageDescriptor) =>
|
||||||
bskyTitle(i18n._(page), opts?.unreadCountLabel)
|
bskyTitle(i18n._(page), unreadCountLabel)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -119,6 +108,16 @@ function commonScreens(
|
|||||||
getComponent={() => NotFoundScreen}
|
getComponent={() => NotFoundScreen}
|
||||||
options={{title: title(msg`Not Found`)}}
|
options={{title: title(msg`Not Found`)}}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="Lists"
|
||||||
|
component={ListsScreen}
|
||||||
|
options={{title: title(msg`Lists`), requireAuth: true}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="Moderation"
|
||||||
|
getComponent={() => ModerationScreen}
|
||||||
|
options={{title: title(msg`Moderation`), requireAuth: true}}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="ModerationModlists"
|
name="ModerationModlists"
|
||||||
getComponent={() => ModerationModlistsScreen}
|
getComponent={() => ModerationModlistsScreen}
|
||||||
@@ -134,6 +133,11 @@ function commonScreens(
|
|||||||
getComponent={() => ModerationBlockedAccounts}
|
getComponent={() => ModerationBlockedAccounts}
|
||||||
options={{title: title(msg`Blocked Accounts`), requireAuth: true}}
|
options={{title: title(msg`Blocked Accounts`), requireAuth: true}}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="Settings"
|
||||||
|
getComponent={() => SettingsScreen}
|
||||||
|
options={{title: title(msg`Settings`), requireAuth: true}}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="LanguageSettings"
|
name="LanguageSettings"
|
||||||
getComponent={() => LanguageSettingsScreen}
|
getComponent={() => LanguageSettingsScreen}
|
||||||
@@ -143,7 +147,7 @@ function commonScreens(
|
|||||||
name="Profile"
|
name="Profile"
|
||||||
getComponent={() => ProfileScreen}
|
getComponent={() => ProfileScreen}
|
||||||
options={({route}) => ({
|
options={({route}) => ({
|
||||||
title: bskyTitle(`@${route.params.name}`, opts?.unreadCountLabel),
|
title: bskyTitle(`@${route.params.name}`, unreadCountLabel),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -277,38 +281,15 @@ function commonScreens(
|
|||||||
getComponent={() => HashtagScreen}
|
getComponent={() => HashtagScreen}
|
||||||
options={{title: title(msg`Hashtag`)}}
|
options={{title: title(msg`Hashtag`)}}
|
||||||
/>
|
/>
|
||||||
{!isNativeTablet && (
|
|
||||||
<>
|
|
||||||
<Stack.Screen
|
|
||||||
name="Moderation"
|
|
||||||
getComponent={() => ModerationScreen}
|
|
||||||
options={{title: title(msg`Moderation`), requireAuth: true}}
|
|
||||||
/>
|
|
||||||
<Stack.Screen
|
|
||||||
name="Lists"
|
|
||||||
component={ListsScreen}
|
|
||||||
options={{title: title(msg`Lists`), requireAuth: true}}
|
|
||||||
/>
|
|
||||||
<Stack.Screen
|
|
||||||
name="Settings"
|
|
||||||
getComponent={() => SettingsScreen}
|
|
||||||
options={{title: title(msg`Settings`), requireAuth: true}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TabsNavigator is used by native mobile to represent the routes
|
* The TabsNavigator is used by native mobile to represent the routes
|
||||||
* in the distinct tab-stacks with a different root screen on each.
|
* in 3 distinct tab-stacks with a different root screen on each.
|
||||||
*/
|
*/
|
||||||
function TabsNavigator() {
|
function TabsNavigator() {
|
||||||
return isNativeTablet ? <TabNavigatorTablet /> : <TabsNavigatorMobile />
|
|
||||||
}
|
|
||||||
|
|
||||||
function TabsNavigatorMobile() {
|
|
||||||
const tabBar = React.useCallback(
|
const tabBar = React.useCallback(
|
||||||
(props: JSX.IntrinsicAttributes & BottomTabBarProps) => (
|
(props: JSX.IntrinsicAttributes & BottomTabBarProps) => (
|
||||||
<BottomBar {...props} />
|
<BottomBar {...props} />
|
||||||
@@ -317,88 +298,28 @@ function TabsNavigatorMobile() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MobileTab.Navigator
|
<Tab.Navigator
|
||||||
initialRouteName="HomeTab"
|
initialRouteName="HomeTab"
|
||||||
backBehavior="initialRoute"
|
backBehavior="initialRoute"
|
||||||
screenOptions={{headerShown: false, lazy: true}}
|
screenOptions={{headerShown: false, lazy: true}}
|
||||||
tabBar={tabBar}>
|
tabBar={tabBar}>
|
||||||
<MobileTab.Screen name="HomeTab" getComponent={() => HomeTabNavigator} />
|
<Tab.Screen name="HomeTab" getComponent={() => HomeTabNavigator} />
|
||||||
<MobileTab.Screen
|
<Tab.Screen name="SearchTab" getComponent={() => SearchTabNavigator} />
|
||||||
name="SearchTab"
|
<Tab.Screen name="FeedsTab" getComponent={() => FeedsTabNavigator} />
|
||||||
getComponent={() => SearchTabNavigator}
|
<Tab.Screen
|
||||||
/>
|
|
||||||
<MobileTab.Screen
|
|
||||||
name="FeedsTab"
|
|
||||||
getComponent={() => FeedsTabNavigator}
|
|
||||||
/>
|
|
||||||
<MobileTab.Screen
|
|
||||||
name="NotificationsTab"
|
name="NotificationsTab"
|
||||||
getComponent={() => NotificationsTabNavigator}
|
getComponent={() => NotificationsTabNavigator}
|
||||||
/>
|
/>
|
||||||
<MobileTab.Screen
|
<Tab.Screen
|
||||||
name="MyProfileTab"
|
name="MyProfileTab"
|
||||||
getComponent={() => MyProfileTabNavigator}
|
getComponent={() => MyProfileTabNavigator}
|
||||||
/>
|
/>
|
||||||
</MobileTab.Navigator>
|
</Tab.Navigator>
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The TabNavigatorTablet is used by native tablet to represent the routes
|
|
||||||
* in all the extra tabs shown in the tablet left nav.
|
|
||||||
*/
|
|
||||||
function TabNavigatorTablet() {
|
|
||||||
// we can't style the wrapper component of the tab navigator
|
|
||||||
// to set it to flex_row, so put the left nav outside of the tab navigator
|
|
||||||
const tabBar = React.useCallback(() => null, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={[a.flex_row, a.flex_1]}>
|
|
||||||
<LeftNav />
|
|
||||||
<TabletTab.Navigator
|
|
||||||
initialRouteName="HomeTab"
|
|
||||||
backBehavior="initialRoute"
|
|
||||||
screenOptions={{headerShown: false, lazy: true}}
|
|
||||||
tabBar={tabBar}>
|
|
||||||
<TabletTab.Screen
|
|
||||||
name="HomeTab"
|
|
||||||
getComponent={() => HomeTabNavigator}
|
|
||||||
/>
|
|
||||||
<TabletTab.Screen
|
|
||||||
name="SearchTab"
|
|
||||||
getComponent={() => SearchTabNavigator}
|
|
||||||
/>
|
|
||||||
<TabletTab.Screen
|
|
||||||
name="FeedsTab"
|
|
||||||
getComponent={() => FeedsTabNavigator}
|
|
||||||
/>
|
|
||||||
<TabletTab.Screen
|
|
||||||
name="NotificationsTab"
|
|
||||||
getComponent={() => NotificationsTabNavigator}
|
|
||||||
/>
|
|
||||||
<TabletTab.Screen
|
|
||||||
name="ListsTab"
|
|
||||||
getComponent={() => ListsTabNavigator}
|
|
||||||
/>
|
|
||||||
<TabletTab.Screen
|
|
||||||
name="ModerationTab"
|
|
||||||
getComponent={() => ModerationTabNavigator}
|
|
||||||
/>
|
|
||||||
<TabletTab.Screen
|
|
||||||
name="MyProfileTab"
|
|
||||||
getComponent={() => MyProfileTabNavigator}
|
|
||||||
/>
|
|
||||||
<TabletTab.Screen
|
|
||||||
name="SettingsTab"
|
|
||||||
getComponent={() => SettingsTabNavigator}
|
|
||||||
/>
|
|
||||||
</TabletTab.Navigator>
|
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function HomeTabNavigator() {
|
function HomeTabNavigator() {
|
||||||
const t = useTheme()
|
const pal = usePalette('default')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HomeTab.Navigator
|
<HomeTab.Navigator
|
||||||
@@ -408,7 +329,7 @@ function HomeTabNavigator() {
|
|||||||
fullScreenGestureEnabled: true,
|
fullScreenGestureEnabled: true,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
animationDuration: 250,
|
animationDuration: 250,
|
||||||
contentStyle: t.atoms.bg,
|
contentStyle: pal.view,
|
||||||
}}>
|
}}>
|
||||||
<HomeTab.Screen
|
<HomeTab.Screen
|
||||||
name="Home"
|
name="Home"
|
||||||
@@ -421,7 +342,7 @@ function HomeTabNavigator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SearchTabNavigator() {
|
function SearchTabNavigator() {
|
||||||
const t = useTheme()
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<SearchTab.Navigator
|
<SearchTab.Navigator
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
@@ -430,7 +351,7 @@ function SearchTabNavigator() {
|
|||||||
fullScreenGestureEnabled: true,
|
fullScreenGestureEnabled: true,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
animationDuration: 250,
|
animationDuration: 250,
|
||||||
contentStyle: t.atoms.bg,
|
contentStyle: pal.view,
|
||||||
}}>
|
}}>
|
||||||
<SearchTab.Screen name="Search" getComponent={() => SearchScreen} />
|
<SearchTab.Screen name="Search" getComponent={() => SearchScreen} />
|
||||||
{commonScreens(SearchTab as typeof HomeTab)}
|
{commonScreens(SearchTab as typeof HomeTab)}
|
||||||
@@ -439,7 +360,7 @@ function SearchTabNavigator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FeedsTabNavigator() {
|
function FeedsTabNavigator() {
|
||||||
const t = useTheme()
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<FeedsTab.Navigator
|
<FeedsTab.Navigator
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
@@ -448,7 +369,7 @@ function FeedsTabNavigator() {
|
|||||||
fullScreenGestureEnabled: true,
|
fullScreenGestureEnabled: true,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
animationDuration: 250,
|
animationDuration: 250,
|
||||||
contentStyle: t.atoms.bg,
|
contentStyle: pal.view,
|
||||||
}}>
|
}}>
|
||||||
<FeedsTab.Screen
|
<FeedsTab.Screen
|
||||||
name="Feeds"
|
name="Feeds"
|
||||||
@@ -461,7 +382,7 @@ function FeedsTabNavigator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function NotificationsTabNavigator() {
|
function NotificationsTabNavigator() {
|
||||||
const t = useTheme()
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<NotificationsTab.Navigator
|
<NotificationsTab.Navigator
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
@@ -470,7 +391,7 @@ function NotificationsTabNavigator() {
|
|||||||
fullScreenGestureEnabled: true,
|
fullScreenGestureEnabled: true,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
animationDuration: 250,
|
animationDuration: 250,
|
||||||
contentStyle: t.atoms.bg,
|
contentStyle: pal.view,
|
||||||
}}>
|
}}>
|
||||||
<NotificationsTab.Screen
|
<NotificationsTab.Screen
|
||||||
name="Notifications"
|
name="Notifications"
|
||||||
@@ -483,7 +404,7 @@ function NotificationsTabNavigator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function MyProfileTabNavigator() {
|
function MyProfileTabNavigator() {
|
||||||
const t = useTheme()
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<MyProfileTab.Navigator
|
<MyProfileTab.Navigator
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
@@ -492,7 +413,7 @@ function MyProfileTabNavigator() {
|
|||||||
fullScreenGestureEnabled: true,
|
fullScreenGestureEnabled: true,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
animationDuration: 250,
|
animationDuration: 250,
|
||||||
contentStyle: t.atoms.bg,
|
contentStyle: pal.view,
|
||||||
}}>
|
}}>
|
||||||
<MyProfileTab.Screen
|
<MyProfileTab.Screen
|
||||||
// @ts-ignore // TODO: fix this broken type in ProfileScreen
|
// @ts-ignore // TODO: fix this broken type in ProfileScreen
|
||||||
@@ -507,78 +428,12 @@ function MyProfileTabNavigator() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ListsTabNavigator() {
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<ListsTab.Navigator
|
|
||||||
screenOptions={{
|
|
||||||
animation: isAndroid ? 'none' : undefined,
|
|
||||||
gestureEnabled: true,
|
|
||||||
fullScreenGestureEnabled: true,
|
|
||||||
headerShown: false,
|
|
||||||
animationDuration: 250,
|
|
||||||
contentStyle: t.atoms.bg,
|
|
||||||
}}>
|
|
||||||
<ListsTab.Screen
|
|
||||||
name="Lists"
|
|
||||||
getComponent={() => ListsScreen}
|
|
||||||
options={{requireAuth: true}}
|
|
||||||
/>
|
|
||||||
{commonScreens(ListsTab as typeof HomeTab)}
|
|
||||||
</ListsTab.Navigator>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ModerationTabNavigator() {
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<ModerationTab.Navigator
|
|
||||||
screenOptions={{
|
|
||||||
animation: isAndroid ? 'none' : undefined,
|
|
||||||
gestureEnabled: true,
|
|
||||||
fullScreenGestureEnabled: true,
|
|
||||||
headerShown: false,
|
|
||||||
animationDuration: 250,
|
|
||||||
contentStyle: t.atoms.bg,
|
|
||||||
}}>
|
|
||||||
<ModerationTab.Screen
|
|
||||||
name="Moderation"
|
|
||||||
getComponent={() => ModerationScreen}
|
|
||||||
options={{requireAuth: true}}
|
|
||||||
/>
|
|
||||||
{commonScreens(ModerationTab as typeof HomeTab)}
|
|
||||||
</ModerationTab.Navigator>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SettingsTabNavigator() {
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<SettingsTab.Navigator
|
|
||||||
screenOptions={{
|
|
||||||
animation: isAndroid ? 'none' : undefined,
|
|
||||||
gestureEnabled: true,
|
|
||||||
fullScreenGestureEnabled: true,
|
|
||||||
headerShown: false,
|
|
||||||
animationDuration: 250,
|
|
||||||
contentStyle: t.atoms.bg,
|
|
||||||
}}>
|
|
||||||
<SettingsTab.Screen
|
|
||||||
name="Settings"
|
|
||||||
getComponent={() => SettingsScreen}
|
|
||||||
options={{requireAuth: true}}
|
|
||||||
/>
|
|
||||||
{commonScreens(SettingsTab as typeof HomeTab)}
|
|
||||||
</SettingsTab.Navigator>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The FlatNavigator is used by Web to represent the routes
|
* The FlatNavigator is used by Web to represent the routes
|
||||||
* in a single ("flat") stack.
|
* in a single ("flat") stack.
|
||||||
*/
|
*/
|
||||||
const FlatNavigator = () => {
|
const FlatNavigator = () => {
|
||||||
const t = useTheme()
|
const pal = usePalette('default')
|
||||||
const numUnread = useUnreadNotifications()
|
const numUnread = useUnreadNotifications()
|
||||||
const screenListeners = useWebScrollRestoration()
|
const screenListeners = useWebScrollRestoration()
|
||||||
const title = (page: MessageDescriptor) => bskyTitle(i18n._(page), numUnread)
|
const title = (page: MessageDescriptor) => bskyTitle(i18n._(page), numUnread)
|
||||||
@@ -591,7 +446,7 @@ const FlatNavigator = () => {
|
|||||||
fullScreenGestureEnabled: true,
|
fullScreenGestureEnabled: true,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
animationDuration: 250,
|
animationDuration: 250,
|
||||||
contentStyle: t.atoms.bg,
|
contentStyle: pal.view,
|
||||||
}}>
|
}}>
|
||||||
<Flat.Screen
|
<Flat.Screen
|
||||||
name="Home"
|
name="Home"
|
||||||
@@ -613,7 +468,7 @@ const FlatNavigator = () => {
|
|||||||
getComponent={() => NotificationsScreen}
|
getComponent={() => NotificationsScreen}
|
||||||
options={{title: title(msg`Notifications`), requireAuth: true}}
|
options={{title: title(msg`Notifications`), requireAuth: true}}
|
||||||
/>
|
/>
|
||||||
{commonScreens(Flat as typeof HomeTab, {unreadCountLabel: numUnread})}
|
{commonScreens(Flat as typeof HomeTab, numUnread)}
|
||||||
</Flat.Navigator>
|
</Flat.Navigator>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -660,26 +515,12 @@ const LINKING = {
|
|||||||
if (name === 'Search') {
|
if (name === 'Search') {
|
||||||
return buildStateObject('SearchTab', 'Search', params)
|
return buildStateObject('SearchTab', 'Search', params)
|
||||||
}
|
}
|
||||||
if (name === 'Feeds') {
|
|
||||||
return buildStateObject('FeedsTab', 'Feeds', params)
|
|
||||||
}
|
|
||||||
if (name === 'Notifications') {
|
if (name === 'Notifications') {
|
||||||
return buildStateObject('NotificationsTab', 'Notifications', params)
|
return buildStateObject('NotificationsTab', 'Notifications', params)
|
||||||
}
|
}
|
||||||
if (name === 'Home') {
|
if (name === 'Home') {
|
||||||
return buildStateObject('HomeTab', 'Home', params)
|
return buildStateObject('HomeTab', 'Home', params)
|
||||||
}
|
}
|
||||||
if (isNativeTablet) {
|
|
||||||
if (name === 'Lists') {
|
|
||||||
return buildStateObject('ListsTab', 'Lists', params)
|
|
||||||
}
|
|
||||||
if (name === 'Moderation') {
|
|
||||||
return buildStateObject('ModerationTab', 'Moderation', params)
|
|
||||||
}
|
|
||||||
if (name === 'Settings') {
|
|
||||||
return buildStateObject('SettingsTab', 'Settings', params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if the path is something else, like a post, profile, or even settings, we need to initialize the home tab as pre-existing state otherwise the back button will not work
|
// if the path is something else, like a post, profile, or even settings, we need to initialize the home tab as pre-existing state otherwise the back button will not work
|
||||||
return buildStateObject('HomeTab', name, params, [
|
return buildStateObject('HomeTab', name, params, [
|
||||||
{
|
{
|
||||||
@@ -862,6 +703,5 @@ export {
|
|||||||
reset,
|
reset,
|
||||||
resetToTab,
|
resetToTab,
|
||||||
RoutesContainer,
|
RoutesContainer,
|
||||||
TabNavigatorTablet,
|
|
||||||
TabsNavigator,
|
TabsNavigator,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {View} from 'react-native'
|
|||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {isNativeTablet} from '#/platform/detection'
|
|
||||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||||
import {Button, ButtonColor, ButtonText} from '#/components/Button'
|
import {Button, ButtonColor, ButtonText} from '#/components/Button'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
@@ -44,11 +43,7 @@ export function Outer({
|
|||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
accessibilityLabelledBy={titleId}
|
accessibilityLabelledBy={titleId}
|
||||||
accessibilityDescribedBy={descriptionId}
|
accessibilityDescribedBy={descriptionId}
|
||||||
style={[
|
style={[gtMobile ? {width: 'auto', maxWidth: 400} : a.w_full]}>
|
||||||
gtMobile && !isNativeTablet
|
|
||||||
? {width: 'auto', maxWidth: 400}
|
|
||||||
: a.w_full,
|
|
||||||
]}>
|
|
||||||
{children}
|
{children}
|
||||||
</Dialog.ScrollableInner>
|
</Dialog.ScrollableInner>
|
||||||
</Context.Provider>
|
</Context.Provider>
|
||||||
@@ -86,7 +81,7 @@ export function Actions({children}: React.PropsWithChildren<{}>) {
|
|||||||
a.w_full,
|
a.w_full,
|
||||||
a.gap_md,
|
a.gap_md,
|
||||||
a.justify_end,
|
a.justify_end,
|
||||||
gtMobile && !isNativeTablet
|
gtMobile
|
||||||
? [a.flex_row, a.flex_row_reverse, a.justify_start]
|
? [a.flex_row, a.flex_row_reverse, a.justify_start]
|
||||||
: [a.flex_col],
|
: [a.flex_col],
|
||||||
]}>
|
]}>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {useNavigationState} from '@react-navigation/native'
|
import {useNavigationState} from '@react-navigation/native'
|
||||||
|
|
||||||
import {getTabState, TabState} from 'lib/routes/helpers'
|
import {getTabState, TabState} from 'lib/routes/helpers'
|
||||||
|
|
||||||
export function useNavigationTabState() {
|
export function useNavigationTabState() {
|
||||||
|
|||||||
+1
-11
@@ -47,17 +47,6 @@ export type BottomTabNavigatorParams = CommonNavigatorParams & {
|
|||||||
MyProfileTab: undefined
|
MyProfileTab: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SideTabNavigatorParams = CommonNavigatorParams & {
|
|
||||||
HomeTab: undefined
|
|
||||||
SearchTab: undefined
|
|
||||||
FeedsTab: undefined
|
|
||||||
NotificationsTab: undefined
|
|
||||||
MyProfileTab: undefined
|
|
||||||
ListsTab: undefined
|
|
||||||
ModerationTab: undefined
|
|
||||||
SettingsTab: undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export type HomeTabNavigatorParams = CommonNavigatorParams & {
|
export type HomeTabNavigatorParams = CommonNavigatorParams & {
|
||||||
Home: undefined
|
Home: undefined
|
||||||
}
|
}
|
||||||
@@ -83,6 +72,7 @@ export type FlatNavigatorParams = CommonNavigatorParams & {
|
|||||||
Search: {q?: string}
|
Search: {q?: string}
|
||||||
Feeds: undefined
|
Feeds: undefined
|
||||||
Notifications: undefined
|
Notifications: undefined
|
||||||
|
Hashtag: {tag: string; author?: string}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AllNavigatorParams = CommonNavigatorParams & {
|
export type AllNavigatorParams = CommonNavigatorParams & {
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ export const ComposePost = observer(function ComposePost({
|
|||||||
<OpenCameraBtn gallery={gallery} />
|
<OpenCameraBtn gallery={gallery} />
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{!isMobile && !isNative ? (
|
{!isMobile ? (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={onEmojiButtonPress}
|
onPress={onEmojiButtonPress}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {useQueryClient} from '@tanstack/react-query'
|
|||||||
|
|
||||||
import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers'
|
import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers'
|
||||||
import {logEvent, useGate} from '#/lib/statsig/statsig'
|
import {logEvent, useGate} from '#/lib/statsig/statsig'
|
||||||
import {isNative, isNativeTablet} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {listenSoftReset} from '#/state/events'
|
import {listenSoftReset} from '#/state/events'
|
||||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||||
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
||||||
@@ -155,9 +155,9 @@ export function FeedPage({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function useHeaderOffset() {
|
function useHeaderOffset() {
|
||||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
const {isDesktop, isTablet} = useWebMediaQueries()
|
||||||
const {fontScale} = useWindowDimensions()
|
const {fontScale} = useWindowDimensions()
|
||||||
if (isTabletOrDesktop && !isNativeTablet) {
|
if (isDesktop || isTablet) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
const navBarHeight = 42
|
const navBarHeight = 42
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {useLingui} from '@lingui/react'
|
|||||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
|
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||||
import {isNativeTablet, isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
import {Logo} from '#/view/icons/Logo'
|
import {Logo} from '#/view/icons/Logo'
|
||||||
|
|
||||||
import {IS_DEV} from '#/env'
|
import {IS_DEV} from '#/env'
|
||||||
@@ -42,28 +42,23 @@ export function HomeHeaderLayoutMobile({
|
|||||||
headerHeight.value = e.nativeEvent.layout.height
|
headerHeight.value = e.nativeEvent.layout.height
|
||||||
}}>
|
}}>
|
||||||
<View style={[pal.view, styles.topBar]}>
|
<View style={[pal.view, styles.topBar]}>
|
||||||
{!isNativeTablet ? (
|
<View style={[pal.view, {width: 100}]}>
|
||||||
<View style={[pal.view, {width: 100}]}>
|
<TouchableOpacity
|
||||||
<TouchableOpacity
|
testID="viewHeaderDrawerBtn"
|
||||||
testID="viewHeaderDrawerBtn"
|
onPress={onPressAvi}
|
||||||
onPress={onPressAvi}
|
accessibilityRole="button"
|
||||||
accessibilityRole="button"
|
accessibilityLabel={_(msg`Open navigation`)}
|
||||||
accessibilityLabel={_(msg`Open navigation`)}
|
accessibilityHint={_(
|
||||||
accessibilityHint={_(
|
msg`Access profile and other navigation links`,
|
||||||
msg`Access profile and other navigation links`,
|
)}
|
||||||
)}
|
hitSlop={HITSLOP_10}>
|
||||||
hitSlop={HITSLOP_10}>
|
<FontAwesomeIcon
|
||||||
<FontAwesomeIcon
|
icon="bars"
|
||||||
icon="bars"
|
size={18}
|
||||||
size={18}
|
color={pal.colors.textLight}
|
||||||
color={pal.colors.textLight}
|
/>
|
||||||
/>
|
</TouchableOpacity>
|
||||||
</TouchableOpacity>
|
</View>
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
// subtract width of LeftNav
|
|
||||||
<View style={{width: 100 - 77}} />
|
|
||||||
)}
|
|
||||||
<View>
|
<View>
|
||||||
<Logo width={30} />
|
<Logo width={30} />
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
|
|||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {BACK_HITSLOP} from 'lib/constants'
|
import {BACK_HITSLOP} from 'lib/constants'
|
||||||
import {isNative, isNativeTablet} from 'platform/detection'
|
import {isNative} from 'platform/detection'
|
||||||
import {useLightboxControls, ImagesLightbox} from '#/state/lightbox'
|
import {useLightboxControls, ImagesLightbox} from '#/state/lightbox'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {Trans, msg} from '@lingui/macro'
|
import {Trans, msg} from '@lingui/macro'
|
||||||
@@ -71,11 +71,9 @@ export function ProfileSubpageHeader({
|
|||||||
}
|
}
|
||||||
}, [openLightbox, avatar])
|
}, [openLightbox, avatar])
|
||||||
|
|
||||||
const isMobileLayout = isMobile || isNativeTablet
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CenteredView style={pal.view}>
|
<CenteredView style={pal.view}>
|
||||||
{isMobileLayout && (
|
{isMobile && (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
@@ -84,7 +82,7 @@ export function ProfileSubpageHeader({
|
|||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
paddingTop: isNative ? 0 : 8,
|
paddingTop: isNative ? 0 : 8,
|
||||||
paddingBottom: 8,
|
paddingBottom: 8,
|
||||||
paddingHorizontal: isMobileLayout ? 12 : 14,
|
paddingHorizontal: isMobile ? 12 : 14,
|
||||||
},
|
},
|
||||||
pal.border,
|
pal.border,
|
||||||
]}>
|
]}>
|
||||||
@@ -121,7 +119,7 @@ export function ProfileSubpageHeader({
|
|||||||
gap: 10,
|
gap: 10,
|
||||||
paddingTop: 14,
|
paddingTop: 14,
|
||||||
paddingBottom: 6,
|
paddingBottom: 6,
|
||||||
paddingHorizontal: isMobileLayout ? 12 : 14,
|
paddingHorizontal: isMobile ? 12 : 14,
|
||||||
}}>
|
}}>
|
||||||
<Pressable
|
<Pressable
|
||||||
testID="headerAviButton"
|
testID="headerAviButton"
|
||||||
@@ -172,7 +170,7 @@ export function ProfileSubpageHeader({
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{!isMobileLayout && (
|
{!isMobile && (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {useSetDrawerOpen} from '#/state/shell'
|
import {useSetDrawerOpen} from '#/state/shell'
|
||||||
import {isNativeTablet, isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
|
|
||||||
const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
|
const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
|
||||||
|
|
||||||
@@ -71,13 +71,13 @@ export function SimpleViewHeader({
|
|||||||
icon="angle-left"
|
icon="angle-left"
|
||||||
style={[styles.backIcon, pal.text]}
|
style={[styles.backIcon, pal.text]}
|
||||||
/>
|
/>
|
||||||
) : !isNativeTablet ? (
|
) : (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
size={18}
|
size={18}
|
||||||
icon="bars"
|
icon="bars"
|
||||||
style={[styles.backIcon, pal.textLight]}
|
style={[styles.backIcon, pal.textLight]}
|
||||||
/>
|
/>
|
||||||
) : null}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
) : null}
|
) : null}
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
|
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|
||||||
import {FABInner, FABProps} from './FABInner'
|
import {FABInner, FABProps} from './FABInner'
|
||||||
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
|
|
||||||
export const FAB = (_opts: FABProps) => {
|
export const FAB = (_opts: FABProps) => {
|
||||||
const {isDesktop} = useWebMediaQueries()
|
const {isDesktop} = useWebMediaQueries()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Animated from 'react-native-reanimated'
|
|||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {LinearGradient} from 'expo-linear-gradient'
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
|
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isNativeTablet, isWeb} from '#/platform/detection'
|
||||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {clamp} from 'lib/numbers'
|
import {clamp} from 'lib/numbers'
|
||||||
@@ -40,7 +40,7 @@ export function FABInner({testID, icon, ...props}: FABProps) {
|
|||||||
styles.outer,
|
styles.outer,
|
||||||
size,
|
size,
|
||||||
tabletSpacing,
|
tabletSpacing,
|
||||||
isMobile && fabMinimalShellTransform,
|
(isMobile || isNativeTablet) && fabMinimalShellTransform,
|
||||||
]}>
|
]}>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
|
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
|
||||||
|
|
||||||
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
testID?: string
|
testID?: string
|
||||||
|
|||||||
@@ -4,16 +4,15 @@ import Animated from 'react-native-reanimated'
|
|||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {useMediaQuery} from 'react-responsive'
|
import {useMediaQuery} from 'react-responsive'
|
||||||
|
|
||||||
import {HITSLOP_20} from '#/lib/constants'
|
import {HITSLOP_20} from 'lib/constants'
|
||||||
import {useMinimalShellMode} from '#/lib/hooks/useMinimalShellMode'
|
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {colors} from '#/lib/styles'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {isNativeTablet, isWeb} from '#/platform/detection'
|
import {colors} from 'lib/styles'
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {useTheme} from '#/alf'
|
|
||||||
|
|
||||||
const AnimatedTouchableOpacity =
|
const AnimatedTouchableOpacity =
|
||||||
Animated.createAnimatedComponent(TouchableOpacity)
|
Animated.createAnimatedComponent(TouchableOpacity)
|
||||||
|
import {isNativeTablet, isWeb} from 'platform/detection'
|
||||||
|
import {useSession} from 'state/session'
|
||||||
|
|
||||||
export function LoadLatestBtn({
|
export function LoadLatestBtn({
|
||||||
onPress,
|
onPress,
|
||||||
@@ -24,7 +23,7 @@ export function LoadLatestBtn({
|
|||||||
label: string
|
label: string
|
||||||
showIndicator: boolean
|
showIndicator: boolean
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const pal = usePalette('default')
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const {isDesktop, isTablet, isMobile, isTabletOrMobile} = useWebMediaQueries()
|
const {isDesktop, isTablet, isMobile, isTabletOrMobile} = useWebMediaQueries()
|
||||||
const {fabMinimalShellTransform} = useMinimalShellMode()
|
const {fabMinimalShellTransform} = useMinimalShellMode()
|
||||||
@@ -34,7 +33,9 @@ export function LoadLatestBtn({
|
|||||||
|
|
||||||
// Adjust height of the fab if we have a session only on mobile web. If we don't have a session, we want to adjust
|
// Adjust height of the fab if we have a session only on mobile web. If we don't have a session, we want to adjust
|
||||||
// it on both tablet and mobile since we are showing the bottom bar (see createNativeStackNavigatorWithAuth)
|
// it on both tablet and mobile since we are showing the bottom bar (see createNativeStackNavigatorWithAuth)
|
||||||
const showBottomBar = hasSession ? isMobile : isTabletOrMobile
|
const showBottomBar = hasSession
|
||||||
|
? isMobile || isNativeTablet
|
||||||
|
: isTabletOrMobile
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedTouchableOpacity
|
<AnimatedTouchableOpacity
|
||||||
@@ -45,8 +46,8 @@ export function LoadLatestBtn({
|
|||||||
? styles.loadLatestOutOfLine
|
? styles.loadLatestOutOfLine
|
||||||
: styles.loadLatestInline),
|
: styles.loadLatestInline),
|
||||||
isTablet && !isNativeTablet && styles.loadLatestInline,
|
isTablet && !isNativeTablet && styles.loadLatestInline,
|
||||||
t.atoms.border_contrast_high,
|
pal.borderDark,
|
||||||
t.atoms.bg,
|
pal.view,
|
||||||
showBottomBar && fabMinimalShellTransform,
|
showBottomBar && fabMinimalShellTransform,
|
||||||
]}
|
]}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
@@ -54,10 +55,8 @@ export function LoadLatestBtn({
|
|||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={label}
|
accessibilityLabel={label}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
<FontAwesomeIcon icon="angle-up" color={t.atoms.text.color} size={19} />
|
<FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
|
||||||
{showIndicator && (
|
{showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
|
||||||
<View style={[styles.indicator, t.atoms.border_contrast_high]} />
|
|
||||||
)}
|
|
||||||
</AnimatedTouchableOpacity>
|
</AnimatedTouchableOpacity>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -461,7 +461,7 @@ export function SearchScreen(
|
|||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const search = useActorAutocompleteFn()
|
const search = useActorAutocompleteFn()
|
||||||
const setMinimalShellMode = useSetMinimalShellMode()
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
const {isTabletOrDesktop, isMobile} = useWebMediaQueries()
|
const {isTabletOrDesktop, isTabletOrMobile} = useWebMediaQueries()
|
||||||
|
|
||||||
const searchDebounceTimeout = React.useRef<NodeJS.Timeout | undefined>(
|
const searchDebounceTimeout = React.useRef<NodeJS.Timeout | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
@@ -637,8 +637,8 @@ export function SearchScreen(
|
|||||||
pal.view,
|
pal.view,
|
||||||
isTabletOrDesktop && {paddingTop: 10},
|
isTabletOrDesktop && {paddingTop: 10},
|
||||||
]}
|
]}
|
||||||
sideBorders={isMobile}>
|
sideBorders={isTabletOrDesktop}>
|
||||||
{isMobile && (
|
{isTabletOrMobile && (
|
||||||
<Pressable
|
<Pressable
|
||||||
testID="viewHeaderBackOrMenuBtn"
|
testID="viewHeaderBackOrMenuBtn"
|
||||||
onPress={onPressMenu}
|
onPress={onPressMenu}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import type {NativeStackNavigatorProps} from '@react-navigation/native-stack/src
|
|||||||
import {NativeStackView} from '@react-navigation/native-stack'
|
import {NativeStackView} from '@react-navigation/native-stack'
|
||||||
|
|
||||||
import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
|
import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
|
||||||
import {LeftNav} from './desktop/LeftNav'
|
import {DesktopLeftNav} from './desktop/LeftNav'
|
||||||
import {DesktopRightNav} from './desktop/RightNav'
|
import {DesktopRightNav} from './desktop/RightNav'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||||
import {useOnboardingState} from '#/state/shell'
|
import {useOnboardingState} from '#/state/shell'
|
||||||
@@ -149,7 +149,7 @@ function NativeStackNavigator({
|
|||||||
{isWeb && showBottomBar && <BottomBarWeb />}
|
{isWeb && showBottomBar && <BottomBarWeb />}
|
||||||
{isWeb && !showBottomBar && (
|
{isWeb && !showBottomBar && (
|
||||||
<>
|
<>
|
||||||
<LeftNav />
|
<DesktopLeftNav />
|
||||||
<DesktopRightNav routeName={activeRoute.name} />
|
<DesktopRightNav routeName={activeRoute.name} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,54 +1,52 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {
|
import {PressableWithHover} from 'view/com/util/PressableWithHover'
|
||||||
FontAwesomeIcon,
|
|
||||||
FontAwesomeIconStyle,
|
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {msg, Trans} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {
|
import {
|
||||||
useLinkProps,
|
useLinkProps,
|
||||||
useNavigation,
|
useNavigation,
|
||||||
useNavigationState,
|
useNavigationState,
|
||||||
} from '@react-navigation/native'
|
} from '@react-navigation/native'
|
||||||
|
import {
|
||||||
import {isInvalidHandle} from '#/lib/strings/handles'
|
FontAwesomeIcon,
|
||||||
import {isWeb} from '#/platform/detection'
|
FontAwesomeIconStyle,
|
||||||
import {emitSoftReset} from '#/state/events'
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
import {useFetchHandle} from '#/state/queries/handle'
|
import {Text} from 'view/com/util/text/Text'
|
||||||
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
import {UserAvatar} from 'view/com/util/UserAvatar'
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
import {Link} from 'view/com/util/Link'
|
||||||
import {useSession} from '#/state/session'
|
import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder'
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
|
import {s, colors} from 'lib/styles'
|
||||||
import {
|
import {
|
||||||
|
HomeIcon,
|
||||||
|
HomeIconSolid,
|
||||||
|
MagnifyingGlassIcon2,
|
||||||
|
MagnifyingGlassIcon2Solid,
|
||||||
BellIcon,
|
BellIcon,
|
||||||
BellIconSolid,
|
BellIconSolid,
|
||||||
|
UserIcon,
|
||||||
|
UserIconSolid,
|
||||||
CogIcon,
|
CogIcon,
|
||||||
CogIconSolid,
|
CogIconSolid,
|
||||||
ComposeIcon2,
|
ComposeIcon2,
|
||||||
HandIcon,
|
|
||||||
HashtagIcon,
|
|
||||||
HomeIcon,
|
|
||||||
HomeIconSolid,
|
|
||||||
ListIcon,
|
ListIcon,
|
||||||
MagnifyingGlassIcon2,
|
HashtagIcon,
|
||||||
MagnifyingGlassIcon2Solid,
|
HandIcon,
|
||||||
UserIcon,
|
|
||||||
UserIconSolid,
|
|
||||||
} from 'lib/icons'
|
} from 'lib/icons'
|
||||||
import {getCurrentRoute, isStateAtTabRoot, isTab} from 'lib/routes/helpers'
|
import {getCurrentRoute, isTab, isStateAtTabRoot} from 'lib/routes/helpers'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {NavigationProp, CommonNavigatorParams} from 'lib/routes/types'
|
||||||
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
|
|
||||||
import {colors, s} from 'lib/styles'
|
|
||||||
import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
|
||||||
import {Link} from 'view/com/util/Link'
|
|
||||||
import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder'
|
|
||||||
import {PressableWithHover} from 'view/com/util/PressableWithHover'
|
|
||||||
import {Text} from 'view/com/util/text/Text'
|
|
||||||
import {UserAvatar} from 'view/com/util/UserAvatar'
|
|
||||||
import {router} from '../../../routes'
|
import {router} from '../../../routes'
|
||||||
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {Trans, msg} from '@lingui/macro'
|
||||||
|
import {useProfileQuery} from '#/state/queries/profile'
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
||||||
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
|
import {useFetchHandle} from '#/state/queries/handle'
|
||||||
|
import {emitSoftReset} from '#/state/events'
|
||||||
|
import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
||||||
|
import {isInvalidHandle} from '#/lib/strings/handles'
|
||||||
|
|
||||||
function ProfileCard() {
|
function ProfileCard() {
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
@@ -268,7 +266,7 @@ function ComposeBtn() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LeftNav() {
|
export function DesktopLeftNav() {
|
||||||
const {hasSession, currentAccount} = useSession()
|
const {hasSession, currentAccount} = useSession()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
@@ -447,19 +445,17 @@ export function LeftNav() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
// @ts-ignore web only
|
leftNav: {
|
||||||
leftNav: isWeb
|
// @ts-ignore web only
|
||||||
? {
|
position: 'fixed',
|
||||||
position: 'fixed',
|
top: 10,
|
||||||
top: 10,
|
// @ts-ignore web only
|
||||||
left: 'calc(50vw - 300px - 220px - 20px)',
|
left: 'calc(50vw - 300px - 220px - 20px)',
|
||||||
width: 220,
|
width: 220,
|
||||||
maxHeight: 'calc(100vh - 10px)',
|
// @ts-ignore web only
|
||||||
overflowY: 'auto',
|
maxHeight: 'calc(100vh - 10px)',
|
||||||
}
|
overflowY: 'auto',
|
||||||
: {
|
},
|
||||||
paddingTop: 12,
|
|
||||||
},
|
|
||||||
leftNavTablet: {
|
leftNavTablet: {
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
@@ -517,7 +513,6 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
paddingHorizontal: 4,
|
paddingHorizontal: 4,
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
},
|
||||||
navItemCountTablet: {
|
navItemCountTablet: {
|
||||||
left: 18,
|
left: 18,
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {msg} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {s} from 'lib/styles'
|
|
||||||
import {TextLink} from 'view/com/util/Link'
|
|
||||||
import {Text} from 'view/com/util/text/Text'
|
|
||||||
import {DesktopFeeds} from './Feeds'
|
|
||||||
import {DesktopSearch} from './Search'
|
import {DesktopSearch} from './Search'
|
||||||
|
import {DesktopFeeds} from './Feeds'
|
||||||
|
import {Text} from 'view/com/util/text/Text'
|
||||||
|
import {TextLink} from 'view/com/util/Link'
|
||||||
|
import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants'
|
||||||
|
import {s} from 'lib/styles'
|
||||||
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
|
||||||
export function DesktopRightNav({routeName}: {routeName: string}) {
|
export function DesktopRightNav({routeName}: {routeName: string}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
@@ -98,18 +97,15 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
// @ts-ignore web only
|
rightNav: {
|
||||||
rightNav: isWeb
|
// @ts-ignore web only
|
||||||
? {
|
position: 'fixed',
|
||||||
position: 'fixed',
|
// @ts-ignore web only
|
||||||
left: 'calc(50vw + 300px + 20px)',
|
left: 'calc(50vw + 300px + 20px)',
|
||||||
width: 300,
|
width: 300,
|
||||||
maxHeight: '100%',
|
maxHeight: '100%',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
}
|
},
|
||||||
: {
|
|
||||||
width: 300,
|
|
||||||
},
|
|
||||||
|
|
||||||
message: {
|
message: {
|
||||||
paddingVertical: 18,
|
paddingVertical: 18,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
useSetDrawerOpen,
|
useSetDrawerOpen,
|
||||||
useIsDrawerSwipeDisabled,
|
useIsDrawerSwipeDisabled,
|
||||||
} from '#/state/shell'
|
} from '#/state/shell'
|
||||||
import {isAndroid, isNativeTablet} from 'platform/detection'
|
import {isAndroid} from 'platform/detection'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {useCloseAnyActiveElement} from '#/state/util'
|
import {useCloseAnyActiveElement} from '#/state/util'
|
||||||
import * as notifications from 'lib/notifications/notifications'
|
import * as notifications from 'lib/notifications/notifications'
|
||||||
@@ -87,19 +87,15 @@ function ShellInner() {
|
|||||||
style={containerPadding}
|
style={containerPadding}
|
||||||
importantForAccessibility={importantForAccessibility}>
|
importantForAccessibility={importantForAccessibility}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
{!isNativeTablet ? (
|
<Drawer
|
||||||
<Drawer
|
renderDrawerContent={renderDrawerContent}
|
||||||
renderDrawerContent={renderDrawerContent}
|
open={isDrawerOpen}
|
||||||
open={isDrawerOpen}
|
onOpen={onOpenDrawer}
|
||||||
onOpen={onOpenDrawer}
|
onClose={onCloseDrawer}
|
||||||
onClose={onCloseDrawer}
|
swipeEdgeWidth={winDim.width / 2}
|
||||||
swipeEdgeWidth={winDim.width / 2}
|
swipeEnabled={!canGoBack && hasSession && !isDrawerSwipeDisabled}>
|
||||||
swipeEnabled={!canGoBack && hasSession && !isDrawerSwipeDisabled}>
|
|
||||||
<TabsNavigator />
|
|
||||||
</Drawer>
|
|
||||||
) : (
|
|
||||||
<TabsNavigator />
|
<TabsNavigator />
|
||||||
)}
|
</Drawer>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
<Composer winHeight={winDim.height} />
|
<Composer winHeight={winDim.height} />
|
||||||
|
|||||||
Reference in New Issue
Block a user