Phecda

feat: authentication flow

... ... @@ -10,7 +10,11 @@ import {
HeaderStyleInterpolators,
} from '@react-navigation/stack';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { MainTabParamList, MainStackParamList } from '../type/Navigation';
import {
MainTabParamList,
MainStackParamList,
AuthStackParamList,
} from '../type/Navigation';
import SystemInfo from './SystemInfo';
import DesignList from './DesignList';
import { useDarkMode } from 'react-native-dark-mode';
... ... @@ -27,6 +31,9 @@ import { useDeepLinking } from '../utility/handleDeepLinking';
import { navigationRef, useMountedRef } from '../utility/rootNavigation';
import ShortcutPage from './ShortcutItem';
import useQuickAction from '../utility/useQuickAction';
import { useReduxState } from '../store/hooks';
import LoginScreen from './Login';
import MeScreen from './Me';
const MainTab = createBottomTabNavigator<MainTabParamList>();
... ... @@ -36,7 +43,7 @@ function getTabHeader(
}
) {
const { state } = route;
if (!state) return 'SystemInfo';
if (!state) return 'Library';
const { routeNames, index } = state;
const routeName = routeNames[index] as keyof MainTabParamList;
return routeName;
... ... @@ -78,6 +85,10 @@ const Home = () => {
name={focused ? 'ios-list-box' : 'ios-list'}
/>
);
case 'Me':
return (
<Ionicons size={size} color={color} name={'ios-person'} />
);
default:
break;
}
... ... @@ -88,15 +99,19 @@ const Home = () => {
<MainTab.Screen name="Library" component={Library} />
<MainTab.Screen name="SystemInfo" component={SystemInfo} />
<MainTab.Screen name="DesignList" component={DesignList} />
<MainTab.Screen name="Me" component={MeScreen} />
</MainTab.Navigator>
);
};
const MainStack = createStackNavigator<MainStackParamList>();
const AuthStack = createStackNavigator<AuthStackParamList>();
const Container = () => {
const inDarkMode = useDarkMode();
const strings = useI18nStrings();
const user = useReduxState('user');
useMountedRef();
useDeepLinking();
... ... @@ -107,6 +122,7 @@ const Container = () => {
ref={navigationRef}
theme={inDarkMode ? themeForNav.dark : themeForNav.light}
>
{user.token ? (
<MainStack.Navigator
screenOptions={{
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
... ... @@ -137,6 +153,11 @@ const Container = () => {
<MainStack.Screen name="RNCode" component={ReadableCode} />
<MainStack.Screen name="ShortcutItem" component={ShortcutPage} />
</MainStack.Navigator>
) : (
<AuthStack.Navigator>
<AuthStack.Screen name="Login" component={LoginScreen} />
</AuthStack.Navigator>
)}
</NavigationContainer>
);
};
... ...
import React, { useCallback, useState } from 'react';
import { BGScroll, Card, ListItem, Divider } from '../component/View';
import { useReduxDispatch, useReduxState } from '../store/hooks';
import { rootActions } from '../store';
import { Alert } from 'react-native';
const LoginScreen = () => {
const { list, current } = useReduxState('user');
const dispatch = useReduxDispatch();
const [userName, setUserName] = useState(current?.name);
const onLogin = useCallback(() => {
const newToken = userName ? 'yes!' + userName : 'no:(';
if (userName) {
const filtered = list.filter(u => u.name === userName)[0];
if (filtered) {
dispatch(rootActions.userActions.setCurrentUser(filtered));
} else {
const newUser = { name: userName };
dispatch(rootActions.userActions.addUser(newUser));
dispatch(rootActions.userActions.setCurrentUser(newUser));
}
}
dispatch(rootActions.userActions.setToken(newToken));
}, [userName, dispatch, list]);
return (
<BGScroll>
<Card round>
<ListItem
title="User Name"
rightTitle={userName}
onPress={() => {
Alert.prompt('Your name?', undefined, setUserName);
}}
/>
<Divider />
<ListItem title="login" onPress={onLogin} />
</Card>
</BGScroll>
);
};
export default LoginScreen;
... ...
import React, { useCallback } from 'react';
import { BGScroll, Card, ListItem } from '../../component/View';
import { useReduxState, useReduxDispatch } from '../../store/hooks';
import { rootActions } from '../../store';
const MeScreen = () => {
const { current } = useReduxState('user');
const dispatch = useReduxDispatch();
const onLogout = useCallback(() => {
dispatch(rootActions.userActions.setToken(null));
}, [dispatch]);
return (
<BGScroll white>
<Card shadow>
<ListItem title={current?.name ?? '请登录'} />
</Card>
<Card shadow>
<ListItem title={'Log out'} onPress={onLogout} />
</Card>
</BGScroll>
);
};
export default MeScreen;
... ...
import { createAction } from 'typesafe-actions';
import { UserActionTypes, User } from './types';
export const setToken = createAction(UserActionTypes.SET_TOKEN)<string>();
export const setToken = createAction(UserActionTypes.SET_TOKEN)<
string | null
>();
export const setCurrentUser = createAction(UserActionTypes.SET_CURRENT)<User>();
... ...
... ... @@ -6,6 +6,7 @@ export type MainTabParamList = {
Library: undefined;
SystemInfo: undefined;
DesignList: undefined;
Me: undefined;
};
export type MainStackParamList = {
... ... @@ -18,6 +19,10 @@ export type MainStackParamList = {
ShortcutItem: { id?: string };
};
export type AuthStackParamList = {
Login: undefined;
};
export type MainTabScreenProps<RouteName extends keyof MainTabParamList> = {
navigation: CompositeNavigationProp<
BottomTabNavigationProp<MainTabParamList, RouteName>,
... ... @@ -30,3 +35,8 @@ export type MainStackScreenProps<RouteName extends keyof MainStackParamList> = {
navigation: StackNavigationProp<MainStackParamList, RouteName>;
route: RouteProp<MainStackParamList, RouteName>;
};
export type AuthStackScreenProps<RouteName extends keyof AuthStackParamList> = {
navigation: StackNavigationProp<AuthStackParamList, RouteName>;
route: RouteProp<AuthStackParamList, RouteName>;
};
... ...