diff --git a/src/app/LogoutButton/index.test.tsx b/src/app/LogoutButton/index.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1021266b9a7accbccafccd245f41c0969bb98dd1
--- /dev/null
+++ b/src/app/LogoutButton/index.test.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import { render, fireEvent, waitFor } from 'utils/testing';
+import * as ROUTES from 'constants/routes';
+
+import LogoutButton from '.';
+import { UserContext } from 'provider';
+
+describe('LogoutButton', () => {
+ const initialUser = {
+ id: null,
+ email: '',
+ name: '',
+ role: null,
+ };
+
+ const userContextMock = {
+ user: initialUser,
+ firstAuthenticated: false,
+ isAuthenticated: true,
+ isUnpaidClient: false,
+ isPaidClient: false,
+ isNutritionist: false,
+ isAdmin: false,
+ isLoading: false,
+ isFirstLoading: false,
+ signup: jest.fn(),
+ login: jest.fn(),
+ loginWithGoogle: jest.fn(),
+ logout: jest.fn(),
+ };
+
+ it('renders correctly', () => {
+ render(, ROUTES.checkout);
+ });
+
+ it('calls logout and redirects to initial page when clicked', async () => {
+ const { getByTestId } = render(
+
+
+ ,
+ ROUTES.checkout,
+ );
+
+ const logoutButton = getByTestId('logoutButton');
+ expect(logoutButton).toBeTruthy();
+
+ await waitFor(() => fireEvent.press(logoutButton));
+ expect(userContextMock.logout).toBeCalledTimes(1);
+ });
+});
diff --git a/src/app/LogoutButton/index.tsx b/src/app/LogoutButton/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6e065f22c12612662eb97f1b5bf8d9cf861bb3ac
--- /dev/null
+++ b/src/app/LogoutButton/index.tsx
@@ -0,0 +1,38 @@
+import React, { FC, useContext } from 'react';
+import { UserContext } from 'provider';
+import { Button, Icon } from 'react-native-elements';
+import { StyleSheet, View } from 'react-native';
+import { useNavigation } from '@react-navigation/core';
+import * as ROUTES from 'constants/routes';
+
+const LogoutButton: FC = () => {
+ const { logout, isAuthenticated } = useContext(UserContext);
+ const navigation = useNavigation();
+
+ const handlePress = async () => {
+ await logout();
+ navigation.reset({
+ index: 0,
+ routes: [{ name: ROUTES.initial }],
+ });
+ };
+
+ return isAuthenticated ? (
+ }
+ buttonStyle={styles.button}
+ onPress={handlePress}
+ testID="logoutButton"
+ />
+ ) : (
+
+ );
+};
+
+const styles = StyleSheet.create({
+ button: {
+ backgroundColor: '#fff',
+ },
+});
+
+export default LogoutButton;
diff --git a/src/app/index.tsx b/src/app/index.tsx
index 6e15b1845ee6eadd46a98acaa83d34cdae1b74bb..c6a84e65dcf0c820226a454bc31709ad315a63e6 100644
--- a/src/app/index.tsx
+++ b/src/app/index.tsx
@@ -10,6 +10,7 @@ import ContextProvider from 'provider';
import { theme } from 'styles/theme';
import { screenOptions, toastConfig } from './styles';
+import LogoutButton from './LogoutButton';
const Stack = createStackNavigator();
@@ -29,6 +30,7 @@ const App: FC = () => {
options={{
title: nav.header,
headerShown: Boolean(nav.header),
+ headerRight: LogoutButton,
}}
/>
))}
diff --git a/src/hooks/useAuthGuardEffect/index.ts b/src/hooks/useAuthGuardEffect/index.ts
index f5c828b94ab01cfead917b8eb6c95ad1fefde37c..a619e37733d5089b872c00ddcbca62d3d20db5bb 100644
--- a/src/hooks/useAuthGuardEffect/index.ts
+++ b/src/hooks/useAuthGuardEffect/index.ts
@@ -4,14 +4,14 @@ import { UserContext } from 'provider';
import * as ROUTES from 'constants/routes';
const useAuthGuardEffect = (signupFallback?: boolean) => {
- const { isAuthenticated } = useContext(UserContext);
- const navigation = useNavigation();
+ const { isAuthenticated, firstAuthenticated } = useContext(UserContext);
+ const navigation = useNavigation();
useEffect(() => {
- if (!isAuthenticated) {
+ if (!isAuthenticated && firstAuthenticated) {
navigation.navigate(signupFallback ? ROUTES.registration : ROUTES.login);
}
- }, [isAuthenticated, signupFallback, navigation]);
+ }, [isAuthenticated, signupFallback, navigation, firstAuthenticated]);
};
export default useAuthGuardEffect;
diff --git a/src/provider/UserContext/index.ts b/src/provider/UserContext/index.ts
index ecf0a4d1b3e2fc00ee350b741f4fa81dc6840d29..8fcdf21a8dee1bfbda8290852e231f08768d7631 100644
--- a/src/provider/UserContext/index.ts
+++ b/src/provider/UserContext/index.ts
@@ -32,6 +32,7 @@ const initialUser = {
export const UserContext = createContext({
user: initialUser,
+ firstAuthenticated: true,
isAuthenticated: false,
isUnpaidClient: false,
isPaidClient: false,
@@ -49,6 +50,7 @@ export const useUserContext = (): iUserContext => {
const [user, setUser] = useState(initialUser);
const [isLoading, setIsLoading] = useState(false);
const [isFirstLoading, setIsFirstLoading] = useState(false);
+ const [firstAuth, setFirstAuth] = useState(true);
const [clientHasPaid] = useState(false);
const logout = useCallback(async () => {
@@ -57,6 +59,7 @@ export const useUserContext = (): iUserContext => {
await removeCache(CACHE_KEYS.refreshToken);
setUser(initialUser);
resetAuthHeader();
+ setFirstAuth(false);
}, []);
const getUser = useCallback(async () => {
@@ -161,6 +164,7 @@ export const useUserContext = (): iUserContext => {
return {
user,
+ firstAuthenticated: firstAuth,
isAuthenticated: user.id !== null,
isUnpaidClient: user.role === UserRole.CLIENT,
isPaidClient: user.role === UserRole.CLIENT && clientHasPaid,
diff --git a/src/provider/UserContext/types.ts b/src/provider/UserContext/types.ts
index f0008b76b3e415ba608c9f9381dbfd82df5b5578..281eac082c58305299a404c367d850b20673ca30 100644
--- a/src/provider/UserContext/types.ts
+++ b/src/provider/UserContext/types.ts
@@ -9,6 +9,7 @@ import {
export interface iUserContext {
user: User;
+ firstAuthenticated: boolean;
isAuthenticated: boolean;
isUnpaidClient: boolean;
isPaidClient: boolean;
diff --git a/src/scenes/auth/ManualRegistrationPage/index.test.tsx b/src/scenes/auth/ManualRegistrationPage/index.test.tsx
index 05ef33f5f04d0e5b4790716adf82f4232d00ee12..beee9e605b012d3c15ad6b91c41a8032924152c7 100644
--- a/src/scenes/auth/ManualRegistrationPage/index.test.tsx
+++ b/src/scenes/auth/ManualRegistrationPage/index.test.tsx
@@ -102,4 +102,15 @@ describe('ManualRegistrationPage', () => {
const nextPageText = queryByText(/Checkout/i);
expect(nextPageText).toBeFalsy();
});
+
+ test('has link button that navigates to Login Page', async () => {
+ const { getByText, queryByText, queryAllByText } = render(
+ ,
+ ROUTES.registration,
+ );
+ expect(queryByText(/Login disini/i)).toBeTruthy();
+ await waitFor(() => fireEvent.press(getByText(/Login disini/i)));
+
+ expect(queryAllByText(/Login/i)).toBeTruthy();
+ });
});
diff --git a/src/scenes/auth/ManualRegistrationPage/index.tsx b/src/scenes/auth/ManualRegistrationPage/index.tsx
index 3fdb10a96363fe23ea0401f6046ff53911f8df65..49bc0db5ebe22d260e668ebc81035a0acfd14331 100644
--- a/src/scenes/auth/ManualRegistrationPage/index.tsx
+++ b/src/scenes/auth/ManualRegistrationPage/index.tsx
@@ -1,8 +1,9 @@
import React, { FC, useContext } from 'react';
import { useAuthEffect, useForm } from 'hooks';
import { ScrollView } from 'react-native-gesture-handler';
+import { useNavigation } from '@react-navigation/core';
-import { BigButton, Toast } from 'components/core';
+import { BigButton, Link, Toast } from 'components/core';
import { Section } from 'components/layout';
import { TextField } from 'components/form';
import { GoogleLoginButton } from '../components';
@@ -10,6 +11,7 @@ import { GoogleLoginButton } from '../components';
import { fieldValidation, initialValues, textField } from './schema';
import { generateValidationSchema } from 'utils/form';
import { UserContext } from 'provider';
+import * as ROUTES from 'constants/routes';
import { layoutStyles } from 'styles';
@@ -18,6 +20,7 @@ const isPasswordField = (name: string) =>
const ManualRegistrationPage: FC = () => {
const { signup, loginWithGoogle, isLoading } = useContext(UserContext);
+ const navigation = useNavigation();
const {
getTextInputProps,
@@ -68,6 +71,12 @@ const ManualRegistrationPage: FC = () => {
+
+ navigation.navigate(ROUTES.login)}
+ />
+
);
};