Selaa lähdekoodia

Added Redux Toolkit

master
Lazar Kostic 2 vuotta sitten
vanhempi
commit
aa0e6bf155
53 muutettua tiedostoa jossa 523 lisäystä ja 1177 poistoa
  1. 11
    24
      App.js
  2. 1
    1
      app.json
  3. 12
    10
      babel.config.js
  4. 2
    2
      components/CustomDrawer/CustomDrawer.jsx
  5. 48
    0
      features/api/apiSlice.js
  6. 30
    0
      features/auth/authApiSlice.js
  7. 31
    0
      features/auth/authSlice.js
  8. 18
    0
      features/posts/postsApiSlice.js
  9. 34
    0
      features/store.js
  10. 0
    1
      hooks/useAuthHook.js
  11. 3
    1
      jsconfig.json
  12. 1
    0
      navigation/AppStack.js
  13. 3
    3
      navigation/RootNavigation.js
  14. 87
    8
      package-lock.json
  15. 4
    1
      package.json
  16. 79
    76
      screens/HomeScreen.jsx
  17. 57
    43
      screens/LoginScreen.jsx
  18. 33
    38
      screens/PostDetailsScreen.jsx
  19. 13
    13
      screens/ProfileScreen.jsx
  20. 56
    43
      screens/RegisterScreen.jsx
  21. 0
    21
      store/actions/actionHelpers/index.js
  22. 0
    5
      store/actions/app/appActionConstants.js
  23. 0
    11
      store/actions/app/appActions.js
  24. 0
    10
      store/actions/authProvider/authProviderActionConstants.js
  25. 0
    20
      store/actions/authProvider/authProviderActions.js
  26. 0
    30
      store/actions/login/loginActionConstants.js
  27. 0
    48
      store/actions/login/loginActions.js
  28. 0
    18
      store/actions/register/registerActionConstants.js
  29. 0
    30
      store/actions/register/registerActions.js
  30. 0
    3
      store/actions/user/userActionConstants.js
  31. 0
    19
      store/actions/user/userActions.js
  32. 0
    32
      store/index.js
  33. 0
    51
      store/middleware/accessTokenMiddleware.js
  34. 0
    22
      store/middleware/authenticationMiddleware.js
  35. 0
    20
      store/middleware/internalServerErrorMiddleware.js
  36. 0
    28
      store/middleware/loadingMiddleware.js
  37. 0
    27
      store/middleware/requestStatusMiddleware.js
  38. 0
    33
      store/reducers/authProvider/authProviderReducer.js
  39. 0
    14
      store/reducers/index.js
  40. 0
    45
      store/reducers/loading/loadingReducer.js
  41. 0
    69
      store/reducers/login/loginReducer.js
  42. 0
    54
      store/reducers/register/registerReducer.js
  43. 0
    37
      store/reducers/user/userReducer.js
  44. 0
    42
      store/saga/authProviderSaga.js
  45. 0
    8
      store/saga/index.js
  46. 0
    82
      store/saga/loginSaga.js
  47. 0
    32
      store/saga/registerSaga.js
  48. 0
    8
      store/selectors/authProviderSelectors.js
  49. 0
    16
      store/selectors/loadingSelectors.js
  50. 0
    29
      store/selectors/loginSelectors.js
  51. 0
    8
      store/selectors/registerSelectors.js
  52. 0
    28
      store/selectors/userSelectors.js
  53. 0
    13
      store/utils/createReducer.js

+ 11
- 24
App.js Näytä tiedosto

import "react-native-gesture-handler"; import "react-native-gesture-handler";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { NavigationContainer } from "@react-navigation/native"; import { NavigationContainer } from "@react-navigation/native";
import { Provider, useDispatch } from "react-redux";
import store from "./store";
import { Provider } from "react-redux";
import { store, persistor } from "@features/store";
import { PersistGate } from "redux-persist/integration/react";
import { ThemeProvider } from "@styles"; import { ThemeProvider } from "@styles";
import '@i18n'
import "@i18n";


import { useFonts } from "expo-font"; import { useFonts } from "expo-font";
import RootNavigation from "./navigation/RootNavigation"; import RootNavigation from "./navigation/RootNavigation";
import { getData, getObjectData } from "@service/asyncStorage";
import {
JWT_REFRESH_TOKEN,
JWT_TOKEN,
LANGUAGE,
} from "@constants/localStorage";
import { fetchUserSuccess } from "@store/actions/login/loginActions";
import { addHeaderToken } from "@request/index";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { getObjectData } from "@service/asyncStorage";
import { LANGUAGE } from "@constants/localStorage";


function App() { function App() {
const { i18n } = useTranslation(); const { i18n } = useTranslation();
const dispatch = useDispatch();
const initialSetup = async () => { const initialSetup = async () => {
const token = await getData(JWT_TOKEN);
const refreshToken = await getData(JWT_REFRESH_TOKEN);
const language = await getObjectData(LANGUAGE); const language = await getObjectData(LANGUAGE);


if (language !== null) { if (language !== null) {
await i18n.changeLanguage(language.code); await i18n.changeLanguage(language.code);
} }

if (token) {
addHeaderToken(token);
}
if (token !== undefined && refreshToken !== undefined) {
dispatch(fetchUserSuccess({ jwt: token, refreshToken }));
}
}; };


useEffect(() => { useEffect(() => {


return ( return (
<Provider store={store}> <Provider store={store}>
<ThemeProvider>
<App />
</ThemeProvider>
<PersistGate loading={null} persistor={persistor}>
<ThemeProvider>
<App />
</ThemeProvider>
</PersistGate>
</Provider> </Provider>
); );
}; };

+ 1
- 1
app.json Näytä tiedosto

"resizeMode": "contain", "resizeMode": "contain",
"backgroundColor": "#ffffff" "backgroundColor": "#ffffff"
}, },
"scheme": "com.diligent.template",
"scheme": "diligent",
"updates": { "updates": {
"fallbackToCacheTimeout": 0 "fallbackToCacheTimeout": 0
}, },

+ 12
- 10
babel.config.js Näytä tiedosto

module.exports = function(api) {
module.exports = function (api) {
api.cache(true); api.cache(true);
return { return {
presets: ['babel-preset-expo'],
presets: ["babel-preset-expo"],
plugins: [ plugins: [
'react-native-reanimated/plugin',
"react-native-reanimated/plugin",
[ [
"module-resolver", "module-resolver",
{ {
"@components": "./components", "@components": "./components",
"@screens": "./screens", "@screens": "./screens",
"@assets": "./assets", "@assets": "./assets",
"@store": './store',
"@styles": './styles',
"@store": "./store",
"@styles": "./styles",
"@utils": "./utils", "@utils": "./utils",
"@schemas": "./schemas", "@schemas": "./schemas",
"@initialValues": "./initialValues", "@initialValues": "./initialValues",
"@constants": "./constants", "@constants": "./constants",
"@service": "./service", "@service": "./service",
"@i18n": "./i18n", "@i18n": "./i18n",
"@request": "./request"
}
}
]
]
"@request": "./request",
"@features": "./features",
"@hooks": "./hooks",
},
},
],
],
}; };
}; };

+ 2
- 2
components/CustomDrawer/CustomDrawer.jsx Näytä tiedosto

import { MaterialIcons } from "@expo/vector-icons"; import { MaterialIcons } from "@expo/vector-icons";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { logoutUser } from "@store/actions/login/loginActions";
import { logOut } from "@features/auth/authSlice";
import useAuthHook from "../../hooks/useAuthHook"; import useAuthHook from "../../hooks/useAuthHook";
import { useTheme } from "@styles"; import { useTheme } from "@styles";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";


const handleLogout = async () => { const handleLogout = async () => {
logoutAuthProvider(); logoutAuthProvider();
dispatch(logoutUser());
dispatch(logOut());
}; };


return ( return (

+ 48
- 0
features/api/apiSlice.js Näytä tiedosto

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { logOut, setCredentials } from "../auth/authSlice";
import jwt_decode from "jwt-decode";

const baseQuery = fetchBaseQuery({
baseUrl: "https://strapi.dilig.net/api",
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token;
if (token) {
headers.set("Authorization", `Bearer ${token.jwt}`);
}
headers.set("Content-Type", "application/json");
return headers;
},
});

const baseQueryWithReauth = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions);
if (result?.error?.status === 401) {
const token = api.getState().auth.token;
const jwtTokenDecoded = jwt_decode(token.jwt);
if (new Date() > new Date(jwtTokenDecoded.exp * 1000)) {
const refreshResult = await baseQuery(
{
url: "/token/refresh",
method: "POST",
body: { refreshToken: token.refreshToken },
},
api,
extraOptions
);
if (refreshResult?.data) {
const user = api.getState().auth.user;
api.dispatch(setCredentials({ ...refreshResult.data, user }));
result = await baseQuery(args, api, extraOptions);
} else {
api.dispatch(logOut());
}
}
}
return result;
};

export const apiSlice = createApi({
baseQuery: baseQueryWithReauth,
// eslint-disable-next-line no-unused-vars
endpoints: (builder) => ({}),
});

+ 30
- 0
features/auth/authApiSlice.js Näytä tiedosto

import { apiSlice } from "@features/api/apiSlice";

export const authApiSlice = apiSlice.injectEndpoints({
endpoints: (builder) => ({
login: builder.mutation({
providesTags: ["User"],
query: (credentials) => ({
url: "/auth/local",
method: "POST",
body: { ...credentials },
}),
}),
register: builder.mutation({
query: (credentials) => ({
url: "/auth/local/register",
method: "POST",
body: { ...credentials },
}),
}),
authProvider: builder.mutation({
query: ({ provider, accessToken }) => ({
url: `/auth/${provider}/callback?access_token=${accessToken}`,
method: 'GET'
}),
}),
}),
});

export const { useLoginMutation, useRegisterMutation, useAuthProviderMutation } =
authApiSlice;

+ 31
- 0
features/auth/authSlice.js Näytä tiedosto

import { createSlice } from "@reduxjs/toolkit";
import { createSelector } from "reselect";

const authSlice = createSlice({
name: "auth",
initialState: { user: null, token: null },
reducers: {
setCredentials: (state, action) => {
const { user, jwt, refreshToken } = action.payload;
state.user = user;
state.token = { jwt, refreshToken };
},
logOut: (state) => {
state.user = null;
state.token = null;
},
},
});

export const { setCredentials, logOut } = authSlice.actions;
export default authSlice.reducer;

export const authSelector = (state) => state.auth;
export const selectCurrentUser = createSelector(
authSelector,
(state) => state.user
);
export const selectCurrentToken = createSelector(
authSelector,
(state) => state.token
);

+ 18
- 0
features/posts/postsApiSlice.js Näytä tiedosto

import { apiSlice } from "@features/api/apiSlice";

export const postsApiSlice = apiSlice.injectEndpoints({
endpoints: (builder) => ({
allPosts: builder.query({
query: () => ({
url: "/posts?populate=*",
}),
}),
singlePost: builder.query({
query: (postId) => ({
url: `/posts/${postId}?populate=*`,
}),
}),
}),
});

export const { useAllPostsQuery, useSinglePostQuery } = postsApiSlice;

+ 34
- 0
features/store.js Näytä tiedosto

import { configureStore } from "@reduxjs/toolkit";
import { apiSlice } from "./api/apiSlice";
import authReducer from "./auth/authSlice";
import {
persistReducer,
persistStore,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from "redux-persist";
import AsyncStorage from "@react-native-async-storage/async-storage";

const authPersistConfig = {
key: "auth",
storage: AsyncStorage,
};

export const store = configureStore({
reducer: {
[apiSlice.reducerPath]: apiSlice.reducer,
auth: persistReducer(authPersistConfig, authReducer),
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat(apiSlice.middleware),
});

export const persistor = persistStore(store);

+ 0
- 1
hooks/useAuthHook.js Näytä tiedosto

import React from "react";
import * as Google from "expo-auth-session/providers/google"; import * as Google from "expo-auth-session/providers/google";
import { getData } from "../service/asyncStorage"; import { getData } from "../service/asyncStorage";
import { ACCESS_TOKEN } from "../constants/localStorage"; import { ACCESS_TOKEN } from "../constants/localStorage";

+ 3
- 1
jsconfig.json Näytä tiedosto

"@constants/*": ["./constants/*"], "@constants/*": ["./constants/*"],
"@service/*": ["./service/*"], "@service/*": ["./service/*"],
"@i18n/*": ["./i18n/*"], "@i18n/*": ["./i18n/*"],
"@request/*": ["./request/*"]
"@request/*": ["./request/*"],
"@features/*": ["./features/*"],
"@hooks/*": ["./hooks/*"]
} }
}, },
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]

+ 1
- 0
navigation/AppStack.js Näytä tiedosto

name="Home" name="Home"
component={TabNavigator} component={TabNavigator}
options={{ options={{
unmountOnBlur: true,
title:t("sidebar.home"), title:t("sidebar.home"),
drawerIcon: ({ color }) => ( drawerIcon: ({ color }) => (
<Ionicons name="home-outline" size={22} color={color} /> <Ionicons name="home-outline" size={22} color={color} />

+ 3
- 3
navigation/RootNavigation.js Näytä tiedosto

import AuthStack from "./AuthStack"; import AuthStack from "./AuthStack";
import { SafeAreaView } from "react-native-safe-area-context"; import { SafeAreaView } from "react-native-safe-area-context";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { selectTokens } from "@store/selectors/loginSelectors";
import { selectCurrentToken } from "@features/auth/authSlice";
import { StatusBar } from "expo-status-bar"; import { StatusBar } from "expo-status-bar";
import { useTheme } from "@styles"; import { useTheme } from "@styles";


const RootNavigation = () => { const RootNavigation = () => {
const { isDark, colors } = useTheme(); const { isDark, colors } = useTheme();
const tokens = useSelector(selectTokens);
const tokens = useSelector(selectCurrentToken);


return !tokens.JwtToken ? (
return !tokens ? (
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}> <SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
<StatusBar <StatusBar
backgroundColor={colors.background} backgroundColor={colors.background}

+ 87
- 8
package-lock.json Näytä tiedosto

"@react-navigation/drawer": "^6.5.5", "@react-navigation/drawer": "^6.5.5",
"@react-navigation/native": "^6.0.16", "@react-navigation/native": "^6.0.16",
"@react-navigation/native-stack": "^6.9.4", "@react-navigation/native-stack": "^6.9.4",
"@reduxjs/toolkit": "^1.9.5",
"axios": "^1.2.1", "axios": "^1.2.1",
"expo": "^48.0.19", "expo": "^48.0.19",
"expo-auth-session": "~4.0.3", "expo-auth-session": "~4.0.3",
"react-native-svg-transformer": "^1.0.0", "react-native-svg-transformer": "^1.0.0",
"react-native-vector-icons": "^9.2.0", "react-native-vector-icons": "^9.2.0",
"react-native-web": "~0.18.7", "react-native-web": "~0.18.7",
"react-redux": "^8.0.5",
"react-redux": "^8.1.1",
"redux": "^4.2.0", "redux": "^4.2.0",
"redux-persist": "^6.0.0",
"redux-saga": "^1.2.2", "redux-saga": "^1.2.2",
"reselect": "^4.1.8",
"yup": "^0.32.11" "yup": "^0.32.11"
}, },
"devDependencies": { "devDependencies": {
"resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz",
"integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==" "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA=="
}, },
"node_modules/@reduxjs/toolkit": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
"integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
"dependencies": {
"immer": "^9.0.21",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"reselect": "^4.1.8"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.0.2"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@segment/loosely-validate-event": { "node_modules/@segment/loosely-validate-event": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz",
"node": ">=4.0" "node": ">=4.0"
} }
}, },
"node_modules/immer": {
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
} }
}, },
"node_modules/react-redux": { "node_modules/react-redux": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
"integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz",
"integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.12.1", "@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1", "@types/hoist-non-react-statics": "^3.3.1",
"react": "^16.8 || ^17.0 || ^18.0", "react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0",
"react-native": ">=0.59", "react-native": ">=0.59",
"redux": "^4"
"redux": "^4 || ^5.0.0-beta.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@types/react": { "@types/react": {
"@babel/runtime": "^7.9.2" "@babel/runtime": "^7.9.2"
} }
}, },
"node_modules/redux-persist": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==",
"peerDependencies": {
"redux": ">4.0.0"
}
},
"node_modules/redux-saga": { "node_modules/redux-saga": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz", "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz",
"@redux-saga/core": "^1.2.3" "@redux-saga/core": "^1.2.3"
} }
}, },
"node_modules/redux-thunk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
"integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"peerDependencies": {
"redux": "^4"
}
},
"node_modules/regenerate": { "node_modules/regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
"resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz",
"integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==" "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA=="
}, },
"@reduxjs/toolkit": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
"integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
"requires": {
"immer": "^9.0.21",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"reselect": "^4.1.8"
}
},
"@segment/loosely-validate-event": { "@segment/loosely-validate-event": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
"integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==" "integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA=="
}, },
"immer": {
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA=="
},
"import-fresh": { "import-fresh": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
} }
}, },
"react-redux": { "react-redux": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
"integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz",
"integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==",
"requires": { "requires": {
"@babel/runtime": "^7.12.1", "@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1", "@types/hoist-non-react-statics": "^3.3.1",
"@babel/runtime": "^7.9.2" "@babel/runtime": "^7.9.2"
} }
}, },
"redux-persist": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==",
"requires": {}
},
"redux-saga": { "redux-saga": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz", "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz",
"@redux-saga/core": "^1.2.3" "@redux-saga/core": "^1.2.3"
} }
}, },
"redux-thunk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
"integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"requires": {}
},
"regenerate": { "regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",

+ 4
- 1
package.json Näytä tiedosto

"@react-navigation/drawer": "^6.5.5", "@react-navigation/drawer": "^6.5.5",
"@react-navigation/native": "^6.0.16", "@react-navigation/native": "^6.0.16",
"@react-navigation/native-stack": "^6.9.4", "@react-navigation/native-stack": "^6.9.4",
"@reduxjs/toolkit": "^1.9.5",
"axios": "^1.2.1", "axios": "^1.2.1",
"expo": "^48.0.19", "expo": "^48.0.19",
"expo-auth-session": "~4.0.3", "expo-auth-session": "~4.0.3",
"react-native-svg-transformer": "^1.0.0", "react-native-svg-transformer": "^1.0.0",
"react-native-vector-icons": "^9.2.0", "react-native-vector-icons": "^9.2.0",
"react-native-web": "~0.18.7", "react-native-web": "~0.18.7",
"react-redux": "^8.0.5",
"react-redux": "^8.1.1",
"redux": "^4.2.0", "redux": "^4.2.0",
"redux-persist": "^6.0.0",
"redux-saga": "^1.2.2", "redux-saga": "^1.2.2",
"reselect": "^4.1.8",
"yup": "^0.32.11" "yup": "^0.32.11"
}, },
"devDependencies": { "devDependencies": {

+ 79
- 76
screens/HomeScreen.jsx Näytä tiedosto

import ListItem from "@components/ListItem/ListItem"; import ListItem from "@components/ListItem/ListItem";
import filter from "lodash.filter"; import filter from "lodash.filter";
import { globalStyles } from "@styles/global"; import { globalStyles } from "@styles/global";
import { getRequest } from "@request/index";
import Layout from "@components/Layout/Layout"; import Layout from "@components/Layout/Layout";
import { useTheme } from "@styles"; import { useTheme } from "@styles";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useAllPostsQuery } from "@features/posts/postsApiSlice";
import Loader from "@components/Loader";


const HomeScreen = ({ navigation }) => { const HomeScreen = ({ navigation }) => {
const { colors } = useTheme(); const { colors } = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const [posts, setPosts] = useState([]);
const { data, isLoading } = useAllPostsQuery();


const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const [filteredData, setFilteredData] = useState(posts);
const [posts, setPosts] = useState([]);
const [filteredData, setFilteredData] = useState([]);


const contains = (name, search) => { const contains = (name, search) => {
if (name.includes(search)) { if (name.includes(search)) {
setQuery(formatedText); setQuery(formatedText);
}; };


const fetchAll = async () => {
const { data } = await getRequest("api/posts?populate=*");

useEffect(() => {
if (data?.data) { if (data?.data) {
setPosts(data?.data); setPosts(data?.data);
} }
};

useEffect(() => {
fetchAll();
}, []);
}, [data]);


useEffect(() => { useEffect(() => {
if (posts) { if (posts) {
}, [posts]); }, [posts]);
return ( return (
<Layout> <Layout>
<View style={styles.wrapper}>
<Text style={{ fontSize: 18, color: colors.textPrimary }}>
{t("common.hello")}, Diligent
</Text>
<TouchableOpacity onPress={() => navigation.openDrawer()}>
<ImageBackground
source={require("../assets/images/diligent-purple.png")}
style={styles.imageBackground}
imageStyle={{ borderRadius: 25 }}
/>
</TouchableOpacity>
</View>
<View style={styles.search}>
<Feather
name="search"
size={20}
color="#C6C6C6"
style={{ marginRight: 5 }}
/>
<TextInput
onChangeText={(text) => searchFilter(text)}
autoCapitalize="none"
autoCorrect={false}
placeholder={t('common.search')}
value={query}
placeholderTextColor="#C6C6C6"
style={{ flex: 1, color: colors.textPrimary }}
<Loader visible={isLoading} />
<View style={styles.wrapper}>
<Text style={{ fontSize: 18, color: colors.textPrimary }}>
{t("common.hello")}, Diligent
</Text>
<TouchableOpacity onPress={() => navigation.openDrawer()}>
<ImageBackground
source={require("../assets/images/diligent-purple.png")}
style={styles.imageBackground}
imageStyle={{ borderRadius: 25 }}
/> />
</View>
<Text>
{filteredData.length === 0 && (
<View>
</TouchableOpacity>
</View>
<View style={styles.search}>
<Feather
name="search"
size={20}
color="#C6C6C6"
style={{ marginRight: 5 }}
/>
<TextInput
onChangeText={(text) => searchFilter(text)}
autoCapitalize="none"
autoCorrect={false}
placeholder={t("common.search")}
value={query}
placeholderTextColor="#C6C6C6"
style={{ flex: 1, color: colors.textPrimary }}
/>
</View>
<Text>
{filteredData?.length === 0 && (
<View style={{paddingHorizontal: 18}}>
<Text <Text
style={[globalStyles.boldText, { color: colors.textPrimary }]} style={[globalStyles.boldText, { color: colors.textPrimary }]}
> >
</Text> </Text>
</View> </View>
)} )}
</Text>
<ScrollView style={{flex: 1,paddingHorizontal: 18}}>
{query.length === 0
? posts?.map((post) => (
<ListItem
key={post?.id}
title={post?.attributes?.title}
photo={post?.attributes?.profileImage?.data?.attributes?.formats}
publishedAt={post?.attributes?.publishedAt}
onPress={() =>
navigation.navigate("PostDetails", {
title: post?.attributes?.title,
id: post?.id,
})
}
/>
))
: filteredData.map((post) => (
<ListItem
key={post?.id}
title={post?.attributes?.title}
photo={post?.attributes?.profileImage?.data?.attributes?.formats}
publishedAt={post?.attributes?.publishedAt}
onPress={() =>
navigation.navigate("PostDetails", {
title: post?.attributes?.title,
id: post?.id,
})
}
/>
))}
</ScrollView>
{!isLoading && (
<ScrollView style={{ flex: 1, paddingHorizontal: 18 }}>
{query.length === 0
? posts?.map((post) => (
<ListItem
key={post?.id}
title={post?.attributes?.title}
photo={
post?.attributes?.profileImage?.data?.attributes?.formats
}
publishedAt={post?.attributes?.publishedAt}
onPress={() =>
navigation.navigate("PostDetails", {
title: post?.attributes?.title,
id: post?.id,
})
}
/>
))
: filteredData.map((post) => (
<ListItem
key={post?.id}
title={post?.attributes?.title}
photo={
post?.attributes?.profileImage?.data?.attributes?.formats
}
publishedAt={post?.attributes?.publishedAt}
onPress={() =>
navigation.navigate("PostDetails", {
title: post?.attributes?.title,
id: post?.id,
})
}
/>
))}
</ScrollView>
)}
</Text>
</Layout> </Layout>
); );
}; };
flexDirection: "row", flexDirection: "row",
justifyContent: "space-between", justifyContent: "space-between",
marginBottom: 20, marginBottom: 20,
padding: 18
padding: 18,
}, },
imageBackground: { imageBackground: {
width: 35, width: 35,
paddingHorizontal: 10, paddingHorizontal: 10,
paddingVertical: 8, paddingVertical: 8,
marginBottom: 20, marginBottom: 20,
marginHorizontal: 18
marginHorizontal: 18,
}, },
}); });



+ 57
- 43
screens/LoginScreen.jsx Näytä tiedosto

import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native"; import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import MaterialIcons from "@expo/vector-icons/MaterialIcons"; import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import Ionicons from "@expo/vector-icons/Ionicons"; import Ionicons from "@expo/vector-icons/Ionicons";

import LoginSVG from "@assets/images/login.svg"; import LoginSVG from "@assets/images/login.svg";
import GoogleSVG from "@assets/images/google.svg"; import GoogleSVG from "@assets/images/google.svg";
import FacebookSVG from "@assets/images/facebook.svg"; import FacebookSVG from "@assets/images/facebook.svg";
import TwitterSVG from "@assets/images/twitter.svg"; import TwitterSVG from "@assets/images/twitter.svg";

import CustomButton from "@components/Buttons/CustomButton"; import CustomButton from "@components/Buttons/CustomButton";
import InputField from "@components/InputField"; import InputField from "@components/InputField";
import { globalStyles } from "@styles/global"; import { globalStyles } from "@styles/global";
import Loader from "@components/Loader"; import Loader from "@components/Loader";
import { Formik } from "formik"; import { Formik } from "formik";
import { loginSchema } from "@schemas/loginSchema"; import { loginSchema } from "@schemas/loginSchema";
import { useDispatch, useSelector } from "react-redux";
import { selectLoginError } from "@store/selectors/loginSelectors";
import { clearLoginErrors, fetchUser } from "@store/actions/login/loginActions";
import { selectIsLoadingByActionType } from "@store/selectors/loadingSelectors";
import { LOGIN_USER_SCOPE } from "@store/actions/login/loginActionConstants";
import { fetchAuthProvider } from "@store/actions/authProvider/authProviderActions";
import { useDispatch } from "react-redux";
import useAuthHook from "../hooks/useAuthHook"; import useAuthHook from "../hooks/useAuthHook";
import { storeData } from "@service/asyncStorage";
import { ACCESS_TOKEN } from "@constants/localStorage";
import Layout from "@components/Layout/Layout"; import Layout from "@components/Layout/Layout";
import { useTheme } from "@styles"; import { useTheme } from "@styles";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useLoginMutation } from "@features/auth/authApiSlice";
import { setCredentials } from "@features/auth/authSlice";
import { useAuthProviderMutation } from "@features/auth/authApiSlice";
import { storeData } from "@service/asyncStorage";
import { ACCESS_TOKEN } from "@constants/localStorage";


const LoginScreen = ({ navigation }) => { const LoginScreen = ({ navigation }) => {
const [authProvider, { isLoading: isLoadingProvider }] =
useAuthProviderMutation();
const [currentProvider, setCurrentProvider] = useState("");
const { colors } = useTheme(); const { colors } = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const [login, { isLoading, error }] = useLoginMutation();
const { response, promptAsync } = useAuthHook(); const { response, promptAsync } = useAuthHook();
const dispatch = useDispatch(); const dispatch = useDispatch();
const error = useSelector(selectLoginError);

const isLoading = useSelector(selectIsLoadingByActionType(LOGIN_USER_SCOPE));

const storeToken = async (token) => {
await storeData(ACCESS_TOKEN, token);
};


useEffect(() => {
const authProviderHandler = async (response) => {
if (response?.type === "success") { if (response?.type === "success") {
const accessToken = response.authentication.accessToken; const accessToken = response.authentication.accessToken;
if (accessToken) { if (accessToken) {
storeToken(accessToken);
dispatch(fetchAuthProvider({ accessToken }));
await storeData(ACCESS_TOKEN, accessToken)
try {
const userResponse = await authProvider({
provider: currentProvider,
accessToken,
}).unwrap();
if (userResponse) {
dispatch(setCredentials(userResponse));
}
} catch (e) {
console.log(e);
}
} }
} }
}, [response]);
};

useEffect(() => {
authProviderHandler(response);
}, [response, currentProvider]);


const handleGoogleAuth = () => { const handleGoogleAuth = () => {
promptAsync({ useProxy: true, showInRecents: true });
promptAsync();
}; };


const handleLogin = (values) => {
const { email, password } = values;
dispatch(clearLoginErrors());
dispatch(
fetchUser({
identifier: email,
password,
})
);
const handleLogin = async (values) => {
const { email: identifier, password } = values;
try {
const userData = await login({ identifier, password }).unwrap();
dispatch(setCredentials(userData));
} catch (e) {
console.log("Login error", e);
}
}; };


return ( return (
<Layout> <Layout>
<Loader visible={isLoading} />
<Loader visible={isLoading || isLoadingProvider} />
<View style={{ paddingHorizontal: 25 }}> <View style={{ paddingHorizontal: 25 }}>
<View style={{ alignItems: "center" }}> <View style={{ alignItems: "center" }}>
<LoginSVG height={300} width={300} /> <LoginSVG height={300} width={300} />
<Text <Text
style={[globalStyles.boldText, { color: colors.textPrimary }]} style={[globalStyles.boldText, { color: colors.textPrimary }]}
> >
{t('login.signIn')}
{t("login.signIn")}
</Text> </Text>
<InputField <InputField
name="email" name="email"
label={t('login.email')}
label={t("login.email")}
keyboardType="email-address" keyboardType="email-address"
onChangeText={handleChange("email")} onChangeText={handleChange("email")}
text={values.email} text={values.email}
)} )}
<InputField <InputField
name="password" name="password"
label={t('login.password')}
filedButtonLabel={t('register.forgot')}
label={t("login.password")}
filedButtonLabel={t("register.forgot")}
fieldButtonFunction={() => {}} fieldButtonFunction={() => {}}
inputType="password" inputType="password"
handleBlur={handleBlur("password")} handleBlur={handleBlur("password")}
{errors.password && ( {errors.password && (
<Text style={styles.errorMessage}>{errors.password}</Text> <Text style={styles.errorMessage}>{errors.password}</Text>
)} )}
{error && <Text style={styles.errorMessage}>{error}</Text>}
<CustomButton label={t('login.login')} onPress={handleSubmit} />
{error && (
<Text style={styles.errorMessage}>
{error?.data?.error?.message}
</Text>
)}
<CustomButton label={t("login.login")} onPress={handleSubmit} />
</> </>
)} )}
</Formik> </Formik>
{ color: colors.textPrimary }, { color: colors.textPrimary },
]} ]}
> >
{t('login.orLoginWith')}
{t("login.orLoginWith")}
</Text> </Text>
<View style={styles.providersContainer}> <View style={styles.providersContainer}>
<TouchableOpacity <TouchableOpacity
onPress={handleGoogleAuth}
onPress={() => {
setCurrentProvider("google");
handleGoogleAuth();
}}
style={globalStyles.iconButton} style={globalStyles.iconButton}
> >
<GoogleSVG height={24} width={24} /> <GoogleSVG height={24} width={24} />
<Text <Text
style={[globalStyles.regularText, { color: colors.textPrimary }]} style={[globalStyles.regularText, { color: colors.textPrimary }]}
> >
{t('login.needAccount')}{" "}
{t("login.needAccount")}{" "}
</Text> </Text>
<TouchableOpacity onPress={() => navigation.navigate("Register")}> <TouchableOpacity onPress={() => navigation.navigate("Register")}>
<Text style={styles.registerButtonText}>{t('login.signUp')}</Text>
<Text style={styles.registerButtonText}>{t("login.signUp")}</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</View> </View>

+ 33
- 38
screens/PostDetailsScreen.jsx Näytä tiedosto

import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { Text, Image, StyleSheet } from "react-native"; import { Text, Image, StyleSheet } from "react-native";
import { getRequest } from "@request/index";
import { globalStyles } from "@styles/global"; import { globalStyles } from "@styles/global";
import { windowWidth } from "@utils/Dimensions"; import { windowWidth } from "@utils/Dimensions";
import Layout from "@components/Layout/Layout"; import Layout from "@components/Layout/Layout";
import { useTheme } from "@styles"; import { useTheme } from "@styles";
import { useSinglePostQuery } from "@features/posts/postsApiSlice";
import Loader from "@components/Loader";


const PostDetailsScreen = ({ navigation, route }) => { const PostDetailsScreen = ({ navigation, route }) => {
const [post, setPost] = useState({});
const { colors } = useTheme(); const { colors } = useTheme();


const fetchPost = async () => {
const { data } = await getRequest(
`api/posts/${route.params.id}?populate=*`
);
if (data) {
setPost(data.data);
}
};

useEffect(() => {
fetchPost();
}, []);
const { data: post, isLoading } = useSinglePostQuery(route.params.id);


return ( return (
<Layout> <Layout>
<Image
style={styles.image}
source={{
uri: `https://strapi.dilig.net${post?.attributes?.profileImage.data.attributes.url}`,
}}
/>
<Text
style={[
globalStyles.boldText,
styles.title,
{ color: colors.textPrimary },
]}
>
{post?.attributes?.title}
</Text>
<Text
style={[
globalStyles.regularText,
styles.description,
{ color: colors.textPrimary },
]}
>
{post?.attributes?.description}
</Text>
{isLoading ? (
<Loader visible={isLoading} />
) : (
<>
<Image
style={styles.image}
source={{
uri: `https://strapi.dilig.net${post.data?.attributes?.profileImage.data.attributes.url}`,
}}
/>
<Text
style={[
globalStyles.boldText,
styles.title,
{ color: colors.textPrimary },
]}
>
{post?.data?.attributes?.title}
</Text>
<Text
style={[
globalStyles.regularText,
styles.description,
{ color: colors.textPrimary },
]}
>
{post?.data?.attributes?.description}
</Text>
</>
)}
</Layout> </Layout>
); );
}; };

+ 13
- 13
screens/ProfileScreen.jsx Näytä tiedosto

import { useTheme } from "@styles"; import { useTheme } from "@styles";


const ProfileScreen = () => { const ProfileScreen = () => {
const { colors } = useTheme();
return (
<Layout>
const { colors } = useTheme();
return (
<Layout>
<View style={styles.container}> <View style={styles.container}>
<Text style={{color: colors.textPrimary}}>Profile</Text>
<Text style={{ color: colors.textPrimary }}>Profile</Text>
</View> </View>
</Layout> </Layout>
)
}
);
};


const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});


export default ProfileScreen;
export default ProfileScreen;

+ 56
- 43
screens/RegisterScreen.jsx Näytä tiedosto

import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import { import {
ScrollView, ScrollView,
View, View,
StyleSheet, StyleSheet,
Alert, Alert,
} from "react-native"; } from "react-native";

import InputField from "@components/InputField"; import InputField from "@components/InputField";

import MaterialIcons from "@expo/vector-icons/MaterialIcons"; import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import Ionicons from "@expo/vector-icons/Ionicons"; import Ionicons from "@expo/vector-icons/Ionicons";

import RegistrationSVG from "@assets/images/registration.svg"; import RegistrationSVG from "@assets/images/registration.svg";
import GoogleSVG from "@assets/images/google.svg"; import GoogleSVG from "@assets/images/google.svg";
import FacebookSVG from "@assets/images/facebook.svg"; import FacebookSVG from "@assets/images/facebook.svg";
import TwitterSVG from "@assets/images/twitter.svg"; import TwitterSVG from "@assets/images/twitter.svg";

import CustomButton from "@components/Buttons/CustomButton"; import CustomButton from "@components/Buttons/CustomButton";
import { globalStyles } from "@styles/global"; import { globalStyles } from "@styles/global";
import Loader from "@components/Loader"; import Loader from "@components/Loader";
import { Formik } from "formik"; import { Formik } from "formik";
import { registerSchema } from "@schemas/registerSchema"; import { registerSchema } from "@schemas/registerSchema";
import { useDispatch, useSelector } from "react-redux";
import { selectRegisterError } from "@store/selectors/registerSelectors";
import { selectIsLoadingByActionType } from "@store/selectors/loadingSelectors";
import { REGISTER_USER_SCOPE } from "@store/actions/register/registerActionConstants";
import {
clearRegisterErrors,
registerUser,
} from "@store/actions/register/registerActions";
import { useDispatch } from "react-redux";
import useAuthHook from "../hooks/useAuthHook"; import useAuthHook from "../hooks/useAuthHook";
import { fetchAuthProvider } from "@store/actions/authProvider/authProviderActions";
import { ACCESS_TOKEN } from "@constants/localStorage";
import { storeData } from "@service/asyncStorage";
import Layout from "@components/Layout/Layout"; import Layout from "@components/Layout/Layout";
import { useTheme } from "@styles"; import { useTheme } from "@styles";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import {
useAuthProviderMutation,
useRegisterMutation,
} from "@features/auth/authApiSlice";
import { setCredentials } from "@features/auth/authSlice";
import { storeData } from "@service/asyncStorage";
import { ACCESS_TOKEN } from "@constants/localStorage";


const RegisterScreen = ({ navigation }) => { const RegisterScreen = ({ navigation }) => {
const [authProvider, { isLoading: isLoadingProvider }] =
useAuthProviderMutation();
const [currentProvider, setCurrentProvider] = useState("");
const { colors } = useTheme(); const { colors } = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const [register, { isLoading, error }] = useRegisterMutation();
const { response, promptAsync } = useAuthHook(); const { response, promptAsync } = useAuthHook();
const dispatch = useDispatch(); const dispatch = useDispatch();
const error = useSelector(selectRegisterError);

const isLoading = useSelector(
selectIsLoadingByActionType(REGISTER_USER_SCOPE)
);

const storeToken = async (token) => {
await storeData(ACCESS_TOKEN, token);
};


const handleApiResponseSuccess = () => { const handleApiResponseSuccess = () => {
Alert.alert(t("common.success"), t("register.successRegisterAccount"), [ Alert.alert(t("common.success"), t("register.successRegisterAccount"), [
]); ]);
}; };


const handleSignup = (values) => {
const { username, email, password } = values;
dispatch(clearRegisterErrors());
dispatch(
registerUser({ username, email, password, handleApiResponseSuccess })
);
};

const handleGoogleAuth = () => {
promptAsync({ useProxy: true, showInRecents: true });
};

useEffect(() => {
const authProviderHandler = async (response) => {
if (response?.type === "success") { if (response?.type === "success") {
const accessToken = response.authentication.accessToken; const accessToken = response.authentication.accessToken;
if (accessToken) { if (accessToken) {
storeToken(accessToken);
dispatch(fetchAuthProvider({ accessToken }));
await storeData(ACCESS_TOKEN, accessToken);
try {
const userResponse = await authProvider({
provider: currentProvider,
accessToken,
}).unwrap();
if (userResponse) {
dispatch(setCredentials(userResponse));
}
} catch (e) {
console.log(e);
}
} }
} }
}, [response]);
};

useEffect(() => {
authProviderHandler(response);
}, [response, currentProvider]);

const handleSignup = async (values) => {
const { username, email, password } = values;
try {
const userData = await register({ username, email, password }).unwrap();
if (userData) {
handleApiResponseSuccess();
}
} catch (e) {
console.log(e?.data);
}
};

const handleGoogleAuth = () => {
promptAsync();
};


return ( return (
<Layout> <Layout>
</Text> </Text>
<View style={styles.providersContainer}> <View style={styles.providersContainer}>
<TouchableOpacity <TouchableOpacity
onPress={handleGoogleAuth}
onPress={() => {
setCurrentProvider("google");
handleGoogleAuth();
}}
style={globalStyles.iconButton} style={globalStyles.iconButton}
> >
<GoogleSVG height={24} width={24} /> <GoogleSVG height={24} width={24} />
{errors.confirmPassword} {errors.confirmPassword}
</Text> </Text>
)} )}
{error && <Text style={styles.errorMessage}>{error}</Text>}
{error && (
<Text style={styles.errorMessage}>
{error?.data?.error?.message}
</Text>
)}
<CustomButton <CustomButton
label={t("register.signUp")} label={t("register.signUp")}
onPress={handleSubmit} onPress={handleSubmit}

+ 0
- 21
store/actions/actionHelpers/index.js Näytä tiedosto

export const FETCH = '[FETCH]';
export const UPDATE = '[UPDATE]';
export const SUCCESS = '[SUCCESS]';
export const SET = '[SET]';
export const ERROR = '[ERROR]';
export const SUBMIT = '[SUBMIT]';
export const CLEAR = '[CLEAR]';
export const DELETE = '[DELETE]';
export const LOADING = '[LOADING]';

const createType = (typeDescription) => (type) => `${typeDescription}${type}`;

export const createFetchType = createType(FETCH);
export const createUpdateType = createType(UPDATE);
export const createSuccessType = createType(SUCCESS);
export const createSetType = createType(SET);
export const createErrorType = createType(ERROR);
export const createSubmitType = createType(SUBMIT);
export const createClearType = createType(CLEAR);
export const createDeleteType = createType(DELETE);
export const createLoadingType = createType(LOADING);

+ 0
- 5
store/actions/app/appActionConstants.js Näytä tiedosto

import { createLoadingType } from "../actionHelpers";

export const APP_LOADING = createLoadingType("APP_LOADING");
export const ADD_LOADER = createLoadingType("ADD_LOADER");
export const REMOVE_LOADER = createLoadingType("REMOVE_LOADER");

+ 0
- 11
store/actions/app/appActions.js Näytä tiedosto

import { ADD_LOADER, REMOVE_LOADER } from "./appActionConstants";

export const addLoader = (payload) => ({
type: ADD_LOADER,
payload,
});

export const removeLoader = (payload) => ({
type: REMOVE_LOADER,
payload,
});

+ 0
- 10
store/actions/authProvider/authProviderActionConstants.js Näytä tiedosto

import {
createErrorType,
createFetchType,
createSuccessType,
} from "../actionHelpers";

export const AUTH_PROVIDER_SCOPE = "AUTH_PROVIDER";
export const AUTH_PROVIDER_FETCH = createFetchType(AUTH_PROVIDER_SCOPE);
export const AUTH_PROVIDER_SUCCESS = createSuccessType(AUTH_PROVIDER_SCOPE);
export const AUTH_PROVIDER_ERROR = createErrorType(AUTH_PROVIDER_SCOPE);

+ 0
- 20
store/actions/authProvider/authProviderActions.js Näytä tiedosto

import {
AUTH_PROVIDER_ERROR,
AUTH_PROVIDER_FETCH,
AUTH_PROVIDER_SUCCESS,
} from "./authProviderActionConstants";

export const fetchAuthProvider = (payload) => ({
type: AUTH_PROVIDER_FETCH,
payload,
});

export const fetchAuthProviderSuccess = (payload) => ({
type: AUTH_PROVIDER_SUCCESS,
payload,
});

export const fetchAuthProviderError = (payload) => ({
type: AUTH_PROVIDER_ERROR,
payload,
});

+ 0
- 30
store/actions/login/loginActionConstants.js Näytä tiedosto

import {
createClearType,
createErrorType,
createFetchType,
createLoadingType,
createSuccessType,
createSubmitType,
} from '../actionHelpers';


export const LOGIN_USER_SCOPE = 'LOGIN_USER';
export const LOGIN_USER_FETCH = createFetchType(LOGIN_USER_SCOPE);
export const LOGIN_USER_SUCCESS = createSuccessType(LOGIN_USER_SCOPE);
export const LOGIN_USER_ERROR = createErrorType(LOGIN_USER_SCOPE);
export const CLEAR_LOGIN_USER_ERROR = createClearType(
`${LOGIN_USER_SCOPE}_ERROR`,
);
export const LOGIN_USER_LOADING = createLoadingType(LOGIN_USER_SCOPE);


export const UPDATE_USER_JWT_TOKEN = 'UPDATE_USER_JWT_TOKEN';
export const RESET_LOGIN_STATE = 'RESET_LOGIN_STATE';
export const AUTHENTICATE_USER = 'AUTHENTICATE_USER';
export const LOGOUT_USER = 'LOGOUT_USER';
export const REFRESH_TOKEN = 'REFRESH_TOKEN';

const GENERATE_TOKEN_SCOPE = 'GENERATE_TOKEN';
export const GENERATE_TOKEN = createSubmitType(GENERATE_TOKEN_SCOPE);
export const GENERATE_TOKEN_SUCCESS = createSuccessType(GENERATE_TOKEN_SCOPE);
export const GENERATE_TOKEN_ERROR = createErrorType(GENERATE_TOKEN_SCOPE);

+ 0
- 48
store/actions/login/loginActions.js Näytä tiedosto

import {
CLEAR_LOGIN_USER_ERROR,
LOGIN_USER_ERROR,
LOGIN_USER_FETCH,
LOGIN_USER_SUCCESS,
LOGOUT_USER,
RESET_LOGIN_STATE,
UPDATE_USER_JWT_TOKEN,
REFRESH_TOKEN,
} from './loginActionConstants';


export const fetchUser = (payload) => ({
type: LOGIN_USER_FETCH,
payload,
});

export const fetchUserSuccess = (payload) => ({
type: LOGIN_USER_SUCCESS,
payload,
});

export const fetchUserError = (payload) => ({
type: LOGIN_USER_ERROR,
payload,
});

export const updateUserToken = (payload) => ({
type: UPDATE_USER_JWT_TOKEN,
payload,
});

export const resetLoginState = () => ({
type: RESET_LOGIN_STATE,
});

export const clearLoginErrors = () => ({
type: CLEAR_LOGIN_USER_ERROR,
});

export const logoutUser = () => ({
type: LOGOUT_USER,
});

export const refreshUserToken = (payload) => ({
type: REFRESH_TOKEN,
payload
});

+ 0
- 18
store/actions/register/registerActionConstants.js Näytä tiedosto

import {
createClearType,
createErrorType,
createFetchType,
createLoadingType,
createSuccessType,
} from "../actionHelpers";

export const REGISTER_USER_SCOPE = "REGISTER_USER";
export const REGISTER_USER_FETCH = createFetchType(REGISTER_USER_SCOPE);
export const REGISTER_USER_SUCCESS = createSuccessType(REGISTER_USER_SCOPE);
export const REGISTER_USER_ERROR = createErrorType(REGISTER_USER_SCOPE);
export const CLEAR_REGISTER_USER_ERROR = createClearType(
`${REGISTER_USER_SCOPE}_ERROR`
);
export const REGISTER_USER_LOADING = createLoadingType(REGISTER_USER_SCOPE);

export const RESET_REGISTER_STATE = "RESET_REGISTER_STATE";

+ 0
- 30
store/actions/register/registerActions.js Näytä tiedosto

import {
CLEAR_REGISTER_USER_ERROR,
REGISTER_USER_ERROR,
REGISTER_USER_FETCH,
REGISTER_USER_SUCCESS,
RESET_REGISTER_STATE,
} from "./registerActionConstants";

export const registerUser = (payload) => ({
type: REGISTER_USER_FETCH,
payload,
});

export const registerUserSuccess = (payload) => ({
type: REGISTER_USER_SUCCESS,
payload,
});

export const registerUserError = (payload) => ({
type: REGISTER_USER_ERROR,
payload,
});

export const resetRegisterState = () => ({
type: RESET_REGISTER_STATE,
});

export const clearRegisterErrors = () => ({
type: CLEAR_REGISTER_USER_ERROR,
});

+ 0
- 3
store/actions/user/userActionConstants.js Näytä tiedosto

export const SET_USER = "SET_USER";
export const SET_USER_ERROR = "SET_USER_ERROR";
export const RESET_USER_STATE = "RESET_USER_STATE";

+ 0
- 19
store/actions/user/userActions.js Näytä tiedosto

import {
RESET_USER_STATE,
SET_USER,
SET_USER_ERROR,
} from './userActionConstants';

export const setUser = (payload) => ({
type: SET_USER,
payload,
});

export const setUserError = (payload) => ({
type: SET_USER_ERROR,
payload,
});

export const resetUserState = () => ({
type: RESET_USER_STATE,
});

+ 0
- 32
store/index.js Näytä tiedosto

import { applyMiddleware, compose, createStore } from "redux";
import createSagaMiddleware from "redux-saga";
import rootReducer from "./reducers";
import rootSaga from "./saga";
import loadingMiddleware from "./middleware/loadingMiddleware";
import requestStatusMiddleware from "./middleware/requestStatusMiddleware";
import accessTokenMiddleware from "./middleware/accessTokenMiddleware";
// import authenticationMiddleware from "./middleware/authenticationMiddleware";
// import internalServerErrorMiddleware from "./middleware/internalServerErrorMiddleware";
const composeEnhancers =
(window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
trace: true,
traceLimit: 25,
})) ||
compose;
const sagaMiddleware = createSagaMiddleware();
export default createStore(
rootReducer,
composeEnhancers(
applyMiddleware(
sagaMiddleware,
loadingMiddleware,
requestStatusMiddleware,
// internalServerErrorMiddleware,
accessTokenMiddleware,
// authenticationMiddleware
)
)
);

sagaMiddleware.run(rootSaga);

+ 0
- 51
store/middleware/accessTokenMiddleware.js Näytä tiedosto

import axios from "axios";
import jwt_decode from "jwt-decode";
import { JWT_REFRESH_TOKEN, JWT_TOKEN } from "../../constants/localStorage";
import { attachBeforeRequestListener } from "../../request/index";
import { getData } from "../../service/asyncStorage";
import { logoutUser, refreshUserToken } from "../actions/login/loginActions";

export const accessTokensMiddlewareInterceptorName = "ACCESS_TOKEN_INTERCEPTOR";

export default ({ dispatch }) =>
(next) =>
(action) => {
attachBeforeRequestListener(async (response) => {
const jwtToken = await getData(JWT_TOKEN);
const refresh = await getData(JWT_REFRESH_TOKEN);
if (!jwtToken || !refresh) return Promise.resolve(response);
const jwtTokenDecoded = jwt_decode(jwtToken);
const refreshTokenDecoded = jwt_decode(refresh);
if (!response.headers?.Authorization) {
response.headers.Authorization = `Bearer ${jwtToken}`;
}

// If refresh token is expired, log out user
if (new Date() > new Date(refreshTokenDecoded?.exp * 1000)) {
dispatch(logoutUser());
return Promise.resolve(response);
}

// If access token is expired, refresh access token
if (new Date() > new Date(jwtTokenDecoded.exp * 1000)) {
const axiosResponse = await axios.post(
"https://strapi.dilig.net/api/token/refresh",
{
refreshToken: refresh,
},
{
headers: { Authorization: `Bearer ${jwtToken}` },
}
);
const newToken = axiosResponse.data;

response.headers.Authorization = `Bearer ${newToken.jwt}`;

dispatch(refreshUserToken(newToken));
}

return Promise.resolve(response);
}, accessTokensMiddlewareInterceptorName);

next(action);
};

+ 0
- 22
store/middleware/authenticationMiddleware.js Näytä tiedosto

import { attachPostRequestListener } from "../../request";
import { logoutUser } from "../actions/login/loginActions";

export const authenticationMiddlewareInterceptorName =
"AUTHENTICATION_MIDDLEWARE";

export default ({ dispatch }) =>
(next) =>
(action) => {
attachPostRequestListener((error) => {
if (!error.response) {
return Promise.reject(error);
}
if (error.response.status === 401) {
dispatch(logoutUser());
return Promise.reject(error);
}
return Promise.resolve();
}, authenticationMiddlewareInterceptorName);

next(action);
};

+ 0
- 20
store/middleware/internalServerErrorMiddleware.js Näytä tiedosto

import { attachPostRequestListener } from "../../request";
import { makeErrorToastMessage } from "../../util/helpers/toastMessage";
import i18next from "i18next";

export const serverErrorMiddlewareInterceptorName =
"INTERNAL_SERVER_ERROR_MIDDLEWARE_INTERCEPTOR";

export default () => (next) => (action) => {
attachPostRequestListener((error) => {
if (!error.response) {
return makeErrorToastMessage(i18next.t("apiErrors.SomethingWentWrong"));
}
if (error.response.status === 500) {
return makeErrorToastMessage(i18next.t("apiErrors.SomethingWentWrong"));
}
return Promise.reject(error);
}, serverErrorMiddlewareInterceptorName);

next(action);
};

+ 0
- 28
store/middleware/loadingMiddleware.js Näytä tiedosto

import {
DELETE,
ERROR,
FETCH,
SUCCESS,
UPDATE,
SUBMIT,
} from "../actions/actionHelpers";
import { addLoader, removeLoader } from "../actions/app/appActions";

const promiseTypes = [FETCH, UPDATE, DELETE, SUBMIT];
export default ({ dispatch }) =>
(next) =>
(action) => {
const promiseType = promiseTypes.find((promiseType) =>
action.type.includes(promiseType)
);
if (promiseType) {
dispatch(addLoader(action.type));
return next(action);
}

if (action.type.includes(SUCCESS) || action.type.includes(ERROR)) {
dispatch(removeLoader(action.type));
return next(action);
}
next(action);
};

+ 0
- 27
store/middleware/requestStatusMiddleware.js Näytä tiedosto

import { attachPostRequestListener } from "../../request";
import apiEndpoints from "../../request/apiEndpoints";
import { logoutUser } from "../actions/login/loginActions";

export const requestStatusMiddlewareInterceptorName =
"REQUEST_STATUS_MIDDLEWARE_INTERCEPTOR";

export default ({ dispatch }) =>
(next) =>
(action) => {
attachPostRequestListener((error) => {
if (!error.response) {
return Promise.reject(error);
}
if (
error.response.config.url !== apiEndpoints.authentications.login &&
error.response.config.url !==
apiEndpoints.authentications.confirmSecurityQuestion &&
error.response.status === 401
) {
return dispatch(logoutUser());
}
return Promise.reject(error);
}, requestStatusMiddlewareInterceptorName);

next(action);
};

+ 0
- 33
store/reducers/authProvider/authProviderReducer.js Näytä tiedosto

import createReducer from "../../utils/createReducer";

import {
AUTH_PROVIDER_ERROR,
AUTH_PROVIDER_SUCCESS,
} from "../../actions/authProvider/authProviderActionConstants";

const initialState = {
success: "",
errorMessage: "",
};

export default createReducer(
{
[AUTH_PROVIDER_SUCCESS]: setProvider,
[AUTH_PROVIDER_ERROR]: setProviderError,
},
initialState
);

function setProvider(state, action) {
return {
...state,
success: action.payload,
};
}

function setProviderError(state, action) {
return {
...state,
errorMessage: action.payload,
};
}

+ 0
- 14
store/reducers/index.js Näytä tiedosto

import { combineReducers } from "redux";
import loginReducer from "./login/loginReducer";
import loadingReducer from "./loading/loadingReducer";
import userReducer from "./user/userReducer";
import registerReducer from "./register/registerReducer";
import authProviderReducer from "./authProvider/authProviderReducer";

export default combineReducers({
login: loginReducer,
user: userReducer,
loading: loadingReducer,
register: registerReducer,
authProvider: authProviderReducer,
});

+ 0
- 45
store/reducers/loading/loadingReducer.js Näytä tiedosto

import createReducer from "../../utils/createReducer";
import {
ADD_LOADER,
REMOVE_LOADER,
} from "../../actions/app/appActionConstants";

const initialState = {
loaderCount: 0,
};
export default createReducer(
{
[ADD_LOADER]: addLoader,
[REMOVE_LOADER]: removeLoader,
},
initialState
);

function addLoader(state, action) {
let loaderCount = state.loaderCount;
let actionType = action.payload.replace("[FETCH]", "");
if (!state[actionType]) {
loaderCount++;
}
return {
...state,
[actionType]: true,
loaderCount,
};
}

function removeLoader(state, action) {
let actionType = action.payload
.replace("[SUCCESS]", "")
.replace("[ERROR]", "");
let loaderCount = state.loaderCount;
if (state[actionType] === true) {
loaderCount--;
}

return {
...state,
[actionType]: false,
loaderCount,
};
}

+ 0
- 69
store/reducers/login/loginReducer.js Näytä tiedosto

import createReducer from '../../utils/createReducer';
import {
CLEAR_LOGIN_USER_ERROR,
LOGIN_USER_ERROR,
LOGIN_USER_SUCCESS,
RESET_LOGIN_STATE,
UPDATE_USER_JWT_TOKEN,
} from '../../actions/login/loginActionConstants';

const initialState = {
email: '',
token: {
JwtRefreshToken: '',
JwtToken: '',
},
errorMessage: '',
};

export default createReducer(
{

[LOGIN_USER_SUCCESS]: setUser,
[UPDATE_USER_JWT_TOKEN]: setUserJwtToken,
[RESET_LOGIN_STATE]: resetLoginState,
[LOGIN_USER_ERROR]: setError,
[CLEAR_LOGIN_USER_ERROR]: clearLoginErrors,
},
initialState,
);


function setUser(state, action) {
return {
...state,
token: {
...state.token,
JwtToken: action.payload.jwt,
JwtRefreshToken: action.payload.refreshToken
},
};
}

function setUserJwtToken(state, action) {
return {
...state,
token: {
...state.token,
JwtToken: action.payload,
},
};
}

function setError(state, action) {
return {
...state,
errorMessage: action.payload,
};
}

function resetLoginState() {
return initialState;
}

function clearLoginErrors(state) {
return {
...state,
errorMessage: '',
};
}

+ 0
- 54
store/reducers/register/registerReducer.js Näytä tiedosto

import createReducer from '../../utils/createReducer';
import {
CLEAR_REGISTER_USER_ERROR,
REGISTER_USER_ERROR,
REGISTER_USER_SUCCESS,
RESET_REGISTER_STATE,
} from '../../actions/register/registerActionConstants';

const initialState = {
token: {
JwtToken: '',
},
errorMessage: '',
};

export default createReducer(
{

[REGISTER_USER_SUCCESS]: setUser,
[RESET_REGISTER_STATE]: resetRegisterState,
[REGISTER_USER_ERROR]: setError,
[CLEAR_REGISTER_USER_ERROR]: clearRegisterErrors,
},
initialState,
);


function setUser(state, action) {
return {
...state,
token: {
...state.token,
JwtToken: action.payload.jwt,
},
};
}

function setError(state, action) {
return {
...state,
errorMessage: action.payload,
};
}

function resetRegisterState() {
return initialState;
}

function clearRegisterErrors(state) {
return {
...state,
errorMessage: '',
};
}

+ 0
- 37
store/reducers/user/userReducer.js Näytä tiedosto

import createReducer from "../../utils/createReducer";
import {
RESET_USER_STATE,
SET_USER,
SET_USER_ERROR,
} from "../../actions/user/userActionConstants";

const initialState = {
user: {},
};

export default createReducer(
{
[SET_USER]: setUser,
[SET_USER_ERROR]: setUserError,
[RESET_USER_STATE]: resetUser,
},
initialState
);

function setUser(state, action) {
return {
...state,
user: action.payload,
};
}

function setUserError(state, action) {
return {
...state,
errorMessage: action.payload,
};
}

function resetUser() {
return initialState;
}

+ 0
- 42
store/saga/authProviderSaga.js Näytä tiedosto

import { all, call, put, takeLatest } from "@redux-saga/core/effects";
import { attemptAuthProvider } from "../../request/loginRequest";
import {
fetchUserError,
fetchUserSuccess,
} from "../actions/login/loginActions";
import { setUser } from "../actions/user/userActions";
import { addHeaderToken } from "../../request";
import { ACCESS_TOKEN, JWT_REFRESH_TOKEN, JWT_TOKEN } from "../../constants/localStorage";
import { storeData } from "../../service/asyncStorage";
import { rejectErrorCodeHelper } from "../../utils/rejectErrorMessageHelper";
import { AUTH_PROVIDER_FETCH } from "../actions/authProvider/authProviderActionConstants";
import {
fetchAuthProviderError,
fetchAuthProviderSuccess,
} from "../actions/authProvider/authProviderActions";

function* fetchAuthProvider({ payload }) {
try {
const { data } = yield call(attemptAuthProvider, payload.accessToken);
if (data?.jwt) {
const user = data?.user;
yield call(storeData, JWT_TOKEN, data.jwt);
yield call(storeData, JWT_REFRESH_TOKEN, data?.refreshToken);
yield call(storeData, ACCESS_TOKEN, payload.accessToken);
yield call(addHeaderToken, data?.jwt);
yield put(setUser(user));
}
yield put(fetchUserSuccess(data));
yield put(fetchAuthProviderSuccess("Success"));
} catch (e) {
if (e.response && e.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, e);
yield put(fetchUserError(errorMessage));
yield put(fetchAuthProviderError('Error'));
}
}
}

export default function* authProviderSaga() {
yield all([takeLatest(AUTH_PROVIDER_FETCH, fetchAuthProvider)]);
}

+ 0
- 8
store/saga/index.js Näytä tiedosto

import { all } from "redux-saga/effects";
import loginSaga from "./loginSaga";
import registerSaga from "./registerSaga";
import authProviderSaga from "./authProviderSaga";

export default function* rootSaga() {
yield all([loginSaga(), registerSaga(), authProviderSaga()]);
}

+ 0
- 82
store/saga/loginSaga.js Näytä tiedosto

import { all, call, put, takeLatest } from "@redux-saga/core/effects";
import jwt_decode from "jwt-decode";
import {
LOGIN_USER_FETCH,
LOGOUT_USER,
REFRESH_TOKEN,
} from "../actions/login/loginActionConstants";
import { attemptLogin, logoutUserRequest } from "@request/loginRequest";
import {
fetchUserError,
fetchUserSuccess,
resetLoginState,
} from "../actions/login/loginActions";
import { resetUserState, setUser } from "../actions/user/userActions";
import { addHeaderToken, removeHeaderToken } from "../../request";
import {
JWT_REFRESH_TOKEN,
JWT_TOKEN,
} from "@constants/localStorage";
import { storeData, getData, removeData } from "@service/asyncStorage";
import { rejectErrorCodeHelper } from "@utils/rejectErrorMessageHelper";

function* fetchUser({ payload }) {
try {
const { data } = yield call(attemptLogin, payload);
if (data?.jwt) {
const user = data?.user;
yield call(storeData, JWT_TOKEN, data.jwt);
yield call(storeData, JWT_REFRESH_TOKEN, data?.refreshToken);
yield call(addHeaderToken, data?.jwt);
yield put(setUser(user));
}
yield put(fetchUserSuccess(data));
} catch (e) {
if (e.response && e.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, e);
yield put(fetchUserError(errorMessage));
}
}
}

function* logoutUser() {
try {
const token = yield call(getData, JWT_REFRESH_TOKEN);
const user = yield call(jwt_decode, token);
if (user) {
yield call(logoutUserRequest);
}
} catch (error) {
console.log(error); // eslint-disable-line
} finally {
yield call(removeHeaderToken);
yield call(removeData, JWT_REFRESH_TOKEN)
yield call(removeData, JWT_TOKEN);
yield put(resetLoginState());
yield put(resetUserState());
}
}

export function* refreshToken({ payload }) {
try {
const newTokenDecoded = jwt_decode(payload.jwt);

yield call(storeData, JWT_TOKEN, payload.jwt);
yield call(storeData, JWT_REFRESH_TOKEN, payload.refreshToken);
addHeaderToken(payload.jwt);
yield put(setUser(newTokenDecoded));
yield put(fetchUserSuccess(payload));
return true;
} catch (error) {
console.log(error); // eslint-disable-line
return false;
}
}

export default function* loginSaga() {
yield all([
takeLatest(LOGIN_USER_FETCH, fetchUser),
takeLatest(LOGOUT_USER, logoutUser),
takeLatest(REFRESH_TOKEN, refreshToken),
]);
}

+ 0
- 32
store/saga/registerSaga.js Näytä tiedosto

import { all, call, put, takeLatest } from "@redux-saga/core/effects";
import { REGISTER_USER_FETCH } from "../actions/register/registerActionConstants";
import { attemptRegister } from "../../request/loginRequest";
import {
registerUserError,
registerUserSuccess,
} from "../actions/register/registerActions";
import { JWT_TOKEN } from "../../constants/localStorage";
import { storeData } from "../../service/asyncStorage";
import { rejectErrorCodeHelper } from "../../utils/rejectErrorMessageHelper";

function* registerUser({ payload }) {
try {
const { data } = yield call(attemptRegister, payload);
if (data?.jwt) {
yield call(storeData, JWT_TOKEN, data.jwt);
}
yield put(registerUserSuccess(data.jwt));
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
} catch (e) {
if (e.response && e.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, e);
yield put(registerUserError(errorMessage));
}
}
}

export default function* registerSaga() {
yield all([takeLatest(REGISTER_USER_FETCH, registerUser)]);
}

+ 0
- 8
store/selectors/authProviderSelectors.js Näytä tiedosto

import createSelector from "reselect";

const authProviderSelector = (state) => state.authProvider;

export const selectLoginError = createSelector(
authProviderSelector,
(state) => state.errorMessage
);

+ 0
- 16
store/selectors/loadingSelectors.js Näytä tiedosto

import { createSelector } from "reselect";

const loadingSelector = (state) => state.loading;

export const selectIsLoadingByActionType = (loadingActionType) =>
createSelector(loadingSelector, (state) => state[`${loadingActionType}`]);

export const selectIsLoadingByActionTypes = (actionTypes) =>
createSelector(loadingSelector, (state) =>
actionTypes.some((actionType) => state[`${actionType}`])
);

export const selectLoaderCount = createSelector(
loadingSelector,
(state) => state.loaderCount
);

+ 0
- 29
store/selectors/loginSelectors.js Näytä tiedosto

import { createSelector } from 'reselect';

const loginSelector = (state) => state.login;

export const selectLoginEmail = createSelector(
loginSelector,
(state) => state.email,
);

export const selectUsernames = createSelector(
loginSelector,
(state) => state.usernames,
);

export const selectTokens = createSelector(
loginSelector,
(state) => state.token,
);

export const selectJWTToken = createSelector(
loginSelector,
(state) => state.token.JwtToken,
);


export const selectLoginError = createSelector(
loginSelector,
(state) => state.errorMessage,
);

+ 0
- 8
store/selectors/registerSelectors.js Näytä tiedosto

import { createSelector } from 'reselect';

const registerSelector = (state) => state.register;

export const selectRegisterError = createSelector(
registerSelector,
(state) => state.errorMessage,
);

+ 0
- 28
store/selectors/userSelectors.js Näytä tiedosto

import { createSelector } from 'reselect';

export const userSelector = (state) => state.user;

export const selectAuthUser = createSelector(
userSelector,
(state) => state.user,
);

export const getUserSecurityQuestion = createSelector(
userSelector,
(state) => state.securityQuestion,
);

export const getForgotPasswordRequest = createSelector(
userSelector,
(state) => state.user,
);

export const selectForgotPasswordError = createSelector(
userSelector,
(state) => state.errorMessage,
);

export const getResetPasswordRequest = createSelector(
userSelector,
(state) => state.user,
);

+ 0
- 13
store/utils/createReducer.js Näytä tiedosto

const createReducer = (handlers, initialState) => {
return (state = initialState, action) => {
const reducer = handlers[action.type];

if (!reducer) {
return state;
}

return reducer(state, action);
};
};

export default createReducer;

Loading…
Peruuta
Tallenna