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.
- package/bin/cli.js +9 -13
- package/bin/commands/create-project.js +106 -0
- package/bin/commands/translate.js +12 -0
- package/bin/config/templates.js +9 -0
- package/bin/utils/file-utils.js +84 -0
- package/bin/utils/project-utils.js +49 -0
- package/package.json +4 -2
- package/templates/react-native/expo-clean-architecture/README.md +154 -0
- package/templates/react-native/expo-clean-architecture/app.config.ts +61 -0
- package/templates/react-native/expo-clean-architecture/assets/config/.gitkeep +3 -0
- package/templates/react-native/expo-clean-architecture/assets/fonts/SpaceMono-Regular.ttf +0 -0
- package/templates/react-native/expo-clean-architecture/assets/images/adaptive-icon.png +0 -0
- package/templates/react-native/expo-clean-architecture/assets/images/favicon.png +0 -0
- package/templates/react-native/expo-clean-architecture/assets/images/icon.png +0 -0
- package/templates/react-native/expo-clean-architecture/assets/images/partial-react-logo.png +0 -0
- package/templates/react-native/expo-clean-architecture/assets/images/react-logo.png +0 -0
- package/templates/react-native/expo-clean-architecture/assets/images/react-logo@2x.png +0 -0
- package/templates/react-native/expo-clean-architecture/assets/images/react-logo@3x.png +0 -0
- package/templates/react-native/expo-clean-architecture/assets/images/splash-icon.png +0 -0
- package/templates/react-native/expo-clean-architecture/babel.config.js +11 -0
- package/templates/react-native/expo-clean-architecture/docs/00-introduction.md +3 -0
- package/templates/react-native/expo-clean-architecture/docs/01-architecture.md +107 -0
- package/templates/react-native/expo-clean-architecture/package.json +78 -0
- package/templates/react-native/expo-clean-architecture/scripts/clean-src.sh +48 -0
- package/templates/react-native/expo-clean-architecture/scripts/generate-feature.sh +40 -0
- package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/_layout.tsx +42 -0
- package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/favorites.tsx +72 -0
- package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/home.tsx +122 -0
- package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/settings/_layout.tsx +5 -0
- package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/settings/index.tsx +29 -0
- package/templates/react-native/expo-clean-architecture/src/app/(protected)/(tabs)/settings/profile.tsx +22 -0
- package/templates/react-native/expo-clean-architecture/src/app/(protected)/_layout.tsx +20 -0
- package/templates/react-native/expo-clean-architecture/src/app/(protected)/details.tsx +124 -0
- package/templates/react-native/expo-clean-architecture/src/app/(public)/_layout.tsx +18 -0
- package/templates/react-native/expo-clean-architecture/src/app/(public)/login.tsx +31 -0
- package/templates/react-native/expo-clean-architecture/src/app/_layout.tsx +33 -0
- package/templates/react-native/expo-clean-architecture/src/app/index.tsx +8 -0
- package/templates/react-native/expo-clean-architecture/src/core/constants/api-constants.ts +10 -0
- package/templates/react-native/expo-clean-architecture/src/core/constants/image-constants.ts +3 -0
- package/templates/react-native/expo-clean-architecture/src/core/constants/query-keys.ts +6 -0
- package/templates/react-native/expo-clean-architecture/src/core/constants/storage-keys.ts +3 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/@types/color-scheme-state.ts +35 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/@types/color-scheme.ts +12 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/components/app-icon.tsx +16 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/components/app-separator.tsx +26 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/hooks/use-app-color-scheme.ts +52 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/hooks/use-app-fonts.ts +12 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/hooks/use-app-styles.ts +28 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/theme/app-colors.ts +21 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/theme/app-fonts.ts +16 -0
- package/templates/react-native/expo-clean-architecture/src/core/design/theme/app-sizes.ts +14 -0
- package/templates/react-native/expo-clean-architecture/src/core/di/injection-container.ts +53 -0
- package/templates/react-native/expo-clean-architecture/src/core/errors/index.ts +1 -0
- package/templates/react-native/expo-clean-architecture/src/core/helpers/@types.ts +23 -0
- package/templates/react-native/expo-clean-architecture/src/core/helpers/rest-client.ts +144 -0
- package/templates/react-native/expo-clean-architecture/src/core/helpers/result.ts +37 -0
- package/templates/react-native/expo-clean-architecture/src/core/helpers/usecase.ts +5 -0
- package/templates/react-native/expo-clean-architecture/src/core/hooks/use-network.ts +18 -0
- package/templates/react-native/expo-clean-architecture/src/core/i18n/@types/i18next.d.ts +11 -0
- package/templates/react-native/expo-clean-architecture/src/core/i18n/index.ts +19 -0
- package/templates/react-native/expo-clean-architecture/src/core/i18n/translations/fr.json +12 -0
- package/templates/react-native/expo-clean-architecture/src/core/services/local-storage-service-impl.ts +29 -0
- package/templates/react-native/expo-clean-architecture/src/core/services/local-storage-service.ts +26 -0
- package/templates/react-native/expo-clean-architecture/src/core/services/monitoring-service-impl.ts +15 -0
- package/templates/react-native/expo-clean-architecture/src/core/services/monitoring-service.ts +13 -0
- package/templates/react-native/expo-clean-architecture/src/core/services/network-service-impl.ts +40 -0
- package/templates/react-native/expo-clean-architecture/src/core/services/network-service.ts +16 -0
- package/templates/react-native/expo-clean-architecture/src/features/auth/@types/session-state.ts +38 -0
- package/templates/react-native/expo-clean-architecture/src/features/auth/@types/session-status-enum.ts +16 -0
- package/templates/react-native/expo-clean-architecture/src/features/auth/presentation/hooks/use-session.ts +18 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/data/datasources/favorites-datasource-impl.ts +25 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/data/datasources/favorites-datasource.ts +5 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/data/repositories/favorites-repository-impl.ts +46 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/repositories/favorites-repository.ts +8 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/usecases/add-to-favorites-usecase.ts +23 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/usecases/clear-favorites-usecase.ts +23 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/usecases/get-favorites-usecase.ts +24 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/domain/usecases/remove-from-favorites-usecase.ts +23 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/components/favorites-card.tsx +77 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/hooks/use-add-favorite.ts +24 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/hooks/use-clear-favorites.ts +22 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/hooks/use-favorites.ts +22 -0
- package/templates/react-native/expo-clean-architecture/src/features/favorites/presentation/hooks/use-remove-favorite.ts +24 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/data/datasources/movies-datasource-impl.ts +50 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/data/datasources/movies-datasource.ts +8 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/data/models/movie-details-model.ts +67 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/data/models/movie-model.ts +30 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/data/models/tmdb-response-model.ts +6 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/data/repositories/movies-repository-impl.ts +34 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/domain/entities/movie-details-entity.ts +28 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/domain/entities/movie-entity.ts +6 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/domain/repositories/movies-repository.ts +25 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/domain/usecases/get-movie-details-usecase.ts +26 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/domain/usecases/get-random-movies-usecase.ts +24 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/domain/usecases/search-movies-usecase.ts +23 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/presentation/components/movie-tile.tsx +69 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/presentation/hooks/use-movie-details.ts +22 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/presentation/hooks/use-random-movies.ts +22 -0
- package/templates/react-native/expo-clean-architecture/src/features/movies/presentation/hooks/use-search-movies.ts +22 -0
- package/templates/react-native/expo-clean-architecture/tests/core/services/local-storage-service-impl.test.ts +108 -0
- package/templates/react-native/expo-clean-architecture/tests/core/services/monitoring-service.test.ts +74 -0
- package/templates/react-native/expo-clean-architecture/tests/core/services/network-service.test.ts +117 -0
- package/templates/react-native/expo-clean-architecture/tests/features/auth/presentation/hooks/use-session.test.ts +69 -0
- package/templates/react-native/expo-clean-architecture/tests/features/favorites/data/datasources/favorites-datasource.test.ts +69 -0
- package/templates/react-native/expo-clean-architecture/tests/features/favorites/data/repositories/favorites-repository-impl.test.ts +124 -0
- package/templates/react-native/expo-clean-architecture/tests/features/favorites/domain/usecases/add-to-favorites-usecase.test.ts +54 -0
- package/templates/react-native/expo-clean-architecture/tests/features/favorites/domain/usecases/clear-favorites-usecase.test.ts +44 -0
- package/templates/react-native/expo-clean-architecture/tests/features/favorites/domain/usecases/get-favorites-usecase.test.ts +74 -0
- package/templates/react-native/expo-clean-architecture/tests/features/favorites/domain/usecases/remove-from-favorites-usecase.test.ts +52 -0
- package/templates/react-native/expo-clean-architecture/tests/setup.ts +9 -0
- 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
|
+
};
|
package/templates/react-native/expo-clean-architecture/src/core/design/@types/color-scheme-state.ts
ADDED
|
@@ -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
|
+
}
|
package/templates/react-native/expo-clean-architecture/src/core/design/components/app-icon.tsx
ADDED
|
@@ -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
|
+
};
|
package/templates/react-native/expo-clean-architecture/src/core/design/components/app-separator.tsx
ADDED
|
@@ -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
|
+
}
|
package/templates/react-native/expo-clean-architecture/src/core/design/hooks/use-app-color-scheme.ts
ADDED
|
@@ -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
|
+
};
|
package/templates/react-native/expo-clean-architecture/src/core/design/hooks/use-app-styles.ts
ADDED
|
@@ -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,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;
|