Điều hướng
Hướng dẫn này đưa ra giả định rằng bạn đang sử dụng React Navigation trong ứng dụng của mình kết hợp với createStackNavigator
.
Nếu bạn đang sử dụng một giải pháp điều hướng khác hoặc đang sử dụng createNativeStackNavigator
, việc cài đặt sẽ tùy thuộc vào cách sắp xếp điều hướng của bạn.
Navigation Container
NavigationContainer
quản lý trạng thái ứng dụng trong React Navigation. Tất cả các trình điều hướng và màn hình lồng nhau đều tồn tại trong nó.
Như đã lưu ý trong Hello PiScale Chat, context PSChat
cần được thiết lập là cấp độ cao nhất của các thành phần trong ứng dụng để PiScale Chat SDK hoạt động như thiết kế.
- ChatNavStackParamList
- AppStackNavigator
- App
import {PSMessageMetadataType, PSUserModel} from '@communi/chat-react-native';
export type ChatNavStackParamList = {
PSThreadsScreen: undefined;
PSMessagesScreen: {
targetThreadId?: string | undefined;
targetUserId?: string | undefined;
targetMessageId?: number | undefined;
};
PSNewGroupThreadScreen: undefined;
PSNewNameGroupThreadScreen: {
selectedUsers?: PSUserModel[];
};
PSNewDirectThreadScreen: undefined;
PSAddMemberScreen: {
threadId: string;
};
PSForwardMessageScreen: {
threadId: string;
messageIds: number[];
};
PSMediaCollectionsScreen: {
threadId: string;
type: PSMessageMetadataType;
};
PSMembersInThreadScreen: {
threadId: string;
};
PSMessagesPinnedScreen: {
threadId: string;
};
PSSearchMessageScreen: {
threadId: string;
};
PSSearchThreadScreen: undefined;
PSThreadProfileScreen: {
threadId: string;
};
};
import {createStackNavigator} from '@react-navigation/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
const HomeStack = createStackNavigator<ChatNavStackParamList>();
const BottomTab = createBottomTabNavigator<ChatNavStackParamList>();
const AppStackNavigator = () => {
return (
<HomeStack.Navigator initialRouteName="HomeTab">
<HomeStack.Screen
name="HomeTab"
options={{
headerShown: false,
}}>
{() => (
<BottomTab.Navigator
initialRouteName="PSThreadsScreen"
screenOptions={{
headerShown: false,
}}>
<BottomTab.Screen
name="PSThreadsScreen"
component={PSThreadsScreen}
options={{
tabBarLabel: 'Chat',
}}
/>
</BottomTab.Navigator>
)}
</HomeStack.Screen>
<HomeStack.Screen
name="PSMessagesScreen"
component={PSMessagesScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSNewGroupThreadScreen"
component={PSNewGroupThreadScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSNewNameGroupThreadScreen"
component={PSNewNameGroupThreadScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSNewDirectThreadScreen"
component={PSNewDirectThreadScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSAddMemberScreen"
component={PSAddMemberScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSForwardMessageScreen"
component={PSForwardMessageScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSMediaCollectionsScreen"
component={PSMediaCollectionsScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSMembersInThreadScreen"
component={PSMembersInThreadScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSMessagesPinnedScreen"
component={PSMessagesPinnedScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSSearchMessageScreen"
component={PSSearchMessageScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSSearchThreadScreen"
component={PSSearchThreadScreen}
options={{
headerShown: false,
}}
/>
<HomeStack.Screen
name="PSThreadProfileScreen"
component={PSThreadProfileScreen}
options={{
headerShown: false,
}}
/>
</HomeStack.Navigator>
);
};
import React from 'react';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {
SafeAreaView,
SafeAreaProvider,
useSafeAreaInsets,
} from 'react-native-safe-area-context';
import {NavigationContainer} from '@react-navigation/native';
import {PSChat, PSChatProps} from '@communi/chat-react-native';
const PSChatApp = () => {
return (
<GestureHandlerRootView style={{flex: 1}}>
<SafeAreaProvider>
<SafeAreaView style={{flex: 1}}>
<NavigationContainer>
<App />
</NavigationContainer>
</SafeAreaView>
</SafeAreaProvider>
</GestureHandlerRootView>
);
};
const App = () => {
const {top, bottom} = useSafeAreaInsets();
const fetchToken = React.useCallback(async (): Promise<string> => {
return 'PISCALE_TOKEN'
}, []);
const chatProps = React.useMemo(() => {
return {
chatApiClientOptions: {
appId: APP_ID,
fetchToken: fetchToken,
},
userId: 'user_id',
deviceId: 'device_id',
areaInsets: {
topInset: top,
bottomInset: bottom,
},
} as PSChatProps;
}, [top, bottom, fetchToken]);
return (
<PSChat props={chatProps}>
<AppStackNavigator />
</PSChat>
)
};
export default PSChatApp;
Use case
Trong 1 số use case cần chuyển hướng đến PSMessages như là onFlashMessagePress
/ onJoinGroupInviteLinkSuccess
trong PSChat hay onViewMessage
trong PSSearchMessage, hay như là xử lý khi nhấn vào thông báo FCM,..v.v. Thì để đảm bảo UX hoạt động như thiết kế chúng tôi khuyến nghị trong NavigationContainer
app của bạn lúc này chỉ có 1 thành phần PSMessages
.
Dưới đây là ví dụ việc triển khai mục đích đó:
<NavigationContainer
ref={navigationRef}
...
</NavigationContainer>
import {
CommonActions,
createNavigationContainerRef,
} from '@react-navigation/native';
import {PiScaleNavStackParamList} from './types';
import {PSBusEvent, PSEventBus} from '@communi/chat-react-native';
export const navigationRef =
createNavigationContainerRef<PiScaleNavStackParamList>();
export const navigateToThreadDetails = ({
targetThreadId,
targetUserId,
targetMessageId,
}: {
targetThreadId?: string;
targetUserId?: string;
targetMessageId?: number;
}) => {
if (navigationRef.isReady()) {
navigationRef.dispatch(state => {
const routes = state.routes.slice();
console.log(`routes = ${JSON.stringify(routes)}`);
if (routes.length) {
const indexRouteOfMessagesScreen = routes.findIndex(
item => item.name === 'PSMessagesScreen',
);
// đảm bảo luôn chỉ có 1 message screen
if (indexRouteOfMessagesScreen >= 0) {
const popCounter = routes.length - 1 - indexRouteOfMessagesScreen;
const messagesScreenStackRoute = routes[indexRouteOfMessagesScreen]!;
const params = messagesScreenStackRoute.params as
| PiScaleNavStackParamList['PSMessagesScreen']
| undefined;
for (let i = 0; i < popCounter; i++) {
routes.pop();
}
if (
(params?.targetThreadId &&
params.targetThreadId === targetThreadId) ||
(params?.targetUserId && params.targetUserId === targetUserId)
) {
if (targetMessageId) {
PSEventBus.getInstance().dispatch(
PSBusEvent.SCROLL_TO_MESSAGE,
targetMessageId,
);
} else {
// không làm gì
}
} else {
routes.pop();
routes.push({
key: `${Date.now()}`,
name: 'PSMessagesScreen',
params: {
targetThreadId: targetThreadId,
targetUserId: targetUserId,
targetMessageId: targetMessageId,
},
});
}
} else {
return CommonActions.navigate({
key: `${Date.now()}`,
name: 'PSMessagesScreen',
params: {
targetThreadId: targetThreadId,
targetUserId: targetUserId,
targetMessageId: targetMessageId,
},
});
}
}
return CommonActions.reset({
...state,
index: routes.length - 1,
routes,
});
});
} else {
console.log('navigation is not ready');
}
};
ví dụ sử dụng khi onViewMessage
trong PSSearchMessage
const onViewMessage = React.useCallback(
(messageId: number) => {
navigateToThreadDetails({
targetThreadId: params.threadId,
targetMessageId: messageId,
});
},
[params.threadId],
);