@imksh/create 1.0.30 → 1.0.32
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 +2 -0
- package/package.json +1 -1
- package/src/commands/create.js +2 -0
- package/src/index.js +1 -1
- package/src/utils/askTemplate.js +2 -1
- package/templates/layers/electron/base/electron/main.js +262 -0
- package/templates/layers/electron/base/electron/preload.js +37 -0
- package/templates/layers/electron/base/electron-builder.json5 +68 -0
- package/templates/layers/electron/javascript/_gitignore +38 -0
- package/templates/layers/electron/javascript/package.json +49 -0
- package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
- package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
- package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
- package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
- package/templates/layers/electron/javascript/src/main.jsx +17 -0
- package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
- package/templates/layers/electron/javascript/vite.config.js +29 -0
- package/templates/layers/electron/typescript/_gitignore +38 -0
- package/templates/layers/electron/typescript/electron/main.ts +213 -0
- package/templates/layers/electron/typescript/electron/preload.ts +52 -0
- package/templates/layers/electron/typescript/package.json +52 -0
- package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
- package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
- package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
- package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
- package/templates/layers/electron/typescript/src/main.tsx +17 -0
- package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
- package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
- package/templates/layers/electron/typescript/tsconfig.json +25 -0
- package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
- package/templates/layers/electron/typescript/vite.config.ts +29 -0
- package/templates/layers/next/javascript/package.json +8 -2
- package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
- package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
- package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
- package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
- package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
- package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
- package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
- package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
- package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
- package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
- package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
- package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
- package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
- package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
- package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
- package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
- package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
- package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
- package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
- package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
- package/templates/layers/next/typescript/.eslintrc.json +12 -0
- package/templates/layers/next/typescript/package.json +14 -3
- package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
- package/templates/layers/next/typescript/src/components/common/Loading.tsx +2 -1
- package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
- package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
- package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
- package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
- package/templates/layers/next/typescript/src/components/common/Table.tsx +205 -39
- package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
- package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
- package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
- package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
- package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +3 -1
- package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
- package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
- package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
- package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
- package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
- package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
- package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
- package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
- package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
- package/templates/layers/next/typescript/src/services/authService.ts +1 -1
- package/templates/layers/next/typescript/src/store/useAuthStore.ts +24 -9
- package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
- package/templates/layers/node/javascript/.env.example +3 -1
- package/templates/layers/node/javascript/package.json +12 -3
- package/templates/layers/node/javascript/src/app.js +2 -1
- package/templates/layers/node/javascript/src/controllers/auth.controller.js +106 -168
- package/templates/layers/node/javascript/src/controllers/push.controller.js +51 -58
- package/templates/layers/node/javascript/src/controllers/upload.controller.js +12 -19
- package/templates/layers/node/javascript/src/middleware/auth.middleware.js +5 -11
- package/templates/layers/node/javascript/src/middleware/errorHandler.js +6 -4
- package/templates/layers/node/javascript/src/routes/auth.route.js +2 -0
- package/templates/layers/node/javascript/src/services/auth.service.js +159 -164
- package/templates/layers/node/javascript/src/utils/ApiError.js +20 -0
- package/templates/layers/node/javascript/src/utils/SuccessResponse.js +15 -0
- package/templates/layers/node/javascript/src/utils/apiResponse.js +4 -0
- package/templates/layers/node/javascript/src/utils/asyncHandler.js +5 -0
- package/templates/layers/node/typescript/.env.example +3 -1
- package/templates/layers/node/typescript/.eslintrc.cjs +28 -0
- package/templates/layers/node/typescript/package-lock.json +2 -2
- package/templates/layers/node/typescript/package.json +17 -3
- package/templates/layers/node/typescript/src/app.ts +3 -2
- package/templates/layers/node/typescript/src/config/db.ts +3 -2
- package/templates/layers/node/typescript/src/config/socket.ts +5 -4
- package/templates/layers/node/typescript/src/controllers/auth.controller.ts +104 -132
- package/templates/layers/node/typescript/src/controllers/push.controller.ts +52 -59
- package/templates/layers/node/typescript/src/controllers/upload.controller.ts +12 -19
- package/templates/layers/node/typescript/src/middleware/auth.middleware.ts +13 -7
- package/templates/layers/node/typescript/src/middleware/errorHandler.ts +18 -5
- package/templates/layers/node/typescript/src/middleware/rateLimiter.ts +2 -2
- package/templates/layers/node/typescript/src/routes/auth.route.ts +2 -0
- package/templates/layers/node/typescript/src/services/auth.service.ts +86 -43
- package/templates/layers/node/typescript/src/types/express.d.ts +5 -3
- package/templates/layers/node/typescript/src/utils/ApiError.ts +25 -0
- package/templates/layers/node/typescript/src/utils/SuccessResponse.ts +17 -0
- package/templates/layers/node/typescript/src/utils/apiResponse.ts +4 -0
- package/templates/layers/node/typescript/src/utils/asyncHandler.ts +13 -0
- package/templates/layers/node/typescript/src/utils/cloudinaryUpload.ts +18 -7
- package/templates/layers/node/typescript/src/utils/expoNotification.ts +1 -1
- package/templates/layers/node/typescript/src/utils/genOtpToken.ts +7 -1
- package/templates/layers/node/typescript/src/utils/jwt.ts +8 -2
- package/templates/layers/node/typescript/src/utils/logger.ts +2 -1
- package/templates/layers/node/typescript/src/utils/pushHelper.ts +13 -9
- package/templates/layers/node-prisma/javascript/.env.example +3 -1
- package/templates/layers/node-prisma/javascript/package.json +11 -2
- package/templates/layers/node-prisma/javascript/src/app.js +2 -1
- package/templates/layers/node-prisma/javascript/src/controllers/auth.controller.js +90 -135
- package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +64 -76
- package/templates/layers/node-prisma/javascript/src/controllers/upload.controller.js +12 -19
- package/templates/layers/node-prisma/javascript/src/middleware/auth.middleware.js +5 -4
- package/templates/layers/node-prisma/javascript/src/middleware/errorHandler.js +6 -4
- package/templates/layers/node-prisma/javascript/src/routes/auth.route.js +2 -0
- package/templates/layers/node-prisma/javascript/src/services/auth.service.js +145 -122
- package/templates/layers/node-prisma/javascript/src/utils/ApiError.js +20 -0
- package/templates/layers/node-prisma/javascript/src/utils/SuccessResponse.js +15 -0
- package/templates/layers/node-prisma/javascript/src/utils/apiResponse.js +4 -0
- package/templates/layers/node-prisma/javascript/src/utils/asyncHandler.js +5 -0
- package/templates/layers/node-prisma/typescript/.env.example +3 -1
- package/templates/layers/node-prisma/typescript/.eslintrc.cjs +28 -0
- package/templates/layers/node-prisma/typescript/package.json +19 -5
- package/templates/layers/node-prisma/typescript/src/app.ts +3 -2
- package/templates/layers/node-prisma/typescript/src/config/prisma.ts +3 -2
- package/templates/layers/node-prisma/typescript/src/config/socket.ts +5 -4
- package/templates/layers/node-prisma/typescript/src/controllers/auth.controller.ts +89 -105
- package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -83
- package/templates/layers/node-prisma/typescript/src/controllers/upload.controller.ts +12 -19
- package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +9 -13
- package/templates/layers/node-prisma/typescript/src/middleware/errorHandler.ts +18 -5
- package/templates/layers/node-prisma/typescript/src/middleware/rateLimiter.ts +2 -2
- package/templates/layers/node-prisma/typescript/src/routes/auth.route.ts +2 -0
- package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +83 -31
- package/templates/layers/node-prisma/typescript/src/types/express.d.ts +5 -3
- package/templates/layers/node-prisma/typescript/src/utils/ApiError.ts +25 -0
- package/templates/layers/node-prisma/typescript/src/utils/SuccessResponse.ts +17 -0
- package/templates/layers/node-prisma/typescript/src/utils/apiResponse.ts +4 -0
- package/templates/layers/node-prisma/typescript/src/utils/asyncHandler.ts +13 -0
- package/templates/layers/node-prisma/typescript/src/utils/cloudinaryUpload.ts +18 -7
- package/templates/layers/node-prisma/typescript/src/utils/genOtpToken.ts +7 -1
- package/templates/layers/node-prisma/typescript/src/utils/jwt.ts +8 -2
- package/templates/layers/node-prisma/typescript/src/utils/logger.ts +2 -1
- package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +10 -6
- package/templates/layers/prisma/javascript/package.json +8 -0
- package/templates/layers/prisma/typescript/package.json +14 -1
- package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
- package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
- package/templates/layers/react/javascript/package.json +6 -0
- package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
- package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
- package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
- package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
- package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
- package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
- package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
- package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
- package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
- package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
- package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
- package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
- package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
- package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
- package/templates/layers/react/typescript/.eslintrc.cjs +20 -0
- package/templates/layers/react/typescript/package.json +11 -2
- package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
- package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
- package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
- package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
- package/templates/layers/react/typescript/src/components/common/Table.tsx +201 -38
- package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
- package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
- package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
- package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
- package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
- package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
- package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
- package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
- package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
- package/templates/layers/react/typescript/src/main.tsx +11 -11
- package/templates/layers/react/typescript/src/services/authService.ts +1 -1
- package/templates/layers/react/typescript/src/store/useAuthStore.ts +24 -9
- package/templates/layers/react-native/base/app.json +9 -22
- package/templates/layers/react-native/base/eslint.config.js +5 -1
- package/templates/layers/react-native/base/package.json +11 -1
- package/templates/layers/react-native/base/src/app/(auth)/edit-profile.tsx +7 -7
- package/templates/layers/react-native/base/src/app/(auth)/login.tsx +31 -7
- package/templates/layers/react-native/base/src/app/(tabs)/_layout.tsx +3 -3
- package/templates/layers/react-native/base/src/app/(tabs)/index.tsx +6 -5
- package/templates/layers/react-native/base/src/app/_layout.tsx +3 -3
- package/templates/layers/react-native/base/src/app/doc-viewer.tsx +3 -3
- package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
- package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +144 -0
- package/templates/layers/react-native/base/src/components/common/LocationLoader.tsx +64 -9
- package/templates/layers/react-native/base/src/components/common/Typography.tsx +54 -19
- package/templates/layers/react-native/base/src/components/ui/ClassicTabBar.tsx +15 -12
- package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +84 -90
- package/templates/layers/react-native/base/src/components/ui/Drawer.tsx +344 -0
- package/templates/layers/react-native/base/src/components/ui/Sidebar.tsx +4 -4
- package/templates/layers/react-native/base/src/components/ui/buttons/Button.tsx +29 -6
- package/templates/layers/react-native/base/src/components/ui/data-display/Accordion.tsx +2 -2
- package/templates/layers/react-native/base/src/components/ui/data-display/Badge.tsx +1 -2
- package/templates/layers/react-native/base/src/components/ui/data-display/Image.tsx +3 -3
- package/templates/layers/react-native/base/src/components/ui/data-display/SwipeableRow.tsx +28 -7
- package/templates/layers/react-native/base/src/components/ui/feedback/GestureWrapper.tsx +11 -4
- package/templates/layers/react-native/base/src/components/ui/inputs/DatePicker.tsx +3 -3
- package/templates/layers/react-native/base/src/components/ui/inputs/ImagePickerModal.tsx +45 -19
- package/templates/layers/react-native/base/src/components/ui/inputs/Input.tsx +28 -9
- package/templates/layers/react-native/base/src/components/ui/inputs/SearchablePicker.tsx +199 -0
- package/templates/layers/react-native/base/src/components/ui/inputs/Switch.tsx +7 -7
- package/templates/layers/react-native/base/src/components/ui/inputs/WebLocationPicker.tsx +146 -72
- package/templates/layers/react-native/base/src/config/api.ts +5 -5
- package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
- package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
- package/templates/layers/react-native/base/src/services/permission.service.ts +0 -2
- package/templates/layers/react-native/base/src/services/socket.service.ts +0 -7
- package/templates/layers/react-native/base/src/store/useAuthStore.ts +61 -16
- package/templates/layers/react-native/base/src/store/useNotificationStore.ts +36 -42
- package/templates/layers/react-native/base/src/types/images.d.ts +7 -5
- package/templates/layers/react-native/base/src/utils/fileDownload.ts +206 -0
- package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
- package/templates/presets/electron/preset.json +9 -0
- package/templates/presets/electron-ts/preset.json +9 -0
- package/templates/presets/production-fullstack/overrides/client/package.json +9 -0
- package/templates/presets/production-fullstack/overrides/mobile/app.json +2 -15
- package/templates/presets/production-fullstack/overrides/mobile/package.json +11 -1
- package/templates/presets/production-fullstack/overrides/mobile/src/app/_layout.tsx +3 -3
- package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/Drawer.tsx +344 -0
- package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/DatePicker.tsx +3 -3
- package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/ImagePickerModal.tsx +3 -3
- package/templates/presets/production-fullstack/overrides/server/package.json +11 -2
- package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
- package/templates/layers/react-native/base/src/components/ui/BottomModal.tsx +0 -176
- package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/BottomModal.tsx +0 -159
- /package/templates/layers/react-native/base/assets/images/logo/{logoWithBg.png → icon.png} +0 -0
|
@@ -11,6 +11,20 @@ import { useNotificationStore } from "./useNotificationStore";
|
|
|
11
11
|
// called from a useEffect, the promise is already resolved or very close.
|
|
12
12
|
const _prefetchedUser: Promise<User | null> = get("user");
|
|
13
13
|
|
|
14
|
+
interface ApiErrorResponse {
|
|
15
|
+
response?: {
|
|
16
|
+
data?: {
|
|
17
|
+
message?: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
message?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getErrorMessage = (err: unknown): string => {
|
|
24
|
+
const apiErr = err as ApiErrorResponse;
|
|
25
|
+
return apiErr.response?.data?.message || apiErr.message || "An unexpected error occurred";
|
|
26
|
+
};
|
|
27
|
+
|
|
14
28
|
export interface AuthState {
|
|
15
29
|
user: User | null;
|
|
16
30
|
loading: boolean;
|
|
@@ -31,6 +45,7 @@ export interface AuthState {
|
|
|
31
45
|
role?: string,
|
|
32
46
|
) => Promise<boolean>;
|
|
33
47
|
login: (email: string, password: string) => Promise<boolean>;
|
|
48
|
+
googleLogin: (idToken: string) => Promise<boolean>;
|
|
34
49
|
demoLogin: () => Promise<void>;
|
|
35
50
|
getMe: () => Promise<void>;
|
|
36
51
|
updateProfile: (updates: Partial<User> | FormData) => Promise<boolean>;
|
|
@@ -111,9 +126,9 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
111
126
|
try {
|
|
112
127
|
await authService.sendOtp(email);
|
|
113
128
|
return true;
|
|
114
|
-
} catch (err:
|
|
129
|
+
} catch (err: unknown) {
|
|
115
130
|
set({
|
|
116
|
-
error: err
|
|
131
|
+
error: getErrorMessage(err),
|
|
117
132
|
loading: false,
|
|
118
133
|
});
|
|
119
134
|
return false;
|
|
@@ -154,9 +169,9 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
154
169
|
notifStore.registerForPushNotificationsAsync();
|
|
155
170
|
notifStore.fetchNotifications();
|
|
156
171
|
return true;
|
|
157
|
-
} catch (err:
|
|
172
|
+
} catch (err: unknown) {
|
|
158
173
|
set({
|
|
159
|
-
error: err
|
|
174
|
+
error: getErrorMessage(err),
|
|
160
175
|
loading: false,
|
|
161
176
|
});
|
|
162
177
|
return false;
|
|
@@ -185,9 +200,39 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
185
200
|
notifStore.registerForPushNotificationsAsync();
|
|
186
201
|
notifStore.fetchNotifications();
|
|
187
202
|
return true;
|
|
188
|
-
} catch (err:
|
|
203
|
+
} catch (err: unknown) {
|
|
204
|
+
set({
|
|
205
|
+
error: getErrorMessage(err),
|
|
206
|
+
loading: false,
|
|
207
|
+
});
|
|
208
|
+
return false;
|
|
209
|
+
} finally {
|
|
210
|
+
set({ loading: false });
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
googleLogin: async (idToken: string) => {
|
|
215
|
+
set({ loading: true, error: null });
|
|
216
|
+
try {
|
|
217
|
+
const { data } = await authService.googleLogin(idToken);
|
|
218
|
+
if (data.data?.token) {
|
|
219
|
+
save("token", data.data.token);
|
|
220
|
+
}
|
|
221
|
+
const user = data.data?.user || data.data;
|
|
222
|
+
if (user) save("user", user);
|
|
223
|
+
set({
|
|
224
|
+
user,
|
|
225
|
+
loading: false,
|
|
226
|
+
});
|
|
227
|
+
initializeSocket();
|
|
228
|
+
const notifStore = useNotificationStore.getState();
|
|
229
|
+
notifStore.listenForSocketEvents();
|
|
230
|
+
notifStore.registerForPushNotificationsAsync();
|
|
231
|
+
notifStore.fetchNotifications();
|
|
232
|
+
return true;
|
|
233
|
+
} catch (err: unknown) {
|
|
189
234
|
set({
|
|
190
|
-
error: err
|
|
235
|
+
error: getErrorMessage(err),
|
|
191
236
|
loading: false,
|
|
192
237
|
});
|
|
193
238
|
return false;
|
|
@@ -231,9 +276,9 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
231
276
|
const { data } = await authService.updateMe(updates);
|
|
232
277
|
set({ user: data.data, loading: false });
|
|
233
278
|
return true;
|
|
234
|
-
} catch (err:
|
|
279
|
+
} catch (err: unknown) {
|
|
235
280
|
set({
|
|
236
|
-
error: err
|
|
281
|
+
error: getErrorMessage(err),
|
|
237
282
|
loading: false,
|
|
238
283
|
});
|
|
239
284
|
return false;
|
|
@@ -264,8 +309,8 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
264
309
|
try {
|
|
265
310
|
await authService.genOtp(email);
|
|
266
311
|
return true;
|
|
267
|
-
} catch (err:
|
|
268
|
-
set({ error: err
|
|
312
|
+
} catch (err: unknown) {
|
|
313
|
+
set({ error: getErrorMessage(err), loading: false });
|
|
269
314
|
return false;
|
|
270
315
|
} finally {
|
|
271
316
|
set({ loading: false });
|
|
@@ -278,8 +323,8 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
278
323
|
const { data } = await authService.verifyOtp(email, otp);
|
|
279
324
|
if (data.token) save("token", data.token);
|
|
280
325
|
return true;
|
|
281
|
-
} catch (err:
|
|
282
|
-
set({ error: err
|
|
326
|
+
} catch (err: unknown) {
|
|
327
|
+
set({ error: getErrorMessage(err), loading: false });
|
|
283
328
|
return false;
|
|
284
329
|
} finally {
|
|
285
330
|
set({ loading: false });
|
|
@@ -291,8 +336,8 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
291
336
|
try {
|
|
292
337
|
await authService.resetPassword(newPassword);
|
|
293
338
|
return true;
|
|
294
|
-
} catch (err:
|
|
295
|
-
set({ error: err
|
|
339
|
+
} catch (err: unknown) {
|
|
340
|
+
set({ error: getErrorMessage(err), loading: false });
|
|
296
341
|
return false;
|
|
297
342
|
} finally {
|
|
298
343
|
set({ loading: false });
|
|
@@ -304,8 +349,8 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
304
349
|
try {
|
|
305
350
|
await authService.changePassword(oldPassword, newPassword);
|
|
306
351
|
return true;
|
|
307
|
-
} catch (err:
|
|
308
|
-
set({ error: err
|
|
352
|
+
} catch (err: unknown) {
|
|
353
|
+
set({ error: getErrorMessage(err), loading: false });
|
|
309
354
|
return false;
|
|
310
355
|
} finally {
|
|
311
356
|
set({ loading: false });
|
|
@@ -12,7 +12,7 @@ export interface AppNotification {
|
|
|
12
12
|
type: string;
|
|
13
13
|
url: string | null;
|
|
14
14
|
isRead: boolean;
|
|
15
|
-
metadata:
|
|
15
|
+
metadata: Record<string, unknown> | null;
|
|
16
16
|
createdAt: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -47,9 +47,12 @@ export const useNotificationStore = create<NotificationState>((set, get) => ({
|
|
|
47
47
|
unreadCount,
|
|
48
48
|
loading: false,
|
|
49
49
|
});
|
|
50
|
-
} catch (error:
|
|
50
|
+
} catch (error: unknown) {
|
|
51
|
+
const message =
|
|
52
|
+
(error as { response?: { data?: { message?: string } } })?.response
|
|
53
|
+
?.data?.message || "Failed to fetch notifications";
|
|
51
54
|
set({
|
|
52
|
-
error:
|
|
55
|
+
error: message,
|
|
53
56
|
loading: false,
|
|
54
57
|
});
|
|
55
58
|
}
|
|
@@ -58,45 +61,38 @@ export const useNotificationStore = create<NotificationState>((set, get) => ({
|
|
|
58
61
|
markAsRead: async (id: string) => {
|
|
59
62
|
try {
|
|
60
63
|
// Optimistic update
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
notifications: updated,
|
|
68
|
-
unreadCount: Math.max(0, unreadCount - 1)
|
|
69
|
-
});
|
|
64
|
+
set((state) => ({
|
|
65
|
+
notifications: state.notifications.map((n) =>
|
|
66
|
+
n.id === id ? { ...n, isRead: true } : n
|
|
67
|
+
),
|
|
68
|
+
unreadCount: Math.max(0, state.unreadCount - 1),
|
|
69
|
+
}));
|
|
70
70
|
|
|
71
71
|
await notificationService.markAsRead(id);
|
|
72
|
-
} catch{
|
|
73
|
-
// Revert if
|
|
72
|
+
} catch {
|
|
73
|
+
// Revert if API fails
|
|
74
74
|
get().fetchNotifications();
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
|
|
78
78
|
markAllAsRead: async () => {
|
|
79
79
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
notifications: updated,
|
|
85
|
-
unreadCount: 0
|
|
86
|
-
});
|
|
80
|
+
set((state) => ({
|
|
81
|
+
notifications: state.notifications.map((n) => ({ ...n, isRead: true })),
|
|
82
|
+
unreadCount: 0,
|
|
83
|
+
}));
|
|
87
84
|
|
|
88
85
|
await notificationService.markAllAsRead();
|
|
89
|
-
} catch{
|
|
86
|
+
} catch {
|
|
90
87
|
get().fetchNotifications();
|
|
91
88
|
}
|
|
92
89
|
},
|
|
93
90
|
|
|
94
91
|
registerForPushNotificationsAsync: async () => {
|
|
95
|
-
let token;
|
|
92
|
+
let token: string | undefined;
|
|
96
93
|
|
|
97
|
-
const isExpoGo = Constants.appOwnership ===
|
|
98
|
-
if (isExpoGo && Platform.OS ===
|
|
99
|
-
console.log('Skipping push notification registration: not supported in Expo Go on Android (SDK 53+).');
|
|
94
|
+
const isExpoGo = Constants.appOwnership === "expo";
|
|
95
|
+
if (isExpoGo && Platform.OS === "android") {
|
|
100
96
|
return;
|
|
101
97
|
}
|
|
102
98
|
|
|
@@ -106,12 +102,12 @@ export const useNotificationStore = create<NotificationState>((set, get) => ({
|
|
|
106
102
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
107
103
|
const Device = require("expo-device");
|
|
108
104
|
|
|
109
|
-
if (Platform.OS ===
|
|
110
|
-
await Notifications.setNotificationChannelAsync(
|
|
111
|
-
name:
|
|
105
|
+
if (Platform.OS === "android") {
|
|
106
|
+
await Notifications.setNotificationChannelAsync("default", {
|
|
107
|
+
name: "default",
|
|
112
108
|
importance: Notifications.AndroidImportance.MAX,
|
|
113
109
|
vibrationPattern: [0, 250, 250, 250],
|
|
114
|
-
lightColor:
|
|
110
|
+
lightColor: "#FF231F7C",
|
|
115
111
|
});
|
|
116
112
|
}
|
|
117
113
|
|
|
@@ -119,13 +115,12 @@ export const useNotificationStore = create<NotificationState>((set, get) => ({
|
|
|
119
115
|
const { status: existingStatus } = await Notifications.getPermissionsAsync();
|
|
120
116
|
let finalStatus = existingStatus;
|
|
121
117
|
|
|
122
|
-
if (existingStatus !==
|
|
118
|
+
if (existingStatus !== "granted") {
|
|
123
119
|
const { status } = await Notifications.requestPermissionsAsync();
|
|
124
120
|
finalStatus = status;
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
if (finalStatus !==
|
|
128
|
-
console.log('Failed to get push token for push notification!');
|
|
123
|
+
if (finalStatus !== "granted") {
|
|
129
124
|
return;
|
|
130
125
|
}
|
|
131
126
|
|
|
@@ -136,17 +131,16 @@ export const useNotificationStore = create<NotificationState>((set, get) => ({
|
|
|
136
131
|
|
|
137
132
|
token = (await Notifications.getExpoPushTokenAsync({ projectId })).data;
|
|
138
133
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
134
|
+
if (token) {
|
|
135
|
+
try {
|
|
136
|
+
await notificationService.registerPushToken(token);
|
|
137
|
+
} catch {
|
|
138
|
+
// Token registration failed silently
|
|
139
|
+
}
|
|
144
140
|
}
|
|
145
|
-
} else {
|
|
146
|
-
console.log('Must use physical device for Push Notifications');
|
|
147
141
|
}
|
|
148
|
-
} catch
|
|
149
|
-
|
|
142
|
+
} catch {
|
|
143
|
+
// Notification setup failed silently
|
|
150
144
|
}
|
|
151
145
|
|
|
152
146
|
return token;
|
|
@@ -1,24 +1,26 @@
|
|
|
1
|
+
import type { ImageSourcePropType } from "react-native";
|
|
2
|
+
|
|
1
3
|
declare module "*.png" {
|
|
2
|
-
const value:
|
|
4
|
+
const value: ImageSourcePropType;
|
|
3
5
|
export default value;
|
|
4
6
|
}
|
|
5
7
|
|
|
6
8
|
declare module "*.jpg" {
|
|
7
|
-
const value:
|
|
9
|
+
const value: ImageSourcePropType;
|
|
8
10
|
export default value;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
declare module "*.jpeg" {
|
|
12
|
-
const value:
|
|
14
|
+
const value: ImageSourcePropType;
|
|
13
15
|
export default value;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
declare module "*.gif" {
|
|
17
|
-
const value:
|
|
19
|
+
const value: ImageSourcePropType;
|
|
18
20
|
export default value;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
declare module "*.svg" {
|
|
22
|
-
const value:
|
|
24
|
+
const value: ImageSourcePropType;
|
|
23
25
|
export default value;
|
|
24
26
|
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { Platform } from "react-native";
|
|
2
|
+
import { toast } from "./toast";
|
|
3
|
+
import * as FileSystem from "expo-file-system/legacy";
|
|
4
|
+
import * as Sharing from "expo-sharing";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Downloads a string or base64 data to a file.
|
|
8
|
+
* - On Web: triggers browser download via an anchor element and Blob.
|
|
9
|
+
* - On Android: saves to user-selected folder via StorageAccessFramework.
|
|
10
|
+
* - On iOS: writes to app document directory and opens native iOS Share/Save sheet.
|
|
11
|
+
*/
|
|
12
|
+
export const downloadFileString = async (
|
|
13
|
+
content: string,
|
|
14
|
+
filename: string,
|
|
15
|
+
mimeType: string,
|
|
16
|
+
isBase64: boolean = false
|
|
17
|
+
): Promise<boolean> => {
|
|
18
|
+
if (Platform.OS === "web") {
|
|
19
|
+
try {
|
|
20
|
+
let blob: Blob;
|
|
21
|
+
if (isBase64) {
|
|
22
|
+
const byteCharacters = atob(content);
|
|
23
|
+
const byteArrays: Uint8Array[] = [];
|
|
24
|
+
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
|
|
25
|
+
const slice = byteCharacters.slice(offset, offset + 512);
|
|
26
|
+
const byteNumbers = new Array(slice.length);
|
|
27
|
+
for (let i = 0; i < slice.length; i++) {
|
|
28
|
+
byteNumbers[i] = slice.charCodeAt(i);
|
|
29
|
+
}
|
|
30
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
31
|
+
byteArrays.push(byteArray);
|
|
32
|
+
}
|
|
33
|
+
blob = new Blob(byteArrays, { type: mimeType });
|
|
34
|
+
} else {
|
|
35
|
+
blob = new Blob([content], { type: mimeType });
|
|
36
|
+
}
|
|
37
|
+
const link = document.createElement("a");
|
|
38
|
+
const url = URL.createObjectURL(blob);
|
|
39
|
+
link.setAttribute("href", url);
|
|
40
|
+
link.setAttribute("download", filename);
|
|
41
|
+
link.style.visibility = "hidden";
|
|
42
|
+
document.body.appendChild(link);
|
|
43
|
+
link.click();
|
|
44
|
+
document.body.removeChild(link);
|
|
45
|
+
URL.revokeObjectURL(url);
|
|
46
|
+
return true;
|
|
47
|
+
} catch (error: unknown) {
|
|
48
|
+
const err = error as Error;
|
|
49
|
+
toast.error("Error", err.message || `Failed to download ${filename}`);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const encoding = isBase64
|
|
56
|
+
? FileSystem.EncodingType.Base64
|
|
57
|
+
: FileSystem.EncodingType.UTF8;
|
|
58
|
+
|
|
59
|
+
if (Platform.OS === "android") {
|
|
60
|
+
const permissions =
|
|
61
|
+
await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
|
|
62
|
+
if (!permissions.granted) {
|
|
63
|
+
toast.info("Cancelled", "Directory permission was not granted.");
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const createdFileUri =
|
|
68
|
+
await FileSystem.StorageAccessFramework.createFileAsync(
|
|
69
|
+
permissions.directoryUri,
|
|
70
|
+
filename,
|
|
71
|
+
mimeType
|
|
72
|
+
);
|
|
73
|
+
await FileSystem.writeAsStringAsync(createdFileUri, content, {
|
|
74
|
+
encoding,
|
|
75
|
+
});
|
|
76
|
+
toast.success("Success", `${filename} saved successfully!`);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// iOS and other native platforms
|
|
81
|
+
const baseDir =
|
|
82
|
+
FileSystem.documentDirectory ?? FileSystem.cacheDirectory ?? "";
|
|
83
|
+
const fileUri = `${baseDir}${filename}`;
|
|
84
|
+
await FileSystem.writeAsStringAsync(fileUri, content, { encoding });
|
|
85
|
+
|
|
86
|
+
const isSharingAvailable = await Sharing.isAvailableAsync();
|
|
87
|
+
if (isSharingAvailable) {
|
|
88
|
+
await Sharing.shareAsync(fileUri, {
|
|
89
|
+
mimeType,
|
|
90
|
+
dialogTitle: `Save ${filename}`,
|
|
91
|
+
UTI: mimeType,
|
|
92
|
+
});
|
|
93
|
+
toast.success("Success", "File ready to save or share.");
|
|
94
|
+
return true;
|
|
95
|
+
} else {
|
|
96
|
+
toast.error("Error", "Sharing is not available on this device.");
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
} catch (error: unknown) {
|
|
100
|
+
const err = error as Error;
|
|
101
|
+
toast.error("Error", err.message || `Failed to download ${filename}`);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Downloads a CSV string as a .csv file.
|
|
108
|
+
*/
|
|
109
|
+
export const downloadCsvString = async (
|
|
110
|
+
csvString: string,
|
|
111
|
+
filename: string = "export.csv"
|
|
112
|
+
): Promise<boolean> => {
|
|
113
|
+
return downloadFileString(csvString, filename, "text/csv;charset=utf-8;");
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Downloads JSON data as a formatted .json file.
|
|
118
|
+
*/
|
|
119
|
+
export const downloadJson = async (
|
|
120
|
+
data: unknown,
|
|
121
|
+
filename: string = "export.json"
|
|
122
|
+
): Promise<boolean> => {
|
|
123
|
+
const jsonString = JSON.stringify(data, null, 2);
|
|
124
|
+
return downloadFileString(jsonString, filename, "application/json");
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Downloads a file from a remote URL.
|
|
129
|
+
* - On Web: triggers browser download.
|
|
130
|
+
* - On Mobile: downloads to local filesystem, then prompts to save on Android
|
|
131
|
+
* via StorageAccessFramework or opens iOS Share sheet.
|
|
132
|
+
*/
|
|
133
|
+
export const downloadFileFromUrl = async (
|
|
134
|
+
url: string,
|
|
135
|
+
filename: string,
|
|
136
|
+
mimeType: string = "application/octet-stream"
|
|
137
|
+
): Promise<boolean> => {
|
|
138
|
+
if (Platform.OS === "web") {
|
|
139
|
+
try {
|
|
140
|
+
const link = document.createElement("a");
|
|
141
|
+
link.href = url;
|
|
142
|
+
link.download = filename;
|
|
143
|
+
link.target = "_blank";
|
|
144
|
+
link.style.visibility = "hidden";
|
|
145
|
+
document.body.appendChild(link);
|
|
146
|
+
link.click();
|
|
147
|
+
document.body.removeChild(link);
|
|
148
|
+
return true;
|
|
149
|
+
} catch (error: unknown) {
|
|
150
|
+
const err = error as Error;
|
|
151
|
+
toast.error("Error", err.message || `Failed to download ${filename}`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const baseDir =
|
|
158
|
+
FileSystem.cacheDirectory ?? FileSystem.documentDirectory ?? "";
|
|
159
|
+
const localUri = `${baseDir}${filename}`;
|
|
160
|
+
const result = await FileSystem.downloadAsync(url, localUri);
|
|
161
|
+
|
|
162
|
+
if (Platform.OS === "android") {
|
|
163
|
+
const permissions =
|
|
164
|
+
await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
|
|
165
|
+
if (!permissions.granted) {
|
|
166
|
+
toast.info("Cancelled", "Directory permission was not granted.");
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const fileBase64 = await FileSystem.readAsStringAsync(result.uri, {
|
|
171
|
+
encoding: FileSystem.EncodingType.Base64,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const createdFileUri =
|
|
175
|
+
await FileSystem.StorageAccessFramework.createFileAsync(
|
|
176
|
+
permissions.directoryUri,
|
|
177
|
+
filename,
|
|
178
|
+
mimeType
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
await FileSystem.writeAsStringAsync(createdFileUri, fileBase64, {
|
|
182
|
+
encoding: FileSystem.EncodingType.Base64,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
toast.success("Success", `${filename} saved successfully!`);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const isSharingAvailable = await Sharing.isAvailableAsync();
|
|
190
|
+
if (isSharingAvailable) {
|
|
191
|
+
await Sharing.shareAsync(result.uri, {
|
|
192
|
+
mimeType,
|
|
193
|
+
dialogTitle: `Save ${filename}`,
|
|
194
|
+
});
|
|
195
|
+
toast.success("Success", "File ready to save or share.");
|
|
196
|
+
return true;
|
|
197
|
+
} else {
|
|
198
|
+
toast.error("Error", "Sharing is not available on this device.");
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
} catch (error: unknown) {
|
|
202
|
+
const err = error as Error;
|
|
203
|
+
toast.error("Error", err.message || `Failed to download ${filename}`);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
2
|
|
|
3
3
|
// Save data
|
|
4
|
-
export const save = async (key: string, value:
|
|
4
|
+
export const save = async (key: string, value: unknown) => {
|
|
5
5
|
if (!key || !value) return;
|
|
6
6
|
try {
|
|
7
7
|
const jsonValue = JSON.stringify(value);
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
"dev": "vite",
|
|
8
8
|
"build": "tsc && vite build",
|
|
9
9
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
10
|
+
"lint:fix": "eslint . --ext ts,tsx --fix",
|
|
11
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,css,json}\"",
|
|
12
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx,css,json}\"",
|
|
13
|
+
"circular-check": "dpdm --circular --no-warning --no-tree --exit-code circular:1 \"src/**/*.{ts,tsx}\"",
|
|
14
|
+
"ts-prune": "ts-prune",
|
|
15
|
+
"ts-purns": "ts-prune",
|
|
10
16
|
"preview": "vite preview"
|
|
11
17
|
},
|
|
12
18
|
"dependencies": {
|
|
@@ -37,10 +43,13 @@
|
|
|
37
43
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
|
38
44
|
"@typescript-eslint/parser": "^6.10.0",
|
|
39
45
|
"@vitejs/plugin-react": "^4.2.0",
|
|
46
|
+
"dpdm": "^3.14.0",
|
|
40
47
|
"eslint": "^8.53.0",
|
|
41
48
|
"eslint-plugin-react": "^7.33.2",
|
|
42
49
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
43
50
|
"eslint-plugin-react-refresh": "^0.4.4",
|
|
51
|
+
"prettier": "^3.4.2",
|
|
52
|
+
"ts-prune": "^0.10.3",
|
|
44
53
|
"typescript": "^5.2.2",
|
|
45
54
|
"vite": "^5.0.0"
|
|
46
55
|
}
|
|
@@ -22,16 +22,8 @@
|
|
|
22
22
|
"package": "com.anonymous.mobile",
|
|
23
23
|
"permissions": [
|
|
24
24
|
"android.permission.CAMERA",
|
|
25
|
-
"android.permission.RECORD_AUDIO",
|
|
26
25
|
"android.permission.ACCESS_FINE_LOCATION",
|
|
27
|
-
"android.permission.ACCESS_COARSE_LOCATION"
|
|
28
|
-
"android.permission.READ_CONTACTS",
|
|
29
|
-
"android.permission.READ_CALENDAR",
|
|
30
|
-
"android.permission.READ_EXTERNAL_STORAGE",
|
|
31
|
-
"android.permission.WRITE_EXTERNAL_STORAGE",
|
|
32
|
-
"android.permission.BLUETOOTH_CONNECT",
|
|
33
|
-
"android.permission.BLUETOOTH_SCAN",
|
|
34
|
-
"android.permission.CALL_PHONE"
|
|
26
|
+
"android.permission.ACCESS_COARSE_LOCATION"
|
|
35
27
|
]
|
|
36
28
|
},
|
|
37
29
|
"web": {
|
|
@@ -58,14 +50,9 @@
|
|
|
58
50
|
{
|
|
59
51
|
"iosPermissions": [
|
|
60
52
|
"Camera",
|
|
61
|
-
"Microphone",
|
|
62
|
-
"LocationAlways",
|
|
63
53
|
"LocationWhenInUse",
|
|
64
54
|
"Notifications",
|
|
65
|
-
"PhotoLibrary"
|
|
66
|
-
"Contacts",
|
|
67
|
-
"Calendars",
|
|
68
|
-
"BluetoothPeripheral"
|
|
55
|
+
"PhotoLibrary"
|
|
69
56
|
]
|
|
70
57
|
}
|
|
71
58
|
]
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"expo-blur": "~57.0.2",
|
|
19
19
|
"expo-constants": "~57.0.6",
|
|
20
20
|
"expo-device": "~57.0.1",
|
|
21
|
+
"expo-file-system": "~18.0.7",
|
|
22
|
+
"expo-sharing": "~13.0.1",
|
|
21
23
|
"expo-font": "~57.0.1",
|
|
22
24
|
"expo-glass-effect": "~57.0.1",
|
|
23
25
|
"expo-image": "~57.0.1",
|
|
@@ -61,9 +63,12 @@
|
|
|
61
63
|
"devDependencies": {
|
|
62
64
|
"@types/react": "~19.2.2",
|
|
63
65
|
"babel-plugin-inline-import": "^3.0.0",
|
|
66
|
+
"dpdm": "^3.14.0",
|
|
64
67
|
"drizzle-kit": "^0.31.10",
|
|
65
68
|
"eslint": "^9.0.0",
|
|
66
69
|
"eslint-config-expo": "~57.0.0",
|
|
70
|
+
"prettier": "^3.4.2",
|
|
71
|
+
"ts-prune": "^0.10.3",
|
|
67
72
|
"typescript": "~6.0.3"
|
|
68
73
|
},
|
|
69
74
|
"scripts": {
|
|
@@ -74,7 +79,12 @@
|
|
|
74
79
|
"ios": "expo run:ios",
|
|
75
80
|
"web": "expo start --web",
|
|
76
81
|
"lint": "expo lint",
|
|
77
|
-
"
|
|
82
|
+
"lint:fix": "expo lint --fix",
|
|
83
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,json}\"",
|
|
84
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx,json}\"",
|
|
85
|
+
"circular-check": "dpdm --circular --no-warning --no-tree --exit-code circular:1 \"src/**/*.{ts,tsx}\"",
|
|
86
|
+
"ts-prune": "ts-prune",
|
|
87
|
+
"ts-purns": "ts-prune",
|
|
78
88
|
"ts:check": "tsc --noEmit",
|
|
79
89
|
"build": "eas build",
|
|
80
90
|
"build:android": "eas build -p android",
|
|
@@ -63,8 +63,8 @@ export default function RootLayout() {
|
|
|
63
63
|
if (!user && !inAuthGroup) {
|
|
64
64
|
// Redirect to the auth group
|
|
65
65
|
router.replace("/(auth)/login");
|
|
66
|
-
} else if (user && inAuthGroup) {
|
|
67
|
-
// Redirect away from auth
|
|
66
|
+
} else if (user && (inAuthGroup || !segments[0])) {
|
|
67
|
+
// Redirect away from auth or root to main app tabs
|
|
68
68
|
router.replace("/(tabs)");
|
|
69
69
|
}
|
|
70
70
|
}, [user, isCheckingAuth, segments]);
|
|
@@ -94,8 +94,8 @@ export default function RootLayout() {
|
|
|
94
94
|
options={{ headerShown: false, animation: "fade" }}
|
|
95
95
|
/>
|
|
96
96
|
</Stack>
|
|
97
|
-
<Toast />
|
|
98
97
|
</PaperProvider>
|
|
98
|
+
<Toast />
|
|
99
99
|
</GestureHandlerRootView>
|
|
100
100
|
</QueryClientProvider>
|
|
101
101
|
);
|