@phygitallabs/tapquest-core 2.7.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +1035 -750
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -35
- package/dist/index.d.ts +29 -35
- package/dist/index.js +1017 -717
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
- package/src/modules/auth/constants/index.ts +6 -0
- package/src/modules/auth/helpers/index.ts +1 -4
- package/src/modules/auth/hooks/index.ts +2 -0
- package/src/modules/auth/hooks/useGoogleLogin.ts +169 -0
- package/src/modules/auth/hooks/useTokenRefresher.ts +39 -0
- package/src/modules/auth/index.ts +8 -2
- package/src/modules/auth/providers/AuthProvider.tsx +214 -186
- package/src/modules/auth/store/authStore.ts +577 -0
- package/src/modules/auth/types/auth.ts +29 -0
- package/src/modules/auth/types/user-data.ts +38 -0
- package/src/modules/auth/utils/user.ts +21 -0
- package/src/modules/data-tracking/hooks/index.ts +25 -1
- package/src/modules/generate-certificate/helpers/index.ts +3 -0
- package/src/modules/generate-certificate/hooks/index.ts +15 -6
- package/src/modules/generate-certificate/index.ts +3 -1
- package/src/modules/notification/providers/index.tsx +3 -3
- package/src/modules/reward/hooks/useRewardService.ts +6 -6
- package/src/modules/session-replay/README.md +334 -0
- package/src/modules/session-replay/hooks/useSessionReplay.ts +16 -0
- package/src/modules/session-replay/index.ts +10 -0
- package/src/modules/session-replay/providers/SessionReplayProvider.tsx +189 -0
- package/src/modules/session-replay/types/index.ts +147 -0
- package/src/modules/session-replay/utils/index.ts +12 -0
- package/src/providers/ServicesProvider.tsx +4 -76
- package/src/providers/TapquestCoreProvider.tsx +31 -36
- package/src/modules/auth/helpers/refreshToken.ts +0 -63
- package/src/modules/auth/store/authSlice.ts +0 -137
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenReplay Session Replay Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for OpenReplay session replay configuration and options
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* OpenReplay Configuration Options
|
|
9
|
+
*/
|
|
10
|
+
export interface OpenReplayConfig {
|
|
11
|
+
/**
|
|
12
|
+
* OpenReplay project key (required)
|
|
13
|
+
* Get this from your OpenReplay dashboard
|
|
14
|
+
*/
|
|
15
|
+
projectKey?: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Custom ingest endpoint URL (optional)
|
|
19
|
+
* For self-hosted OpenReplay instances
|
|
20
|
+
*/
|
|
21
|
+
ingestPoint?: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Enable user ID tracking (optional)
|
|
25
|
+
* When enabled, the tracker will automatically set user IDs
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
userIdEnabled?: boolean;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Custom function to get user ID (optional)
|
|
32
|
+
* If not provided and userIdEnabled is true, a UUID will be generated
|
|
33
|
+
*/
|
|
34
|
+
getUserId?: () => string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Enable debug mode (optional)
|
|
38
|
+
* Shows debug logs in console when enabled
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
debug?: boolean;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Capture exceptions automatically (optional)
|
|
45
|
+
* @default true
|
|
46
|
+
*/
|
|
47
|
+
captureExceptions?: boolean;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Capture performance metrics (optional)
|
|
51
|
+
* @default true
|
|
52
|
+
*/
|
|
53
|
+
capturePerformance?: boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Network tracking options (optional)
|
|
57
|
+
*/
|
|
58
|
+
network?: {
|
|
59
|
+
/**
|
|
60
|
+
* Capture network payload data
|
|
61
|
+
* @default true
|
|
62
|
+
*/
|
|
63
|
+
capturePayload?: boolean;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Sanitizer function for network data
|
|
67
|
+
* Use this to redact sensitive information
|
|
68
|
+
*/
|
|
69
|
+
sanitizer?: (data: any) => any;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Console log tracking options (optional)
|
|
74
|
+
*/
|
|
75
|
+
console?: {
|
|
76
|
+
/**
|
|
77
|
+
* Console log levels to capture
|
|
78
|
+
* @default ["error", "warn", "log"]
|
|
79
|
+
*/
|
|
80
|
+
levels?: Array<"error" | "warn" | "log" | "info" | "debug">;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Privacy settings
|
|
85
|
+
*/
|
|
86
|
+
obscureTextEmails?: boolean;
|
|
87
|
+
obscureTextNumbers?: boolean;
|
|
88
|
+
obscureInputEmails?: boolean;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Disable secure mode (for development)
|
|
92
|
+
* @default false in production, true in development
|
|
93
|
+
*/
|
|
94
|
+
__DISABLE_SECURE_MODE?: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Session Replay Provider Props
|
|
99
|
+
*/
|
|
100
|
+
export interface SessionReplayProviderProps {
|
|
101
|
+
children: React.ReactNode;
|
|
102
|
+
config?: OpenReplayConfig;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Tracker State
|
|
107
|
+
* Internal state for the tracker context reducer
|
|
108
|
+
*/
|
|
109
|
+
export interface TrackerState {
|
|
110
|
+
tracker: any | null;
|
|
111
|
+
config: OpenReplayConfig;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Tracker Actions
|
|
116
|
+
*/
|
|
117
|
+
export type TrackerAction =
|
|
118
|
+
| { type: "init" }
|
|
119
|
+
| { type: "start" }
|
|
120
|
+
| { type: "setUserId"; payload: string }
|
|
121
|
+
| { type: "setMetadata"; payload: Record<string, any> };
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Tracker Context Value
|
|
125
|
+
* Methods available through the TrackerContext
|
|
126
|
+
*/
|
|
127
|
+
export interface TrackerContextValue {
|
|
128
|
+
/**
|
|
129
|
+
* Initialize the OpenReplay tracker
|
|
130
|
+
* Must be called before startTracking
|
|
131
|
+
*/
|
|
132
|
+
initTracker: () => void;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Start tracking the session
|
|
136
|
+
* Must call initTracker first
|
|
137
|
+
*/
|
|
138
|
+
startTracking: () => void;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Set or update the user ID for the current session
|
|
142
|
+
* @param userId - The user ID to set
|
|
143
|
+
*/
|
|
144
|
+
setUserId: (userId: string) => void;
|
|
145
|
+
|
|
146
|
+
setMetadata: (metadata: Record<string, any>) => void;
|
|
147
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getDeviceUid } from "../../auth/helpers";
|
|
2
|
+
|
|
3
|
+
export const getSessionUserId = (userId?: string): string | null => {
|
|
4
|
+
if (userId) {
|
|
5
|
+
return userId;
|
|
6
|
+
}
|
|
7
|
+
return getDeviceUid();
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const isBrowser = (): boolean => {
|
|
11
|
+
return typeof window !== "undefined";
|
|
12
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useState
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { QueryClient } from "@tanstack/react-query";
|
|
3
3
|
import { PGLCoreServiceProvider } from "@phygitallabs/api-core";
|
|
4
4
|
import { RewardServiceProvider } from "@phygitallabs/reward";
|
|
@@ -8,13 +8,7 @@ import { GenerateCertificateServiceProvider } from "@phygitallabs/generate-certi
|
|
|
8
8
|
import serviceApiUrl from "../constants/service";
|
|
9
9
|
import { APIConfig, ServiceConfig } from "../types/service";
|
|
10
10
|
|
|
11
|
-
import { checkDeviceUid
|
|
12
|
-
|
|
13
|
-
import { httpMaxRetries } from "../modules/auth";
|
|
14
|
-
|
|
15
|
-
import { useAuth } from "../modules/auth";
|
|
16
|
-
|
|
17
|
-
import axios from "axios";
|
|
11
|
+
import { checkDeviceUid } from "../modules/auth/helpers";
|
|
18
12
|
|
|
19
13
|
interface ServicesProviderProps {
|
|
20
14
|
children: React.ReactNode;
|
|
@@ -30,23 +24,10 @@ export const ServicesProvider: React.FC<ServicesProviderProps> = ({
|
|
|
30
24
|
environment: "dev",
|
|
31
25
|
version: "v1"
|
|
32
26
|
},
|
|
33
|
-
firebaseConfig
|
|
34
27
|
}) => {
|
|
35
|
-
const { refreshUser, signOut } = useAuth();
|
|
36
28
|
const { environment, version } = apiConfig;
|
|
37
29
|
const [commonServiceConfig, setCommonServiceConfig] = useState<ServiceConfig | null>(null);
|
|
38
30
|
|
|
39
|
-
// Create memoized refresh token function
|
|
40
|
-
const memoizedRefreshToken = useMemo(() => {
|
|
41
|
-
if (!firebaseConfig?.apiKey) {
|
|
42
|
-
console.warn("Firebase API key not provided, refresh token functionality will not work");
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
return createRefreshTokenFunction({
|
|
46
|
-
firebaseApiKey: firebaseConfig.apiKey
|
|
47
|
-
});
|
|
48
|
-
}, [firebaseConfig?.apiKey]);
|
|
49
|
-
|
|
50
31
|
// Init client
|
|
51
32
|
useEffect(() => {
|
|
52
33
|
const initClient = async () => {
|
|
@@ -55,64 +36,11 @@ export const ServicesProvider: React.FC<ServicesProviderProps> = ({
|
|
|
55
36
|
|
|
56
37
|
const responseInterceptors = ({
|
|
57
38
|
onFulfilled: (response: any) => response,
|
|
58
|
-
onRejected:
|
|
59
|
-
|
|
60
|
-
// TODO: Remove
|
|
61
|
-
// Feature: use refresh token to get new access token when token expired, have maximum `retry attempts`
|
|
62
|
-
const originalRequest = error.config;
|
|
63
|
-
|
|
64
|
-
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
65
|
-
const retryAttempts = parseInt(
|
|
66
|
-
getRetryAttemptsRefreshToken() || "0",
|
|
67
|
-
10
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
if (retryAttempts >= httpMaxRetries) {
|
|
71
|
-
await signOut();
|
|
72
|
-
return Promise.reject(error);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
setRetryAttemptsRefreshToken(`${retryAttempts + 1}`);
|
|
76
|
-
originalRequest._retry = true;
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
if (!memoizedRefreshToken) {
|
|
80
|
-
await signOut();
|
|
81
|
-
return Promise.reject(error);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const result = await memoizedRefreshToken();
|
|
85
|
-
|
|
86
|
-
if (result?.accessToken) {
|
|
87
|
-
originalRequest.headers[
|
|
88
|
-
"Authorization"
|
|
89
|
-
] = `Bearer ${result.accessToken}`;
|
|
90
|
-
|
|
91
|
-
setRetryAttemptsRefreshToken("0");
|
|
92
|
-
|
|
93
|
-
refreshUser(result);
|
|
94
|
-
|
|
95
|
-
return axios(originalRequest);
|
|
96
|
-
}
|
|
97
|
-
} catch (refreshError) {
|
|
98
|
-
console.log("Failed to refresh token:", refreshError);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return Promise.reject(error);
|
|
103
|
-
},
|
|
39
|
+
onRejected: async (error: any) => Promise.reject(error),
|
|
104
40
|
})
|
|
105
41
|
|
|
106
42
|
const requestInterceptors = ({
|
|
107
|
-
onFulfilled: (config: any) =>
|
|
108
|
-
// Feature: set access token to request header
|
|
109
|
-
const currentToken = getAccessToken();
|
|
110
|
-
|
|
111
|
-
if (currentToken && !config.headers.Authorization) {
|
|
112
|
-
config.headers.Authorization = `Bearer ${currentToken}`;
|
|
113
|
-
}
|
|
114
|
-
return config;
|
|
115
|
-
},
|
|
43
|
+
onFulfilled: (config: any) => config,
|
|
116
44
|
onRejected: (error: any) => Promise.reject(error),
|
|
117
45
|
})
|
|
118
46
|
|
|
@@ -1,64 +1,59 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
|
-
import { Provider } from "react-redux";
|
|
3
|
-
import { PersistGate } from "redux-persist/integration/react";
|
|
4
2
|
|
|
5
3
|
import { ServicesProvider } from "./ServicesProvider";
|
|
6
4
|
import { APIConfig } from "../types/service";
|
|
7
5
|
import { QueryClient } from "@tanstack/react-query";
|
|
8
6
|
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import { FirebaseConfig } from "../modules/auth/types";
|
|
7
|
+
import { AuthProvider } from "../modules/auth";
|
|
8
|
+
import serviceApiUrl from "../constants/service";
|
|
9
|
+
import { OpenReplayConfig, SessionReplayProvider } from "../modules/session-replay";
|
|
13
10
|
|
|
14
11
|
interface TapquestCoreProviderProps {
|
|
15
12
|
children: React.ReactNode;
|
|
16
13
|
queryClient: QueryClient;
|
|
17
14
|
apiConfig: APIConfig;
|
|
18
|
-
firebaseConfig?: FirebaseConfig;
|
|
19
|
-
authCallbacks?: AuthCallbacks;
|
|
20
15
|
}
|
|
21
16
|
|
|
22
17
|
export const TapquestCoreProvider: React.FC<TapquestCoreProviderProps> = ({
|
|
23
18
|
children,
|
|
24
19
|
queryClient,
|
|
25
20
|
apiConfig,
|
|
26
|
-
|
|
27
|
-
authCallbacks
|
|
21
|
+
// authCallbacks
|
|
28
22
|
}) => {
|
|
23
|
+
const { environment } = apiConfig;
|
|
29
24
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} catch (error) {
|
|
37
|
-
console.warn("Failed to create auth service from firebase config:", error);
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
25
|
+
const sessionReplayConfig: OpenReplayConfig | undefined = useMemo(() => {
|
|
26
|
+
const projectKey = process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY;
|
|
27
|
+
|
|
28
|
+
// Only configure if project key is provided
|
|
29
|
+
if (!projectKey) {
|
|
30
|
+
return undefined;
|
|
40
31
|
}
|
|
41
32
|
|
|
42
|
-
return
|
|
43
|
-
|
|
33
|
+
return {
|
|
34
|
+
projectKey,
|
|
35
|
+
ingestPoint: process.env.NEXT_PUBLIC_OPENREPLAY_INGEST_POINT,
|
|
36
|
+
debug: process.env.NODE_ENV === "development",
|
|
37
|
+
captureExceptions: true,
|
|
38
|
+
capturePerformance: true,
|
|
39
|
+
obscureTextEmails: true,
|
|
40
|
+
obscureInputEmails: true,
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
44
43
|
|
|
45
44
|
return (
|
|
46
|
-
<
|
|
47
|
-
<
|
|
45
|
+
<SessionReplayProvider config={sessionReplayConfig}>
|
|
46
|
+
<ServicesProvider
|
|
47
|
+
queryClient={queryClient}
|
|
48
|
+
apiConfig={apiConfig}
|
|
49
|
+
>
|
|
48
50
|
<AuthProvider
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
baseURL={`${serviceApiUrl[environment].API_BASE_URL}`}
|
|
52
|
+
queryClient={queryClient}
|
|
51
53
|
>
|
|
52
|
-
|
|
53
|
-
queryClient={queryClient}
|
|
54
|
-
apiConfig={apiConfig}
|
|
55
|
-
firebaseConfig={firebaseConfig}
|
|
56
|
-
>
|
|
57
|
-
{children}
|
|
58
|
-
</ServicesProvider>
|
|
54
|
+
{children}
|
|
59
55
|
</AuthProvider>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
</Provider>
|
|
56
|
+
</ServicesProvider>
|
|
57
|
+
</SessionReplayProvider>
|
|
63
58
|
);
|
|
64
59
|
};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import mem from "mem";
|
|
2
|
-
import { setUserInfo, removeUserInfo, getUserInfo, getRefreshToken } from "./index";
|
|
3
|
-
|
|
4
|
-
// Internal configuration - managed by tapquest-core
|
|
5
|
-
const REFRESH_TOKEN_CONFIG = {
|
|
6
|
-
maxAge: 10000, // 10 seconds cache
|
|
7
|
-
} as const;
|
|
8
|
-
|
|
9
|
-
export interface RefreshTokenConfig {
|
|
10
|
-
firebaseApiKey: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const createRefreshTokenFunction = (config: RefreshTokenConfig) => {
|
|
14
|
-
const refreshTokenFn = async () => {
|
|
15
|
-
try {
|
|
16
|
-
const session = getUserInfo();
|
|
17
|
-
const refreshToken = getRefreshToken();
|
|
18
|
-
|
|
19
|
-
if (!refreshToken) {
|
|
20
|
-
removeUserInfo();
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const params = new URLSearchParams();
|
|
25
|
-
params.append("grant_type", "refresh_token");
|
|
26
|
-
params.append("refresh_token", refreshToken);
|
|
27
|
-
|
|
28
|
-
const fetchData = {
|
|
29
|
-
method: "POST",
|
|
30
|
-
headers: new Headers({
|
|
31
|
-
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
32
|
-
}),
|
|
33
|
-
body: params,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const response = await fetch(
|
|
37
|
-
`https://securetoken.googleapis.com/v1/token?key=${config.firebaseApiKey}`,
|
|
38
|
-
fetchData
|
|
39
|
-
);
|
|
40
|
-
const data = await response.json();
|
|
41
|
-
|
|
42
|
-
if (data.error) {
|
|
43
|
-
removeUserInfo();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const newSession = {
|
|
48
|
-
...session,
|
|
49
|
-
accessToken: data.access_token,
|
|
50
|
-
refreshToken: data.refresh_token,
|
|
51
|
-
};
|
|
52
|
-
setUserInfo(newSession);
|
|
53
|
-
|
|
54
|
-
return newSession;
|
|
55
|
-
} catch {
|
|
56
|
-
removeUserInfo();
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
return mem(refreshTokenFn, { maxAge: REFRESH_TOKEN_CONFIG.maxAge });
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export { createRefreshTokenFunction };
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|
2
|
-
import { UserData, UserRole } from "../types";
|
|
3
|
-
import {
|
|
4
|
-
setUserInfo,
|
|
5
|
-
removeUserInfo,
|
|
6
|
-
getUserInfo,
|
|
7
|
-
setAccessToken,
|
|
8
|
-
setRefreshToken,
|
|
9
|
-
removeAccessToken,
|
|
10
|
-
removeRefreshToken,
|
|
11
|
-
getAccessToken,
|
|
12
|
-
} from "../helpers";
|
|
13
|
-
|
|
14
|
-
const defaultUser: UserData = {
|
|
15
|
-
uid: "",
|
|
16
|
-
id: "",
|
|
17
|
-
userName: "",
|
|
18
|
-
displayName: "",
|
|
19
|
-
avatar: "/images/default-avatar.jpg",
|
|
20
|
-
email: "",
|
|
21
|
-
exp: 0,
|
|
22
|
-
emailVerified: false,
|
|
23
|
-
refreshToken: "",
|
|
24
|
-
accessToken: "",
|
|
25
|
-
role: UserRole.NULL,
|
|
26
|
-
scanStatus: false,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export interface AuthState {
|
|
30
|
-
user: UserData;
|
|
31
|
-
isSignedIn: boolean;
|
|
32
|
-
pending: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const initialState: AuthState = {
|
|
36
|
-
user: defaultUser,
|
|
37
|
-
isSignedIn: false,
|
|
38
|
-
pending: true,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export const authSlice = createSlice<
|
|
42
|
-
AuthState,
|
|
43
|
-
{
|
|
44
|
-
signIn: (state: AuthState, action: PayloadAction<UserData>) => void;
|
|
45
|
-
signOut: (state: AuthState) => void;
|
|
46
|
-
updateScanStatus: (state: AuthState, action: PayloadAction<boolean>) => void;
|
|
47
|
-
refreshUser: (state: AuthState, action: PayloadAction<UserData>) => void;
|
|
48
|
-
setPending: (state: AuthState, action: PayloadAction<boolean>) => void;
|
|
49
|
-
initializeFromStorage: (state: AuthState) => void;
|
|
50
|
-
},
|
|
51
|
-
'auth'
|
|
52
|
-
>({
|
|
53
|
-
name: "auth",
|
|
54
|
-
initialState,
|
|
55
|
-
reducers: {
|
|
56
|
-
signIn: (state, action: PayloadAction<UserData>) => {
|
|
57
|
-
const userData = action.payload;
|
|
58
|
-
state.user = userData;
|
|
59
|
-
state.isSignedIn = true;
|
|
60
|
-
state.pending = false;
|
|
61
|
-
|
|
62
|
-
// Store in localStorage for persistence
|
|
63
|
-
setUserInfo(userData);
|
|
64
|
-
setAccessToken(userData.accessToken);
|
|
65
|
-
setRefreshToken(userData.refreshToken);
|
|
66
|
-
},
|
|
67
|
-
signOut: (state) => {
|
|
68
|
-
state.user = defaultUser;
|
|
69
|
-
state.isSignedIn = false;
|
|
70
|
-
state.pending = false;
|
|
71
|
-
|
|
72
|
-
// Clear localStorage
|
|
73
|
-
removeUserInfo();
|
|
74
|
-
removeAccessToken();
|
|
75
|
-
removeRefreshToken();
|
|
76
|
-
},
|
|
77
|
-
updateScanStatus: (state, action: PayloadAction<boolean>) => {
|
|
78
|
-
state.user.scanStatus = action.payload;
|
|
79
|
-
|
|
80
|
-
// Update localStorage
|
|
81
|
-
setUserInfo(state.user);
|
|
82
|
-
},
|
|
83
|
-
refreshUser: (state, action: PayloadAction<UserData>) => {
|
|
84
|
-
const userData = action.payload;
|
|
85
|
-
if (userData) {
|
|
86
|
-
state.user = userData;
|
|
87
|
-
state.isSignedIn = true;
|
|
88
|
-
state.pending = false;
|
|
89
|
-
|
|
90
|
-
// Update localStorage
|
|
91
|
-
setUserInfo(userData);
|
|
92
|
-
setAccessToken(userData.accessToken);
|
|
93
|
-
setRefreshToken(userData.refreshToken);
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
setPending: (state, action: PayloadAction<boolean>) => {
|
|
97
|
-
state.pending = action.payload;
|
|
98
|
-
},
|
|
99
|
-
initializeFromStorage: (state) => {
|
|
100
|
-
const storedUser = getUserInfo();
|
|
101
|
-
const accessToken = getAccessToken();
|
|
102
|
-
|
|
103
|
-
if (storedUser && accessToken) {
|
|
104
|
-
state.user = storedUser;
|
|
105
|
-
state.isSignedIn = true;
|
|
106
|
-
}
|
|
107
|
-
state.pending = false;
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
export const {
|
|
113
|
-
signIn,
|
|
114
|
-
signOut,
|
|
115
|
-
updateScanStatus,
|
|
116
|
-
refreshUser,
|
|
117
|
-
setPending,
|
|
118
|
-
initializeFromStorage,
|
|
119
|
-
} = authSlice.actions;
|
|
120
|
-
|
|
121
|
-
// Selectors
|
|
122
|
-
export const selectAuth = (state: any) => state.auth;
|
|
123
|
-
export const selectUser = (state: any) => state.auth.user;
|
|
124
|
-
export const selectUserUId = (state: any) =>
|
|
125
|
-
state.auth.user.uid || state.auth.user.id;
|
|
126
|
-
export const selectUserAccesstoken = (state: any) =>
|
|
127
|
-
state.auth.user.accessToken;
|
|
128
|
-
export const selectUserEmail = (state: any) => state.auth.user.email;
|
|
129
|
-
export const selectUsername = (state: any) => state.auth.user.userName;
|
|
130
|
-
export const selectSignInProvider = (state: any) =>
|
|
131
|
-
state.auth.user.signInProvider;
|
|
132
|
-
export const selectUserScanStatus = (state: any) =>
|
|
133
|
-
state.auth.user.scanStatus;
|
|
134
|
-
export const selectIsSignedIn = (state: any) => state.auth.isSignedIn;
|
|
135
|
-
export const selectIsLoading = (state: any) => state.auth.pending;
|
|
136
|
-
|
|
137
|
-
export default authSlice.reducer;
|