├── .gitignore
├── README.md
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
└── src
├── assets
├── logo.png
└── preloader.svg
├── components
├── Dialog
│ └── index.js
├── Main
│ ├── index.css
│ └── index.js
└── Ride
│ ├── index.css
│ └── index.js
├── index.js
├── serviceWorker.js
└── store
├── actions
└── main.actions.js
├── constants.js
├── fedux.js
├── reducers
└── main.reducer.js
└── state
└── initialState.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Bird app in-complete clone
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | This app is build with?
17 | ----
18 | - Nodejs
19 | - Reactjs
20 | - MaterialUI
21 | - ES6
22 | - JSX
23 | - Third parties modules like (axios, money)
24 | - Fedux (one-way-flow, one-directional state container)
25 |
26 | Features
27 | ----
28 | - Can login registered users with Bird App.
29 | - Can enter any `vehicleId` and find vehicle details like status,battery,code from Bird.
30 | - Can start/stop any `vehicle` Bird from anywhere without verifying driving-license, parking-picture - **(will implement, still in development-process)**.
31 |
32 | Installation
33 | ----
34 |
35 | ```node
36 | $ npm install
37 | $ npm run start || npm run build
38 | ```
39 |
40 | Additional information
41 | ----
42 | For testing this app only, you can copy `vehicle_id` value from this Get Vehicles Nearby JSON response.
43 |
44 |
45 | **Please note:** This application is only for demo purposes to check/show how Bird app works also all assets and apis get used is property of `Bird Company` and created for testing my **Fedux** library.
46 |
47 | License
48 | ----
49 |
50 | MIT
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-bird-clone",
3 | "version": "0.1.0",
4 | "description": "A in-complete Bird app clone",
5 | "main": "src/index.js",
6 | "dependencies": {
7 | "@material-ui/core": "^4.0.0-rc.0",
8 | "@material-ui/icons": "^4.0.1",
9 | "axios": "^0.19.0",
10 | "faker": "^4.1.0",
11 | "react": "^16.8.6",
12 | "react-dom": "^16.8.6",
13 | "react-scripts": "3.0.1"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/fadidevv/react-bird-clone.git"
24 | },
25 | "keywords": [
26 | "react",
27 | "react-component",
28 | "component",
29 | "bird",
30 | "vehicle",
31 | "node-bird",
32 | "scooter",
33 | "fedux"
34 | ],
35 | "author": "@FadiDev ",
36 | "license": "MIT",
37 | "bugs": {
38 | "url": "https://github.com/fadidevv/react-bird-clone/issues"
39 | },
40 | "homepage": "https://birder.netlify.com",
41 | "eslintConfig": {
42 | "extends": "react-app"
43 | },
44 | "peerDependencies": {
45 | "react": "~0.13.x || ~0.14.x || ^15.0.0 || ^16.0.0"
46 | },
47 | "browserslist": {
48 | "production": [
49 | ">0.2%",
50 | "not dead",
51 | "not op_mini all"
52 | ],
53 | "development": [
54 | "last 1 chrome version",
55 | "last 1 firefox version",
56 | "last 1 safari version"
57 | ]
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fadidevv/react-bird-clone/54195c8cfa69b0cac47664960f494a4e5c63ff86/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
16 | Bird - Clone
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bird clone",
3 | "name": "Bird - Clone",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fadidevv/react-bird-clone/54195c8cfa69b0cac47664960f494a4e5c63ff86/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/preloader.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Dialog/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Dialog as AlertBird, Button } from '@material-ui/core'
3 | import DialogActions from '@material-ui/core/DialogActions'
4 | import DialogContent from '@material-ui/core/DialogContent'
5 | import DialogContentText from '@material-ui/core/DialogContentText'
6 | import DialogTitle from '@material-ui/core/DialogTitle'
7 |
8 | import { connectWith } from '../../store/fedux'
9 |
10 | const Dialog = props => {
11 | return (
12 | <>
13 |
18 | {props.dialog.title}
19 |
20 |
21 | {props.dialog.content}
22 |
23 |
24 |
25 |
34 |
35 |
36 | >
37 | )
38 | }
39 |
40 | export default connectWith()(Dialog, 'dialog')()
41 |
--------------------------------------------------------------------------------
/src/components/Main/index.css:
--------------------------------------------------------------------------------
1 | #inputRide,
2 | .MuiInputBase-input {
3 | width: 310px;
4 | margin-top: 300px;
5 | padding: 5px;
6 | color: #212121;
7 | text-align: center;
8 | }
9 |
10 | .MuiInput-underline:before {
11 | border-bottom-color: #4f2277 !important;
12 | }
13 |
14 | .MuiInput-underline:after {
15 | border-bottom-color: #714399 !important;
16 | }
17 |
18 | #birdLogo {
19 | width: 150px;
20 | }
21 |
22 | #buttonRide {
23 | font-family: sans-serif;
24 | color: white;
25 | background-color: #714399;
26 | font-weight: 320;
27 | width: 300px;
28 | border-radius: 2px;
29 | box-shadow: none;
30 | margin-top: 20px;
31 | }
32 |
33 | #birdPreloader {
34 | width: 70px;
35 | margin-top: 10px;
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/Main/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Grid, Button, Input } from '@material-ui/core'
3 |
4 | import '../Main/index.css'
5 | import logo from '../../assets/logo.png'
6 | import preloader from '../../assets/preloader.svg'
7 | import Dialog from '../Dialog/index'
8 | import Ride from '../Ride/index'
9 |
10 | class Main extends Component {
11 | render() {
12 | return (
13 |
14 |
15 | {this.props.data.token ? (
16 |
17 | ) : (
18 | <>
19 | {this.props.data.preloader && (
20 |
21 | )}
22 |
28 | this.props.emit({
29 | handleInputRide: true,
30 | inputRideValue: [target]
31 | })
32 | }
33 | required
34 | autoFocus={true}
35 | />
36 |
55 | >
56 | )}
57 | {this.props.data.error && (
58 |
63 | )}
64 |
65 | )
66 | }
67 | }
68 |
69 | export default Main
70 |
--------------------------------------------------------------------------------
/src/components/Ride/index.css:
--------------------------------------------------------------------------------
1 | #inputStartStopRide,
2 | .MuiInputBase-input {
3 | width: 310px;
4 | padding: 5px;
5 | color: #212121;
6 | text-align: center;
7 | }
8 |
9 | #buttonStartStopRide {
10 | font-family: sans-serif;
11 | color: white;
12 | background-color: #61a263;
13 | font-weight: 320;
14 | width: 80px;
15 | height: 80px;
16 | box-shadow: none;
17 | margin-top: 20px;
18 | }
19 |
20 | #scooterDetails {
21 | display: flex;
22 | font-family: sans-serif;
23 | padding: 10px;
24 | margin-top: 30px;
25 | }
26 |
27 | #batteryColor {
28 | color: #4caf50;
29 | }
30 |
31 | #codeColor {
32 | color: #714399;
33 | }
34 |
35 | #lockColor {
36 | color: #714399;
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/Ride/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Input } from '@material-ui/core'
3 | import Fab from '@material-ui/core/Fab'
4 | import Card from '@material-ui/core/Card'
5 | import CardContent from '@material-ui/core/CardContent'
6 | import { Battery90Sharp, LockSharp } from '@material-ui/icons'
7 |
8 | import '../Ride/index.css'
9 | import { connectWith } from '../../store/fedux'
10 |
11 | const Ride = props => {
12 | return (
13 | <>
14 | {props.ride.code && (
15 |
16 |
17 |
18 | CODE: {props.ride.code}
19 |
20 |
21 | Battery Level:
22 |
23 |
24 | Locked:
25 |
26 |
27 |
28 | )}
29 |
35 | props.emit({
36 | handleStartStopRide: true,
37 | inputStartStopRideValue: [target]
38 | })
39 | }
40 | style={{ marginTop: props.ride.code ? 100 : 300 }}
41 | required
42 | autoFocus={true}
43 | />
44 |
48 | props.emit({
49 | handleRideConfirmations: true,
50 | payload: [
51 | props.ride.inputStartStopRideValue,
52 | props.ride.lat,
53 | props.ride.lang,
54 | props.ride.token
55 | ]
56 | })
57 | }
58 | >
59 | {props.ride.buttonStartStopText}
60 |
61 | >
62 | )
63 | }
64 |
65 | export default connectWith()(Ride, 'ride')()
66 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | import { createStore } from './store/fedux'
5 | import mainReducer from './store/reducers/main.reducer'
6 | import initialState from './store/state/initialState'
7 | import {
8 | handleInputRide,
9 | handleStartStopRide,
10 | handleConfirmation,
11 | verifyBirdLoginRequest,
12 | handleRideConfirmations
13 | } from './store/actions/main.actions'
14 | import Main from './components/Main'
15 | import * as serviceWorker from './serviceWorker'
16 |
17 | const FeduxProvider = createStore(
18 | mainReducer,
19 | {
20 | handleInputRide,
21 | handleStartStopRide,
22 | handleConfirmation,
23 | verifyBirdLoginRequest,
24 | handleRideConfirmations
25 | },
26 | false
27 | )(Main)(initialState)
28 |
29 | ReactDOM.render(, document.getElementById('root'))
30 | serviceWorker.unregister()
31 |
32 | if (module.hot) module.hot.accept()
33 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/store/actions/main.actions.js:
--------------------------------------------------------------------------------
1 | import {
2 | HANDLE_INPUT_RIDE,
3 | SAVE_TOKEN,
4 | HANDLE_START_STOP_RIDE,
5 | CONTROL_RIDE_BUTTON_TEXT,
6 | CONTROL_RIDE_INPUT_TEXT,
7 | HANDLE_PRELOADER,
8 | HANDLE_CODE,
9 | HANDLE_RIDE_BUTTON_TEXT,
10 | HANDLE_ERROR
11 | } from '../constants'
12 |
13 | import { dispatcher } from '../fedux'
14 | import axios from 'axios'
15 |
16 | export const handleInputRide = inputRideValue => {
17 | const { value: currentValue } = inputRideValue
18 | return {
19 | type: HANDLE_INPUT_RIDE,
20 | payload: currentValue
21 | }
22 | }
23 |
24 | export const handleStartStopRide = inputStartStopRideValue => {
25 | const { value: currentValue } = inputStartStopRideValue
26 | return {
27 | type: HANDLE_START_STOP_RIDE,
28 | payload: currentValue
29 | }
30 | }
31 |
32 | export const handleConfirmation = inputRideValue => {
33 | if (
34 | inputRideValue === '' ||
35 | inputRideValue === null ||
36 | !inputRideValue.match('@')
37 | ) {
38 | return {
39 | type: HANDLE_ERROR,
40 | payload: 'Please enter your email'
41 | }
42 | } else {
43 | (async () => await makeBirdLoginRequest(inputRideValue))()
44 | return {
45 | type: HANDLE_ERROR,
46 | payload: false
47 | }
48 | }
49 | }
50 |
51 | export const handleRideConfirmations = (
52 | inputStartStopRideValue,
53 | lat,
54 | lang,
55 | token
56 | ) => {
57 | if (inputStartStopRideValue === '' || inputStartStopRideValue === null) {
58 | return {
59 | type: HANDLE_ERROR,
60 | payload: 'Please enter any scooterId'
61 | }
62 | } else {
63 | (async () =>
64 | await getBirdScooterById(inputStartStopRideValue, lat, lang, token))()
65 | return {
66 | type: HANDLE_ERROR,
67 | payload: false
68 | }
69 | }
70 | }
71 |
72 | export const saveToken = token => {
73 | return {
74 | type: SAVE_TOKEN,
75 | payload: token
76 | }
77 | }
78 |
79 | export const controlRideButtonText = () => {
80 | return {
81 | type: CONTROL_RIDE_BUTTON_TEXT,
82 | payload: 'ENTER CODE'
83 | }
84 | }
85 |
86 | export const controlRideInputText = () => {
87 | return {
88 | type: CONTROL_RIDE_INPUT_TEXT,
89 | payload: 'ENTER YOUR CODE'
90 | }
91 | }
92 |
93 | const _birdRequestConfig = (_token = null) => {
94 | const token = _token !== null ? { Authorization: `Bird ${_token}` } : ''
95 | const request = axios.create({
96 | baseURL: 'https://api.birdapp.com',
97 | headers: {
98 | 'Device-id': '06b8b764-22dd-4fe1-b897-c78af5231ca1',
99 | Platform: 'android',
100 | 'App-Version': '4.35.1.1',
101 | ...token
102 | }
103 | })
104 | return request
105 | }
106 |
107 | const makeBirdLoginRequest = async email => {
108 | try {
109 | dispatcher([{ type: HANDLE_PRELOADER, payload: true }])
110 | let response = await _birdRequestConfig().request({
111 | method: 'POST',
112 | url: '/user/login',
113 | data: {
114 | email: email
115 | },
116 | responseType: 'json'
117 | })
118 | if (response.status === 200 && !response.data.token) {
119 | dispatcher([
120 | { type: HANDLE_INPUT_RIDE, payload: '' },
121 | controlRideButtonText,
122 | controlRideInputText,
123 | { type: HANDLE_PRELOADER, payload: false }
124 | ])
125 | return response.data
126 | } else {
127 | dispatcher([
128 | { type: HANDLE_INPUT_RIDE, payload: '' },
129 | {
130 | type: HANDLE_ERROR,
131 | payload:
132 | 'Sorry, only already registered users are allowed from Bird App'
133 | },
134 | { type: HANDLE_PRELOADER, payload: false }
135 | ])
136 | }
137 | } catch (error) {
138 | if (error.response) {
139 | dispatcher([
140 | { type: HANDLE_ERROR, payload: error.response.data.errors },
141 | { type: HANDLE_INPUT_RIDE, payload: '' },
142 | { type: HANDLE_PRELOADER, payload: false }
143 | ])
144 | }
145 | }
146 | }
147 |
148 | export const verifyBirdLoginRequest = async code => {
149 | try {
150 | dispatcher([{ type: HANDLE_PRELOADER, payload: true }])
151 | let response = await _birdRequestConfig().request({
152 | method: 'PUT',
153 | url: '/request/accept',
154 | data: {
155 | token: code
156 | },
157 | responseType: 'json'
158 | })
159 | if (response.status === 200 && response.data.token) {
160 | dispatcher([
161 | { type: SAVE_TOKEN, payload: response.data.token },
162 | { type: HANDLE_PRELOADER, payload: false }
163 | ])
164 | }
165 | } catch (error) {
166 | if (error.response) {
167 | dispatcher([
168 | { type: HANDLE_ERROR, payload: error.response.data.message },
169 | { type: HANDLE_PRELOADER, payload: false }
170 | ])
171 | }
172 | }
173 | }
174 |
175 | export const getBirdScooterById = async (
176 | scooterId,
177 | lat = '47.3817767',
178 | lang = '8.5043783',
179 | token
180 | ) => {
181 | try {
182 | dispatcher([
183 | { type: HANDLE_CODE, payload: '' },
184 | { type: HANDLE_PRELOADER, payload: true }
185 | ])
186 | let response = await _birdRequestConfig(token).request({
187 | method: 'PUT',
188 | url: `/bird/chirp`,
189 | data: {
190 | alarm: false,
191 | bird_id: scooterId
192 | },
193 | headers: {
194 | Location: JSON.stringify({ latitude: lat, longitude: lang })
195 | },
196 | responseType: 'json'
197 | })
198 | if (response.status === 200) {
199 | dispatcher([
200 | { type: HANDLE_START_STOP_RIDE, payload: '' },
201 | { type: HANDLE_RIDE_BUTTON_TEXT, payload: 'START' },
202 | { type: HANDLE_CODE, payload: response.data.code },
203 | { type: HANDLE_PRELOADER, payload: false }
204 | ])
205 | }
206 | } catch (error) {
207 | if (error.response) {
208 | dispatcher([
209 | { type: HANDLE_ERROR, payload: error.response.data.message },
210 | { type: HANDLE_PRELOADER, payload: false }
211 | ])
212 | }
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/src/store/constants.js:
--------------------------------------------------------------------------------
1 | export const HANDLE_INPUT_RIDE = 'HANDLE_INPUT_RIDE'
2 | export const SAVE_TOKEN = 'SAVE_TOKEN'
3 | export const CONTROL_RIDE_BUTTON_TEXT = 'CONTROL_RIDE_BUTTON_TEXT'
4 | export const CONTROL_RIDE_INPUT_TEXT = 'CONTROL_RIDE_INPUT_TEXT'
5 | export const HANDLE_START_STOP_RIDE = 'HANDLE_START_STOP_RIDE'
6 | export const HANDLE_PRELOADER = 'HANDLE_PRELOADER'
7 | export const HANDLE_CODE = 'HANDLE_CODE'
8 | export const HANDLE_RIDE_BUTTON_TEXT = 'HANDLE_RIDE_BUTTON_TEXT'
9 | export const HANDLE_ERROR = 'HANDLE_ERROR'
10 |
--------------------------------------------------------------------------------
/src/store/fedux.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @Author @FadiDev gray at Thailand, Bangkok
3 | */
4 | /* eslint-disable */
5 | import React from 'react'
6 | var { EventEmitter } = require('eventemitter3'),
7 | feduxListener = new EventEmitter()
8 |
9 | var _holdOn
10 | var globalStore = {}
11 | var _propers = {}
12 |
13 | const _ParentConnector = (dispatchToProps, setFeduxToGlobal) => (
14 | Component,
15 | _propName,
16 | _feduxName,
17 | _state = {},
18 | _initialState = {}
19 | ) => {
20 | window.toComponents = _initialState
21 | if (Component.toString().match('componentWillUpdate')) {
22 | class Fedux extends React.Component {
23 | static displayName = _feduxName || this.name
24 | componentDidMount() {
25 | if (setFeduxToGlobal) setToGlobal()
26 | if (Object.keys(dispatchToProps).length > 0) {
27 | window.GLOBAL_FEDUX_ACTION_PROPS = dispatchToProps
28 | window.globalFeduxContext = this
29 | }
30 | }
31 | render() {
32 | return (
33 | <>
34 |
45 | >
46 | )
47 | }
48 | }
49 | return Fedux
50 | }
51 | }
52 |
53 | const _ParentEnchancer = (Component, _enchancerName, _reducers) => {
54 | return class Enchancer extends Component {
55 | static displayName = _enchancerName || this.name
56 | componentDidMount() {
57 | window.globalParentContext = this
58 | window.globalParentName = Component.name
59 | _globalFedux(_reducers)
60 | }
61 | componentWillUpdate = (nextProps, nextState) => {
62 | window.toComponents = nextState
63 | _objectMerge(globalStore, nextState)
64 | window.Store = { ...globalStore }
65 | }
66 | render = () => super.render()
67 | }
68 | }
69 |
70 | const _globalFedux = _reducers => {
71 | if (_reducers !== null) return (window.GLOBAL_FEDUX_REDUCERS = _reducers)
72 | }
73 |
74 | const _emit = (name = {}) => {
75 | let globalActionsFromProps = window.GLOBAL_FEDUX_ACTION_PROPS
76 | if (typeof globalActionsFromProps === 'object') {
77 | let ifTruthy = Object.keys(name).some(
78 | isTruthful => name[isTruthful] === true
79 | )
80 | if (ifTruthy) {
81 | if (name instanceof Object && Object.keys(name).length === 1) {
82 | for (var actions in name) {
83 | if (globalActionsFromProps[actions]) {
84 | return _dispatch(globalActionsFromProps[actions]())
85 | }
86 | }
87 | } else if (name instanceof Object && Object.keys(name).length === 2) {
88 | for (var actions in name) {
89 | if (globalActionsFromProps[actions]) {
90 | return _dispatch(
91 | globalActionsFromProps[actions](
92 | ...name[Object.entries(name)[1][0]]
93 | )
94 | )
95 | }
96 | }
97 | } else {
98 | throw new Error(`
99 | '${JSON.stringify(name)}' is not a object type or it contains
100 | more then 1 or 2 objects or objects are empty
101 | `)
102 | }
103 | } else {
104 | throw new Error(`
105 | Did you set the action ${JSON.stringify(name)} object
106 | value to 'true' like this ${JSON.stringify(name).replace(
107 | 'false',
108 | 'true'
109 | )}?
110 | `)
111 | }
112 | } else {
113 | throw new Error(`
114 | Did you passed your actions or actionCreators to the parent
115 | class '${window.globalParentName}' component as a props?
116 | `)
117 | }
118 | }
119 |
120 | const dispose = () =>
121 | window.globalParentContext !== null || undefined
122 | ? window.globalParentContext
123 | : ''
124 |
125 | const _dispatch = (action = {}) => {
126 | if (action !== null) {
127 | var response = window.GLOBAL_FEDUX_REDUCERS(action)
128 | window.globalParentContext.setState({ ...response }, () => {
129 | window.globalFeduxContext.forceUpdate()
130 | })
131 | }
132 | }
133 |
134 | const dispatcher = actions => {
135 | if (Array.isArray(actions)) {
136 | actions.map(dispose => {
137 | if (typeof dispose === 'object') {
138 | _dispatch(dispose)
139 | } else {
140 | _dispatch(dispose())
141 | }
142 | })
143 | } else {
144 | return typeof actions === 'object'
145 | ? _dispatch(actions)
146 | : _dispatch(actions())
147 | }
148 | }
149 |
150 | const _ifGlobalFedux = _reducersRef => {
151 | return {
152 | ['getState']: getState,
153 | ['subscribe']: subscribe,
154 | ['dispatch']: dispatcher
155 | }
156 | }
157 |
158 | const setToGlobal = () => (window.Fedux = _ifGlobalFedux())
159 |
160 | const _feduxListenerHandler = (
161 | hookDefault = '__hookFeduxStore',
162 | _propers = {}
163 | ) =>
164 | feduxListener.on(hookDefault, state => _objectMerge(_propers, { ...state }))
165 |
166 | const connectWith = (
167 | reducers = null,
168 | dispatchToProps = {},
169 | setFeduxToGlobal = false
170 | ) => (Component, propName = 'data', feduxName, enchancerName) => (
171 | initialState = {}
172 | ) => {
173 | if (
174 | (Component.toString().match('return Object') &&
175 | Component.toString().match('apply')) ||
176 | Component.toString().match('class')
177 | ) {
178 | return _ParentConnector(dispatchToProps, setFeduxToGlobal)(
179 | _ParentEnchancer(Component, enchancerName, reducers),
180 | propName,
181 | feduxName,
182 | getState,
183 | initialState
184 | )
185 | }
186 | if (
187 | Component.toString().match('createElement') &&
188 | Component.toString().match('Fragment')
189 | ) {
190 | return _ParentChildScope()(Component, enchancerName, propName, getState)
191 | }
192 | }
193 |
194 | const _ParentChildScope = () => (
195 | Component,
196 | _enchancerName,
197 | _propName,
198 | _state = {}
199 | ) => {
200 | const Enchancer = props => {
201 | Enchancer.displayName = _enchancerName || Enchancer.name
202 | _objectMerge(_propers, { ..._state() })
203 | return (
204 | <>
205 |
215 | >
216 | )
217 | }
218 | return Enchancer
219 | }
220 |
221 | const _store = () => {
222 | subscribe()
223 | return !!window.Store || !!global.Store !== undefined || null
224 | ? window.Store || global.Store
225 | : ''
226 | }
227 |
228 | const getState = () =>
229 | _store() !== undefined || null ? _store() || clearTimeout(_holdOn) : ''
230 |
231 | const _hookFedux = _reference => (
232 | (window.hookFedux = _reference), (global.hookFedux = _reference)
233 | )
234 |
235 | const _updateOccurred = _state =>
236 | (_holdOn = setTimeout(
237 | () => (
238 | window.hookFedux.setState({ ..._state }),
239 | global.hookFedux.setState({ ..._state })
240 | ),
241 | null
242 | ))
243 |
244 | const subscribe = listener =>
245 | typeof listener === 'function' ? listener() : null
246 |
247 | const _objectMerge = (_propers, _state) =>
248 | Object.assign(_propers, { ..._state })
249 |
250 | export {
251 | connectWith as createStore,
252 | connectWith,
253 | getState,
254 | subscribe,
255 | dispatcher
256 | }
257 |
--------------------------------------------------------------------------------
/src/store/reducers/main.reducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | HANDLE_INPUT_RIDE,
3 | SAVE_TOKEN,
4 | HANDLE_START_STOP_RIDE,
5 | CONTROL_RIDE_BUTTON_TEXT,
6 | CONTROL_RIDE_INPUT_TEXT,
7 | HANDLE_PRELOADER,
8 | HANDLE_CODE,
9 | HANDLE_RIDE_BUTTON_TEXT,
10 | HANDLE_ERROR
11 | } from '../constants'
12 | import state from '../state/initialState'
13 |
14 | const mainReducer = actions => {
15 | switch (actions.type) {
16 | case HANDLE_INPUT_RIDE:
17 | return {
18 | inputRideValue: actions.payload
19 | }
20 | case SAVE_TOKEN:
21 | return {
22 | token: actions.payload
23 | }
24 | case CONTROL_RIDE_BUTTON_TEXT:
25 | return {
26 | textRide: actions.payload
27 | }
28 | case CONTROL_RIDE_INPUT_TEXT:
29 | return {
30 | textInputRide: actions.payload
31 | }
32 | case HANDLE_START_STOP_RIDE:
33 | return {
34 | inputStartStopRideValue: actions.payload
35 | }
36 | case HANDLE_PRELOADER:
37 | return {
38 | preloader: actions.payload
39 | }
40 | case HANDLE_CODE:
41 | return {
42 | code: actions.payload
43 | }
44 | case HANDLE_RIDE_BUTTON_TEXT:
45 | return {
46 | buttonStartStopText: actions.payload
47 | }
48 | case HANDLE_ERROR:
49 | return {
50 | error: actions.payload
51 | }
52 | default:
53 | return state
54 | }
55 | }
56 |
57 | export default mainReducer
58 |
--------------------------------------------------------------------------------
/src/store/state/initialState.js:
--------------------------------------------------------------------------------
1 | export default {
2 | inputRideValue: '',
3 | inputStartStopRideValue: '',
4 | token: '',
5 | textInputRide: 'ENTER EMAIL',
6 | textRide: 'RIDE',
7 | inputStartStopText: 'ENTER ANY SCOOTER ID',
8 | buttonStartStopText: 'FIND',
9 | preloader: false,
10 | code: '',
11 | error: ''
12 | }
13 |
--------------------------------------------------------------------------------