├── .gitignore
├── App.tsx
├── README.md
├── app.json
├── assets
├── fonts
│ ├── LucidaGrandeBold.ttf
│ └── LucidaGrandeRegular.ttf
├── icon.png
├── package.json
├── splash.png
└── transparent.png
├── babel.config.js
├── components
├── buttons
│ ├── ButtonBase.tsx
│ └── SimpleButton.tsx
├── checkboxes
│ ├── CheckboxBase.tsx
│ └── SimpleCheckbox.tsx
├── fields
│ ├── FieldBase.tsx
│ ├── PasswordField.tsx
│ └── SimpleField.tsx
├── images
│ ├── ImageBase.tsx
│ └── SimpleImage.tsx
├── package.json
├── panels
│ ├── FormPanel.tsx
│ ├── PanelBase.tsx
│ └── SimplePanel.tsx
└── text
│ ├── PageTitleText.tsx
│ ├── SimpleText.tsx
│ └── TextBase.tsx
├── icon.png
├── package-lock.json
├── package.json
├── rn-cli.config.js
├── screenshot.png
├── tsconfig.json
├── views
├── ContentView.tsx
├── LoadingView.tsx
├── LoginView.tsx
├── ViewBase.tsx
└── package.json
├── workflow.png
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.*
3 | .expo
4 | *.jks
5 | *.key
6 | *.mobileprovision
7 | *.p12
8 |
--------------------------------------------------------------------------------
/App.tsx:
--------------------------------------------------------------------------------
1 | import LoadingView from "views/LoadingView";
2 | import LoginView from "views/LoginView";
3 | import React from "react";
4 | import { Component } from "react";
5 | import { Font } from "expo";
6 |
7 | interface AppState {
8 | fontsAreLoaded?: boolean;
9 | }
10 |
11 | export default class App extends Component<{}, AppState> {
12 | constructor(props: any) {
13 | super(props);
14 | this.state = { fontsAreLoaded: false };
15 | }
16 |
17 | async componentDidMount() {
18 | await Font.loadAsync({
19 | "lucida-grande-regular": require("./assets/fonts/LucidaGrandeRegular.ttf"),
20 | "lucida-grande-bold": require("./assets/fonts/LucidaGrandeBold.ttf")
21 | });
22 |
23 | this.setState({ fontsAreLoaded: true });
24 | }
25 |
26 | render() {
27 | if (!this.state.fontsAreLoaded) return ;
28 | return ;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | #
FetGo
4 |
5 | ## Development
6 |
7 | ### Workflow
8 |
9 | I plan to develop this app in a very iterative way. I’ll start by picking what I believe to be the most fundamental feature that would make the app useful, for instance, instant messaging, and develop that feature until it is a first-class experience: including, but not limited to, responsive layout, slick animations and page transitions, reactive loading, native notifications, and so on… Once that feature is sufficiently polished, I’ll distribute it to early adopters for alpha testing – as the app will then begin to have a usefulness over the full website – and I can obtain feedback in order to tweak and finalise the implementation. Only once the user experience is top-tier will I move onto developing the next key feature; at least until I have an MVP release out anyway.
10 |
11 | 
12 |
13 | ### Environment
14 |
15 | #### Node.js
16 |
17 | 1. Go to https://www.nodejs.org/ and download the latest current (not LTS) build of _Node.js_.
18 | 2. Run though the installer, make sure the _npm_ package manager is checked on the list of features to install, check the box that asks about automatically installing necessary build tools, and finally click _“Install”_.
19 | 3. When the installer has finished, a terminal window will appear for the aforementioned build tools. Press any key twice to start them installing.
20 | 4. Press _[Enter]_ when prompted to close the window, once installation has finished.
21 |
22 | #### npm
23 |
24 | 1. Open a PowerShell instance (assuming Node was added to the path during its install).
25 | 2. Change the PowerShell working directory to the root of the repo once you’ve cloned it to your machine.
26 | 3. Issue the following command to install all of the repository’s dependencies:
27 | ```
28 | npm install
29 | ```
30 |
31 | #### Yarn
32 |
33 | 1. Open an elevated (as an admin) PowerShell instance.
34 | 2. Issue the following command to install Yarn:
35 | ```
36 | choco install yarn
37 | ```
38 | 3. Select _“[Y]es”_ when asked if you want to run the Chocolatey install script.
39 |
40 | #### Expo
41 |
42 | 1. In PowerShell, issue the following command to install the Expo CLI:
43 | ```
44 | npm install -g expo-cli
45 | ```
46 | 2. Change the PowerShell working directory to the root of the repo.
47 | 3. Type `expo start` to spin-up a local development server.
48 | 4. Allow private network access when prompted by the firewall dialogue.
49 |
50 | #### Devices
51 |
52 | 1. Install the [Expo Android app](https://play.google.com/store/apps/details?id=host.exp.exponent) to your Android device.
53 | 2. Once installed, open the app and tap _“Scan QR Code”_.
54 | 3. Scan the QR code that was printed to the PowerShell window.
55 | 4. Give the app a chance to download across the local network and then FetGo will load within the Expo app.
56 |
57 | #### Changes
58 |
59 | If you have the Expo dev server running, saving changes to source files will result in the app dynamically updating and reloading on your phone.
60 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "FetGo",
4 | "slug": "FetGo",
5 | "privacy": "public",
6 | "sdkVersion": "32.0.0",
7 | "platforms": [
8 | "ios",
9 | "android"
10 | ],
11 | "version": "0.0.0",
12 | "orientation": "portrait",
13 | "icon": "./assets/icon.png",
14 | "splash": {
15 | "image": "./assets/splash.png",
16 | "resizeMode": "contain",
17 | "backgroundColor": "#000000"
18 | },
19 | "updates": {
20 | "fallbackToCacheTimeout": 0
21 | },
22 | "assetBundlePatterns": [
23 | "**/*"
24 | ],
25 | "ios": {
26 | "supportsTablet": true
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/assets/fonts/LucidaGrandeBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stack-in-a-box/fetgo/be9686d00ea67664edb1adfef36fcbb54ea1ce27/assets/fonts/LucidaGrandeBold.ttf
--------------------------------------------------------------------------------
/assets/fonts/LucidaGrandeRegular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stack-in-a-box/fetgo/be9686d00ea67664edb1adfef36fcbb54ea1ce27/assets/fonts/LucidaGrandeRegular.ttf
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stack-in-a-box/fetgo/be9686d00ea67664edb1adfef36fcbb54ea1ce27/assets/icon.png
--------------------------------------------------------------------------------
/assets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "assets"
3 | }
4 |
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stack-in-a-box/fetgo/be9686d00ea67664edb1adfef36fcbb54ea1ce27/assets/splash.png
--------------------------------------------------------------------------------
/assets/transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stack-in-a-box/fetgo/be9686d00ea67664edb1adfef36fcbb54ea1ce27/assets/transparent.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return { presets: ['babel-preset-expo'] };
4 | };
5 |
--------------------------------------------------------------------------------
/components/buttons/ButtonBase.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button } from "react-native-elements";
3 | import { Component } from "react";
4 | import { StyleProp, StyleSheet, TextStyle, ViewStyle } from "react-native";
5 |
6 | interface ButtonBaseProps {
7 | title?: string;
8 | buttonStyle?: StyleProp;
9 | titleStyle?: StyleProp;
10 | }
11 |
12 | export default class ButtonBase extends Component {
13 | render() {
14 | const { buttonStyle, title, titleStyle } = this.props;
15 |
16 | return ;
21 | }
22 | }
23 |
24 | const styles = StyleSheet.create({
25 | button: {
26 | backgroundColor: "#AA0000"
27 | },
28 | title: {
29 | fontFamily: "lucida-grande-regular"
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/components/buttons/SimpleButton.tsx:
--------------------------------------------------------------------------------
1 | import ButtonBase from "components/buttons/ButtonBase";
2 | import React from "react";
3 | import { Component } from "react";
4 |
5 | interface SimpleButtonProps {
6 | title?: string;
7 | }
8 |
9 | export default class SimpleButton extends Component {
10 | render() {
11 | return ;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/components/checkboxes/CheckboxBase.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { CheckBox } from "react-native-elements";
3 | import { Component } from "react";
4 | import { StyleProp, StyleSheet, TextStyle, ViewStyle } from "react-native";
5 |
6 | interface CheckboxBaseProps {
7 | title?: string;
8 | containerStyle?: StyleProp;
9 | titleStyle?: StyleProp;
10 | }
11 |
12 | interface CheckboxBaseState {
13 | isChecked: boolean;
14 | }
15 |
16 | export default class CheckboxBase extends Component {
17 | constructor(props: CheckboxBaseProps) {
18 | super(props);
19 | this.state = { isChecked: false };
20 | }
21 |
22 | render() {
23 | const { containerStyle, titleStyle } = this.props;
24 | const { isChecked } = this.state;
25 |
26 | return this.setState({ isChecked: !isChecked })}
36 | />;
37 | }
38 | }
39 |
40 | const styles = StyleSheet.create({
41 | container: {
42 | borderWidth: 0,
43 | backgroundColor: "transparent"
44 | },
45 | title: {
46 | marginLeft: 5,
47 | fontFamily: "lucida-grande-regular",
48 | color: "#CCCCCC"
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/components/checkboxes/SimpleCheckbox.tsx:
--------------------------------------------------------------------------------
1 | import CheckboxBase from "components/checkboxes/CheckboxBase";
2 | import React from "react";
3 | import { Component } from "react";
4 |
5 | interface SimpleCheckboxProps {
6 | title?: string;
7 | }
8 |
9 | export default class SimpleCheckbox extends Component {
10 | render() {
11 | return ;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/components/fields/FieldBase.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Component } from "react";
3 | import { StyleSheet, TextInput } from "react-native";
4 |
5 | interface FieldBaseProps {
6 | shouldObfuscateText?: boolean;
7 | }
8 |
9 | interface FieldBaseState {
10 | isFocused?: boolean;
11 | }
12 |
13 | export default class FieldBase extends Component {
14 | constructor(props: FieldBaseProps) {
15 | super(props);
16 | this.state = { isFocused: false };
17 | }
18 |
19 | render() {
20 | const style = Object.assign(
21 | {},
22 | styles.field,
23 | this.state.isFocused ? styles.focused : {}
24 | );
25 |
26 | return this.setState({ isFocused: true })}
30 | onBlur={() => this.setState({ isFocused: false})}
31 | />;
32 | }
33 | }
34 |
35 | const styles = StyleSheet.create({
36 | field: {
37 | marginTop: 5,
38 | marginBottom: 5,
39 | paddingLeft: 10,
40 | paddingRight: 10,
41 | paddingTop: 2,
42 | paddingBottom: 2,
43 | borderRadius: 4,
44 | borderWidth: 2,
45 | backgroundColor: "#404040",
46 | borderColor: "#404040",
47 | color: "#CCCCCC"
48 | },
49 | focused: {
50 | borderColor: "#666666"
51 | }
52 | });
53 |
--------------------------------------------------------------------------------
/components/fields/PasswordField.tsx:
--------------------------------------------------------------------------------
1 | import FieldBase from "components/fields/FieldBase";
2 | import React from "react";
3 | import { Component } from "react";
4 |
5 | export default class PasswordField extends Component {
6 | render() {
7 | return ;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/components/fields/SimpleField.tsx:
--------------------------------------------------------------------------------
1 | import FieldBase from "components/fields/FieldBase";
2 | import React from "react";
3 | import { Component } from "react";
4 |
5 | export default class SimpleField extends Component {
6 | render() {
7 | return ;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/components/images/ImageBase.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Component } from "react";
3 | import { Image, ImageSourcePropType, StyleSheet } from "react-native";
4 |
5 | interface ImageBaseProps {
6 | source: ImageSourcePropType;
7 | scale?: number;
8 | }
9 |
10 | export default class ImageBase extends Component {
11 | render() {
12 | const { scale, source } = this.props;
13 |
14 | const styles = StyleSheet.create({
15 | image: {
16 | width: scale,
17 | height: scale
18 | }
19 | });
20 |
21 | return ;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/components/images/SimpleImage.tsx:
--------------------------------------------------------------------------------
1 | import ImageBase from "components/images/ImageBase";
2 | import React from "react";
3 | import { Component } from "react";
4 | import { ImageSourcePropType } from "react-native";
5 |
6 | interface SimpleImageProps {
7 | source: ImageSourcePropType;
8 | scale?: number;
9 | }
10 |
11 | export default class SimpleImage extends Component {
12 | render() {
13 | return ;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/components/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "components"
3 | }
4 |
--------------------------------------------------------------------------------
/components/panels/FormPanel.tsx:
--------------------------------------------------------------------------------
1 | import PanelBase from "components/panels/PanelBase";
2 | import React from "react";
3 | import { Component } from "react";
4 | import { StyleProp, StyleSheet, ViewStyle } from "react-native";
5 |
6 | interface FormPanelProps {
7 | style?: StyleProp;
8 | }
9 |
10 | export default class FormPanel extends Component {
11 | render() {
12 | return (
13 |
14 | {this.props.children}
15 |
16 | );
17 | }
18 | }
19 |
20 | const styles = StyleSheet.create({
21 | panel: {
22 | borderRadius: 5,
23 | backgroundColor: "#101010"
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/components/panels/PanelBase.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Component } from "react";
3 | import { StyleProp, View, ViewStyle } from "react-native";
4 |
5 | interface PanelBaseProps {
6 | style?: StyleProp;
7 | }
8 |
9 | export default class PanelBase extends Component {
10 | render() {
11 | return (
12 |
13 | {this.props.children}
14 |
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/components/panels/SimplePanel.tsx:
--------------------------------------------------------------------------------
1 | import PanelBase from "components/panels/PanelBase";
2 | import React from "react";
3 | import { Component } from "react";
4 | import { StyleProp, ViewStyle } from "react-native";
5 |
6 | interface SimplePanelProps {
7 | style?: StyleProp;
8 | }
9 |
10 | export default class SimplePanel extends Component {
11 | render() {
12 | return (
13 |
14 | {this.props.children}
15 |
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/components/text/PageTitleText.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TextBase from "components/text/TextBase";
3 | import { Component } from "react";
4 | import { StyleSheet } from "react-native";
5 |
6 | export default class PageTitleText extends Component {
7 | render() {
8 | return ;
12 | }
13 | }
14 |
15 | const styles = StyleSheet.create({
16 | text: {
17 | fontFamily: "lucida-grande-bold",
18 | fontSize: 30,
19 | color: "#E00000"
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/components/text/SimpleText.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TextBase from "components/text/TextBase";
3 | import { Component } from "react";
4 |
5 | export default class SimpleText extends Component {
6 | render() {
7 | return ;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/components/text/TextBase.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Component } from "react";
3 | import { StyleSheet, StyleProp, Text, TextStyle } from "react-native";
4 |
5 | interface TextBaseProps {
6 | style?: StyleProp;
7 | }
8 |
9 | export default class TextBase extends Component {
10 | render() {
11 | const { children, style } = this.props;
12 |
13 | return ;
17 | }
18 | }
19 |
20 | const styles = StyleSheet.create({
21 | text: {
22 | fontFamily: "lucida-grande-regular",
23 | color: "#9C9C9C"
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stack-in-a-box/fetgo/be9686d00ea67664edb1adfef36fcbb54ea1ce27/icon.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "eject": "expo eject"
8 | },
9 | "dependencies": {
10 | "expo": "^32.0.6",
11 | "react": "16.5.0",
12 | "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
13 | "react-native-elements": "^1.1.0"
14 | },
15 | "devDependencies": {
16 | "@types/expo": "^32.0.0",
17 | "@types/react": "^16.8.17",
18 | "@types/react-native": "^0.57.0",
19 | "babel-preset-expo": "^5.0.0",
20 | "react-native-typescript-transformer": "^1.2.12",
21 | "typescript": "^3.4.5"
22 | },
23 | "private": true
24 | }
25 |
--------------------------------------------------------------------------------
/rn-cli.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | getTransformModulePath() {
3 | return require.resolve("react-native-typescript-transformer");
4 | },
5 | getSourceExts() {
6 | return ["ts", "tsx"];
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stack-in-a-box/fetgo/be9686d00ea67664edb1adfef36fcbb54ea1ce27/screenshot.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "commonjs",
5 | "lib": ["es6"],
6 | "allowJs": false,
7 | "jsx": "react-native",
8 | "noEmit": true,
9 | "isolatedModules": true,
10 | "strict": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "moduleResolution": "node",
13 | "baseUrl": ".",
14 | "allowSyntheticDefaultImports": true,
15 | "esModuleInterop": true,
16 | "experimentalDecorators": true
17 | },
18 | "exclude": ["node_modules"]
19 | }
20 |
--------------------------------------------------------------------------------
/views/ContentView.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ViewBase from "views/ViewBase";
3 | import { Component } from "react";
4 | import { StyleSheet } from "react-native";
5 |
6 | export default class ContentView extends Component {
7 | render() {
8 | return (
9 |
10 | {this.props.children}
11 |
12 | );
13 | }
14 | }
15 |
16 | const styles = StyleSheet.create({
17 | view: {
18 | backgroundColor: "#1C1C1C"
19 | }
20 | });
21 |
--------------------------------------------------------------------------------
/views/LoadingView.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ViewBase from "views/ViewBase";
3 | import { Component } from "react";
4 |
5 | export default class LoadingView extends Component {
6 | render() {
7 | return
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/views/LoginView.tsx:
--------------------------------------------------------------------------------
1 | import ContentView from "views/ContentView";
2 | import FormPanel from "components/panels/FormPanel";
3 | import PageTitleText from "components/text/PageTitleText";
4 | import PasswordField from "components/fields/PasswordField";
5 | import React from "react";
6 | import SimpleButton from "components/buttons/SimpleButton";
7 | import SimpleCheckbox from "components/checkboxes/SimpleCheckbox";
8 | import SimpleField from "components/fields/SimpleField";
9 | import SimpleImage from "components/images/SimpleImage";
10 | import SimplePanel from "components/panels/SimplePanel";
11 | import SimpleText from "components/text/SimpleText";
12 | import { Component } from "react";
13 | import { StyleSheet } from "react-native";
14 |
15 | export default class LoginView extends Component {
16 | render() {
17 | return (
18 |
19 | {this.titles()}
20 | {this.form()}
21 |
22 | );
23 | }
24 |
25 | private titles() {
26 | return (
27 |
28 |
32 | Welcome to FetGo
33 | Please log into your FetLife™ account to continue…
34 |
35 | );
36 | }
37 |
38 | private form() {
39 | return (
40 |
41 |
42 | {this.fields()}
43 | {this.button()}
44 |
45 |
46 | );
47 | }
48 |
49 | private fields() {
50 | return (
51 |
52 | {this.username()}
53 | {this.password()}
54 | {this.remember()}
55 |
56 | );
57 | }
58 |
59 | private username() {
60 | return (
61 |
62 | Username / email address:
63 |
64 |
65 | );
66 | }
67 |
68 | private password() {
69 | return (
70 |
71 | Password:
72 |
73 |
74 | );
75 | }
76 |
77 | private remember() {
78 | return ;
79 | }
80 |
81 | private button() {
82 | return (
83 |
84 |
85 |
86 | );
87 | }
88 | }
89 |
90 | const styles = StyleSheet.create({
91 | titlesContainer: {
92 | alignItems: "center"
93 | },
94 | formPanelContainer: {
95 | width: "100%"
96 | },
97 | formPanel: {
98 | margin: 15
99 | },
100 | fieldsContainer: {
101 | paddingTop: 13,
102 | paddingLeft: 13,
103 | paddingRight: 13
104 | },
105 | passwordPanel: {
106 | marginTop: 10
107 | },
108 | buttonPanel: {
109 | padding: 13,
110 | borderTopWidth: 1,
111 | borderTopColor: "#333333"
112 | }
113 | });
114 |
--------------------------------------------------------------------------------
/views/ViewBase.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Component } from "react";
3 | import { StyleProp, StyleSheet, View, ViewStyle } from "react-native";
4 |
5 | interface ViewBaseProps {
6 | style?: StyleProp
7 | }
8 |
9 | export default class ViewBase extends Component {
10 | render() {
11 | return (
12 |
13 | {this.props.children}
14 |
15 | );
16 | }
17 | }
18 |
19 | const styles = StyleSheet.create({
20 | view: {
21 | flex: 1,
22 | alignItems: "center",
23 | justifyContent: "center",
24 | backgroundColor: "black",
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/views/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "views"
3 | }
4 |
--------------------------------------------------------------------------------
/workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Stack-in-a-box/fetgo/be9686d00ea67664edb1adfef36fcbb54ea1ce27/workflow.png
--------------------------------------------------------------------------------