refactor(navigation): consolidate navigation with href helpers and extract push device hook
- Migrate all navigation calls from magic strings to centralized href helpers - Extract inline device registration logic into useRegisterPushDevice hook - Remove unused terms and privacy policy routes from profile stack - Add hrefTradeDetail helper for trade detail navigation - Restore routePayloadCache.stashSystemMessage for group request/invite handling - Change desktop shell tab navigation from replace to push
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useCallback } from 'react';
|
||||
import { Platform, Pressable, useWindowDimensions } from 'react-native';
|
||||
import { Tabs, usePathname } from 'expo-router';
|
||||
import { Tabs, usePathname, useRouter } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import type { PressableProps } from 'react-native';
|
||||
@@ -8,6 +8,7 @@ import type { PressableProps } from 'react-native';
|
||||
import { useAppColors, shadows } from '../../../src/theme';
|
||||
import { BREAKPOINTS } from '../../../src/hooks';
|
||||
import { useHomeTabBarVisibilityStore, useTotalUnreadCount, useHomeTabPressStore } from '../../../src/stores';
|
||||
import { hrefHome } from '../../../src/navigation/hrefs';
|
||||
|
||||
const TAB_BAR_HEIGHT = 56;
|
||||
const TAB_BAR_FLOATING_MARGIN = 12;
|
||||
@@ -28,6 +29,7 @@ export default function TabsLayout() {
|
||||
const { width } = useWindowDimensions();
|
||||
const insets = useSafeAreaInsets();
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const unreadCount = useTotalUnreadCount();
|
||||
const scrollHideTabBar = useHomeTabBarVisibilityStore((s) => s.bottomTabBarHiddenByScroll);
|
||||
const triggerHomeTabPress = useHomeTabPressStore((s) => s.triggerPress);
|
||||
@@ -35,11 +37,18 @@ export default function TabsLayout() {
|
||||
|
||||
const isHomeStackRoute = pathname === '/home' || pathname.startsWith('/home/');
|
||||
|
||||
const handleHomeTabPress = useCallback(() => {
|
||||
if (pathname === '/home') {
|
||||
triggerHomeTabPress();
|
||||
}
|
||||
}, [pathname, triggerHomeTabPress]);
|
||||
const handleHomeTabPress = useCallback(
|
||||
(e: { preventDefault: () => void }) => {
|
||||
if (!isHomeStackRoute) return;
|
||||
e.preventDefault();
|
||||
if (pathname === '/home') {
|
||||
triggerHomeTabPress();
|
||||
} else {
|
||||
router.navigate(hrefHome());
|
||||
}
|
||||
},
|
||||
[isHomeStackRoute, pathname, triggerHomeTabPress, router]
|
||||
);
|
||||
|
||||
const tabBarStyle = useMemo(() => {
|
||||
if (hideTabBar) {
|
||||
|
||||
@@ -17,8 +17,6 @@ export default function ProfileStackLayout() {
|
||||
<Stack.Screen name="blocked-users" />
|
||||
<Stack.Screen name="chat-settings" />
|
||||
<Stack.Screen name="about" />
|
||||
<Stack.Screen name="terms" />
|
||||
<Stack.Screen name="privacy" />
|
||||
<Stack.Screen name="verification" />
|
||||
<Stack.Screen name="data-storage" />
|
||||
<Stack.Screen name="privacy-settings" />
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { PrivacyPolicyScreen } from '../../../../src/screens/profile/PrivacyPolicyScreen';
|
||||
|
||||
export default function PrivacyPolicyRoute() {
|
||||
return <PrivacyPolicyScreen />;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { TermsOfServiceScreen } from '../../../../src/screens/profile/TermsOfServiceScreen';
|
||||
|
||||
export default function TermsOfServiceRoute() {
|
||||
return <TermsOfServiceScreen />;
|
||||
}
|
||||
@@ -1,83 +1,24 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
import { useEffect } from 'react';
|
||||
import { Redirect } from 'expo-router';
|
||||
|
||||
import { AppRouteStack } from '../../src/app-navigation/AppRouteStack';
|
||||
import { messageManager, useAuthStore } from '../../src/stores';
|
||||
import { jpushService } from '../../src/services/notification/jpushService';
|
||||
|
||||
function getDeviceType(): 'ios' | 'android' | 'web' {
|
||||
switch (Platform.OS) {
|
||||
case 'ios': return 'ios';
|
||||
case 'android': return 'android';
|
||||
default: return 'web';
|
||||
}
|
||||
}
|
||||
|
||||
async function getOrCreateDeviceID(): Promise<string> {
|
||||
try {
|
||||
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
||||
const DEVICE_ID_KEY = 'withyou_device_id';
|
||||
let deviceId = await AsyncStorage.getItem(DEVICE_ID_KEY);
|
||||
if (deviceId) return deviceId;
|
||||
const timestamp = Date.now().toString(36);
|
||||
const random = Math.random().toString(36).substring(2, 8);
|
||||
deviceId = `${Platform.OS}_${timestamp}_${random}`;
|
||||
await AsyncStorage.setItem(DEVICE_ID_KEY, deviceId);
|
||||
return deviceId;
|
||||
} catch {
|
||||
return `${Platform.OS}_${Date.now()}`;
|
||||
}
|
||||
}
|
||||
import { useRegisterPushDevice } from '../../src/hooks';
|
||||
import { hrefAuthLogin } from '../../src/navigation/hrefs';
|
||||
|
||||
export default function AppLayout() {
|
||||
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
|
||||
const userID = useAuthStore((s) => s.currentUser?.id);
|
||||
const deviceRegistered = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
messageManager.initialize();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated || !userID) return;
|
||||
|
||||
if (!deviceRegistered.current) {
|
||||
deviceRegistered.current = true;
|
||||
|
||||
if (Platform.OS === 'web') {
|
||||
// Web: register device without push_token (JPush not available on web)
|
||||
getOrCreateDeviceID().then((deviceId) => {
|
||||
const { pushService } = require('../../src/services/notification/pushService');
|
||||
pushService.registerDevice({
|
||||
device_id: deviceId,
|
||||
device_type: 'web',
|
||||
device_name: 'Web Browser',
|
||||
}).catch((err: any) => {
|
||||
console.warn('[Push] web device register failed:', err?.message);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Mobile: JPush handles registration internally in initialize()
|
||||
jpushService.initialize().then((ok) => {
|
||||
if (ok && userID) {
|
||||
jpushService.setAliasForUser(userID);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (!isAuthenticated) {
|
||||
deviceRegistered.current = false;
|
||||
jpushService.cleanup();
|
||||
}
|
||||
};
|
||||
}, [isAuthenticated, userID]);
|
||||
useRegisterPushDevice(isAuthenticated, userID);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Redirect href="/login" />;
|
||||
return <Redirect href={hrefAuthLogin()} />;
|
||||
}
|
||||
|
||||
return <AppRouteStack />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { AppDesktopShell } from '../../src/app-navigation/AppDesktopShell';
|
||||
import { AppRouteStack } from '../../src/app-navigation/AppRouteStack';
|
||||
import { BREAKPOINTS } from '../../src/hooks';
|
||||
import { messageManager, useAuthStore } from '../../src/stores';
|
||||
import { hrefAuthLogin } from '../../src/navigation/hrefs';
|
||||
|
||||
export default function AppLayoutWeb() {
|
||||
const { width } = useWindowDimensions();
|
||||
@@ -17,7 +18,7 @@ export default function AppLayoutWeb() {
|
||||
}, []);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Redirect href="/login" />;
|
||||
return <Redirect href={hrefAuthLogin()} />;
|
||||
}
|
||||
|
||||
if (useDesktopShell) {
|
||||
|
||||
Reference in New Issue
Block a user