├── .gitignore
├── README.md
├── capacitor.config.ts
├── ionic.config.json
├── package-lock.json
├── package.json
├── public
├── assets
│ ├── icon
│ │ ├── favicon.png
│ │ └── icon.png
│ └── shapes.svg
├── index.html
└── manifest.json
├── src
├── App.test.tsx
├── App.tsx
├── Firebase.tsx
├── components
│ ├── Map
│ │ ├── Map.scss
│ │ └── Map.tsx
│ └── WorkerSelector
│ │ ├── WorkerSelector.scss
│ │ └── WorkerSelector.tsx
├── data
│ └── workerList.tsx
├── index.tsx
├── pages
│ ├── ConfirmTab
│ │ ├── ConfirmTab.scss
│ │ └── ConfirmTab.tsx
│ ├── GetStarted
│ │ ├── GetStarted.scss
│ │ └── GetStarted.tsx
│ ├── HomeTab
│ │ ├── HomeTab.scss
│ │ └── HomeTab.tsx
│ ├── LoginTab
│ │ ├── Login.scss
│ │ └── Login.tsx
│ ├── RootTab
│ │ └── RootTab.tsx
│ └── SearchTab
│ │ ├── SearchTab.scss
│ │ └── SearchTab.tsx
├── react-app-env.d.ts
├── reportWebVitals.ts
├── service-worker.ts
├── serviceWorkerRegistration.ts
├── setupTests.ts
├── theme
│ └── variables.scss
└── utilities
│ └── useRouter
│ └── useRouter.tsx
└── tsconfig.json
/.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 | .vscode
21 | .idea
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # Optional eslint cache
28 | .eslintcache
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wuberapp-ionic6-react-and-firebase-app
2 |
3 | [DEMO](https://uberclone-ionic.vercel.app/)
4 |
5 | ## Packages installed
6 |
7 | 1. [sass - npm](https://www.npmjs.com/package/sass)
8 |
9 | ```
10 | npm install sass
11 | ```
12 |
13 | 2. [Add the Mapbox GL JS module](https://www.mapbox.com/install/js/bundler-install/)
14 |
15 | 3. [mapbox-gl-js Installation](https://docs.mapbox.com/mapbox-gl-js/guides/install/)
16 |
17 | ```
18 | npm install mapbox-gl --save
19 |
20 | // & add
21 | // mapbox-gl in _app.js
22 | import "mapbox-gl/dist/mapbox-gl.css"
23 |
24 | // Include the GL JS CSS file in the
of your HTML file.
25 | ```
26 |
27 | ### References
28 |
29 | 1. [router-link](https://ionicframework.com/docs/v4/api/router-link)
30 |
31 | 2. [useRouter in Typescript](//https://codesandbox.io/s/3rwq8r85p?file=/src/useRouter.ts)
32 |
33 | 3. [“typescript get url params” Code Answer](https://www.codegrepper.com/code-examples/javascript/typescript++get+url+params)
34 |
35 | 4. [forceRefresh: bool](https://v5.reactrouter.com/web/api/BrowserRouter/forcerefresh-bool)
36 |
37 | 5. [Firebase Auth with React Typescript](https://javascript.plainenglish.io/firebase-auth-with-react-typescript-4b9d9605fa53)
38 |
39 | #### Tasks
40 |
41 | 1. Add Navigation [x]
42 | 2. Calculate the prices for the worker[x]
43 | 3. Add Login Page[x]
44 | 4. Add firebase[]
45 | 5. Implement Type safety using TypeScript.
46 | Protect Routes using Router Guard.
47 | Protect & Safely access Firebase credentials.
48 | Social Authentification using Firebase.
49 | Global access to Authenticated User object
50 |
--------------------------------------------------------------------------------
/capacitor.config.ts:
--------------------------------------------------------------------------------
1 | import { CapacitorConfig } from '@capacitor/cli';
2 |
3 | const config: CapacitorConfig = {
4 | appId: 'io.ionic.starter',
5 | appName: 'wuberAppIonic',
6 | webDir: 'build',
7 | bundledWebRuntime: false
8 | };
9 |
10 | export default config;
11 |
--------------------------------------------------------------------------------
/ionic.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wuberAppIonic",
3 | "integrations": {
4 | "capacitor": {}
5 | },
6 | "type": "react"
7 | }
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wuberAppIonic",
3 | "version": "0.0.1",
4 | "private": true,
5 | "dependencies": {
6 | "@capacitor/app": "1.1.1",
7 | "@capacitor/core": "3.6.0",
8 | "@capacitor/haptics": "1.1.4",
9 | "@capacitor/keyboard": "1.2.3",
10 | "@capacitor/status-bar": "1.0.8",
11 | "@ionic/react": "^6.0.0",
12 | "@ionic/react-router": "^6.0.0",
13 | "@testing-library/jest-dom": "^5.11.9",
14 | "@testing-library/react": "^11.2.5",
15 | "@testing-library/user-event": "^12.6.3",
16 | "@types/jest": "^26.0.20",
17 | "@types/node": "^12.19.15",
18 | "@types/react": "^16.14.3",
19 | "@types/react-dom": "^16.9.10",
20 | "@types/react-router": "^5.1.11",
21 | "@types/react-router-dom": "^5.1.7",
22 | "firebase": "^9.8.4",
23 | "ionicons": "^5.4.0",
24 | "mapbox-gl": "^2.9.0",
25 | "react": "^17.0.1",
26 | "react-dom": "^17.0.1",
27 | "react-router": "^5.2.0",
28 | "react-router-dom": "^5.2.0",
29 | "react-scripts": "^5.0.0",
30 | "sass": "^1.52.3",
31 | "typescript": "^4.1.3",
32 | "web-vitals": "^0.2.4",
33 | "workbox-background-sync": "^5.1.4",
34 | "workbox-broadcast-update": "^5.1.4",
35 | "workbox-cacheable-response": "^5.1.4",
36 | "workbox-core": "^5.1.4",
37 | "workbox-expiration": "^5.1.4",
38 | "workbox-google-analytics": "^5.1.4",
39 | "workbox-navigation-preload": "^5.1.4",
40 | "workbox-precaching": "^5.1.4",
41 | "workbox-range-requests": "^5.1.4",
42 | "workbox-routing": "^5.1.4",
43 | "workbox-strategies": "^5.1.4",
44 | "workbox-streams": "^5.1.4"
45 | },
46 | "scripts": {
47 | "start": "react-scripts start",
48 | "build": "react-scripts build",
49 | "test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
50 | "eject": "react-scripts eject"
51 | },
52 | "eslintConfig": {
53 | "extends": [
54 | "react-app",
55 | "react-app/jest"
56 | ]
57 | },
58 | "browserslist": {
59 | "production": [
60 | ">0.2%",
61 | "not dead",
62 | "not op_mini all"
63 | ],
64 | "development": [
65 | "last 1 chrome version",
66 | "last 1 firefox version",
67 | "last 1 safari version"
68 | ]
69 | },
70 | "devDependencies": {
71 | "@capacitor/cli": "5.7.4"
72 | },
73 | "description": "An Ionic project"
74 | }
75 |
--------------------------------------------------------------------------------
/public/assets/icon/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cboy220/uberclone-ts-ionic-firebase/8f54725e74c4b3cb1ece251d583f702e1b7a0d56/public/assets/icon/favicon.png
--------------------------------------------------------------------------------
/public/assets/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cboy220/uberclone-ts-ionic-firebase/8f54725e74c4b3cb1ece251d583f702e1b7a0d56/public/assets/icon/icon.png
--------------------------------------------------------------------------------
/public/assets/shapes.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ionic App
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Ionic App",
3 | "name": "My Ionic App",
4 | "icons": [
5 | {
6 | "src": "assets/icon/favicon.png",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "assets/icon/icon.png",
12 | "type": "image/png",
13 | "sizes": "512x512",
14 | "purpose": "maskable"
15 | }
16 | ],
17 | "start_url": ".",
18 | "display": "standalone",
19 | "theme_color": "#ffffff",
20 | "background_color": "#ffffff"
21 | }
22 |
--------------------------------------------------------------------------------
/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders without crashing', () => {
6 | const { baseElement } = render();
7 | expect(baseElement).toBeDefined();
8 | });
9 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Redirect, Route, Switch } from "react-router-dom";
2 | import {
3 | IonApp,
4 | setupIonicReact,
5 | } from "@ionic/react";
6 | import { IonReactRouter } from "@ionic/react-router";
7 |
8 | //Tabs
9 | import RootTab from "./pages/RootTab/RootTab";
10 | //
11 | import HomeTab from "./pages/HomeTab/HomeTab";
12 | import SearchTab from "./pages/SearchTab/SearchTab";
13 | import ConfirmTab from "./pages/ConfirmTab/ConfirmTab";
14 |
15 | /* Core CSS required for Ionic components to work properly */
16 | import "@ionic/react/css/core.css";
17 |
18 | /* Basic CSS for apps built with Ionic */
19 | import "@ionic/react/css/normalize.css";
20 | import "@ionic/react/css/structure.css";
21 | import "@ionic/react/css/typography.css";
22 |
23 | /* Optional CSS utils that can be commented out */
24 | import "@ionic/react/css/padding.css";
25 | import "@ionic/react/css/float-elements.css";
26 | import "@ionic/react/css/text-alignment.css";
27 | import "@ionic/react/css/text-transformation.css";
28 | import "@ionic/react/css/flex-utils.css";
29 | import "@ionic/react/css/display.css";
30 |
31 | /* Theme variables */
32 | import "./theme/variables.scss";
33 |
34 | /**Mapbox-gl */
35 | import "mapbox-gl/dist/mapbox-gl.css";
36 | import Login from "./pages/LoginTab/Login";
37 |
38 | setupIonicReact();
39 |
40 | const App: React.FC = () => (
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | }
60 | />
61 |
62 |
63 |
64 | );
65 |
66 | export default App;
67 |
--------------------------------------------------------------------------------
/src/Firebase.tsx:
--------------------------------------------------------------------------------
1 | // Import the functions you need from the SDKs you need
2 | import { initializeApp } from "firebase/app";
3 | import { getAnalytics } from "firebase/analytics";
4 | //Auth
5 | import {GoogleAuthProvider, getAuth} from 'firebase/auth';
6 | // TODO: Add SDKs for Firebase products that you want to use
7 | // https://firebase.google.com/docs/web/setup#available-libraries
8 |
9 | // Your web app's Firebase configuration
10 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional
11 | const firebaseConfig = {
12 | apiKey: "AIzaSyADscLF9ri8wA6SxWp4ep5NKYti-IloqHo",
13 | authDomain: "fixit-ionic-app.firebaseapp.com",
14 | projectId: "fixit-ionic-app",
15 | storageBucket: "fixit-ionic-app.appspot.com",
16 | messagingSenderId: "842200201597",
17 | appId: "1:842200201597:web:e0bed9e4c5acbe303ef414",
18 | measurementId: "G-KMRQTJJZY4",
19 | };
20 |
21 | // Initialize Firebase
22 | const app = initializeApp(firebaseConfig);
23 | const analytics = getAnalytics(app);
24 |
25 |
26 | const provider = new GoogleAuthProvider();
27 |
28 | // gets error when i try to export them all
29 | export const auth = getAuth();
30 |
31 | //exported here so other files can have access to auth, provider and app
32 | export {app, provider};
33 |
--------------------------------------------------------------------------------
/src/components/Map/Map.scss:
--------------------------------------------------------------------------------
1 | .container__map{
2 | height: 50%;
3 | max-height: 50vh;
4 | width: 100%;
5 | overflow-x: hidden;
6 | overflow-y: hidden;
7 | overflow: auto;
8 | position: fixed;
9 | }
--------------------------------------------------------------------------------
/src/components/Map/Map.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | //scss
3 | import "./Map.scss";
4 | import { useEffect } from "react";
5 |
6 | const mapboxgl = require("mapbox-gl");
7 |
8 | mapboxgl.accessToken =
9 | "pk.eyJ1Ijoib21hcmFzaHplaW5ob205OCIsImEiOiJjbDRrMXY5c3MwN3ZpM2NxcHp3ZGVmN3ZyIn0.3Ziuh7Utama_wz_4s8qh2g";
10 |
11 | //import passed map id
12 | interface MapsProps {
13 | name: string;
14 | pickupCoordinates: number[];
15 | dropOffCoordinates: number[];
16 | }
17 |
18 | const Map: React.FC = (props, { name }) => {
19 | //debug props
20 | // console.log(props);
21 |
22 | //initializes map
23 | useEffect(() => {
24 | const map = new mapboxgl.Map({
25 | //important id for the container insert into div
26 | container: "map",
27 | style:
28 | "mapbox://styles/omarashzeinhom98/cl4k5xuzh002h14mtoho5qips?optimize=true",
29 | center: [31.239661,30.056156],
30 | zoom: 15
31 | });
32 | if (props.pickupCoordinates) {
33 | addToMap(map, props.pickupCoordinates);
34 | }
35 | if (props.dropOffCoordinates) {
36 | addToMap(map, props.dropOffCoordinates);
37 | }
38 | if (props.pickupCoordinates && props.dropOffCoordinates) {
39 | map.fitBounds([props.dropOffCoordinates, props.pickupCoordinates, {}], {
40 | padding: 50,
41 | zoom: 10,
42 | });
43 | }
44 | }, [props.pickupCoordinates, props.dropOffCoordinates]);
45 |
46 | const addToMap = (map: any, coordinates: any) => {
47 | // Set marker options.
48 | const marker = new mapboxgl.Marker({
49 | color: "#568203",
50 | draggable: true,
51 | })
52 | .setLngLat(coordinates)
53 | .addTo(map);
54 | };
55 |
56 | // Pick Up & Drop Off Coordinates useEffect Displays props
57 | useEffect(() => {
58 | /*debug code */
59 | //console.log(props);
60 | //console.log(props.pickupCoordinates);
61 | //console.log(props.dropOffCoordinates);
62 | }, [props.pickupCoordinates, props.dropOffCoordinates]);
63 |
64 | //
65 |
66 | return (
67 |
72 | {name}
73 |
74 | );
75 | };
76 |
77 | export default Map;
78 |
--------------------------------------------------------------------------------
/src/components/WorkerSelector/WorkerSelector.scss:
--------------------------------------------------------------------------------
1 | .worker__time {
2 | color: green;
3 | }
4 |
5 | ion-toggle {
6 | --handle-box-shadow: 0 3px 12px rgba(255, 0, 0, 0.6),
7 | 0 3px 1px rgba(50, 70, 255, 0.6);
8 |
9 | overflow: visible;
10 |
11 | contain: none;
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/WorkerSelector/WorkerSelector.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IonAvatar,
3 | IonImg,
4 | IonItem,
5 | IonItemDivider,
6 | IonList,
7 | IonText,
8 | } from "@ionic/react";
9 | import React, { useState, useEffect } from "react";
10 | import "./WorkerSelector.scss";
11 | // Fake api data to test out functionality for now
12 | import workerList from "../../data/workerList";
13 |
14 | interface MapsProps {
15 | pickupCoordinates: number[];
16 | dropOffCoordinates: number[];
17 | }
18 |
19 | const WorkerSelector: React.FC = ({
20 | pickupCoordinates,
21 | dropOffCoordinates,
22 | }) => {
23 | // Make Sure Maps
24 | //console.log(pickupCoordinates,dropOffCoordinates)
25 | const [tripTime, setTripTime] = useState(0);
26 |
27 | useEffect(() => {
28 | const pickupdrive = `${pickupCoordinates[0]},${pickupCoordinates[1]}`;
29 | const dropoffdrive = `${dropOffCoordinates[0]},${dropOffCoordinates[1]}`;
30 | const access_token = `?access_token=pk.eyJ1Ijoib21hcmFzaHplaW5ob205OCIsImEiOiJjbDRrMXY5c3MwN3ZpM2NxcHp3ZGVmN3ZyIn0.3Ziuh7Utama_wz_4s8qh2g`;
31 | const apidirections = `https://api.mapbox.com/directions/v5/mapbox/driving/${pickupdrive};${dropoffdrive}${access_token}`;
32 | //console.log(`${htt}`)
33 |
34 | //get trip time from map box api
35 | //2pickup coordinates and 2 points for the dropoff location [x]
36 | const tripTime = fetch(`${[apidirections]}`)
37 | .then((response) => response.json())
38 | .then((data) => {
39 | setTripTime(data.routes[0].duration / 100);
40 | });
41 | //these are added to the dependency array to show the setTripTime dont remove or it will not be shown
42 | }, [pickupCoordinates, dropOffCoordinates]);
43 |
44 | return (
45 |
46 | {workerList.map((worker, index) => (
47 |
48 |
49 |
50 |
51 |
52 | {worker.service}
53 | 15 mins away
54 |
55 |
56 | {"$" + (tripTime * worker.multiplier).toFixed(2)}
57 |
58 |
59 | ))}
60 |
61 |
62 |
63 | );
64 | };
65 |
66 | export default WorkerSelector;
67 |
--------------------------------------------------------------------------------
/src/data/workerList.tsx:
--------------------------------------------------------------------------------
1 | const workerList = [
2 | {
3 | imgUrl:
4 | "https://res.cloudinary.com/dxgqvvg0z/image/upload/v1656005176/FIXITAPP/nextjs-app-images/confirm/worker-svgrepo-com_vipu1n.svg",
5 | service: "Worker",
6 | multiplier: 1,
7 | },
8 |
9 | {
10 | imgUrl:
11 | "https://res.cloudinary.com/dxgqvvg0z/image/upload/v1656005555/FIXITAPP/ionic-app-images/confirm/worker-svgrepo-com_5_d2cql2.svg",
12 | service: "Female Worker(Females Only)",
13 | multiplier: 1,
14 | },
15 |
16 | {
17 | imgUrl:
18 | "https://res.cloudinary.com/dxgqvvg0z/image/upload/v1656005313/FIXITAPP/ionic-app-images/confirm/worker-svgrepo-com_1_qldnrx.svg",
19 | service: "Worker+",
20 | multiplier: 1.5,
21 | },
22 | {
23 | imgUrl:
24 | "https://res.cloudinary.com/dxgqvvg0z/image/upload/v1656005176/FIXITAPP/nextjs-app-images/confirm/worker-svgrepo-com_3_efxhpc.svg",
25 | service: "Worker Female+",
26 | multiplier: 1.5,
27 | },
28 | {
29 | imgUrl:
30 | "https://res.cloudinary.com/dxgqvvg0z/image/upload/v1656005176/FIXITAPP/nextjs-app-images/confirm/worker-svgrepo-com_4_rl35jn.svg",
31 | service: "Multiple Workers rate increases by 1 to be split upon workers",
32 | multiplier: 2,
33 | },
34 | ];
35 |
36 | export default workerList;
37 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import * as serviceWorkerRegistration from './serviceWorkerRegistration';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 |
8 |
9 |
10 | ReactDOM.render(
11 |
12 |
13 | ,
14 | document.getElementById('root')
15 | );
16 |
17 | // If you want your app to work offline and load faster, you can change
18 | // unregister() to register() below. Note this comes with some pitfalls.
19 | // Learn more about service workers: https://cra.link/PWA
20 | serviceWorkerRegistration.unregister();
21 |
22 | // If you want to start measuring performance in your app, pass a function
23 | // to log results (for example: reportWebVitals(console.log))
24 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
25 | reportWebVitals();
26 |
--------------------------------------------------------------------------------
/src/pages/ConfirmTab/ConfirmTab.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cboy220/uberclone-ts-ionic-firebase/8f54725e74c4b3cb1ece251d583f702e1b7a0d56/src/pages/ConfirmTab/ConfirmTab.scss
--------------------------------------------------------------------------------
/src/pages/ConfirmTab/ConfirmTab.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IonBackButton,
3 | IonButton,
4 | IonButtons,
5 | IonContent,
6 | IonGrid,
7 | IonHeader,
8 | IonPage,
9 | IonRefresherContent,
10 | IonText,
11 | IonTitle,
12 | IonToolbar,
13 | } from "@ionic/react";
14 | import "./ConfirmTab.scss";
15 | import Map from "../../components/Map/Map";
16 | import { useEffect, useState } from "react";
17 | //
18 | //import { useRouter } from "../../utilities/useRouter/useRouter";
19 | import WorkerSelector from "../../components/WorkerSelector/WorkerSelector";
20 |
21 | interface MapsProps {
22 | name: string;
23 | pickupCoordinates: number[];
24 | dropOffCoordinates: number[];
25 | }
26 |
27 | const ConfirmTab: React.FC = () => {
28 | //Defined router from utils
29 | //const router = useRouter();
30 | // used query here as a string with window & @@params
31 | const queryString = window.location.search;
32 | const urlParams = new URLSearchParams(queryString);
33 | // Get the drop off and pickup
34 | const dropoff = urlParams.get("dropoff");
35 | const pickUp = urlParams.get("pickup");
36 | // Debug the drop off and
37 | //console.log("PickUp:", pickUp);
38 | //console.log("DropOff:", dropoff);
39 |
40 | //debug router
41 | //console.log(router);
42 |
43 | //debug props
44 | //console.log(props);
45 |
46 | const [pickupCoordinates, setPickupCoordinates] = useState([0,0]);
47 | const [dropOffCoordinates, setDropOffCoordinates] = useState([0,0]);
48 |
49 | //get PickupCoordinates Start
50 | const getPickUpCoordinates = () => {
51 | // Fetch Function
52 |
53 | fetch(
54 | `https://api.mapbox.com/geocoding/v5/mapbox.places/${pickUp}.json?` +
55 | new URLSearchParams({
56 | access_token:
57 | "pk.eyJ1Ijoib21hcmFzaHplaW5ob205OCIsImEiOiJjbDRrMXY5c3MwN3ZpM2NxcHp3ZGVmN3ZyIn0.3Ziuh7Utama_wz_4s8qh2g",
58 | })
59 | )
60 | .then((response) => response.json())
61 | .then((data) => {
62 | setPickupCoordinates(data.features[0].center);
63 | });
64 | };
65 | //get PickupCoordinates end
66 |
67 | // get DropOffCoordinates Start
68 | const getDropOffCoordinates = () => {
69 | // Fetch Function
70 | fetch(
71 | `https://api.mapbox.com/geocoding/v5/mapbox.places/${dropoff}.json?` +
72 | new URLSearchParams({
73 | access_token:
74 | "pk.eyJ1Ijoib21hcmFzaHplaW5ob205OCIsImEiOiJjbDRrMXY5c3MwN3ZpM2NxcHp3ZGVmN3ZyIn0.3Ziuh7Utama_wz_4s8qh2g",
75 | })
76 | )
77 | .then((response) => response.json())
78 | .then((data) => {
79 | setDropOffCoordinates(data.features[0].center);
80 | });
81 | };
82 | //debug useState
83 | //console.log(pickupCoordinates, dropOffCoordinates);
84 | //call functions with useEffect
85 |
86 | useEffect(() => {
87 | getPickUpCoordinates();
88 | getDropOffCoordinates();
89 | //console.log(pickUp, dropoff);
90 | /**
91 | * return () => {
92 | ;
93 | };
94 | */
95 | }, [pickUp, dropoff]);
96 |
97 | return (
98 |
99 |
100 |
101 | Confirm
102 |
103 |
104 |
105 |
106 |
107 |
111 |
112 |
113 | Confirm
114 |
115 |
116 |
117 |
122 |
123 |
124 | {/**Worker Selector */}
125 |
126 | Confirm Worker
127 |
128 |
132 | {/**Confirm Button */}
133 |
134 | Confirm Worker Reservation
135 |
136 |
137 |
138 |
139 | );
140 | };
141 |
142 | export default ConfirmTab;
143 |
--------------------------------------------------------------------------------
/src/pages/GetStarted/GetStarted.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cboy220/uberclone-ts-ionic-firebase/8f54725e74c4b3cb1ece251d583f702e1b7a0d56/src/pages/GetStarted/GetStarted.scss
--------------------------------------------------------------------------------
/src/pages/GetStarted/GetStarted.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./GetStarted.scss";
3 |
4 | const GetStarted = () => {
5 | return GetStarted
;
6 | };
7 |
8 | export default GetStarted;
9 |
--------------------------------------------------------------------------------
/src/pages/HomeTab/HomeTab.scss:
--------------------------------------------------------------------------------
1 | .fixit__header__logo {
2 | width: 100%;
3 | height: 100%;
4 | max-height: 150px;
5 | max-width: 150px;
6 | }
7 |
8 | .fixit__header__avatar {
9 | background-color: #000000;
10 | background-image: linear-gradient(315deg, #000000 0%, #414141 74%);
11 | border-radius: 28px;
12 | width: 100%;
13 | height: 100%;
14 | max-width: 150px;
15 | max-height: 150px;
16 | padding-bottom: 1px;
17 |
18 | cursor: pointer;
19 | }
20 |
--------------------------------------------------------------------------------
/src/pages/HomeTab/HomeTab.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IonAvatar,
3 | IonButton,
4 | IonCard,
5 | IonCardContent,
6 | IonCardSubtitle,
7 | IonCol,
8 | IonContent,
9 | IonGrid,
10 | IonHeader,
11 | IonImg,
12 | IonPage,
13 | IonRouterLink,
14 | IonRow,
15 | IonTitle,
16 | IonToolbar,
17 | } from "@ionic/react";
18 | //Scss
19 | import "./HomeTab.scss";
20 | //Components
21 | import Map from "../../components/Map/Map";
22 | //Custom function useRouter like next js
23 | import { useRouter } from "../../utilities/useRouter/useRouter";
24 |
25 | //React
26 | import { useState, useEffect } from "react";
27 | import { useHistory } from "react-router";
28 |
29 | //FireBase
30 | import { onAuthStateChanged, signOut, User } from "firebase/auth";
31 | //FireBase File in root folder
32 | import { auth } from "../../Firebase";
33 |
34 | interface MapsProps {
35 | pickupCoordinates: number[];
36 | dropOffCoordinates: number[];
37 | username: string;
38 | password: string;
39 | prevState: null;
40 | name: string;
41 | }
42 |
43 | const HomeTab: React.FC = () => {
44 | const [user, setUser] = useState(null);
45 |
46 | console.log(user);
47 | console.log(setUser);
48 |
49 | useEffect(() => {
50 | return onAuthStateChanged(auth, (user) => {
51 | if (user != null) {
52 | //history.push('/');
53 | setUser({
54 | name: user.displayName,
55 | photoUrl: user.photoURL,
56 | });
57 | } else {
58 | setUser(null);
59 | //history.push("/login");
60 | }
61 | });
62 | }, []);
63 |
64 | return (
65 |
66 |
67 |
68 | Home
69 |
70 |
71 |
72 |
77 | {/**Header */}
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | Worker
87 |
88 |
89 | {/**{user && user.phototUrl} */}
90 | signOut(auth)}
93 | />
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | Profession
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | Reserve
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | Where to?
133 |
134 |
135 |
136 |
137 |
138 | {/**Action Btns */}
139 |
140 | {/**input btn */}
141 |
142 |
143 |
144 |
145 |
146 |
147 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | {user && user.name}
163 |
164 |
165 |
166 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | );
179 | };
180 |
181 | export default HomeTab;
182 |
--------------------------------------------------------------------------------
/src/pages/LoginTab/Login.scss:
--------------------------------------------------------------------------------
1 | .login__icon{
2 | max-width: 75px;
3 | max-height: 75px;
4 | width: 100%;
5 | height: 100%;
6 | object-fit: contain;
7 | }
8 |
9 |
10 |
11 | .login__svg{
12 | max-height: 350px;
13 | height: 100%;
14 | width: 100%;
15 | border-radius: 8px;
16 | justify-content: center;
17 | left: 50%;
18 | margin-bottom: 0.4rem;
19 | }
--------------------------------------------------------------------------------
/src/pages/LoginTab/Login.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IonButton,
3 | IonCol,
4 | IonContent,
5 | IonGrid,
6 | IonHeader,
7 | IonImg,
8 | IonPage,
9 | IonRouterLink,
10 | IonRow,
11 | IonTitle,
12 | IonToolbar,
13 | } from "@ionic/react";
14 | import React, { useEffect } from "react";
15 | import "./Login.scss";
16 | //Custom use router
17 | import { useRouter } from "../../utilities/useRouter/useRouter";
18 | //Sign with popup imported directly from firebase auth
19 | import { signInWithPopup, onAuthStateChanged, getAuth } from "firebase/auth";
20 | //from Firebase.tsx file in root folder
21 | import { auth, provider } from "../../Firebase";
22 | import { useHistory } from "react-router";
23 | //
24 |
25 | const Login = () => {
26 | const history = useHistory();
27 | console.log(history);
28 |
29 | useEffect(() => {
30 | return onAuthStateChanged(auth, (user) => {
31 | if (user) {
32 | history.push("/");
33 | setUser({
34 | name: user.displayName,
35 | photoUrl: user.photoURL,
36 | });
37 | }
38 | });
39 | });
40 |
41 | return (
42 |
43 |
44 |
45 |
46 | Login
47 |
48 |
49 |
50 |
51 |
52 |
56 |
57 |
58 |
64 |
65 |
66 | signInWithPopup(auth, provider)}
70 | >
71 | Sign in with Google
72 |
73 |
74 |
75 |
76 | Forgot Password ?
77 |
78 |
79 |
80 | Register account
81 |
82 |
83 |
84 |
85 |
86 |
87 | );
88 | };
89 |
90 | export default Login;
91 |
92 |
93 | function setUser(arg0: { name: string | null; photoUrl: string | null }) {
94 | throw new Error("Function not implemented.");
95 | }
96 |
--------------------------------------------------------------------------------
/src/pages/RootTab/RootTab.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Redirect, Route } from "react-router-dom";
3 | import {
4 | IonIcon,
5 | IonLabel,
6 | IonRouterOutlet,
7 | IonTabBar,
8 | IonTabButton,
9 | IonTabs,
10 | } from "@ionic/react";
11 | import { search, map, checkmarkDone, logInSharp } from "ionicons/icons";
12 | import HomeTab from "../HomeTab/HomeTab";
13 | import SearchTab from "../SearchTab/SearchTab";
14 | import ConfirmTab from "../ConfirmTab/ConfirmTab";
15 | import Login from "../LoginTab/Login";
16 |
17 | // import Tab1Detail from './Tab1Detail';
18 |
19 | const RootTab: React.FC = () => (
20 |
21 |
22 |
23 |
24 |
25 | {/**Change Login tab to a sidemenu item */}
26 | }
29 | exact={true}
30 | />
31 | }
34 | exact={true}
35 | />
36 |
37 |
38 |
39 | {/**Change Login tab to a sidemenu item */}
40 |
41 |
42 |
43 |
44 |
45 | Home
46 |
47 |
48 |
49 | Search
50 |
51 |
52 |
53 | Confirm
54 |
55 |
56 |
57 | );
58 |
59 | export default React.memo(RootTab);
60 |
--------------------------------------------------------------------------------
/src/pages/SearchTab/SearchTab.scss:
--------------------------------------------------------------------------------
1 | .search__ion__input {
2 | background: gainsboro;
3 | width: 100%;
4 | max-width: 100%;
5 | margin-bottom: 0.2rem;
6 | font-size: 1rem;
7 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
8 | transition: ease-in-out 0.9s;
9 | margin-top: 0.5rem;
10 | }
11 |
12 | .search__ion__input:hover {
13 | background: rgb(32, 156, 1);
14 | }
15 |
16 | .search__ion__line__icon {
17 | max-width: 15px;
18 | width: 100%;
19 | height: 100%;
20 | max-height: 25px;
21 | }
22 |
23 | /*Icons*/
24 | .circle__ion__icon {
25 | size: 1.5rem;
26 | color: blue;
27 | transition: 0.9s ease-in-out;
28 | }
29 |
30 | .circle__ion__icon__success {
31 | size: 1.5rem;
32 | color: green;
33 | transition: 0.9s ease-in-out;
34 | }
35 |
36 | .circle__ion__icon:hover {
37 | transform: scale(1.5);
38 | }
39 |
40 | .circle__ion__icon__success:hover {
41 | transform: scale(1.5);
42 | }
43 |
44 | .add__ion__icon {
45 | size: 3rem;
46 | margin-top: 1.5rem;
47 | font-size: 3rem;
48 | padding: 0.5rem;
49 | width: 100%;
50 | height: 100%;
51 | max-height: 35px;
52 | max-width: 35px;
53 | transition: 0.9s ease-in-out;
54 | }
55 |
56 | .add__ion__icon:hover {
57 | transform: scale(1.5);
58 | }
59 |
60 | .line__ion__icon {
61 | padding-right: 3rem;
62 | width: 100%;
63 | height: 100%;
64 | max-height: 35px;
65 | max-width: 150px;
66 | color: black;
67 | }
68 |
--------------------------------------------------------------------------------
/src/pages/SearchTab/SearchTab.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IonBackButton,
3 | IonButton,
4 | IonButtons,
5 | IonCol,
6 | IonContent,
7 | IonGrid,
8 | IonHeader,
9 | IonIcon,
10 | IonImg,
11 | IonInput,
12 | IonItemDivider,
13 | IonPage,
14 | IonRow,
15 | IonTitle,
16 | IonToolbar,
17 | } from "@ionic/react";
18 | import {
19 | addCircleOutline,
20 | ellipsisHorizontalCircle,
21 | ellipsisHorizontalCircleOutline,
22 | star,
23 | } from "ionicons/icons";
24 | import "./SearchTab.scss";
25 |
26 | //React imports
27 | import React from "react";
28 | import { useState } from "react";
29 | //import { useRouter } from "../../utilities/useRouter/useRouter";
30 |
31 | //React Router Dom
32 | import { Link } from "react-router-dom";
33 |
34 | interface MapsProps {
35 | name: string;
36 | }
37 |
38 | const SearchTab: React.FC = (/*props*/) => {
39 | //debug router
40 | //const router = useRouter();
41 | //console.log(router);
42 |
43 | //debug props
44 | //console.log(props);
45 |
46 | const [homeAddrrQuery, setHomeAddrQuery] = useState("");
47 | const [workerAddrrQuery, setWorkerAddrQuery] = useState("");
48 |
49 | console.log(homeAddrrQuery, workerAddrrQuery);
50 | console.log(setHomeAddrQuery);
51 | console.log(setWorkerAddrQuery);
52 |
53 | // Set location const for home and worker addresses.
54 | const location = {
55 | pathname: `/tabs/confirm?pickup=${homeAddrrQuery}&dropoff=${workerAddrrQuery}`,
56 | state: {
57 | query: {
58 | pickup: homeAddrrQuery,
59 | dropoff: workerAddrrQuery,
60 | },
61 | },
62 | staticContext: true,
63 | };
64 |
65 | class LocationLink extends React.Component {
66 | render() {
67 | return (
68 |
69 |
70 | Confirm worker reservation
71 |
72 |
73 | );
74 | }
75 | }
76 |
77 | return (
78 |
79 |
80 |
81 | Search
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
97 |
98 |
102 |
103 |
108 |
109 |
110 |
111 |
116 | setHomeAddrQuery(event.detail.value!)
117 | }
118 | />
119 |
120 |
125 | setWorkerAddrQuery(event.detail.value!)
126 | }
127 | />
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | Saved Places
136 |
137 |
138 |
139 | {/**Confirm WorkerReservation Link */}
140 |
141 | {/**Confirm WorkerReservation Link */}
142 |
143 |
144 |
145 | );
146 | };
147 |
148 | export default SearchTab;
149 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/src/service-worker.ts:
--------------------------------------------------------------------------------
1 | ///
2 | /* eslint-disable no-restricted-globals */
3 |
4 | // This service worker can be customized!
5 | // See https://developers.google.com/web/tools/workbox/modules
6 | // for the list of available Workbox modules, or add any other
7 | // code you'd like.
8 | // You can also remove this file if you'd prefer not to use a
9 | // service worker, and the Workbox build step will be skipped.
10 |
11 | import { clientsClaim } from 'workbox-core';
12 | import { ExpirationPlugin } from 'workbox-expiration';
13 | import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
14 | import { registerRoute } from 'workbox-routing';
15 | import { StaleWhileRevalidate } from 'workbox-strategies';
16 |
17 | declare const self: ServiceWorkerGlobalScope;
18 |
19 | clientsClaim();
20 |
21 | // Precache all of the assets generated by your build process.
22 | // Their URLs are injected into the manifest variable below.
23 | // This variable must be present somewhere in your service worker file,
24 | // even if you decide not to use precaching. See https://cra.link/PWA
25 | precacheAndRoute(self.__WB_MANIFEST);
26 |
27 | // Set up App Shell-style routing, so that all navigation requests
28 | // are fulfilled with your index.html shell. Learn more at
29 | // https://developers.google.com/web/fundamentals/architecture/app-shell
30 | const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$');
31 | registerRoute(
32 | // Return false to exempt requests from being fulfilled by index.html.
33 | ({ request, url }: { request: Request; url: URL }) => {
34 | // If this isn't a navigation, skip.
35 | if (request.mode !== 'navigate') {
36 | return false;
37 | }
38 |
39 | // If this is a URL that starts with /_, skip.
40 | if (url.pathname.startsWith('/_')) {
41 | return false;
42 | }
43 |
44 | // If this looks like a URL for a resource, because it contains
45 | // a file extension, skip.
46 | if (url.pathname.match(fileExtensionRegexp)) {
47 | return false;
48 | }
49 |
50 | // Return true to signal that we want to use the handler.
51 | return true;
52 | },
53 | createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
54 | );
55 |
56 | // An example runtime caching route for requests that aren't handled by the
57 | // precache, in this case same-origin .png requests like those from in public/
58 | registerRoute(
59 | // Add in any other file extensions or routing criteria as needed.
60 | ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'),
61 | // Customize this strategy as needed, e.g., by changing to CacheFirst.
62 | new StaleWhileRevalidate({
63 | cacheName: 'images',
64 | plugins: [
65 | // Ensure that once this runtime cache reaches a maximum size the
66 | // least-recently used images are removed.
67 | new ExpirationPlugin({ maxEntries: 50 }),
68 | ],
69 | })
70 | );
71 |
72 | // This allows the web app to trigger skipWaiting via
73 | // registration.waiting.postMessage({type: 'SKIP_WAITING'})
74 | self.addEventListener('message', (event) => {
75 | if (event.data && event.data.type === 'SKIP_WAITING') {
76 | self.skipWaiting();
77 | }
78 | });
79 |
80 | // Any other custom service worker logic can go here.
81 |
--------------------------------------------------------------------------------
/src/serviceWorkerRegistration.ts:
--------------------------------------------------------------------------------
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://cra.link/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.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
19 | );
20 |
21 | type Config = {
22 | onSuccess?: (registration: ServiceWorkerRegistration) => void;
23 | onUpdate?: (registration: ServiceWorkerRegistration) => void;
24 | };
25 |
26 | export function register(config?: Config) {
27 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
28 | // The URL constructor is available in all browsers that support SW.
29 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
30 | if (publicUrl.origin !== window.location.origin) {
31 | // Our service worker won't work if PUBLIC_URL is on a different origin
32 | // from what our page is served on. This might happen if a CDN is used to
33 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
34 | return;
35 | }
36 |
37 | window.addEventListener('load', () => {
38 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
39 |
40 | if (isLocalhost) {
41 | // This is running on localhost. Let's check if a service worker still exists or not.
42 | checkValidServiceWorker(swUrl, config);
43 |
44 | // Add some additional logging to localhost, pointing developers to the
45 | // service worker/PWA documentation.
46 | navigator.serviceWorker.ready.then(() => {
47 | console.log(
48 | 'This web app is being served cache-first by a service ' +
49 | 'worker. To learn more, visit https://cra.link/PWA'
50 | );
51 | });
52 | } else {
53 | // Is not localhost. Just register service worker
54 | registerValidSW(swUrl, config);
55 | }
56 | });
57 | }
58 | }
59 |
60 | function registerValidSW(swUrl: string, config?: Config) {
61 | navigator.serviceWorker
62 | .register(swUrl)
63 | .then((registration) => {
64 | registration.onupdatefound = () => {
65 | const installingWorker = registration.installing;
66 | if (installingWorker == null) {
67 | return;
68 | }
69 | installingWorker.onstatechange = () => {
70 | if (installingWorker.state === 'installed') {
71 | if (navigator.serviceWorker.controller) {
72 | // At this point, the updated precached content has been fetched,
73 | // but the previous service worker will still serve the older
74 | // content until all client tabs are closed.
75 | console.log(
76 | 'New content is available and will be used when all ' +
77 | 'tabs for this page are closed. See https://cra.link/PWA.'
78 | );
79 |
80 | // Execute callback
81 | if (config && config.onUpdate) {
82 | config.onUpdate(registration);
83 | }
84 | } else {
85 | // At this point, everything has been precached.
86 | // It's the perfect time to display a
87 | // "Content is cached for offline use." message.
88 | console.log('Content is cached for offline use.');
89 |
90 | // Execute callback
91 | if (config && config.onSuccess) {
92 | config.onSuccess(registration);
93 | }
94 | }
95 | }
96 | };
97 | };
98 | })
99 | .catch((error) => {
100 | console.error('Error during service worker registration:', error);
101 | });
102 | }
103 |
104 | function checkValidServiceWorker(swUrl: string, config?: Config) {
105 | // Check if the service worker can be found. If it can't reload the page.
106 | fetch(swUrl, {
107 | headers: { 'Service-Worker': 'script' },
108 | })
109 | .then((response) => {
110 | // Ensure service worker exists, and that we really are getting a JS file.
111 | const contentType = response.headers.get('content-type');
112 | if (
113 | response.status === 404 ||
114 | (contentType != null && contentType.indexOf('javascript') === -1)
115 | ) {
116 | // No service worker found. Probably a different app. Reload the page.
117 | navigator.serviceWorker.ready.then((registration) => {
118 | registration.unregister().then(() => {
119 | window.location.reload();
120 | });
121 | });
122 | } else {
123 | // Service worker found. Proceed as normal.
124 | registerValidSW(swUrl, config);
125 | }
126 | })
127 | .catch(() => {
128 | console.log('No internet connection found. App is running in offline mode.');
129 | });
130 | }
131 |
132 | export function unregister() {
133 | if ('serviceWorker' in navigator) {
134 | navigator.serviceWorker.ready
135 | .then((registration) => {
136 | registration.unregister();
137 | })
138 | .catch((error) => {
139 | console.error(error.message);
140 | });
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
7 | // Mock matchmedia
8 | window.matchMedia = window.matchMedia || function() {
9 | return {
10 | matches: false,
11 | addListener: function() {},
12 | removeListener: function() {}
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/src/theme/variables.scss:
--------------------------------------------------------------------------------
1 | /* Ionic Variables and Theming. For more info, please see:
2 | http://ionicframework.com/docs/theming/ */
3 |
4 | /** Ionic CSS Variables **/
5 | :root {
6 | /** primary **/
7 | --ion-color-primary: #3880ff;
8 | --ion-color-primary-rgb: 56, 128, 255;
9 | --ion-color-primary-contrast: #ffffff;
10 | --ion-color-primary-contrast-rgb: 255, 255, 255;
11 | --ion-color-primary-shade: #3171e0;
12 | --ion-color-primary-tint: #4c8dff;
13 |
14 | /** secondary **/
15 | --ion-color-secondary: #3dc2ff;
16 | --ion-color-secondary-rgb: 61, 194, 255;
17 | --ion-color-secondary-contrast: #ffffff;
18 | --ion-color-secondary-contrast-rgb: 255, 255, 255;
19 | --ion-color-secondary-shade: #36abe0;
20 | --ion-color-secondary-tint: #50c8ff;
21 |
22 | /** tertiary **/
23 | --ion-color-tertiary: #5260ff;
24 | --ion-color-tertiary-rgb: 82, 96, 255;
25 | --ion-color-tertiary-contrast: #ffffff;
26 | --ion-color-tertiary-contrast-rgb: 255, 255, 255;
27 | --ion-color-tertiary-shade: #4854e0;
28 | --ion-color-tertiary-tint: #6370ff;
29 |
30 | /** success **/
31 | --ion-color-success: #2dd36f;
32 | --ion-color-success-rgb: 45, 211, 111;
33 | --ion-color-success-contrast: #ffffff;
34 | --ion-color-success-contrast-rgb: 255, 255, 255;
35 | --ion-color-success-shade: #28ba62;
36 | --ion-color-success-tint: #42d77d;
37 |
38 | /** warning **/
39 | --ion-color-warning: #ffc409;
40 | --ion-color-warning-rgb: 255, 196, 9;
41 | --ion-color-warning-contrast: #000000;
42 | --ion-color-warning-contrast-rgb: 0, 0, 0;
43 | --ion-color-warning-shade: #e0ac08;
44 | --ion-color-warning-tint: #ffca22;
45 |
46 | /** danger **/
47 | --ion-color-danger: #eb445a;
48 | --ion-color-danger-rgb: 235, 68, 90;
49 | --ion-color-danger-contrast: #ffffff;
50 | --ion-color-danger-contrast-rgb: 255, 255, 255;
51 | --ion-color-danger-shade: #cf3c4f;
52 | --ion-color-danger-tint: #ed576b;
53 |
54 | /** dark **/
55 | --ion-color-dark: #222428;
56 | --ion-color-dark-rgb: 34, 36, 40;
57 | --ion-color-dark-contrast: #ffffff;
58 | --ion-color-dark-contrast-rgb: 255, 255, 255;
59 | --ion-color-dark-shade: #1e2023;
60 | --ion-color-dark-tint: #383a3e;
61 |
62 | /** medium **/
63 | --ion-color-medium: #92949c;
64 | --ion-color-medium-rgb: 146, 148, 156;
65 | --ion-color-medium-contrast: #ffffff;
66 | --ion-color-medium-contrast-rgb: 255, 255, 255;
67 | --ion-color-medium-shade: #808289;
68 | --ion-color-medium-tint: #9d9fa6;
69 |
70 | /** light **/
71 | --ion-color-light: #f4f5f8;
72 | --ion-color-light-rgb: 244, 245, 248;
73 | --ion-color-light-contrast: #000000;
74 | --ion-color-light-contrast-rgb: 0, 0, 0;
75 | --ion-color-light-shade: #d7d8da;
76 | --ion-color-light-tint: #f5f6f9;
77 | }
78 |
79 | @media (prefers-color-scheme: dark) {
80 | /*
81 | * Dark Colors
82 | * -------------------------------------------
83 | */
84 |
85 | body {
86 | --ion-color-primary: #428cff;
87 | --ion-color-primary-rgb: 66,140,255;
88 | --ion-color-primary-contrast: #ffffff;
89 | --ion-color-primary-contrast-rgb: 255,255,255;
90 | --ion-color-primary-shade: #3a7be0;
91 | --ion-color-primary-tint: #5598ff;
92 |
93 | --ion-color-secondary: #50c8ff;
94 | --ion-color-secondary-rgb: 80,200,255;
95 | --ion-color-secondary-contrast: #ffffff;
96 | --ion-color-secondary-contrast-rgb: 255,255,255;
97 | --ion-color-secondary-shade: #46b0e0;
98 | --ion-color-secondary-tint: #62ceff;
99 |
100 | --ion-color-tertiary: #6a64ff;
101 | --ion-color-tertiary-rgb: 106,100,255;
102 | --ion-color-tertiary-contrast: #ffffff;
103 | --ion-color-tertiary-contrast-rgb: 255,255,255;
104 | --ion-color-tertiary-shade: #5d58e0;
105 | --ion-color-tertiary-tint: #7974ff;
106 |
107 | --ion-color-success: #2fdf75;
108 | --ion-color-success-rgb: 47,223,117;
109 | --ion-color-success-contrast: #000000;
110 | --ion-color-success-contrast-rgb: 0,0,0;
111 | --ion-color-success-shade: #29c467;
112 | --ion-color-success-tint: #44e283;
113 |
114 | --ion-color-warning: #ffd534;
115 | --ion-color-warning-rgb: 255,213,52;
116 | --ion-color-warning-contrast: #000000;
117 | --ion-color-warning-contrast-rgb: 0,0,0;
118 | --ion-color-warning-shade: #e0bb2e;
119 | --ion-color-warning-tint: #ffd948;
120 |
121 | --ion-color-danger: #ff4961;
122 | --ion-color-danger-rgb: 255,73,97;
123 | --ion-color-danger-contrast: #ffffff;
124 | --ion-color-danger-contrast-rgb: 255,255,255;
125 | --ion-color-danger-shade: #e04055;
126 | --ion-color-danger-tint: #ff5b71;
127 |
128 | --ion-color-dark: #f4f5f8;
129 | --ion-color-dark-rgb: 244,245,248;
130 | --ion-color-dark-contrast: #000000;
131 | --ion-color-dark-contrast-rgb: 0,0,0;
132 | --ion-color-dark-shade: #d7d8da;
133 | --ion-color-dark-tint: #f5f6f9;
134 |
135 | --ion-color-medium: #989aa2;
136 | --ion-color-medium-rgb: 152,154,162;
137 | --ion-color-medium-contrast: #000000;
138 | --ion-color-medium-contrast-rgb: 0,0,0;
139 | --ion-color-medium-shade: #86888f;
140 | --ion-color-medium-tint: #a2a4ab;
141 |
142 | --ion-color-light: #222428;
143 | --ion-color-light-rgb: 34,36,40;
144 | --ion-color-light-contrast: #ffffff;
145 | --ion-color-light-contrast-rgb: 255,255,255;
146 | --ion-color-light-shade: #1e2023;
147 | --ion-color-light-tint: #383a3e;
148 | }
149 |
150 | /*
151 | * iOS Dark Theme
152 | * -------------------------------------------
153 | */
154 |
155 | .ios body {
156 | --ion-background-color: #000000;
157 | --ion-background-color-rgb: 0,0,0;
158 |
159 | --ion-text-color: #ffffff;
160 | --ion-text-color-rgb: 255,255,255;
161 |
162 | --ion-color-step-50: #0d0d0d;
163 | --ion-color-step-100: #1a1a1a;
164 | --ion-color-step-150: #262626;
165 | --ion-color-step-200: #333333;
166 | --ion-color-step-250: #404040;
167 | --ion-color-step-300: #4d4d4d;
168 | --ion-color-step-350: #595959;
169 | --ion-color-step-400: #666666;
170 | --ion-color-step-450: #737373;
171 | --ion-color-step-500: #808080;
172 | --ion-color-step-550: #8c8c8c;
173 | --ion-color-step-600: #999999;
174 | --ion-color-step-650: #a6a6a6;
175 | --ion-color-step-700: #b3b3b3;
176 | --ion-color-step-750: #bfbfbf;
177 | --ion-color-step-800: #cccccc;
178 | --ion-color-step-850: #d9d9d9;
179 | --ion-color-step-900: #e6e6e6;
180 | --ion-color-step-950: #f2f2f2;
181 |
182 | --ion-item-background: #000000;
183 |
184 | --ion-card-background: #1c1c1d;
185 | }
186 |
187 | .ios ion-modal {
188 | --ion-background-color: var(--ion-color-step-100);
189 | --ion-toolbar-background: var(--ion-color-step-150);
190 | --ion-toolbar-border-color: var(--ion-color-step-250);
191 | }
192 |
193 |
194 | /*
195 | * Material Design Dark Theme
196 | * -------------------------------------------
197 | */
198 |
199 | .md body {
200 | --ion-background-color: #121212;
201 | --ion-background-color-rgb: 18,18,18;
202 |
203 | --ion-text-color: #ffffff;
204 | --ion-text-color-rgb: 255,255,255;
205 |
206 | --ion-border-color: #222222;
207 |
208 | --ion-color-step-50: #1e1e1e;
209 | --ion-color-step-100: #2a2a2a;
210 | --ion-color-step-150: #363636;
211 | --ion-color-step-200: #414141;
212 | --ion-color-step-250: #4d4d4d;
213 | --ion-color-step-300: #595959;
214 | --ion-color-step-350: #656565;
215 | --ion-color-step-400: #717171;
216 | --ion-color-step-450: #7d7d7d;
217 | --ion-color-step-500: #898989;
218 | --ion-color-step-550: #949494;
219 | --ion-color-step-600: #a0a0a0;
220 | --ion-color-step-650: #acacac;
221 | --ion-color-step-700: #b8b8b8;
222 | --ion-color-step-750: #c4c4c4;
223 | --ion-color-step-800: #d0d0d0;
224 | --ion-color-step-850: #dbdbdb;
225 | --ion-color-step-900: #e7e7e7;
226 | --ion-color-step-950: #f3f3f3;
227 |
228 | --ion-item-background: #1e1e1e;
229 |
230 | --ion-toolbar-background: #1f1f1f;
231 |
232 | --ion-tab-bar-background: #1f1f1f;
233 |
234 | --ion-card-background: #1e1e1e;
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/utilities/useRouter/useRouter.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { RouteComponentProps } from "react-router";
3 | import { __RouterContext } from "react-router";
4 |
5 |
6 | const queryString = window.location.search;
7 | const urlParams = new URLSearchParams(queryString);
8 | const code = urlParams.get('code')
9 |
10 | export const useRouter = () => {
11 | return useContext(__RouterContext) as RouteComponentProps;
12 | };
13 |
14 | //https://codesandbox.io/s/3rwq8r85p?file=/src/useRouter.ts
15 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------