@highbeek/create-rnstarterkit 1.0.0 → 1.0.1-beta.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/dist/bin/create-rnstarterkit.js +21 -7
- package/dist/src/generators/appGenerator.js +976 -60
- package/dist/templates/cli-base/.bundle/config +2 -0
- package/dist/templates/cli-base/.eslintrc.js +4 -0
- package/dist/templates/cli-base/.prettierrc.js +5 -0
- package/dist/templates/cli-base/.watchmanconfig +1 -0
- package/dist/templates/cli-base/App.tsx +45 -0
- package/dist/templates/cli-base/Gemfile +16 -0
- package/dist/templates/cli-base/Gemfile.lock +169 -0
- package/dist/templates/cli-base/README.md +97 -0
- package/dist/templates/cli-base/__tests__/App.test.tsx +13 -0
- package/dist/templates/cli-base/android/app/build.gradle +119 -0
- package/dist/templates/cli-base/android/app/debug.keystore +0 -0
- package/dist/templates/cli-base/android/app/proguard-rules.pro +10 -0
- package/dist/templates/cli-base/android/app/src/main/AndroidManifest.xml +27 -0
- package/dist/templates/cli-base/android/app/src/main/java/com/baseapp/MainActivity.kt +22 -0
- package/dist/templates/cli-base/android/app/src/main/java/com/baseapp/MainApplication.kt +27 -0
- package/dist/templates/cli-base/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/dist/templates/cli-base/android/app/src/main/res/values/strings.xml +3 -0
- package/dist/templates/cli-base/android/app/src/main/res/values/styles.xml +9 -0
- package/dist/templates/cli-base/android/build.gradle +21 -0
- package/dist/templates/cli-base/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/dist/templates/cli-base/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/dist/templates/cli-base/android/gradle.properties +44 -0
- package/dist/templates/cli-base/android/gradlew +251 -0
- package/dist/templates/cli-base/android/gradlew.bat +99 -0
- package/dist/templates/cli-base/android/settings.gradle +6 -0
- package/dist/templates/cli-base/app.json +4 -0
- package/dist/templates/cli-base/babel.config.js +3 -0
- package/dist/templates/cli-base/index.js +9 -0
- package/dist/templates/cli-base/ios/.xcode.env +11 -0
- package/dist/templates/cli-base/ios/BaseApp/AppDelegate.swift +48 -0
- package/dist/templates/cli-base/ios/BaseApp/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/dist/templates/cli-base/ios/BaseApp/Images.xcassets/Contents.json +6 -0
- package/dist/templates/cli-base/ios/BaseApp/Info.plist +60 -0
- package/dist/templates/cli-base/ios/BaseApp/LaunchScreen.storyboard +47 -0
- package/dist/templates/cli-base/ios/BaseApp/PrivacyInfo.xcprivacy +37 -0
- package/dist/templates/cli-base/ios/BaseApp.xcodeproj/project.pbxproj +494 -0
- package/dist/templates/cli-base/ios/BaseApp.xcodeproj/xcshareddata/xcschemes/BaseApp.xcscheme +88 -0
- package/dist/templates/cli-base/ios/BaseApp.xcworkspace/contents.xcworkspacedata +10 -0
- package/dist/templates/cli-base/ios/Podfile +34 -0
- package/dist/templates/cli-base/ios/Podfile.lock +2165 -0
- package/dist/templates/cli-base/jest.config.js +3 -0
- package/dist/templates/cli-base/metro.config.js +11 -0
- package/dist/templates/cli-base/package-lock.json +11859 -0
- package/dist/templates/cli-base/package.json +41 -0
- package/dist/templates/cli-base/tsconfig.json +8 -0
- package/dist/templates/expo-base/.vscode/extensions.json +1 -0
- package/dist/templates/expo-base/.vscode/settings.json +7 -0
- package/dist/templates/expo-base/README.md +50 -0
- package/dist/templates/expo-base/app/_layout.tsx +12 -0
- package/dist/templates/expo-base/app/index.tsx +9 -0
- package/dist/templates/expo-base/app.json +48 -0
- package/dist/templates/expo-base/assets/images/android-icon-background.png +0 -0
- package/dist/templates/expo-base/assets/images/android-icon-foreground.png +0 -0
- package/dist/templates/expo-base/assets/images/android-icon-monochrome.png +0 -0
- package/dist/templates/expo-base/assets/images/favicon.png +0 -0
- package/dist/templates/expo-base/assets/images/icon.png +0 -0
- package/dist/templates/expo-base/assets/images/partial-react-logo.png +0 -0
- package/dist/templates/expo-base/assets/images/react-logo.png +0 -0
- package/dist/templates/expo-base/assets/images/react-logo@2x.png +0 -0
- package/dist/templates/expo-base/assets/images/react-logo@3x.png +0 -0
- package/dist/templates/expo-base/assets/images/splash-icon.png +0 -0
- package/dist/templates/expo-base/components/ui/collapsible.tsx +45 -0
- package/dist/templates/expo-base/components/ui/icon-symbol.ios.tsx +32 -0
- package/dist/templates/expo-base/components/ui/icon-symbol.tsx +41 -0
- package/dist/templates/expo-base/eslint.config.js +10 -0
- package/dist/templates/expo-base/package-lock.json +12916 -0
- package/dist/templates/expo-base/package.json +46 -0
- package/dist/templates/expo-base/tsconfig.json +17 -0
- package/dist/templates/optional/apiClient/api/client.ts +142 -0
- package/dist/templates/optional/apiClient/api/index.ts +1 -0
- package/dist/templates/optional/apiClient/config/env.cli.ts +5 -0
- package/dist/templates/optional/apiClient/config/env.expo.ts +4 -0
- package/dist/templates/optional/apiClient/config/env.ts +1 -0
- package/dist/templates/optional/apiClient/env.d.ts +3 -0
- package/dist/templates/optional/auth-context/api/endpoints/auth.ts +14 -0
- package/dist/templates/optional/auth-context/context/AuthContext.tsx +47 -0
- package/dist/templates/optional/auth-context/navigation/ProtectedStack.tsx +38 -0
- package/dist/templates/optional/auth-context/navigation/cli/BottomTabs.tsx +17 -0
- package/dist/templates/optional/auth-context/navigation/expo/BottomTabs.tsx +29 -0
- package/dist/templates/optional/auth-context/screens/HomeScreen.tsx +15 -0
- package/dist/templates/optional/auth-context/screens/LoginScreen.tsx +63 -0
- package/dist/templates/optional/auth-context/screens/ProfileScreen.tsx +11 -0
- package/dist/templates/optional/auth-context/screens/RegisterScreen.tsx +63 -0
- package/dist/templates/optional/auth-context/screens/SettingsScreen.tsx +11 -0
- package/dist/templates/optional/auth-context/utils/storage.ts +13 -0
- package/dist/templates/optional/auth-redux/api/endpoints/auth.ts +14 -0
- package/dist/templates/optional/auth-redux/navigation/ProtectedStack.tsx +30 -0
- package/dist/templates/optional/auth-redux/navigation/cli/BottomTabs.tsx +17 -0
- package/dist/templates/optional/auth-redux/navigation/expo/BottomTabs.tsx +31 -0
- package/dist/templates/optional/auth-redux/screens/HomeScreen.tsx +16 -0
- package/dist/templates/optional/auth-redux/screens/LoginScreen.tsx +64 -0
- package/dist/templates/optional/auth-redux/screens/ProfileScreen.tsx +15 -0
- package/dist/templates/optional/auth-redux/screens/RegisterScreen.tsx +64 -0
- package/dist/templates/optional/auth-redux/screens/SettingsScreen.tsx +15 -0
- package/dist/templates/optional/auth-redux/store/authSlice.ts +25 -0
- package/dist/templates/optional/auth-redux/store/store.ts +11 -0
- package/dist/templates/optional/auth-zustand/api/endpoints/auth.ts +14 -0
- package/dist/templates/optional/auth-zustand/navigation/BottomTabs.tsx +1 -0
- package/dist/templates/optional/auth-zustand/navigation/ProtectedStack.tsx +44 -0
- package/dist/templates/optional/auth-zustand/navigation/cli/BottomTabs.tsx +17 -0
- package/dist/templates/optional/auth-zustand/navigation/expo/BottomTabs.tsx +31 -0
- package/dist/templates/optional/auth-zustand/screens/HomeScreen.tsx +15 -0
- package/dist/templates/optional/auth-zustand/screens/LoginScreen.tsx +63 -0
- package/dist/templates/optional/auth-zustand/screens/ProfileScreen.tsx +11 -0
- package/dist/templates/optional/auth-zustand/screens/RegisterScreen.tsx +63 -0
- package/dist/templates/optional/auth-zustand/screens/SettingsScreen.tsx +11 -0
- package/dist/templates/optional/auth-zustand/store/authStore.ts +30 -0
- package/dist/templates/optional/auth-zustand/utils/storage.ts +13 -0
- package/package.json +2 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text, Button } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
import { useDispatch } from "react-redux";
|
|
5
|
+
import { logout } from "../store/authSlice";
|
|
6
|
+
|
|
7
|
+
export default function HomeScreen() {
|
|
8
|
+
const dispatch = useDispatch();
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
12
|
+
<Text>Welcome Home!</Text>
|
|
13
|
+
<Button title="Logout" onPress={() => dispatch(logout())} />
|
|
14
|
+
</SafeAreaView>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { TextInput, Button, Text } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
import { useDispatch } from "react-redux";
|
|
5
|
+
import { login } from "../store/authSlice";
|
|
6
|
+
import { loginApi } from "../api";
|
|
7
|
+
|
|
8
|
+
export default function LoginScreen() {
|
|
9
|
+
const dispatch = useDispatch();
|
|
10
|
+
const [email, setEmail] = useState("");
|
|
11
|
+
const [password, setPassword] = useState("");
|
|
12
|
+
const [error, setError] = useState("");
|
|
13
|
+
|
|
14
|
+
const handleLogin = async () => {
|
|
15
|
+
try {
|
|
16
|
+
setError("");
|
|
17
|
+
const token = await loginApi(email, password);
|
|
18
|
+
dispatch(login(token));
|
|
19
|
+
} catch (err) {
|
|
20
|
+
setError(err instanceof Error ? err.message : "Login failed");
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
26
|
+
<TextInput
|
|
27
|
+
placeholder="Email"
|
|
28
|
+
value={email}
|
|
29
|
+
onChangeText={setEmail}
|
|
30
|
+
autoCapitalize="none"
|
|
31
|
+
autoCorrect={false}
|
|
32
|
+
keyboardType="email-address"
|
|
33
|
+
placeholderTextColor="#888"
|
|
34
|
+
style={{
|
|
35
|
+
borderWidth: 1,
|
|
36
|
+
borderColor: "#ccc",
|
|
37
|
+
borderRadius: 8,
|
|
38
|
+
paddingHorizontal: 12,
|
|
39
|
+
paddingVertical: 10,
|
|
40
|
+
color: "#111",
|
|
41
|
+
marginBottom: 12,
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<TextInput
|
|
45
|
+
placeholder="Password"
|
|
46
|
+
value={password}
|
|
47
|
+
onChangeText={setPassword}
|
|
48
|
+
secureTextEntry
|
|
49
|
+
placeholderTextColor="#888"
|
|
50
|
+
style={{
|
|
51
|
+
borderWidth: 1,
|
|
52
|
+
borderColor: "#ccc",
|
|
53
|
+
borderRadius: 8,
|
|
54
|
+
paddingHorizontal: 12,
|
|
55
|
+
paddingVertical: 10,
|
|
56
|
+
color: "#111",
|
|
57
|
+
marginBottom: 12,
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
{!!error && <Text style={{ color: "red", marginBottom: 8 }}>{error}</Text>}
|
|
61
|
+
<Button title="Login" onPress={() => void handleLogin()} />
|
|
62
|
+
</SafeAreaView>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StyleSheet, Text } from 'react-native';
|
|
2
|
+
import { SafeAreaView } from 'react-native-safe-area-context'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
const ProfileScreen = () => {
|
|
6
|
+
return (
|
|
7
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
8
|
+
<Text>ProfileScreen</Text>
|
|
9
|
+
</SafeAreaView>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default ProfileScreen
|
|
14
|
+
|
|
15
|
+
const styles = StyleSheet.create({})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { TextInput, Button, Text } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
import { useDispatch } from "react-redux";
|
|
5
|
+
import { login } from "../store/authSlice";
|
|
6
|
+
import { registerApi } from "../api";
|
|
7
|
+
|
|
8
|
+
export default function RegisterScreen() {
|
|
9
|
+
const dispatch = useDispatch();
|
|
10
|
+
const [email, setEmail] = useState("");
|
|
11
|
+
const [password, setPassword] = useState("");
|
|
12
|
+
const [error, setError] = useState("");
|
|
13
|
+
|
|
14
|
+
const handleRegister = async () => {
|
|
15
|
+
try {
|
|
16
|
+
setError("");
|
|
17
|
+
const token = await registerApi(email, password);
|
|
18
|
+
dispatch(login(token));
|
|
19
|
+
} catch (err) {
|
|
20
|
+
setError(err instanceof Error ? err.message : "Registration failed");
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
26
|
+
<TextInput
|
|
27
|
+
placeholder="Email"
|
|
28
|
+
value={email}
|
|
29
|
+
onChangeText={setEmail}
|
|
30
|
+
autoCapitalize="none"
|
|
31
|
+
autoCorrect={false}
|
|
32
|
+
keyboardType="email-address"
|
|
33
|
+
placeholderTextColor="#888"
|
|
34
|
+
style={{
|
|
35
|
+
borderWidth: 1,
|
|
36
|
+
borderColor: "#ccc",
|
|
37
|
+
borderRadius: 8,
|
|
38
|
+
paddingHorizontal: 12,
|
|
39
|
+
paddingVertical: 10,
|
|
40
|
+
color: "#111",
|
|
41
|
+
marginBottom: 12,
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<TextInput
|
|
45
|
+
placeholder="Password"
|
|
46
|
+
value={password}
|
|
47
|
+
onChangeText={setPassword}
|
|
48
|
+
secureTextEntry
|
|
49
|
+
placeholderTextColor="#888"
|
|
50
|
+
style={{
|
|
51
|
+
borderWidth: 1,
|
|
52
|
+
borderColor: "#ccc",
|
|
53
|
+
borderRadius: 8,
|
|
54
|
+
paddingHorizontal: 12,
|
|
55
|
+
paddingVertical: 10,
|
|
56
|
+
color: "#111",
|
|
57
|
+
marginBottom: 12,
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
{!!error && <Text style={{ color: "red", marginBottom: 8 }}>{error}</Text>}
|
|
61
|
+
<Button title="Register" onPress={() => void handleRegister()} />
|
|
62
|
+
</SafeAreaView>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StyleSheet, Text } from "react-native";
|
|
2
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
3
|
+
import React from "react";
|
|
4
|
+
|
|
5
|
+
const SettingsScreen = () => {
|
|
6
|
+
return (
|
|
7
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
8
|
+
<Text>SettingsScreen</Text>
|
|
9
|
+
</SafeAreaView>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default SettingsScreen;
|
|
14
|
+
|
|
15
|
+
const styles = StyleSheet.create({});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|
2
|
+
|
|
3
|
+
interface AuthState {
|
|
4
|
+
token: string | null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const initialState: AuthState = {
|
|
8
|
+
token: null,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const authSlice = createSlice({
|
|
12
|
+
name: "auth",
|
|
13
|
+
initialState,
|
|
14
|
+
reducers: {
|
|
15
|
+
login(state, action: PayloadAction<string>) {
|
|
16
|
+
state.token = action.payload;
|
|
17
|
+
},
|
|
18
|
+
logout(state) {
|
|
19
|
+
state.token = null;
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const { login, logout } = authSlice.actions;
|
|
25
|
+
export default authSlice.reducer;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { configureStore } from "@reduxjs/toolkit";
|
|
2
|
+
import authReducer from "./authSlice";
|
|
3
|
+
|
|
4
|
+
export const store = configureStore({
|
|
5
|
+
reducer: {
|
|
6
|
+
auth: authReducer,
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type RootState = ReturnType<typeof store.getState>;
|
|
11
|
+
export type AppDispatch = typeof store.dispatch;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function assertCredentials(email: string, password: string) {
|
|
2
|
+
if (!email.trim()) throw new Error("Email is required");
|
|
3
|
+
if (!password.trim()) throw new Error("Password is required");
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export const loginApi = async (email: string, password: string) => {
|
|
7
|
+
assertCredentials(email, password);
|
|
8
|
+
return `demo-token-${email.trim().toLowerCase()}`;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const registerApi = async (email: string, password: string) => {
|
|
12
|
+
assertCredentials(email, password);
|
|
13
|
+
return `demo-token-${email.trim().toLowerCase()}`;
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./cli/BottomTabs";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import { ActivityIndicator, View } from "react-native";
|
|
3
|
+
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
|
4
|
+
import LoginScreen from "../screens/LoginScreen";
|
|
5
|
+
import RegisterScreen from "../screens/RegisterScreen";
|
|
6
|
+
import BottomTabs from "./BottomTabs";
|
|
7
|
+
import { useAuthStore } from "../store/authStore";
|
|
8
|
+
|
|
9
|
+
const Stack = createNativeStackNavigator();
|
|
10
|
+
|
|
11
|
+
export default function ProtectedStack() {
|
|
12
|
+
const token = useAuthStore((state) => state.token);
|
|
13
|
+
const isHydrated = useAuthStore((state) => state.isHydrated);
|
|
14
|
+
const hydrate = useAuthStore((state) => state.hydrate);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
void hydrate();
|
|
18
|
+
}, [hydrate]);
|
|
19
|
+
|
|
20
|
+
if (!isHydrated) {
|
|
21
|
+
return (
|
|
22
|
+
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
|
|
23
|
+
<ActivityIndicator />
|
|
24
|
+
</View>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Stack.Navigator>
|
|
30
|
+
{token ? (
|
|
31
|
+
<Stack.Screen
|
|
32
|
+
name="Main"
|
|
33
|
+
component={BottomTabs}
|
|
34
|
+
options={{ headerShown: false }}
|
|
35
|
+
/>
|
|
36
|
+
) : (
|
|
37
|
+
<>
|
|
38
|
+
<Stack.Screen name="Login" component={LoginScreen} />
|
|
39
|
+
<Stack.Screen name="Register" component={RegisterScreen} />
|
|
40
|
+
</>
|
|
41
|
+
)}
|
|
42
|
+
</Stack.Navigator>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
|
3
|
+
import HomeScreen from "../../screens/HomeScreen";
|
|
4
|
+
import ProfileScreen from "../../screens/ProfileScreen";
|
|
5
|
+
import SettingsScreen from "../../screens/SettingsScreen";
|
|
6
|
+
|
|
7
|
+
const Tab = createBottomTabNavigator();
|
|
8
|
+
|
|
9
|
+
export default function BottomTabs() {
|
|
10
|
+
return (
|
|
11
|
+
<Tab.Navigator screenOptions={{ headerShown: false }}>
|
|
12
|
+
<Tab.Screen name="Home" component={HomeScreen} />
|
|
13
|
+
<Tab.Screen name="Profile" component={ProfileScreen} />
|
|
14
|
+
<Tab.Screen name="Settings" component={SettingsScreen} />
|
|
15
|
+
</Tab.Navigator>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
|
3
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
import HomeScreen from "../../screens/HomeScreen";
|
|
5
|
+
import ProfileScreen from "../../screens/ProfileScreen";
|
|
6
|
+
import SettingsScreen from "../../screens/SettingsScreen";
|
|
7
|
+
|
|
8
|
+
const Tab = createBottomTabNavigator();
|
|
9
|
+
|
|
10
|
+
export default function BottomTabs() {
|
|
11
|
+
return (
|
|
12
|
+
<Tab.Navigator
|
|
13
|
+
screenOptions={({ route }) => ({
|
|
14
|
+
headerShown: false,
|
|
15
|
+
tabBarIcon: ({ color, size }) => {
|
|
16
|
+
let iconName: string = "home";
|
|
17
|
+
|
|
18
|
+
if (route.name === "Home") iconName = "home";
|
|
19
|
+
else if (route.name === "Profile") iconName = "person";
|
|
20
|
+
else if (route.name === "Settings") iconName = "settings";
|
|
21
|
+
|
|
22
|
+
return <Ionicons name={iconName as any} size={size} color={color} />;
|
|
23
|
+
},
|
|
24
|
+
})}
|
|
25
|
+
>
|
|
26
|
+
<Tab.Screen name="Home" component={HomeScreen} />
|
|
27
|
+
<Tab.Screen name="Profile" component={ProfileScreen} />
|
|
28
|
+
<Tab.Screen name="Settings" component={SettingsScreen} />
|
|
29
|
+
</Tab.Navigator>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text, Button } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
import { useAuthStore } from "../store/authStore";
|
|
5
|
+
|
|
6
|
+
export default function HomeScreen() {
|
|
7
|
+
const logout = useAuthStore((state) => state.logout);
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
11
|
+
<Text>Welcome Home!</Text>
|
|
12
|
+
<Button title="Logout" onPress={() => void logout()} />
|
|
13
|
+
</SafeAreaView>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { TextInput, Button, Text } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
import { loginApi } from "../api";
|
|
5
|
+
import { useAuthStore } from "../store/authStore";
|
|
6
|
+
|
|
7
|
+
export default function LoginScreen() {
|
|
8
|
+
const login = useAuthStore((state) => state.login);
|
|
9
|
+
const [email, setEmail] = useState("");
|
|
10
|
+
const [password, setPassword] = useState("");
|
|
11
|
+
const [error, setError] = useState("");
|
|
12
|
+
|
|
13
|
+
const handleLogin = async () => {
|
|
14
|
+
try {
|
|
15
|
+
setError("");
|
|
16
|
+
const token = await loginApi(email, password);
|
|
17
|
+
await login(token);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
setError(err instanceof Error ? err.message : "Login failed");
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
25
|
+
<TextInput
|
|
26
|
+
placeholder="Email"
|
|
27
|
+
value={email}
|
|
28
|
+
onChangeText={setEmail}
|
|
29
|
+
autoCapitalize="none"
|
|
30
|
+
autoCorrect={false}
|
|
31
|
+
keyboardType="email-address"
|
|
32
|
+
placeholderTextColor="#888"
|
|
33
|
+
style={{
|
|
34
|
+
borderWidth: 1,
|
|
35
|
+
borderColor: "#ccc",
|
|
36
|
+
borderRadius: 8,
|
|
37
|
+
paddingHorizontal: 12,
|
|
38
|
+
paddingVertical: 10,
|
|
39
|
+
color: "#111",
|
|
40
|
+
marginBottom: 12,
|
|
41
|
+
}}
|
|
42
|
+
/>
|
|
43
|
+
<TextInput
|
|
44
|
+
placeholder="Password"
|
|
45
|
+
value={password}
|
|
46
|
+
onChangeText={setPassword}
|
|
47
|
+
secureTextEntry
|
|
48
|
+
placeholderTextColor="#888"
|
|
49
|
+
style={{
|
|
50
|
+
borderWidth: 1,
|
|
51
|
+
borderColor: "#ccc",
|
|
52
|
+
borderRadius: 8,
|
|
53
|
+
paddingHorizontal: 12,
|
|
54
|
+
paddingVertical: 10,
|
|
55
|
+
color: "#111",
|
|
56
|
+
marginBottom: 12,
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
{!!error && <Text style={{ color: "red", marginBottom: 8 }}>{error}</Text>}
|
|
60
|
+
<Button title="Login" onPress={() => void handleLogin()} />
|
|
61
|
+
</SafeAreaView>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
|
|
5
|
+
export default function ProfileScreen() {
|
|
6
|
+
return (
|
|
7
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
8
|
+
<Text>This is the Profile Screen</Text>
|
|
9
|
+
</SafeAreaView>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { TextInput, Button, Text } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
import { registerApi } from "../api";
|
|
5
|
+
import { useAuthStore } from "../store/authStore";
|
|
6
|
+
|
|
7
|
+
export default function RegisterScreen() {
|
|
8
|
+
const login = useAuthStore((state) => state.login);
|
|
9
|
+
const [email, setEmail] = useState("");
|
|
10
|
+
const [password, setPassword] = useState("");
|
|
11
|
+
const [error, setError] = useState("");
|
|
12
|
+
|
|
13
|
+
const handleRegister = async () => {
|
|
14
|
+
try {
|
|
15
|
+
setError("");
|
|
16
|
+
const token = await registerApi(email, password);
|
|
17
|
+
await login(token);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
setError(err instanceof Error ? err.message : "Registration failed");
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
25
|
+
<TextInput
|
|
26
|
+
placeholder="Email"
|
|
27
|
+
value={email}
|
|
28
|
+
onChangeText={setEmail}
|
|
29
|
+
autoCapitalize="none"
|
|
30
|
+
autoCorrect={false}
|
|
31
|
+
keyboardType="email-address"
|
|
32
|
+
placeholderTextColor="#888"
|
|
33
|
+
style={{
|
|
34
|
+
borderWidth: 1,
|
|
35
|
+
borderColor: "#ccc",
|
|
36
|
+
borderRadius: 8,
|
|
37
|
+
paddingHorizontal: 12,
|
|
38
|
+
paddingVertical: 10,
|
|
39
|
+
color: "#111",
|
|
40
|
+
marginBottom: 12,
|
|
41
|
+
}}
|
|
42
|
+
/>
|
|
43
|
+
<TextInput
|
|
44
|
+
placeholder="Password"
|
|
45
|
+
value={password}
|
|
46
|
+
onChangeText={setPassword}
|
|
47
|
+
secureTextEntry
|
|
48
|
+
placeholderTextColor="#888"
|
|
49
|
+
style={{
|
|
50
|
+
borderWidth: 1,
|
|
51
|
+
borderColor: "#ccc",
|
|
52
|
+
borderRadius: 8,
|
|
53
|
+
paddingHorizontal: 12,
|
|
54
|
+
paddingVertical: 10,
|
|
55
|
+
color: "#111",
|
|
56
|
+
marginBottom: 12,
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
{!!error && <Text style={{ color: "red", marginBottom: 8 }}>{error}</Text>}
|
|
60
|
+
<Button title="Register" onPress={() => void handleRegister()} />
|
|
61
|
+
</SafeAreaView>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
|
+
|
|
5
|
+
export default function SettingsScreen() {
|
|
6
|
+
return (
|
|
7
|
+
<SafeAreaView style={{ flex: 1, padding: 20 }}>
|
|
8
|
+
<Text>This is the Settings Screen</Text>
|
|
9
|
+
</SafeAreaView>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { create } from "zustand";
|
|
2
|
+
import { getToken, removeToken, storeToken } from "../utils/storage";
|
|
3
|
+
|
|
4
|
+
type AuthState = {
|
|
5
|
+
token: string | null;
|
|
6
|
+
isHydrated: boolean;
|
|
7
|
+
hydrate: () => Promise<void>;
|
|
8
|
+
login: (token: string) => Promise<void>;
|
|
9
|
+
logout: () => Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const useAuthStore = create<AuthState>((set) => ({
|
|
13
|
+
token: null,
|
|
14
|
+
isHydrated: false,
|
|
15
|
+
|
|
16
|
+
hydrate: async () => {
|
|
17
|
+
const token = await getToken();
|
|
18
|
+
set({ token, isHydrated: true });
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
login: async (token: string) => {
|
|
22
|
+
await storeToken(token);
|
|
23
|
+
set({ token });
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
logout: async () => {
|
|
27
|
+
await removeToken();
|
|
28
|
+
set({ token: null });
|
|
29
|
+
},
|
|
30
|
+
}));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
2
|
+
|
|
3
|
+
export const storeToken = async (token: string) => {
|
|
4
|
+
await AsyncStorage.setItem("@token", token);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const getToken = async () => {
|
|
8
|
+
return await AsyncStorage.getItem("@token");
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const removeToken = async () => {
|
|
12
|
+
await AsyncStorage.removeItem("@token");
|
|
13
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@highbeek/create-rnstarterkit",
|
|
3
|
-
"version": "1.0.0",
|
|
3
|
+
"version": "1.0.1-beta.0",
|
|
4
4
|
"description": "CLI to scaffold production-ready React Native app structures.",
|
|
5
5
|
"main": "dist/src/generators/appGenerator.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"README.md"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "tsc -p tsconfig.build.json",
|
|
14
|
+
"build": "tsc -p tsconfig.build.json && node scripts/copy-templates.js",
|
|
15
15
|
"prepublishOnly": "npm run build",
|
|
16
16
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
17
|
},
|