added 260907 version of worker toolkit
This commit is contained in:
8
worker-toolkit-flaredown/repo/native/.dockerignore
Normal file
8
worker-toolkit-flaredown/repo/native/.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
||||
bower_components
|
||||
node_modules
|
||||
dist
|
||||
tmp
|
||||
.git
|
||||
README.md
|
||||
LICENSE
|
||||
.gitignore
|
||||
4
worker-toolkit-flaredown/repo/native/.eslintrc.js
Normal file
4
worker-toolkit-flaredown/repo/native/.eslintrc.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["universe/native"],
|
||||
};
|
||||
35
worker-toolkit-flaredown/repo/native/.gitignore
vendored
Normal file
35
worker-toolkit-flaredown/repo/native/.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# Expo
|
||||
.expo/
|
||||
dist/
|
||||
web-build/
|
||||
|
||||
# Native
|
||||
*.orig.*
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
|
||||
# Metro
|
||||
.metro-health-check*
|
||||
|
||||
# debug
|
||||
npm-debug.*
|
||||
yarn-debug.*
|
||||
yarn-error.*
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
1
worker-toolkit-flaredown/repo/native/.nvmrc
Normal file
1
worker-toolkit-flaredown/repo/native/.nvmrc
Normal file
@@ -0,0 +1 @@
|
||||
v18.18.0
|
||||
5
worker-toolkit-flaredown/repo/native/AppEntry.js
Normal file
5
worker-toolkit-flaredown/repo/native/AppEntry.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import registerRootComponent from "expo/build/launch/registerRootComponent";
|
||||
|
||||
import App from "./src/App";
|
||||
|
||||
registerRootComponent(App);
|
||||
33
worker-toolkit-flaredown/repo/native/Dockerfile
Normal file
33
worker-toolkit-flaredown/repo/native/Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
||||
# pull base image
|
||||
FROM node:18.18.0
|
||||
|
||||
# set our node environment, either development or production
|
||||
# defaults to production, compose overrides this to development on build and run
|
||||
ARG NODE_ENV=production
|
||||
ENV NODE_ENV $NODE_ENV
|
||||
|
||||
# default to port 19006 for node, and 19001 and 19002 (tests) for debug
|
||||
ARG PORT=19006
|
||||
ENV PORT $PORT
|
||||
EXPOSE $PORT 19001 19002
|
||||
|
||||
# install global packages
|
||||
ENV NPM_CONFIG_PREFIX=/home/node/.npm-global
|
||||
ENV PATH /home/node/.npm-global/bin:$PATH
|
||||
RUN npm i --unsafe-perm --allow-root -g npm@latest expo-cli@latest
|
||||
|
||||
# install dependencies first, in a different location for easier app bind mounting for local development
|
||||
# due to default /opt permissions we have to create the dir with root and change perms
|
||||
RUN mkdir /opt/native
|
||||
WORKDIR /opt/native
|
||||
ENV PATH /opt/native/.bin:$PATH
|
||||
COPY ./package.json ./package-lock.json ./
|
||||
RUN npm install
|
||||
|
||||
# copy in our source code last, as it changes the most
|
||||
WORKDIR /opt/native/app
|
||||
# for development, we bind mount volumes; comment out for production
|
||||
COPY . .
|
||||
|
||||
ENTRYPOINT ["npm", "run"]
|
||||
CMD ["web"]
|
||||
28
worker-toolkit-flaredown/repo/native/app.json
Normal file
28
worker-toolkit-flaredown/repo/native/app.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "native",
|
||||
"slug": "native",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"userInterfaceStyle": "light",
|
||||
"splash": {
|
||||
"image": "./assets/splash.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"assetBundlePatterns": ["**/*"],
|
||||
"ios": {
|
||||
"supportsTablet": true
|
||||
},
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#ffffff"
|
||||
}
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
worker-toolkit-flaredown/repo/native/assets/adaptive-icon.png
Normal file
BIN
worker-toolkit-flaredown/repo/native/assets/adaptive-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
worker-toolkit-flaredown/repo/native/assets/favicon.png
Normal file
BIN
worker-toolkit-flaredown/repo/native/assets/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
worker-toolkit-flaredown/repo/native/assets/icon.png
Normal file
BIN
worker-toolkit-flaredown/repo/native/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
worker-toolkit-flaredown/repo/native/assets/splash.png
Normal file
BIN
worker-toolkit-flaredown/repo/native/assets/splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
6
worker-toolkit-flaredown/repo/native/babel.config.js
Normal file
6
worker-toolkit-flaredown/repo/native/babel.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ["babel-preset-expo"],
|
||||
};
|
||||
};
|
||||
26321
worker-toolkit-flaredown/repo/native/package-lock.json
generated
Normal file
26321
worker-toolkit-flaredown/repo/native/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
50
worker-toolkit-flaredown/repo/native/package.json
Normal file
50
worker-toolkit-flaredown/repo/native/package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "flaredown",
|
||||
"version": "1.0.0",
|
||||
"main": "node_modules/expo/AppEntry.js",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"web": "expo start --web",
|
||||
"test": "jest",
|
||||
"tsc": "tsc",
|
||||
"lint": "eslint . && prettier -c .",
|
||||
"lint:fix": "eslint . --fix && prettier . --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/webpack-config": "^19.0.0",
|
||||
"@react-navigation/native": "^6.1.9",
|
||||
"@react-navigation/native-stack": "^6.9.17",
|
||||
"@types/react": "~18.2.14",
|
||||
"expo": "~49.0.13",
|
||||
"expo-status-bar": "~1.6.0",
|
||||
"jest": "^29.2.1",
|
||||
"jest-expo": "~49.0.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-native": "0.72.6",
|
||||
"react-native-paper": "^5.11.7",
|
||||
"react-native-status-bar-height": "^2.6.0",
|
||||
"react-native-web": "~0.19.6",
|
||||
"typescript": "^5.1.3",
|
||||
"webpack": "^5.64.4",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@testing-library/react-native": "^12.4.3",
|
||||
"@types/jest": "^29.5.11",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-universe": "^12.0.0",
|
||||
"prettier": "^3.1.1"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-expo",
|
||||
"transformIgnorePatterns": [
|
||||
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)"
|
||||
]
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
14
worker-toolkit-flaredown/repo/native/src/App.test.tsx
Normal file
14
worker-toolkit-flaredown/repo/native/src/App.test.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { render, screen } from "@testing-library/react-native";
|
||||
import React from "react";
|
||||
|
||||
import App from "../src/App";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe("<App />", () => {
|
||||
it("renders title", async () => {
|
||||
render(<App />);
|
||||
|
||||
expect(screen.getByText("Welcome to Flaredown")).toBeDefined();
|
||||
});
|
||||
});
|
||||
38
worker-toolkit-flaredown/repo/native/src/App.tsx
Normal file
38
worker-toolkit-flaredown/repo/native/src/App.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||
import React from "react";
|
||||
import { Provider } from "react-native-paper";
|
||||
|
||||
import { Theme } from "./Theme";
|
||||
import CreateAccountScreen from "./screens/CreateAccountScreen";
|
||||
import Dashboard from "./screens/Dashboard";
|
||||
import LoginScreen from "./screens/LoginScreen";
|
||||
import ResetPasswordScreen from "./screens/ResetPasswordScreen";
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Provider theme={Theme}>
|
||||
<NavigationContainer>
|
||||
<Stack.Navigator
|
||||
initialRouteName="LoginScreen"
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="LoginScreen" component={LoginScreen} />
|
||||
<Stack.Screen
|
||||
name="CreateAccountScreen"
|
||||
component={CreateAccountScreen}
|
||||
/>
|
||||
<Stack.Screen name="Dashboard" component={Dashboard} />
|
||||
<Stack.Screen
|
||||
name="ResetPasswordScreen"
|
||||
component={ResetPasswordScreen}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
8
worker-toolkit-flaredown/repo/native/src/Theme.ts
Normal file
8
worker-toolkit-flaredown/repo/native/src/Theme.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { DefaultTheme } from "react-native-paper";
|
||||
|
||||
export const Theme = {
|
||||
...DefaultTheme,
|
||||
colors: {
|
||||
...DefaultTheme.colors,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import { TouchableOpacity, StyleSheet } from "react-native";
|
||||
import { getStatusBarHeight } from "react-native-status-bar-height";
|
||||
|
||||
export default function BackButton({ goBack }) {
|
||||
return (
|
||||
<TouchableOpacity onPress={goBack} style={styles.container}>
|
||||
{/* <Text>{"< Back"}</Text> */}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
position: "absolute",
|
||||
top: 20 + getStatusBarHeight(),
|
||||
left: 4,
|
||||
},
|
||||
image: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, KeyboardAvoidingView, View } from "react-native";
|
||||
|
||||
import { Theme } from "../Theme";
|
||||
|
||||
export default function Background({ children }) {
|
||||
return (
|
||||
<View style={styles.background}>
|
||||
<KeyboardAvoidingView style={styles.container} behavior="padding">
|
||||
{children}
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
background: {
|
||||
flex: 1,
|
||||
width: "100%",
|
||||
backgroundColor: Theme.colors.background,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 20,
|
||||
width: "100%",
|
||||
maxWidth: 500,
|
||||
alignSelf: "center",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
import { StyleSheet } from "react-native";
|
||||
import { Button as PaperButton } from "react-native-paper";
|
||||
|
||||
import { Theme } from "../Theme";
|
||||
|
||||
export default function Button({ mode, style = {}, children, ...props }) {
|
||||
return (
|
||||
<PaperButton
|
||||
style={[
|
||||
styles.button,
|
||||
mode === "outlined" && { backgroundColor: Theme.colors.surface },
|
||||
style,
|
||||
]}
|
||||
labelStyle={styles.text}
|
||||
mode={mode}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</PaperButton>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
width: "100%",
|
||||
marginVertical: 10,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
text: {
|
||||
fontWeight: "bold",
|
||||
fontSize: 15,
|
||||
lineHeight: 26,
|
||||
},
|
||||
});
|
||||
15
worker-toolkit-flaredown/repo/native/src/components/Card.tsx
Normal file
15
worker-toolkit-flaredown/repo/native/src/components/Card.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import { StyleSheet } from "react-native";
|
||||
import { Card } from "react-native-paper";
|
||||
|
||||
export default function Background({ children }) {
|
||||
return <Card style={styles.card}>{children}</Card>;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
padding: 30,
|
||||
width: "100%",
|
||||
maxWidth: 500,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import React from "react";
|
||||
import { View, Text, StyleSheet, Linking } from "react-native";
|
||||
|
||||
import Button from "./Button";
|
||||
import Link from "./Link";
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
export default function Footer({ navigation }) {
|
||||
const handleHelpPress = () => {
|
||||
Linking.openURL(`mailto:contact@flaredown.com`);
|
||||
};
|
||||
|
||||
const handleTosPress = () => {
|
||||
navigation.navigate("ToS");
|
||||
};
|
||||
|
||||
const handlePrivacyPress = () => {
|
||||
navigation.navigate("PrivacyPolicy");
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.footer}>
|
||||
<Button onPress={handleHelpPress} mode="outlined">
|
||||
Help & Feedback
|
||||
</Button>
|
||||
<View style={styles.links}>
|
||||
<Link onPress={handleTosPress}>Terms of Service</Link>
|
||||
<Link onPress={handlePrivacyPress}>Privacy Policy</Link>
|
||||
</View>
|
||||
<Text>Flaredown © {currentYear}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
footer: {
|
||||
marginTop: 10,
|
||||
gap: 10,
|
||||
alignItems: "center",
|
||||
},
|
||||
links: {
|
||||
flexDirection: "row",
|
||||
gap: 10,
|
||||
display: "flex",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
import { StyleSheet } from "react-native";
|
||||
import { Text } from "react-native-paper";
|
||||
|
||||
import { Theme } from "../Theme";
|
||||
|
||||
export default function Header(props) {
|
||||
return <Text style={styles.header} {...props} />;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
header: {
|
||||
fontSize: 21,
|
||||
color: Theme.colors.primary,
|
||||
fontWeight: "bold",
|
||||
paddingVertical: 12,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, KeyboardAvoidingView, View } from "react-native";
|
||||
|
||||
import Footer from "./Footer";
|
||||
import { Theme } from "../Theme";
|
||||
|
||||
export default function Background({ navigation, children }) {
|
||||
return (
|
||||
<View style={styles.background}>
|
||||
<KeyboardAvoidingView style={styles.container} behavior="padding">
|
||||
{children}
|
||||
<Footer navigation={navigation} />
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
background: {
|
||||
flex: 1,
|
||||
width: "100%",
|
||||
backgroundColor: Theme.colors.background,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 20,
|
||||
width: "100%",
|
||||
maxWidth: 500,
|
||||
alignSelf: "center",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
});
|
||||
24
worker-toolkit-flaredown/repo/native/src/components/Link.tsx
Normal file
24
worker-toolkit-flaredown/repo/native/src/components/Link.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, Text, TouchableOpacity } from "react-native";
|
||||
|
||||
import { Theme } from "../Theme";
|
||||
|
||||
interface LinkProps {
|
||||
onPress: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function Link({ onPress, children }: LinkProps) {
|
||||
return (
|
||||
<TouchableOpacity onPress={onPress}>
|
||||
<Text style={styles.link}>{children}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
link: {
|
||||
fontWeight: "bold",
|
||||
color: Theme.colors.primary,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
import { View, StyleSheet, Text } from "react-native";
|
||||
import { TextInput as Input } from "react-native-paper";
|
||||
|
||||
import { Theme } from "../Theme";
|
||||
|
||||
export default function TextInput({ errorText, description = "", ...props }) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Input
|
||||
style={styles.input}
|
||||
selectionColor={Theme.colors.primary}
|
||||
underlineColor="transparent"
|
||||
mode="outlined"
|
||||
{...props}
|
||||
/>
|
||||
{description && !errorText ? (
|
||||
<Text style={styles.description}>{description}</Text>
|
||||
) : null}
|
||||
{errorText ? <Text style={styles.error}>{errorText}</Text> : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: "100%",
|
||||
marginVertical: 12,
|
||||
},
|
||||
input: {
|
||||
backgroundColor: Theme.colors.surface,
|
||||
},
|
||||
description: {
|
||||
fontSize: 13,
|
||||
color: Theme.colors.secondary,
|
||||
paddingTop: 8,
|
||||
},
|
||||
error: {
|
||||
fontSize: 13,
|
||||
color: Theme.colors.error,
|
||||
paddingTop: 8,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
export function emailValidator(email) {
|
||||
const re = /\S+@\S+\.\S+/;
|
||||
if (!email) return "Email address is required.";
|
||||
if (!re.test(email)) return "Please enter a valid email address.";
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export function passwordValidator(password) {
|
||||
if (!password) return "Password is required.";
|
||||
if (password.length < 8)
|
||||
return "Password should contain at least 8 characters.";
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export function usernameValidator(name) {
|
||||
if (!name) return "Username is required.";
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import React, { useState } from "react";
|
||||
import { View, StyleSheet } from "react-native";
|
||||
import { Text } from "react-native-paper";
|
||||
|
||||
import BackButton from "../components/BackButton";
|
||||
import Button from "../components/Button";
|
||||
import Card from "../components/Card";
|
||||
import Header from "../components/Header";
|
||||
import Layout from "../components/Layout";
|
||||
import Link from "../components/Link";
|
||||
import TextInput from "../components/TextInput";
|
||||
import { emailValidator } from "../helpers/emailValidator";
|
||||
import { passwordValidator } from "../helpers/passwordValidator";
|
||||
import { usernameValidator } from "../helpers/usernameValidator";
|
||||
|
||||
export default function CreateAccountScreen({ navigation }) {
|
||||
const [username, setUsername] = useState({ value: "", error: "" });
|
||||
const [email, setEmail] = useState({ value: "", error: "" });
|
||||
const [password, setPassword] = useState({ value: "", error: "" });
|
||||
|
||||
const onSignUpPressed = () => {
|
||||
const usernameError = usernameValidator(username.value);
|
||||
const emailError = emailValidator(email.value);
|
||||
const passwordError = passwordValidator(password.value);
|
||||
if (emailError || passwordError || usernameError) {
|
||||
setUsername({ ...username, error: usernameError });
|
||||
setEmail({ ...email, error: emailError });
|
||||
setPassword({ ...password, error: passwordError });
|
||||
return;
|
||||
}
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: "Dashboard" }],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout navigation={navigation}>
|
||||
<BackButton goBack={navigation.goBack} />
|
||||
<Header>Create account</Header>
|
||||
<Card>
|
||||
<TextInput
|
||||
label="Username"
|
||||
returnKeyType="next"
|
||||
value={username.value}
|
||||
onChangeText={(text) => setUsername({ value: text, error: "" })}
|
||||
error={!!username.error}
|
||||
errorText={username.error}
|
||||
/>
|
||||
<TextInput
|
||||
label="Email"
|
||||
returnKeyType="next"
|
||||
value={email.value}
|
||||
onChangeText={(text) => setEmail({ value: text, error: "" })}
|
||||
error={!!email.error}
|
||||
errorText={email.error}
|
||||
autoCapitalize="none"
|
||||
autoCompleteType="email"
|
||||
textContentType="emailAddress"
|
||||
keyboardType="email-address"
|
||||
/>
|
||||
<TextInput
|
||||
label="Password"
|
||||
returnKeyType="done"
|
||||
value={password.value}
|
||||
onChangeText={(text) => setPassword({ value: text, error: "" })}
|
||||
error={!!password.error}
|
||||
errorText={password.error}
|
||||
secureTextEntry
|
||||
/>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={onSignUpPressed}
|
||||
style={{ marginTop: 24 }}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
<View style={styles.row}>
|
||||
<Text>Already a member? </Text>
|
||||
<Link onPress={() => navigation.replace("LoginScreen")}>Log in</Link>
|
||||
</View>
|
||||
</Card>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
flexDirection: "row",
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
import Layout from "../components/Layout";
|
||||
import Link from "../components/Link";
|
||||
|
||||
export default function Dashboard({ navigation }) {
|
||||
return (
|
||||
<Layout navigation={navigation}>
|
||||
<View>
|
||||
<Link onPress={() => navigation.replace("LoginScreen")}>Log out</Link>
|
||||
</View>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import React, { useState } from "react";
|
||||
import { TouchableOpacity, StyleSheet, View } from "react-native";
|
||||
import { Text } from "react-native-paper";
|
||||
|
||||
import { Theme } from "../Theme";
|
||||
import BackButton from "../components/BackButton";
|
||||
import Button from "../components/Button";
|
||||
import Card from "../components/Card";
|
||||
import Header from "../components/Header";
|
||||
import Layout from "../components/Layout";
|
||||
import Link from "../components/Link";
|
||||
import TextInput from "../components/TextInput";
|
||||
import { emailValidator } from "../helpers/emailValidator";
|
||||
import { passwordValidator } from "../helpers/passwordValidator";
|
||||
|
||||
export default function LoginScreen({ navigation }) {
|
||||
const [email, setEmail] = useState({ value: "", error: "" });
|
||||
const [password, setPassword] = useState({ value: "", error: "" });
|
||||
|
||||
const onLoginPressed = () => {
|
||||
const emailError = emailValidator(email.value);
|
||||
const passwordError = passwordValidator(password.value);
|
||||
if (emailError || passwordError) {
|
||||
setEmail({ ...email, error: emailError });
|
||||
setPassword({ ...password, error: passwordError });
|
||||
return;
|
||||
}
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: "Dashboard" }],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout navigation={navigation}>
|
||||
<BackButton goBack={navigation.goBack} />
|
||||
<Header>Welcome to Flaredown</Header>
|
||||
<Card>
|
||||
<TextInput
|
||||
label="Email"
|
||||
returnKeyType="next"
|
||||
value={email.value}
|
||||
onChangeText={(text) => setEmail({ value: text, error: "" })}
|
||||
error={!!email.error}
|
||||
errorText={email.error}
|
||||
autoCapitalize="none"
|
||||
autoCompleteType="email"
|
||||
textContentType="emailAddress"
|
||||
keyboardType="email-address"
|
||||
/>
|
||||
<TextInput
|
||||
label="Password"
|
||||
returnKeyType="done"
|
||||
value={password.value}
|
||||
onChangeText={(text) => setPassword({ value: text, error: "" })}
|
||||
error={!!password.error}
|
||||
errorText={password.error}
|
||||
secureTextEntry
|
||||
/>
|
||||
<View style={styles.forgotPassword}>
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.navigate("ResetPasswordScreen")}
|
||||
>
|
||||
<Text style={styles.forgot}>Forgot password</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<Button mode="contained" onPress={onLoginPressed}>
|
||||
Log in
|
||||
</Button>
|
||||
<View style={styles.row}>
|
||||
<Text>Not a member yet? </Text>
|
||||
<Link onPress={() => navigation.replace("CreateAccountScreen")}>
|
||||
Sign up now
|
||||
</Link>
|
||||
</View>
|
||||
</Card>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
forgotPassword: {
|
||||
width: "100%",
|
||||
alignItems: "flex-end",
|
||||
marginBottom: 10,
|
||||
},
|
||||
row: {
|
||||
flexDirection: "row",
|
||||
marginTop: 4,
|
||||
},
|
||||
forgot: {
|
||||
fontSize: 13,
|
||||
color: Theme.colors.secondary,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import BackButton from "../components/BackButton";
|
||||
import Button from "../components/Button";
|
||||
import Card from "../components/Card";
|
||||
import Header from "../components/Header";
|
||||
import Layout from "../components/Layout";
|
||||
import TextInput from "../components/TextInput";
|
||||
import { emailValidator } from "../helpers/emailValidator";
|
||||
|
||||
export default function ResetPasswordScreen({ navigation }) {
|
||||
const [email, setEmail] = useState({ value: "", error: "" });
|
||||
|
||||
const sendResetPasswordEmail = () => {
|
||||
const emailError = emailValidator(email.value);
|
||||
if (emailError) {
|
||||
setEmail({ ...email, error: emailError });
|
||||
return;
|
||||
}
|
||||
navigation.navigate("LoginScreen");
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout navigation={navigation}>
|
||||
<BackButton goBack={navigation.goBack} />
|
||||
<Header>Reset password</Header>
|
||||
<Card>
|
||||
<TextInput
|
||||
label="Email"
|
||||
returnKeyType="done"
|
||||
value={email.value}
|
||||
onChangeText={(text) => setEmail({ value: text, error: "" })}
|
||||
error={!!email.error}
|
||||
errorText={email.error}
|
||||
autoCapitalize="none"
|
||||
autoCompleteType="email"
|
||||
textContentType="emailAddress"
|
||||
keyboardType="email-address"
|
||||
description="You will receive an email with the reset link."
|
||||
/>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={sendResetPasswordEmail}
|
||||
style={{ marginTop: 16 }}
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</Card>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
6
worker-toolkit-flaredown/repo/native/tsconfig.json
Normal file
6
worker-toolkit-flaredown/repo/native/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react"
|
||||
},
|
||||
"extends": "expo/tsconfig.base"
|
||||
}
|
||||
21
worker-toolkit-flaredown/repo/native/webpack.config.js
Normal file
21
worker-toolkit-flaredown/repo/native/webpack.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const createExpoWebpackConfigAsync = require("@expo/webpack-config");
|
||||
|
||||
module.exports = async function (env, argv) {
|
||||
const config = await createExpoWebpackConfigAsync(env, argv);
|
||||
|
||||
// If you want to add a new alias to the config.
|
||||
config.entry = ["./AppEntry.js"];
|
||||
|
||||
// Maybe you want to turn off compression in dev mode.
|
||||
if (config.mode === "development") {
|
||||
config.devServer.compress = false;
|
||||
}
|
||||
|
||||
// Or prevent minimizing the bundle when you build.
|
||||
if (config.mode === "production") {
|
||||
config.optimization.minimize = false;
|
||||
}
|
||||
|
||||
// Finally return the new config for the CLI to use.
|
||||
return config;
|
||||
};
|
||||
Reference in New Issue
Block a user