nico-tools 1.0.0 → 1.1.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.
Files changed (111) hide show
  1. package/bin/cli.js +9 -13
  2. package/bin/commands/create-project.js +106 -0
  3. package/bin/commands/translate.js +12 -0
  4. package/bin/config/templates.js +9 -0
  5. package/bin/utils/file-utils.js +84 -0
  6. package/bin/utils/project-utils.js +49 -0
  7. package/package.json +4 -2
  8. package/templates/react-native/expo-clean-architecture/README.md +154 -0
  9. package/templates/react-native/expo-clean-architecture/app.config.ts +61 -0
  10. package/templates/react-native/expo-clean-architecture/assets/config/.gitkeep +3 -0
  11. package/templates/react-native/expo-clean-architecture/assets/fonts/SpaceMono-Regular.ttf +0 -0
  12. package/templates/react-native/expo-clean-architecture/assets/images/adaptive-icon.png +0 -0
  13. package/templates/react-native/expo-clean-architecture/assets/images/favicon.png +0 -0
  14. package/templates/react-native/expo-clean-architecture/assets/images/icon.png +0 -0
  15. package/templates/react-native/expo-clean-architecture/assets/images/partial-react-logo.png +0 -0
  16. package/templates/react-native/expo-clean-architecture/assets/images/react-logo.png +0 -0
  17. package/templates/react-native/expo-clean-architecture/assets/images/react-logo@2x.png +0 -0
  18. package/templates/react-native/expo-clean-architecture/assets/images/react-logo@3x.png +0 -0
  19. package/templates/react-native/expo-clean-architecture/assets/images/splash-icon.png +0 -0
  20. package/templates/react-native/expo-clean-architecture/babel.config.js +11 -0
  21. package/templates/react-native/expo-clean-architecture/docs/00-introduction.md +3 -0
  22. package/templates/react-native/expo-clean-architecture/docs/01-architecture.md +107 -0
  23. package/templates/react-native/expo-clean-architecture/package.json +78 -0
  24. package/templates/react-native/expo-clean-architecture/scripts/clean-src.sh +48 -0
  25. package/templates/react-native/expo-clean-architecture/scripts/generate-feature.sh +40 -0
  26. package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/_layout.tsx +42 -0
  27. package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/favorites.tsx +72 -0
  28. package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/home.tsx +122 -0
  29. package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/settings/_layout.tsx +5 -0
  30. package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/settings/index.tsx +29 -0
  31. package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/settings/profile.tsx +22 -0
  32. package/templates/react-native/expo-clean-architecture/src/app/(protected)/_layout.tsx +20 -0
  33. package/templates/react-native/expo-clean-architecture/src/app/(protected)/details.tsx +124 -0
  34. package/templates/react-native/expo-clean-architecture/src/app/(public)/_layout.tsx +18 -0
  35. package/templates/react-native/expo-clean-architecture/src/app/(public)/login.tsx +31 -0
  36. package/templates/react-native/expo-clean-architecture/src/app/_layout.tsx +33 -0
  37. package/templates/react-native/expo-clean-architecture/src/app/index.tsx +8 -0
  38. package/templates/react-native/expo-clean-architecture/src/core/constants/api-constants.ts +10 -0
  39. package/templates/react-native/expo-clean-architecture/src/core/constants/image-constants.ts +3 -0
  40. package/templates/react-native/expo-clean-architecture/src/core/constants/query-keys.ts +6 -0
  41. package/templates/react-native/expo-clean-architecture/src/core/constants/storage-keys.ts +3 -0
  42. package/templates/react-native/expo-clean-architecture/src/core/design/@types/color-scheme-state.ts +35 -0
  43. package/templates/react-native/expo-clean-architecture/src/core/design/@types/color-scheme.ts +12 -0
  44. package/templates/react-native/expo-clean-architecture/src/core/design/components/app-icon.tsx +16 -0
  45. package/templates/react-native/expo-clean-architecture/src/core/design/components/app-separator.tsx +26 -0
  46. package/templates/react-native/expo-clean-architecture/src/core/design/hooks/use-app-color-scheme.ts +52 -0
  47. package/templates/react-native/expo-clean-architecture/src/core/design/hooks/use-app-fonts.ts +12 -0
  48. package/templates/react-native/expo-clean-architecture/src/core/design/hooks/use-app-styles.ts +28 -0
  49. package/templates/react-native/expo-clean-architecture/src/core/design/theme/app-colors.ts +21 -0
  50. package/templates/react-native/expo-clean-architecture/src/core/design/theme/app-fonts.ts +16 -0
  51. package/templates/react-native/expo-clean-architecture/src/core/design/theme/app-sizes.ts +14 -0
  52. package/templates/react-native/expo-clean-architecture/src/core/di/injection-container.ts +53 -0
  53. package/templates/react-native/expo-clean-architecture/src/core/errors/index.ts +1 -0
  54. package/templates/react-native/expo-clean-architecture/src/core/helpers/@types.ts +23 -0
  55. package/templates/react-native/expo-clean-architecture/src/core/helpers/rest-client.ts +144 -0
  56. package/templates/react-native/expo-clean-architecture/src/core/helpers/result.ts +37 -0
  57. package/templates/react-native/expo-clean-architecture/src/core/helpers/usecase.ts +5 -0
  58. package/templates/react-native/expo-clean-architecture/src/core/hooks/use-network.ts +18 -0
  59. package/templates/react-native/expo-clean-architecture/src/core/i18n/@types/i18next.d.ts +11 -0
  60. package/templates/react-native/expo-clean-architecture/src/core/i18n/index.ts +19 -0
  61. package/templates/react-native/expo-clean-architecture/src/core/i18n/translations/fr.json +12 -0
  62. package/templates/react-native/expo-clean-architecture/src/core/services/local-storage-service-impl.ts +29 -0
  63. package/templates/react-native/expo-clean-architecture/src/core/services/local-storage-service.ts +26 -0
  64. package/templates/react-native/expo-clean-architecture/src/core/services/monitoring-service-impl.ts +15 -0
  65. package/templates/react-native/expo-clean-architecture/src/core/services/monitoring-service.ts +13 -0
  66. package/templates/react-native/expo-clean-architecture/src/core/services/network-service-impl.ts +40 -0
  67. package/templates/react-native/expo-clean-architecture/src/core/services/network-service.ts +16 -0
  68. package/templates/react-native/expo-clean-architecture/src/features/auth/@types/session-state.ts +38 -0
  69. package/templates/react-native/expo-clean-architecture/src/features/auth/@types/session-status-enum.ts +16 -0
  70. package/templates/react-native/expo-clean-architecture/src/features/auth/presentation/hooks/use-session.ts +18 -0
  71. package/templates/react-native/expo-clean-architecture/src/features/favorites/data/datasources/favorites-datasource-impl.ts +25 -0
  72. package/templates/react-native/expo-clean-architecture/src/features/favorites/data/datasources/favorites-datasource.ts +5 -0
  73. package/templates/react-native/expo-clean-architecture/src/features/favorites/data/repositories/favorites-repository-impl.ts +46 -0
  74. package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/repositories/favorites-repository.ts +8 -0
  75. package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/usecases/add-to-favorites-usecase.ts +23 -0
  76. package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/usecases/clear-favorites-usecase.ts +23 -0
  77. package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/usecases/get-favorites-usecase.ts +24 -0
  78. package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/usecases/remove-from-favorites-usecase.ts +23 -0
  79. package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/components/favorites-card.tsx +77 -0
  80. package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/hooks/use-add-favorite.ts +24 -0
  81. package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/hooks/use-clear-favorites.ts +22 -0
  82. package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/hooks/use-favorites.ts +22 -0
  83. package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/hooks/use-remove-favorite.ts +24 -0
  84. package/templates/react-native/expo-clean-architecture/src/features/movies/data/datasources/movies-datasource-impl.ts +50 -0
  85. package/templates/react-native/expo-clean-architecture/src/features/movies/data/datasources/movies-datasource.ts +8 -0
  86. package/templates/react-native/expo-clean-architecture/src/features/movies/data/models/movie-details-model.ts +67 -0
  87. package/templates/react-native/expo-clean-architecture/src/features/movies/data/models/movie-model.ts +30 -0
  88. package/templates/react-native/expo-clean-architecture/src/features/movies/data/models/tmdb-response-model.ts +6 -0
  89. package/templates/react-native/expo-clean-architecture/src/features/movies/data/repositories/movies-repository-impl.ts +34 -0
  90. package/templates/react-native/expo-clean-architecture/src/features/movies/domain/entities/movie-details-entity.ts +28 -0
  91. package/templates/react-native/expo-clean-architecture/src/features/movies/domain/entities/movie-entity.ts +6 -0
  92. package/templates/react-native/expo-clean-architecture/src/features/movies/domain/repositories/movies-repository.ts +25 -0
  93. package/templates/react-native/expo-clean-architecture/src/features/movies/domain/usecases/get-movie-details-usecase.ts +26 -0
  94. package/templates/react-native/expo-clean-architecture/src/features/movies/domain/usecases/get-random-movies-usecase.ts +24 -0
  95. package/templates/react-native/expo-clean-architecture/src/features/movies/domain/usecases/search-movies-usecase.ts +23 -0
  96. package/templates/react-native/expo-clean-architecture/src/features/movies/presentation/components/movie-tile.tsx +69 -0
  97. package/templates/react-native/expo-clean-architecture/src/features/movies/presentation/hooks/use-movie-details.ts +22 -0
  98. package/templates/react-native/expo-clean-architecture/src/features/movies/presentation/hooks/use-random-movies.ts +22 -0
  99. package/templates/react-native/expo-clean-architecture/src/features/movies/presentation/hooks/use-search-movies.ts +22 -0
  100. package/templates/react-native/expo-clean-architecture/tests/core/services/local-storage-service-impl.test.ts +108 -0
  101. package/templates/react-native/expo-clean-architecture/tests/core/services/monitoring-service.test.ts +74 -0
  102. package/templates/react-native/expo-clean-architecture/tests/core/services/network-service.test.ts +117 -0
  103. package/templates/react-native/expo-clean-architecture/tests/features/auth/presentation/hooks/use-session.test.ts +69 -0
  104. package/templates/react-native/expo-clean-architecture/tests/features/favorites/data/datasources/favorites-datasource.test.ts +69 -0
  105. package/templates/react-native/expo-clean-architecture/tests/features/favorites/data/repositories/favorites-repository-impl.test.ts +124 -0
  106. package/templates/react-native/expo-clean-architecture/tests/features/favorites/domain/usecases/add-to-favorites-usecase.test.ts +54 -0
  107. package/templates/react-native/expo-clean-architecture/tests/features/favorites/domain/usecases/clear-favorites-usecase.test.ts +44 -0
  108. package/templates/react-native/expo-clean-architecture/tests/features/favorites/domain/usecases/get-favorites-usecase.test.ts +74 -0
  109. package/templates/react-native/expo-clean-architecture/tests/features/favorites/domain/usecases/remove-from-favorites-usecase.test.ts +52 -0
  110. package/templates/react-native/expo-clean-architecture/tests/setup.ts +9 -0
  111. package/templates/react-native/expo-clean-architecture/tsconfig.json +20 -0
@@ -0,0 +1,124 @@
1
+ import {
2
+ StyleSheet,
3
+ View,
4
+ Text,
5
+ ActivityIndicator,
6
+ Image,
7
+ Button,
8
+ } from "react-native";
9
+ import { useLocalSearchParams } from "expo-router";
10
+
11
+ import { useAppStyles } from "@/core/design/hooks/use-app-styles";
12
+ import { useMovieDetails } from "@/features/movies/presentation/hooks/use-movie-details";
13
+ import { AppSpacing } from "@/core/design/theme/app-sizes";
14
+ import { AppFontSize } from "@/core/design/theme/app-fonts";
15
+ import { ColorScheme } from "@/core/design/@types/color-scheme";
16
+ import { useAddFavorite } from "@/features/favorites/presentation/hooks/use-add-favorite";
17
+ import { useRemoveFavorite } from "@/features/favorites/presentation/hooks/use-remove-favorite";
18
+ import { useFavorites } from "@/features/favorites/presentation/hooks/use-favorites";
19
+ import { useRandomMovies } from "@/features/movies/presentation/hooks/use-random-movies";
20
+ import { AppSeparator } from "@/core/design/components/app-separator";
21
+ import { movieDetailsEntityToMovieEntity } from "@/features/movies/domain/entities/movie-details-entity";
22
+
23
+ export default function MovieDetailsPage() {
24
+ const styles = useAppStyles(createStyles);
25
+ const { movieId } = useLocalSearchParams<{ movieId: string }>();
26
+ const { data: movieDetails, isLoading, error } = useMovieDetails(movieId);
27
+ const { data: favorites } = useFavorites();
28
+ const { mutate: addFavorite } = useAddFavorite();
29
+ const { mutate: removeFavorite } = useRemoveFavorite();
30
+
31
+ const isFavorite = favorites?.some((favorite) => favorite.id === movieId);
32
+
33
+ if (isLoading) {
34
+ return (
35
+ <View style={styles.centeredContainer}>
36
+ <ActivityIndicator size="large" />
37
+ </View>
38
+ );
39
+ }
40
+
41
+ if (error) {
42
+ return (
43
+ <View style={styles.centeredContainer}>
44
+ <Text>Error</Text>
45
+ </View>
46
+ );
47
+ }
48
+
49
+ const {
50
+ backdrop_path,
51
+ title,
52
+ overview,
53
+ vote_average,
54
+ release_date,
55
+ runtime,
56
+ } = movieDetails!;
57
+
58
+ return (
59
+ <View style={styles.container}>
60
+ {!!backdrop_path && (
61
+ <Image source={{ uri: backdrop_path }} style={styles.imageBackground} />
62
+ )}
63
+ <View style={styles.content}>
64
+ <Text style={styles.info}>
65
+ {runtime}min •{" "}
66
+ {new Date(release_date).toLocaleDateString("fr-FR", {
67
+ day: "2-digit",
68
+ month: "long",
69
+ year: "numeric",
70
+ })}
71
+ • {vote_average.toPrecision(2)}/10
72
+ </Text>
73
+ <Text style={styles.title}>{title}</Text>
74
+ <Text style={styles.overview}>{overview}</Text>
75
+ <AppSeparator />
76
+ <Button
77
+ title={isFavorite ? "Supprimer des favoris" : "Ajouter aux favoris"}
78
+ onPress={() => {
79
+ if (isFavorite) {
80
+ removeFavorite(movieId);
81
+ } else {
82
+ const movie = movieDetailsEntityToMovieEntity(movieDetails!);
83
+
84
+ if (movie) {
85
+ addFavorite(movie);
86
+ }
87
+ }
88
+ }}
89
+ />
90
+ </View>
91
+ </View>
92
+ );
93
+ }
94
+
95
+ function createStyles(colorScheme: ColorScheme) {
96
+ return StyleSheet.create({
97
+ centeredContainer: {
98
+ flex: 1,
99
+ justifyContent: "center",
100
+ alignItems: "center",
101
+ },
102
+ container: {
103
+ flex: 1,
104
+ },
105
+ content: {
106
+ padding: AppSpacing.medium,
107
+ },
108
+ imageBackground: {
109
+ width: "100%",
110
+ height: 300,
111
+ resizeMode: "cover",
112
+ },
113
+ info: {
114
+ fontSize: AppFontSize.regular,
115
+ color: colorScheme.textSecondary,
116
+ },
117
+ title: {
118
+ fontSize: AppFontSize.extraLarge,
119
+ fontWeight: "bold",
120
+ marginVertical: AppSpacing.medium,
121
+ },
122
+ overview: {},
123
+ });
124
+ }
@@ -0,0 +1,18 @@
1
+ import { Redirect, Stack } from "expo-router";
2
+ import { useSession } from "@/features/auth/presentation/hooks/use-session";
3
+ import { useTranslation } from "react-i18next";
4
+
5
+ export default function PublicLayout() {
6
+ const { isAuthenticated } = useSession();
7
+ const { t } = useTranslation();
8
+
9
+ if (isAuthenticated()) {
10
+ return <Redirect href="/home" />;
11
+ }
12
+
13
+ return (
14
+ <Stack>
15
+ <Stack.Screen name="login" options={{ title: t("auth.login") }} />
16
+ </Stack>
17
+ );
18
+ }
@@ -0,0 +1,31 @@
1
+ import { Button, StyleSheet, View } from "react-native";
2
+ import { useTranslation } from "react-i18next";
3
+
4
+ import { ColorScheme } from "@/core/design/@types/color-scheme";
5
+ import { useAppStyles } from "@/core/design/hooks/use-app-styles";
6
+ import { useSession } from "@/features/auth/presentation/hooks/use-session";
7
+
8
+ export default function LoginPage() {
9
+ const styles = useAppStyles(createStyles);
10
+ const { login } = useSession();
11
+ const { t } = useTranslation();
12
+
13
+ return (
14
+ <View style={styles.container}>
15
+ <Button title={t("auth.login")} onPress={login} />
16
+ </View>
17
+ );
18
+ }
19
+
20
+ function createStyles(colorScheme: ColorScheme) {
21
+ return StyleSheet.create({
22
+ container: {
23
+ flex: 1,
24
+ justifyContent: "center",
25
+ alignItems: "center",
26
+ },
27
+ text: {
28
+ color: colorScheme.text,
29
+ },
30
+ });
31
+ }
@@ -0,0 +1,33 @@
1
+ import i18n from "@/core/i18n";
2
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
3
+ import { Stack } from "expo-router";
4
+ import { I18nextProvider } from "react-i18next";
5
+
6
+ const queryClient = new QueryClient({
7
+ defaultOptions: {
8
+ mutations: {
9
+ onError: (error) => {
10
+ console.log(error);
11
+ },
12
+ },
13
+ },
14
+ });
15
+
16
+ export default function RootLayout() {
17
+ return (
18
+ <QueryClientProvider client={queryClient}>
19
+ <I18nextProvider i18n={i18n}>
20
+ <Stack>
21
+ <Stack.Screen
22
+ name="(protected)"
23
+ options={{ headerShown: false, animation: "none" }}
24
+ />
25
+ <Stack.Screen
26
+ name="(public)"
27
+ options={{ headerShown: false, animation: "none" }}
28
+ />
29
+ </Stack>
30
+ </I18nextProvider>
31
+ </QueryClientProvider>
32
+ );
33
+ }
@@ -0,0 +1,8 @@
1
+ import { useSession } from "@/features/auth/presentation/hooks/use-session";
2
+ import { Redirect } from "expo-router";
3
+
4
+ export default function Index() {
5
+ const { isAuthenticated } = useSession();
6
+
7
+ return <Redirect href={isAuthenticated() ? "/home" : "/login"} />;
8
+ }
@@ -0,0 +1,10 @@
1
+ export const API_CONSTANTS = {
2
+ baseUrl: "https://api.themoviedb.org/3",
3
+ token: process.env.EXPO_PUBLIC_TMDB_TOKEN,
4
+ movieImageBaseUrl: "https://image.tmdb.org/t/p/w500",
5
+ endpoints: {
6
+ discover: "/discover/movie",
7
+ search: "/search/movie",
8
+ movieDetails: (id: string) => `/movie/${id}`,
9
+ },
10
+ };
@@ -0,0 +1,3 @@
1
+ export const IMAGE_CONSTANTS = {
2
+ posterAspectRatio: 500 / 750,
3
+ };
@@ -0,0 +1,6 @@
1
+ export const QUERY_KEYS = {
2
+ FAVORITES: "favorites",
3
+ MOVIE_DETAILS: "movie-details",
4
+ RANDOM_MOVIES: "random-movies",
5
+ SEARCH_MOVIES: "search-movies",
6
+ };
@@ -0,0 +1,3 @@
1
+ export const STORAGE_KEYS = {
2
+ FAVORITES: "favorites",
3
+ };
@@ -0,0 +1,35 @@
1
+ import { ColorSchemeName, StatusBarStyle } from "react-native";
2
+ import { ColorScheme } from "@/core/design/@types/color-scheme";
3
+
4
+ export interface ColorSchemeState {
5
+ /**
6
+ * The current colors.
7
+ */
8
+ colorScheme: ColorScheme;
9
+
10
+ /**
11
+ * The current color scheme name.
12
+ */
13
+ currentColorScheme: ColorSchemeName;
14
+
15
+ /**
16
+ * Whether the color scheme has been initialized.
17
+ */
18
+ initialized: boolean;
19
+
20
+ /**
21
+ * The current status bar style.
22
+ */
23
+ statusBarStyle: StatusBarStyle;
24
+
25
+ /**
26
+ * Set the color scheme.
27
+ * @param colorSchemeName - The color scheme name to set.
28
+ */
29
+ setColorScheme: (colorSchemeName: ColorSchemeName) => void;
30
+
31
+ /**
32
+ * Switch to the opposite color scheme.
33
+ */
34
+ toggleColorScheme: () => void;
35
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Interface for the color scheme used in the app.
3
+ */
4
+ export interface ColorScheme {
5
+ background: string;
6
+ text: string;
7
+ textSecondary: string;
8
+ danger: string;
9
+ success: string;
10
+ warning: string;
11
+ info: string;
12
+ }
@@ -0,0 +1,16 @@
1
+ import IonIcons from "@expo/vector-icons/Ionicons";
2
+ import { useAppColorScheme } from "@/core/design/hooks/use-app-color-scheme";
3
+
4
+ export const AppIcon = ({
5
+ name,
6
+ size = 24,
7
+ color,
8
+ }: {
9
+ name: keyof typeof IonIcons.glyphMap;
10
+ size?: number;
11
+ color?: string;
12
+ }) => {
13
+ const { colorScheme } = useAppColorScheme();
14
+
15
+ return <IonIcons name={name} size={size} color={color ?? colorScheme.text} />;
16
+ };
@@ -0,0 +1,26 @@
1
+ import { View, StyleSheet } from "react-native";
2
+ import { useAppStyles } from "@/core/design/hooks/use-app-styles";
3
+ import { AppSpacing } from "@/core/design/theme/app-sizes";
4
+
5
+ type Props = {
6
+ size?: AppSpacing;
7
+ axis?: "horizontal" | "vertical";
8
+ };
9
+
10
+ export function AppSeparator({
11
+ size = AppSpacing.medium,
12
+ axis = "vertical",
13
+ }: Props) {
14
+ const styles = useAppStyles(() => createStyles(size, axis));
15
+
16
+ return <View style={styles.separator} />;
17
+ }
18
+
19
+ function createStyles(size: AppSpacing, axis: "horizontal" | "vertical") {
20
+ return StyleSheet.create({
21
+ separator: {
22
+ height: axis === "vertical" ? size : null,
23
+ width: axis === "horizontal" ? size : null,
24
+ },
25
+ });
26
+ }
@@ -0,0 +1,52 @@
1
+ import { ColorSchemeName } from "react-native";
2
+ import { create } from "zustand";
3
+ import { createJSONStorage, persist } from "zustand/middleware";
4
+ import { darkColorScheme, lightColorScheme } from "@/core/design/theme/app-colors";
5
+ import { ColorSchemeState } from "@/core/design/@types/color-scheme-state";
6
+ import AsyncStorage from "@react-native-async-storage/async-storage";
7
+
8
+ /**
9
+ * Hook to manage the app's color scheme.
10
+ *
11
+ * The state is persisted locally on the user's device using `AsyncStorage`.
12
+ *
13
+ * @returns {ColorSchemeState} The current color scheme state.
14
+ */
15
+ export const useAppColorScheme = create<ColorSchemeState>()(
16
+ persist(
17
+ (set) => ({
18
+ colorScheme: lightColorScheme,
19
+ initialized: false,
20
+ currentColorScheme: "light",
21
+ statusBarStyle: "light-content",
22
+ setColorScheme: (colorSchemeName: ColorSchemeName) => {
23
+ set({
24
+ initialized: true,
25
+ colorScheme:
26
+ colorSchemeName === "dark" ? darkColorScheme : lightColorScheme,
27
+ statusBarStyle:
28
+ colorSchemeName === "dark" ? "dark-content" : "light-content",
29
+ currentColorScheme: colorSchemeName,
30
+ });
31
+ },
32
+ toggleColorScheme: () => {
33
+ set((state) => ({
34
+ colorScheme:
35
+ state.currentColorScheme === "dark"
36
+ ? lightColorScheme
37
+ : darkColorScheme,
38
+ statusBarStyle:
39
+ state.currentColorScheme === "dark"
40
+ ? "light-content"
41
+ : "dark-content",
42
+ currentColorScheme:
43
+ state.currentColorScheme === "dark" ? "light" : "dark",
44
+ }));
45
+ },
46
+ }),
47
+ {
48
+ name: "color-scheme",
49
+ storage: createJSONStorage(() => AsyncStorage),
50
+ }
51
+ )
52
+ );
@@ -0,0 +1,12 @@
1
+ import { useFonts } from "expo-font";
2
+
3
+ /**
4
+ * Register app fonts.
5
+ * @returns The fonts object
6
+ */
7
+ export const useAppFonts = () => {
8
+ return useFonts({
9
+ // Example:
10
+ // "Poppins": require("../../../../assets/fonts/Poppins-Regular.ttf"),
11
+ });
12
+ };
@@ -0,0 +1,28 @@
1
+ import { useMemo } from "react";
2
+ import { useAppColorScheme } from "@/core/design/hooks/use-app-color-scheme";
3
+ import { ColorScheme } from "@/core/design/@types/color-scheme";
4
+ import { useHeaderHeight } from "@react-navigation/elements";
5
+ import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs";
6
+
7
+ /**
8
+ * Use styles hook
9
+ * Returns a memoized styles object based on the color scheme
10
+ * @param styleCreator - A function that creates styles based on the color scheme
11
+ * @returns The styles object
12
+ */
13
+ export function useAppStyles<T>(
14
+ styleCreator: (
15
+ colorScheme: ColorScheme,
16
+ headerHeight: number,
17
+ bottomTabHeight: number
18
+ ) => T
19
+ ) {
20
+ const { colorScheme } = useAppColorScheme();
21
+ const headerHeight = useHeaderHeight();
22
+ //const bottomTabHeight = useBottomTabBarHeight();
23
+
24
+ return useMemo(
25
+ () => styleCreator(colorScheme, headerHeight, 0),
26
+ [colorScheme, styleCreator, headerHeight]
27
+ );
28
+ }
@@ -0,0 +1,21 @@
1
+ import { ColorScheme } from "@/core/design/@types/color-scheme";
2
+
3
+ export const lightColorScheme: ColorScheme = {
4
+ background: "white",
5
+ text: "black",
6
+ textSecondary: "gray",
7
+ danger: "red",
8
+ success: "green",
9
+ warning: "yellow",
10
+ info: "blue",
11
+ };
12
+
13
+ export const darkColorScheme: ColorScheme = {
14
+ background: "black",
15
+ text: "white",
16
+ textSecondary: "gray",
17
+ danger: "red",
18
+ success: "green",
19
+ warning: "yellow",
20
+ info: "blue",
21
+ };
@@ -0,0 +1,16 @@
1
+ export enum AppFontWeight {
2
+ light = 300,
3
+ regular = 400,
4
+ medium = 500,
5
+ semibold = 600,
6
+ bold = 700,
7
+ }
8
+
9
+ export enum AppFontSize {
10
+ extraSmall = 10,
11
+ small = 12,
12
+ regular = 14,
13
+ medium = 16,
14
+ large = 20,
15
+ extraLarge = 24,
16
+ }
@@ -0,0 +1,14 @@
1
+ export enum AppRadius {
2
+ square = 0,
3
+ rounded = 8,
4
+ circle = 1000,
5
+ }
6
+
7
+ export enum AppSpacing {
8
+ extraSmall = 4,
9
+ small = 8,
10
+ regular = 12,
11
+ medium = 16,
12
+ large = 24,
13
+ extraLarge = 32,
14
+ }
@@ -0,0 +1,53 @@
1
+ import { Container } from "inversify";
2
+
3
+ import { NetworkService } from "@/core/services/network-service";
4
+ import { NetworkServiceImpl } from "@/core/services/network-service-impl";
5
+ import { MonitoringService } from "@/core/services/monitoring-service";
6
+ import { MonitoringServiceImpl } from "@/core/services/monitoring-service-impl";
7
+ import { LocalStorageService } from "@/core/services/local-storage-service";
8
+ import { LocalStorageServiceImpl } from "@/core/services/local-storage-service-impl";
9
+ import { MoviesDatasource } from "@/features/movies/data/datasources/movies-datasource";
10
+ import { MoviesDatasourceImpl } from "@/features/movies/data/datasources/movies-datasource-impl";
11
+ import { MoviesRepository } from "@/features/movies/domain/repositories/movies-repository";
12
+ import { MoviesRepositoryImpl } from "@/features/movies/data/repositories/movies-repository-impl";
13
+ import { GetRandomMoviesUsecase } from "@/features/movies/domain/usecases/get-random-movies-usecase";
14
+ import { SearchMoviesUsecase } from "@/features/movies/domain/usecases/search-movies-usecase";
15
+ import { GetMovieDetailsUsecase } from "@/features/movies/domain/usecases/get-movie-details-usecase";
16
+ import { FavoritesDatasource } from "@/features/favorites/data/datasources/favorites-datasource";
17
+ import { FavoritesDatasourceImpl } from "@/features/favorites/data/datasources/favorites-datasource-impl";
18
+ import { FavoritesRepository } from "@/features/favorites/domain/repositories/favorites-repository";
19
+ import { FavoritesRepositoryImpl } from "@/features/favorites/data/repositories/favorites-repository-impl";
20
+ import { GetFavoritesUsecase } from "@/features/favorites/domain/usecases/get-favorites-usecase";
21
+ import { AddToFavoritesUsecase } from "@/features/favorites/domain/usecases/add-to-favorites-usecase";
22
+ import { RemoveFromFavoritesUsecase } from "@/features/favorites/domain/usecases/remove-from-favorites-usecase";
23
+ import { ClearFavoritesUsecase } from "@/features/favorites/domain/usecases/clear-favorites-usecase";
24
+
25
+ const container = new Container({ defaultScope: "Singleton" });
26
+
27
+ // CORE : Services
28
+ container.bind<NetworkService>(NetworkService).to(NetworkServiceImpl);
29
+ container.bind<MonitoringService>(MonitoringService).to(MonitoringServiceImpl);
30
+ container
31
+ .bind<LocalStorageService>(LocalStorageService)
32
+ .to(LocalStorageServiceImpl);
33
+
34
+ // FEATURE : Movies
35
+ container.bind<MoviesRepository>(MoviesRepository).to(MoviesRepositoryImpl);
36
+ container.bind<MoviesDatasource>(MoviesDatasource).to(MoviesDatasourceImpl);
37
+ container.bind<GetRandomMoviesUsecase>(GetRandomMoviesUsecase).toSelf();
38
+ container.bind<GetMovieDetailsUsecase>(GetMovieDetailsUsecase).toSelf();
39
+ container.bind<SearchMoviesUsecase>(SearchMoviesUsecase).toSelf();
40
+
41
+ // FEATURE : Favorites
42
+ container
43
+ .bind<FavoritesDatasource>(FavoritesDatasource)
44
+ .to(FavoritesDatasourceImpl);
45
+ container
46
+ .bind<FavoritesRepository>(FavoritesRepository)
47
+ .to(FavoritesRepositoryImpl);
48
+ container.bind<GetFavoritesUsecase>(GetFavoritesUsecase).toSelf();
49
+ container.bind<AddToFavoritesUsecase>(AddToFavoritesUsecase).toSelf();
50
+ container.bind<RemoveFromFavoritesUsecase>(RemoveFromFavoritesUsecase).toSelf();
51
+ container.bind<ClearFavoritesUsecase>(ClearFavoritesUsecase).toSelf();
52
+
53
+ export default container;
@@ -0,0 +1 @@
1
+ export class UnknownError extends Error {}
@@ -0,0 +1,23 @@
1
+ export type RestClientOptions = {
2
+ baseUrl?: string;
3
+ headers?: Record<string, string>;
4
+ };
5
+
6
+ export type RequestConfig = {
7
+ path: string;
8
+ options?: RequestInit;
9
+ };
10
+
11
+ export type GetRequestConfig = RequestConfig & {
12
+ queryParameters?: Record<string, any>;
13
+ };
14
+
15
+ export type PostRequestConfig = RequestConfig & {
16
+ body: any;
17
+ };
18
+
19
+ export type PutRequestConfig = RequestConfig & {
20
+ body: any;
21
+ };
22
+
23
+ export type DeleteRequestConfig = RequestConfig;