mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-05-20 23:20:47 +08:00
207 lines
4.7 KiB
TypeScript
207 lines
4.7 KiB
TypeScript
import * as React from 'react';
|
|
import { View, TextInput, ActivityIndicator, StyleSheet } from 'react-native';
|
|
import { Title, Button } from 'react-native-paper';
|
|
import { useTheme, ParamListBase } from '@react-navigation/native';
|
|
import {
|
|
createStackNavigator,
|
|
HeaderBackButton,
|
|
StackNavigationProp,
|
|
} from '@react-navigation/stack';
|
|
|
|
type AuthStackParams = {
|
|
Splash: undefined;
|
|
Home: undefined;
|
|
SignIn: undefined;
|
|
PostSignOut: undefined;
|
|
};
|
|
|
|
const AUTH_CONTEXT_ERROR =
|
|
'Authentication context not found. Have your wrapped your components with AuthContext.Consumer?';
|
|
|
|
const AuthContext = React.createContext<{
|
|
signIn: () => void;
|
|
signOut: () => void;
|
|
}>({
|
|
signIn: () => {
|
|
throw new Error(AUTH_CONTEXT_ERROR);
|
|
},
|
|
signOut: () => {
|
|
throw new Error(AUTH_CONTEXT_ERROR);
|
|
},
|
|
});
|
|
|
|
const SplashScreen = () => {
|
|
return (
|
|
<View style={styles.content}>
|
|
<ActivityIndicator />
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const SignInScreen = () => {
|
|
const { signIn } = React.useContext(AuthContext);
|
|
const { colors } = useTheme();
|
|
|
|
return (
|
|
<View style={styles.content}>
|
|
<TextInput
|
|
placeholder="Username"
|
|
style={[
|
|
styles.input,
|
|
{ backgroundColor: colors.card, color: colors.text },
|
|
]}
|
|
/>
|
|
<TextInput
|
|
placeholder="Password"
|
|
secureTextEntry
|
|
style={[
|
|
styles.input,
|
|
{ backgroundColor: colors.card, color: colors.text },
|
|
]}
|
|
/>
|
|
<Button mode="contained" onPress={signIn} style={styles.button}>
|
|
Sign in
|
|
</Button>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const HomeScreen = () => {
|
|
const { signOut } = React.useContext(AuthContext);
|
|
|
|
return (
|
|
<View style={styles.content}>
|
|
<Title style={styles.text}>Signed in successfully 🎉</Title>
|
|
<Button onPress={signOut} style={styles.button}>
|
|
Sign out
|
|
</Button>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const SimpleStack = createStackNavigator<AuthStackParams>();
|
|
|
|
type Props = {
|
|
navigation: StackNavigationProp<ParamListBase>;
|
|
};
|
|
|
|
type State = {
|
|
isLoading: boolean;
|
|
isSignout: boolean;
|
|
userToken: undefined | string;
|
|
};
|
|
|
|
type Action =
|
|
| { type: 'RESTORE_TOKEN'; token: undefined | string }
|
|
| { type: 'SIGN_IN'; token: string }
|
|
| { type: 'SIGN_OUT' };
|
|
|
|
export default function SimpleStackScreen({ navigation }: Props) {
|
|
const [state, dispatch] = React.useReducer<React.Reducer<State, Action>>(
|
|
(prevState, action) => {
|
|
switch (action.type) {
|
|
case 'RESTORE_TOKEN':
|
|
return {
|
|
...prevState,
|
|
userToken: action.token,
|
|
isLoading: false,
|
|
};
|
|
case 'SIGN_IN':
|
|
return {
|
|
...prevState,
|
|
isSignout: false,
|
|
userToken: action.token,
|
|
};
|
|
case 'SIGN_OUT':
|
|
return {
|
|
...prevState,
|
|
isSignout: true,
|
|
userToken: undefined,
|
|
};
|
|
}
|
|
},
|
|
{
|
|
isLoading: true,
|
|
isSignout: false,
|
|
userToken: undefined,
|
|
}
|
|
);
|
|
|
|
React.useEffect(() => {
|
|
const timer = setTimeout(() => {
|
|
dispatch({ type: 'RESTORE_TOKEN', token: undefined });
|
|
}, 1000);
|
|
|
|
return () => clearTimeout(timer);
|
|
}, []);
|
|
|
|
navigation.setOptions({
|
|
headerShown: false,
|
|
});
|
|
|
|
const authContext = React.useMemo(
|
|
() => ({
|
|
signIn: () => dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' }),
|
|
signOut: () => dispatch({ type: 'SIGN_OUT' }),
|
|
}),
|
|
[]
|
|
);
|
|
|
|
return (
|
|
<AuthContext.Provider value={authContext}>
|
|
<SimpleStack.Navigator
|
|
screenOptions={{
|
|
headerLeft: () => (
|
|
<HeaderBackButton onPress={() => navigation.goBack()} />
|
|
),
|
|
}}
|
|
>
|
|
{state.isLoading ? (
|
|
<SimpleStack.Screen
|
|
name="Splash"
|
|
component={SplashScreen}
|
|
options={{ title: 'Auth Flow' }}
|
|
/>
|
|
) : state.userToken === undefined ? (
|
|
<SimpleStack.Screen
|
|
name="SignIn"
|
|
options={{
|
|
title: 'Sign in',
|
|
animationTypeForReplace: state.isSignout ? 'pop' : 'push',
|
|
}}
|
|
component={SignInScreen}
|
|
/>
|
|
) : (
|
|
<SimpleStack.Screen
|
|
name="Home"
|
|
options={{ title: 'Home' }}
|
|
component={HomeScreen}
|
|
/>
|
|
)}
|
|
</SimpleStack.Navigator>
|
|
</AuthContext.Provider>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
content: {
|
|
flex: 1,
|
|
padding: 16,
|
|
justifyContent: 'center',
|
|
},
|
|
input: {
|
|
margin: 8,
|
|
padding: 10,
|
|
borderRadius: 3,
|
|
borderWidth: StyleSheet.hairlineWidth,
|
|
borderColor: 'rgba(0, 0, 0, 0.08)',
|
|
},
|
|
button: {
|
|
margin: 8,
|
|
},
|
|
text: {
|
|
textAlign: 'center',
|
|
margin: 8,
|
|
},
|
|
});
|