├── .gitignore
├── .vscode
└── launch.json
├── README.md
├── images.d.ts
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── schema.json
├── src
├── Components
│ ├── AddressBar
│ │ ├── AddressBar.tsx
│ │ └── index.ts
│ ├── App
│ │ ├── AppContainer.tsx
│ │ ├── AppPresenter.tsx
│ │ ├── AppQueries.local.ts
│ │ └── index.ts
│ ├── BackArrow
│ │ ├── BackArrow.tsx
│ │ └── index.ts
│ ├── Button
│ │ ├── Button.tsx
│ │ └── index.ts
│ ├── Form
│ │ ├── Form.tsx
│ │ └── index.ts
│ ├── Header
│ │ ├── Header.tsx
│ │ └── index.ts
│ ├── Input
│ │ ├── Input.tsx
│ │ └── index.ts
│ ├── Menu
│ │ ├── MenuContainer.tsx
│ │ ├── MenuPresenter.tsx
│ │ ├── MenuQueries.ts
│ │ └── index.ts
│ ├── Message
│ │ ├── Message.tsx
│ │ └── index.ts
│ ├── PhotoInput
│ │ ├── PhotoInput.tsx
│ │ └── index.ts
│ ├── Place
│ │ ├── PlaceContainer.tsx
│ │ ├── PlacePresenter.tsx
│ │ ├── PlaceQueries.ts
│ │ └── index.ts
│ └── RidePopUp
│ │ ├── RidePopUp.tsx
│ │ └── index.ts
├── Routes
│ ├── AddPlace
│ │ ├── AddPlaceContainer.tsx
│ │ ├── AddPlacePresenter.tsx
│ │ ├── AddPlaceQuery.ts
│ │ └── index.tsx
│ ├── Chat
│ │ ├── ChatContainer.tsx
│ │ ├── ChatPresenter.tsx
│ │ ├── ChatQueries.ts
│ │ └── index.ts
│ ├── EditAccount
│ │ ├── EditAccountContainer.tsx
│ │ ├── EditAccountPresenter.tsx
│ │ ├── EditAccountQueries.ts
│ │ └── index.tsx
│ ├── FindAddress
│ │ ├── FindAddressContainer.tsx
│ │ ├── FindAddressPresenter.tsx
│ │ └── index.tsx
│ ├── Home
│ │ ├── HomeContainer.tsx
│ │ ├── HomePresenter.tsx
│ │ ├── HomeQueries.ts
│ │ └── index.tsx
│ ├── Login
│ │ ├── LoginPresenter.tsx
│ │ └── index.tsx
│ ├── PhoneLogin
│ │ ├── PhoneLoginContainer.tsx
│ │ ├── PhoneLoginPresenter.tsx
│ │ ├── PhoneQueries.ts
│ │ └── index.tsx
│ ├── Places
│ │ ├── PlacesContainer.tsx
│ │ ├── PlacesPresenter.tsx
│ │ └── index.tsx
│ ├── Ride
│ │ ├── RideContainer.tsx
│ │ ├── RidePresenter.tsx
│ │ ├── RideQueries.ts
│ │ └── index.tsx
│ ├── Settings
│ │ ├── SettingsContainer.tsx
│ │ ├── SettingsPresenter.tsx
│ │ └── index.tsx
│ ├── SocialLogin
│ │ ├── SocialLoginContainer.tsx
│ │ ├── SocialLoginPresenter.tsx
│ │ ├── SocialLoginQueries.ts
│ │ └── index.tsx
│ └── VerifyPhone
│ │ ├── VerifyPhoneContainer.tsx
│ │ ├── VerifyPhonePresenter.tsx
│ │ ├── VerifyPhoneQueries.ts
│ │ └── index.tsx
├── apollo.ts
├── countries.ts
├── global-styles.ts
├── images
│ └── bg.png
├── index.tsx
├── keys.ts
├── mapHelpers.ts
├── sharedQueries.local.ts
├── sharedQueries.ts
├── theme.ts
├── typed-components.ts
└── types
│ └── api.d.ts
├── tsconfig.json
├── tsconfig.prod.json
├── tsconfig.test.json
├── tslint.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/start"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nuber Client
2 |
3 | Client for the (N)Uber Clone Course on Nomad Academy. ReactJS, Apollo, Typescript
4 |
5 | ## Screens:
6 |
7 | ### Logged Out:
8 |
9 | - [x] Home
10 | - [x] Phone Login
11 | - [x] Verify Phone Number
12 | - [x] Social Login
13 |
14 | ### Logged In:
15 |
16 | - [x] Home
17 | - [x] Ride
18 | - [x] Chat
19 | - [x] Edit Account
20 | - [x] Settings
21 | - [x] Places
22 | - [x] Add Place
23 | - [x] Find Address
24 | - [ ] Challenge: Ride History
25 | - [ ] Challenge: Email Sign In
26 |
--------------------------------------------------------------------------------
/images.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.svg'
2 | declare module '*.png'
3 | declare module '*.jpg'
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nuber-client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@types/react-router-dom": "^4.3.0",
7 | "apollo-boost": "^0.1.12",
8 | "apollo-cache-inmemory": "^1.2.7",
9 | "apollo-client": "^2.3.8",
10 | "apollo-link": "^1.2.2",
11 | "apollo-link-error": "^1.1.0",
12 | "apollo-link-http": "^1.5.4",
13 | "apollo-link-state": "^0.4.1",
14 | "apollo-link-ws": "^1.0.8",
15 | "apollo-utilities": "^1.0.18",
16 | "axios": "^0.18.0",
17 | "google-maps-react": "^2.0.2",
18 | "graphql": "^0.13.2",
19 | "prop-types": "^15.6.2",
20 | "react": "^16.4.2",
21 | "react-apollo": "^2.1.9",
22 | "react-dom": "^16.4.2",
23 | "react-facebook-login": "^4.0.1",
24 | "react-helmet": "^5.2.0",
25 | "react-router-dom": "^4.3.1",
26 | "react-scripts-ts": "2.17.0",
27 | "react-sidebar": "^3.0.2",
28 | "react-toastify": "^4.1.0",
29 | "styled-components": "^3.4.2",
30 | "styled-reset": "^1.3.6",
31 | "subscriptions-transport-ws": "^0.9.14"
32 | },
33 | "scripts": {
34 | "start": "react-scripts-ts start",
35 | "build": "react-scripts-ts build",
36 | "test": "react-scripts-ts test --env=jsdom",
37 | "eject": "react-scripts-ts eject",
38 | "precodegen": "apollo schema:download --endpoint=http://localhost:4000/graphql",
39 | "codegen": "apollo codegen:generate src/types/api.d.ts --queries='src/**/!(*.local).ts' --addTypename --schema schema.json --target typescript --outputFlat",
40 | "predeploy": "yarn run build",
41 | "deploy": "gh-pages -d build"
42 | },
43 | "devDependencies": {
44 | "@types/axios": "^0.14.0",
45 | "@types/googlemaps": "^3.30.11",
46 | "@types/jest": "^23.3.1",
47 | "@types/node": "^10.5.7",
48 | "@types/prop-types": "^15.5.4",
49 | "@types/react": "^16.4.8",
50 | "@types/react-dom": "^16.0.7",
51 | "@types/react-sidebar": "^3.0.0",
52 | "gh-pages": "^1.2.0",
53 | "typescript": "^3.0.1"
54 | },
55 | "homepage": "https://nomadcoders.github.io/nuber-client/"
56 | }
57 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nomadcoders/nuber-client/2cf12dfcc2eed3c3fc3e5c8c4765a656b5015dcc/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Nuber
24 |
25 |
26 |
27 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/schema.json:
--------------------------------------------------------------------------------
1 | {"queryType":{"name":"Query"},"mutationType":{"name":"Mutation"},"subscriptionType":{"name":"Subscription"},"types":[{"kind":"OBJECT","name":"Query","description":"","fields":[{"name":"GetChat","description":"","args":[{"name":"chatId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GetChatResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"GetMyPlaces","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GetMyPlacesResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"GetNearbyRide","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GetNearbyRideResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"GetRide","description":"","args":[{"name":"rideId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GetRideResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"GetMyProfile","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GetMyProfileResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"GetNearbyDrivers","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GetNearbyDriversResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Int","description":"The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. ","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GetChatResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"chat","description":"","args":[],"type":{"kind":"OBJECT","name":"Chat","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Boolean","description":"The `Boolean` scalar type represents `true` or `false`.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"String","description":"The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Chat","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"messages","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Message","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"passengerId","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"passenger","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"driverId","description":"","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"driver","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"rideId","description":"","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ride","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Ride","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Message","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"text","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"chatId","description":"","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"chat","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Chat","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userId","description":"","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"User","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"verifiedEmail","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"firstName","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastName","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"age","description":"","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"password","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"phoneNumber","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"verifiedPhoneNumber","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"profilePhoto","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"fullName","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isDriving","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isRiding","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isTaken","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastLng","description":"","args":[],"type":{"kind":"SCALAR","name":"Float","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lastLat","description":"","args":[],"type":{"kind":"SCALAR","name":"Float","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lastOrientation","description":"","args":[],"type":{"kind":"SCALAR","name":"Float","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"fbId","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"messages","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Message","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ridesAsPassenger","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Ride","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ridesAsDriver","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Ride","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"chatsAsDriver","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Chat","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"chatsAsPassenger","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Chat","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"places","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Place","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Float","description":"The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Ride","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"status","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pickUpAddress","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pickUpLat","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pickUpLng","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"dropOffAddress","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"dropOffLat","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"dropOffLng","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"price","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"distance","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"duration","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"driverId","description":"","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"driver","description":"","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"passengerId","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"passenger","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"chat","description":"","args":[],"type":{"kind":"OBJECT","name":"Chat","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"chatId","description":"","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Place","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lat","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lng","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"address","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isFav","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userId","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GetMyPlacesResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"places","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Place","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GetNearbyRideResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ride","description":"","args":[],"type":{"kind":"OBJECT","name":"Ride","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GetRideResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ride","description":"","args":[],"type":{"kind":"OBJECT","name":"Ride","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GetMyProfileResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GetNearbyDriversResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"drivers","description":"","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Mutation","description":"","fields":[{"name":"SendChatMessage","description":"","args":[{"name":"text","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"chatId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SendChatMessageResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"AddPlace","description":"","args":[{"name":"name","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"lat","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"defaultValue":null},{"name":"lng","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"defaultValue":null},{"name":"address","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"isFav","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"AddPlaceResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"DeletePlace","description":"","args":[{"name":"placeId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"DeletePlaceResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"EditPlace","description":"","args":[{"name":"placeId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"name","description":"","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"isFav","description":"","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EditPlaceResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"RequestRide","description":"","args":[{"name":"pickUpAddress","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"pickUpLat","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"defaultValue":null},{"name":"pickUpLng","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"defaultValue":null},{"name":"dropOffAddress","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"dropOffLat","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"defaultValue":null},{"name":"dropOffLng","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"defaultValue":null},{"name":"price","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"defaultValue":null},{"name":"distance","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"duration","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RequestRideResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"UpdateRideStatus","description":"","args":[{"name":"rideId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"status","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"StatusOptions","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UpdateRideStatusResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"CompleteEmailVerification","description":"","args":[{"name":"key","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CompleteEmailVerificationResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"CompletePhoneVerification","description":"","args":[{"name":"phoneNumber","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"key","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CompletePhoneVerificationResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"EmailSignIn","description":"","args":[{"name":"email","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"password","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EmailSignInResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"EmailSignUp","description":"","args":[{"name":"firstName","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"lastName","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"email","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"password","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"profilePhoto","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"age","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"phoneNumber","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EmailSignUpResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"FacebookConnect","description":"","args":[{"name":"firstName","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"lastName","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"email","description":"","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"fbId","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"FacebookConnectResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ReportMovement","description":"","args":[{"name":"orientation","description":"","type":{"kind":"SCALAR","name":"Float","ofType":null},"defaultValue":null},{"name":"lastLat","description":"","type":{"kind":"SCALAR","name":"Float","ofType":null},"defaultValue":null},{"name":"lastLng","description":"","type":{"kind":"SCALAR","name":"Float","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReportMovementResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"RequestEmailVerification","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RequestEmailVerificationResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"StartPhoneVerification","description":"","args":[{"name":"phoneNumber","description":"","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StartPhoneVerificationResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ToggleDrivingMode","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ToggleDrivingModeResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"UpdateMyProfile","description":"","args":[{"name":"firstName","description":"","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"lastName","description":"","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"email","description":"","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"password","description":"","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"profilePhoto","description":"","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"age","description":"","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UpdateMyProfileResponse","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SendChatMessageResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"","args":[],"type":{"kind":"OBJECT","name":"Message","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddPlaceResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeletePlaceResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EditPlaceResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RequestRideResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ride","description":"","args":[],"type":{"kind":"OBJECT","name":"Ride","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"StatusOptions","description":"","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ACCEPTED","description":"","isDeprecated":false,"deprecationReason":null},{"name":"FINISHED","description":"","isDeprecated":false,"deprecationReason":null},{"name":"CANCELED","description":"","isDeprecated":false,"deprecationReason":null},{"name":"REQUESTING","description":"","isDeprecated":false,"deprecationReason":null},{"name":"ONROUTE","description":"","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"UpdateRideStatusResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"rideId","description":"","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CompleteEmailVerificationResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CompletePhoneVerificationResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"token","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EmailSignInResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"token","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EmailSignUpResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"token","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"FacebookConnectResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"token","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReportMovementResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RequestEmailVerificationResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"StartPhoneVerificationResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ToggleDrivingModeResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateMyProfileResponse","description":"","fields":[{"name":"ok","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"error","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Subscription","description":"","fields":[{"name":"MessageSubscription","description":"","args":[],"type":{"kind":"OBJECT","name":"Message","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"NearbyRideSubscription","description":"","args":[],"type":{"kind":"OBJECT","name":"Ride","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"RideStatusSubscription","description":"","args":[],"type":{"kind":"OBJECT","name":"Ride","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"DriversSubscription","description":"","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Schema","description":"A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.","fields":[{"name":"types","description":"A list of all types supported by this server.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"queryType","description":"The type that query operations will be rooted at.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"mutationType","description":"If this server supports mutation, the type that mutation operations will be rooted at.","args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"subscriptionType","description":"If this server support subscription, the type that subscription operations will be rooted at.","args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"directives","description":"A list of all directives supported by this server.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Directive","ofType":null}}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Type","description":"The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.","fields":[{"name":"kind","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"__TypeKind","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"fields","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Field","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"interfaces","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"possibleTypes","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"enumValues","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__EnumValue","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"inputFields","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"ofType","description":null,"args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"__TypeKind","description":"An enum describing what kind of type a given `__Type` is.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"SCALAR","description":"Indicates this type is a scalar.","isDeprecated":false,"deprecationReason":null},{"name":"OBJECT","description":"Indicates this type is an object. `fields` and `interfaces` are valid fields.","isDeprecated":false,"deprecationReason":null},{"name":"INTERFACE","description":"Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.","isDeprecated":false,"deprecationReason":null},{"name":"UNION","description":"Indicates this type is a union. `possibleTypes` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM","description":"Indicates this type is an enum. `enumValues` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_OBJECT","description":"Indicates this type is an input object. `inputFields` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"LIST","description":"Indicates this type is a list. `ofType` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"NON_NULL","description":"Indicates this type is a non-null. `ofType` is a valid field.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"__Field","description":"Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.","fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"args","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__InputValue","description":"Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.","fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"defaultValue","description":"A GraphQL-formatted string representing the default value for this input value.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__EnumValue","description":"One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.","fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Directive","description":"A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.","fields":[{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"locations","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"__DirectiveLocation","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"args","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"onOperation","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"Use `locations`."},{"name":"onFragment","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"Use `locations`."},{"name":"onField","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"Use `locations`."}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"__DirectiveLocation","description":"A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"QUERY","description":"Location adjacent to a query operation.","isDeprecated":false,"deprecationReason":null},{"name":"MUTATION","description":"Location adjacent to a mutation operation.","isDeprecated":false,"deprecationReason":null},{"name":"SUBSCRIPTION","description":"Location adjacent to a subscription operation.","isDeprecated":false,"deprecationReason":null},{"name":"FIELD","description":"Location adjacent to a field.","isDeprecated":false,"deprecationReason":null},{"name":"FRAGMENT_DEFINITION","description":"Location adjacent to a fragment definition.","isDeprecated":false,"deprecationReason":null},{"name":"FRAGMENT_SPREAD","description":"Location adjacent to a fragment spread.","isDeprecated":false,"deprecationReason":null},{"name":"INLINE_FRAGMENT","description":"Location adjacent to an inline fragment.","isDeprecated":false,"deprecationReason":null},{"name":"SCHEMA","description":"Location adjacent to a schema definition.","isDeprecated":false,"deprecationReason":null},{"name":"SCALAR","description":"Location adjacent to a scalar definition.","isDeprecated":false,"deprecationReason":null},{"name":"OBJECT","description":"Location adjacent to an object type definition.","isDeprecated":false,"deprecationReason":null},{"name":"FIELD_DEFINITION","description":"Location adjacent to a field definition.","isDeprecated":false,"deprecationReason":null},{"name":"ARGUMENT_DEFINITION","description":"Location adjacent to an argument definition.","isDeprecated":false,"deprecationReason":null},{"name":"INTERFACE","description":"Location adjacent to an interface definition.","isDeprecated":false,"deprecationReason":null},{"name":"UNION","description":"Location adjacent to a union definition.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM","description":"Location adjacent to an enum definition.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM_VALUE","description":"Location adjacent to an enum value definition.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_OBJECT","description":"Location adjacent to an input object type definition.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_FIELD_DEFINITION","description":"Location adjacent to an input object field definition.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"Verification","description":"","fields":[{"name":"id","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"target","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"payload","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"key","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"verified","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null}],"directives":[{"name":"skip","description":"Directs the executor to skip this field or fragment when the `if` argument is true.","locations":["FIELD","FRAGMENT_SPREAD","INLINE_FRAGMENT"],"args":[{"name":"if","description":"Skipped when true.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}]},{"name":"include","description":"Directs the executor to include this field or fragment only when the `if` argument is true.","locations":["FIELD","FRAGMENT_SPREAD","INLINE_FRAGMENT"],"args":[{"name":"if","description":"Included when true.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}]},{"name":"deprecated","description":"Marks an element of a GraphQL schema as no longer supported.","locations":["FIELD_DEFINITION","ENUM_VALUE"],"args":[{"name":"reason","description":"Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":"\"No longer supported\""}]}]}
--------------------------------------------------------------------------------
/src/Components/AddressBar/AddressBar.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "../../typed-components";
3 |
4 | const Container = styled.input`
5 | position: absolute;
6 | background-color: white;
7 | border-radius: 5px;
8 | -webkit-appearance: none;
9 | z-index: 2;
10 | width: 80%;
11 | border: 0;
12 | font-size: 16px;
13 | padding: 15px 10px;
14 | box-shadow: 0 18px 35px rgba(50, 50, 93, 0.1), 0 8px 15px rgba(0, 0, 0, 0.07);
15 | margin: auto;
16 | top: 10px;
17 | left: 0;
18 | right: 0;
19 | height: auto;
20 | `;
21 |
22 | interface IProps {
23 | value: string;
24 | onBlur: any;
25 | name: string;
26 | onChange: (event: React.ChangeEvent) => void;
27 | }
28 |
29 | const AddressBar: React.SFC = ({ value, onBlur, onChange, name }) => (
30 |
38 | );
39 |
40 | export default AddressBar;
41 |
--------------------------------------------------------------------------------
/src/Components/AddressBar/index.ts:
--------------------------------------------------------------------------------
1 | import AddressBar from "./AddressBar";
2 | export default AddressBar;
3 |
--------------------------------------------------------------------------------
/src/Components/App/AppContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { graphql } from "react-apollo";
3 | import { ToastContainer } from "react-toastify";
4 | import "react-toastify/dist/ReactToastify.min.css";
5 | import theme from "../../theme";
6 | import { ThemeProvider } from "../../typed-components";
7 | import AppPresenter from "./AppPresenter";
8 | import { IS_LOGGED_IN } from "./AppQueries.local";
9 |
10 | const AppContainer = ({ data }) => (
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 |
19 | export default graphql(IS_LOGGED_IN)(AppContainer);
20 |
--------------------------------------------------------------------------------
/src/Components/App/AppPresenter.tsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React from "react";
3 | import { BrowserRouter, Redirect, Route, Switch } from "react-router-dom";
4 | import AddPlace from "../../Routes/AddPlace";
5 | import Chat from "../../Routes/Chat";
6 | import EditAccount from "../../Routes/EditAccount";
7 | import FindAddress from "../../Routes/FindAddress";
8 | import Home from "../../Routes/Home";
9 | import Login from "../../Routes/Login";
10 | import PhoneLogin from "../../Routes/PhoneLogin";
11 | import Places from "../../Routes/Places";
12 | import Ride from "../../Routes/Ride";
13 | import Settings from "../../Routes/Settings";
14 | import SocialLogin from "../../Routes/SocialLogin";
15 | import VerifyPhone from "../../Routes/VerifyPhone";
16 |
17 | interface IProps {
18 | isLoggedIn: boolean;
19 | }
20 |
21 | const AppPresenter: React.SFC = ({ isLoggedIn }) => (
22 |
23 | {isLoggedIn ? : }
24 |
25 | );
26 |
27 | const LoggedOutRoutes: React.SFC = () => (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 |
37 | const LoggedInRoutes: React.SFC = () => (
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | );
50 |
51 | AppPresenter.propTypes = {
52 | isLoggedIn: PropTypes.bool.isRequired
53 | };
54 |
55 | export default AppPresenter;
56 |
--------------------------------------------------------------------------------
/src/Components/App/AppQueries.local.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const IS_LOGGED_IN = gql`
4 | {
5 | auth {
6 | isLoggedIn @client
7 | }
8 | }
9 | `;
10 |
--------------------------------------------------------------------------------
/src/Components/App/index.ts:
--------------------------------------------------------------------------------
1 | import AppContainer from "./AppContainer";
2 | export default AppContainer;
3 |
--------------------------------------------------------------------------------
/src/Components/BackArrow/BackArrow.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import styled from "../../typed-components";
4 |
5 | const Container = styled.div`
6 | transform: scale(0.8);
7 | `;
8 |
9 | interface IProps {
10 | backTo: string;
11 | className?: string;
12 | }
13 |
14 | const BackArrow: React.SFC = ({ backTo, className }) => (
15 |
16 |
17 |
26 |
27 |
28 | );
29 |
30 | export default BackArrow;
31 |
--------------------------------------------------------------------------------
/src/Components/BackArrow/index.ts:
--------------------------------------------------------------------------------
1 | import BackArrow from "./BackArrow";
2 | export default BackArrow;
3 |
--------------------------------------------------------------------------------
/src/Components/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "../../typed-components";
3 |
4 | const Container = styled.input`
5 | width: 100%;
6 | background-color: black;
7 | color: white;
8 | text-transform: uppercase;
9 | padding: 15px 0;
10 | font-size: 16px;
11 | border: 0;
12 | cursor: pointer;
13 | font-weight: 500;
14 | text-align: center;
15 | &:active,
16 | &:focus {
17 | outline: none;
18 | }
19 | &:disabled {
20 | opacity: 0.8;
21 | cursor: not-allowed;
22 | }
23 | `;
24 |
25 | interface IProps {
26 | value: string;
27 | onClick: any;
28 | disabled?: boolean;
29 | className?: string;
30 | }
31 |
32 | const Button: React.SFC = ({
33 | value,
34 | onClick,
35 | disabled = false,
36 | className
37 | }) => (
38 |
45 | );
46 |
47 | export default Button;
48 |
--------------------------------------------------------------------------------
/src/Components/Button/index.ts:
--------------------------------------------------------------------------------
1 | import Button from "./Button";
2 | export default Button;
3 |
--------------------------------------------------------------------------------
/src/Components/Form/Form.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | interface IProps {
4 | submitFn: any;
5 | className?: string;
6 | }
7 |
8 | const Form: React.SFC = ({ submitFn, className, children }) => (
9 |
18 | );
19 |
20 | export default Form;
21 |
--------------------------------------------------------------------------------
/src/Components/Form/index.ts:
--------------------------------------------------------------------------------
1 | import Form from "./Form";
2 | export default Form;
3 |
--------------------------------------------------------------------------------
/src/Components/Header/Header.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "../../typed-components";
3 | import BackArrow from "../BackArrow";
4 |
5 | const Container = styled.header`
6 | background-color: black;
7 | color: white;
8 | display: flex;
9 | height: 50px;
10 | font-size: 20px;
11 | font-weight: 300;
12 | align-items: center;
13 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
14 | & svg {
15 | fill: white;
16 | }
17 | margin-bottom: 50px;
18 | padding: 0 10px;
19 | `;
20 |
21 | const Title = styled.h2`
22 | margin-left: 10px;
23 | `;
24 |
25 | interface IProps {
26 | title: string;
27 | backTo?: string;
28 | }
29 |
30 | const Header: React.SFC = ({ title, backTo }) => (
31 |
32 | {backTo && }
33 | {title}
34 |
35 | );
36 |
37 | export default Header;
38 |
--------------------------------------------------------------------------------
/src/Components/Header/index.ts:
--------------------------------------------------------------------------------
1 | import Header from "./Header";
2 | export default Header;
3 |
--------------------------------------------------------------------------------
/src/Components/Input/Input.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "../../typed-components";
3 |
4 | const Container = styled.input`
5 | border: none;
6 | border-bottom: 2px solid ${props => props.theme.greyColor};
7 | font-size: 20px;
8 | width: 100%;
9 | padding-bottom: 10px;
10 | font-weight: 500;
11 | transition: border-bottom 0.1s linear;
12 | &:-webkit-autofill {
13 | box-shadow: 0 0 0px 1000px white inset !important;
14 | }
15 | &:focus {
16 | border-bottom-color: #2c3e50;
17 | outline: none;
18 | }
19 | &::placeholder {
20 | color: ${props => props.theme.greyColor};
21 | font-weight: 300;
22 | }
23 | `;
24 |
25 | interface IProps {
26 | placeholder?: string;
27 | type?: string;
28 | required?: boolean;
29 | value: string;
30 | name?: string;
31 | onChange: any;
32 | className?: string;
33 | }
34 |
35 | const Input: React.SFC = ({
36 | placeholder = "",
37 | type = "text",
38 | required = true,
39 | value,
40 | name = "",
41 | onChange,
42 | className
43 | }) => (
44 |
53 | );
54 |
55 | export default Input;
56 |
--------------------------------------------------------------------------------
/src/Components/Input/index.ts:
--------------------------------------------------------------------------------
1 | import Input from "./Input";
2 | export default Input;
3 |
--------------------------------------------------------------------------------
/src/Components/Menu/MenuContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Mutation, Query } from "react-apollo";
3 | import { toast } from "react-toastify";
4 | import { USER_PROFILE } from "../../sharedQueries";
5 | import { toggleDriving, userProfile } from "../../types/api";
6 | import MenuPresenter from "./MenuPresenter";
7 | import { TOGGLE_DRIVING } from "./MenuQueries";
8 |
9 | class ProfileQuery extends Query {}
10 | class ToggleDrivingMutation extends Mutation {}
11 |
12 | class MenuContainer extends React.Component {
13 | public render() {
14 | return (
15 | {
18 | if (data) {
19 | const { ToggleDrivingMode } = data;
20 | if (!ToggleDrivingMode.ok) {
21 | toast.error(ToggleDrivingMode.error);
22 | return;
23 | }
24 | const query: userProfile | null = cache.readQuery({
25 | query: USER_PROFILE
26 | });
27 | if (query) {
28 | const {
29 | GetMyProfile: { user }
30 | } = query;
31 | if (user) {
32 | user.isDriving = !user.isDriving;
33 | }
34 | }
35 | cache.writeQuery({ query: USER_PROFILE, data: query });
36 | }
37 | }}
38 | >
39 | {toggleDrivingFn => (
40 |
41 | {({ data, loading }) => (
42 |
47 | )}
48 |
49 | )}
50 |
51 | );
52 | }
53 | }
54 |
55 | export default MenuContainer;
56 |
--------------------------------------------------------------------------------
/src/Components/Menu/MenuPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { MutationFn } from "react-apollo";
3 | import { Link } from "react-router-dom";
4 | import styled from "../../typed-components";
5 | import { toggleDriving, userProfile } from "../../types/api";
6 |
7 | const Container = styled.div`
8 | height: 100%;
9 | `;
10 |
11 | const Header = styled.div`
12 | background-color: black;
13 | height: 20%;
14 | margin-bottom: 30px;
15 | padding: 0 15px;
16 | color: white;
17 | `;
18 |
19 | const SLink = styled(Link)`
20 | font-size: 22px;
21 | display: block;
22 | margin-left: 15px;
23 | margin-bottom: 25px;
24 | font-weight: 400;
25 | `;
26 |
27 | const Image = styled.img`
28 | height: 80px;
29 | width: 80px;
30 | background-color: grey;
31 | border-radius: 40px;
32 | overflow: hidden;
33 | `;
34 |
35 | const Name = styled.h2`
36 | font-size: 22px;
37 | color: white;
38 | margin-bottom: 10px;
39 | white-space: nowrap;
40 | overflow: hidden;
41 | text-overflow: ellipsis;
42 | `;
43 |
44 | const Rating = styled.h5`
45 | font-size: 18px;
46 | color: white;
47 | `;
48 |
49 | const Text = styled.span`
50 | text-overflow: ellipsis;
51 | white-space: nowrap;
52 | overflow: hidden;
53 | `;
54 |
55 | const Grid = styled.div`
56 | display: grid;
57 | grid-template-columns: 1fr 2fr;
58 | grid-gap: 10px;
59 | height: 100%;
60 | align-items: center;
61 | `;
62 |
63 | interface IToggleProps {
64 | isDriving: boolean;
65 | }
66 |
67 | const ToggleDriving = styled("button")`
68 | -webkit-appearance: none;
69 | background-color: ${props =>
70 | props.isDriving ? props.theme.yellowColor : props.theme.greenColor};
71 | width: 100%;
72 | color: white;
73 | font-size: 18px;
74 | border: 0;
75 | padding: 15px 0px;
76 | cursor: pointer;
77 | `;
78 |
79 | interface IProps {
80 | data?: userProfile;
81 | loading: boolean;
82 | toggleDrivingFn: MutationFn;
83 | }
84 |
85 | const MenuPresenter: React.SFC = ({
86 | data: { GetMyProfile: { user = null } = {} } = {},
87 | loading,
88 | toggleDrivingFn
89 | }) => (
90 |
91 | {!loading &&
92 | user &&
93 | user.fullName && (
94 |
95 |
96 |
97 |
98 |
104 |
105 |
106 | {user.fullName}
107 | 4.5
108 |
109 |
110 |
111 | Your Trips
112 | Settings
113 |
114 | {user.isDriving ? "Stop driving" : "Start driving"}
115 |
116 |
117 | )}
118 |
119 | );
120 |
121 | export default MenuPresenter;
122 |
--------------------------------------------------------------------------------
/src/Components/Menu/MenuQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const TOGGLE_DRIVING = gql`
4 | mutation toggleDriving {
5 | ToggleDrivingMode {
6 | ok
7 | error
8 | }
9 | }
10 | `;
11 |
--------------------------------------------------------------------------------
/src/Components/Menu/index.ts:
--------------------------------------------------------------------------------
1 | import MenuContainer from "./MenuContainer";
2 | export default MenuContainer;
3 |
--------------------------------------------------------------------------------
/src/Components/Message/Message.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "../../typed-components";
3 |
4 | const Container = styled<{ mine: boolean }, any>("div")`
5 | background-color: ${props =>
6 | props.mine ? props.theme.blueColor : props.theme.greyColor};
7 | color: white;
8 | padding: 10px 20px;
9 | border-radius: 20px;
10 | align-self: ${props => (props.mine ? "flex-end" : "flex-start")};
11 | border-bottom-right-radius: ${props => (props.mine ? "0px" : "20px")};
12 | border-bottom-left-radius: ${props => (!props.mine ? "0px" : "20px")};
13 | margin-bottom: 10px;
14 | `;
15 |
16 | interface IProps {
17 | text: string;
18 | mine: boolean;
19 | }
20 |
21 | const Message: React.SFC = ({ text, mine }) => (
22 | {text}
23 | );
24 |
25 | export default Message;
26 |
--------------------------------------------------------------------------------
/src/Components/Message/index.ts:
--------------------------------------------------------------------------------
1 | import Message from "./Message";
2 | export default Message;
3 |
--------------------------------------------------------------------------------
/src/Components/PhotoInput/PhotoInput.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "../../typed-components";
3 |
4 | const Container = styled.div``;
5 |
6 | const Image = styled.label`
7 | cursor: pointer;
8 | height: 80px;
9 | width: 80px;
10 | border: 2px solid black;
11 | display: block;
12 | border-radius: 50%;
13 | margin-bottom: 35px;
14 | display: flex;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: 28px;
18 | overflow: hidden;
19 | & img {
20 | width: 80px;
21 | height: 80px;
22 | }
23 | `;
24 |
25 | const Input = styled.input`
26 | color: white;
27 | opacity: 0;
28 | height: 1px;
29 | &:focus {
30 | outline: none;
31 | }
32 | `;
33 |
34 | interface IProps {
35 | uploading: boolean;
36 | fileUrl: string;
37 | onChange: (event: React.ChangeEvent) => void;
38 | }
39 |
40 | const PhotoInput: React.SFC = ({ uploading, fileUrl, onChange }) => (
41 |
42 |
43 |
44 | {uploading && "⏰"}
45 | {!uploading &&
}
46 |
47 |
48 | );
49 |
50 | export default PhotoInput;
51 |
--------------------------------------------------------------------------------
/src/Components/PhotoInput/index.ts:
--------------------------------------------------------------------------------
1 | import PhotoInput from "./PhotoInput";
2 | export default PhotoInput;
3 |
--------------------------------------------------------------------------------
/src/Components/Place/PlaceContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Mutation } from "react-apollo";
3 | import { GET_PLACES } from "../../sharedQueries";
4 | import { editPlace, editPlaceVariables } from "../../types/api";
5 | import PlacePresenter from "./PlacePresenter";
6 | import { EDIT_PLACE } from "./PlaceQueries";
7 |
8 | interface IProps {
9 | fav: boolean;
10 | name: string;
11 | address: string;
12 | id: number;
13 | }
14 |
15 | class FavMutation extends Mutation {}
16 |
17 | class PlaceContainer extends React.Component {
18 | public render() {
19 | const { id, fav, name, address } = this.props;
20 | return (
21 |
29 | {editPlaceFn => (
30 |
36 | )}
37 |
38 | );
39 | }
40 | }
41 |
42 | export default PlaceContainer;
43 |
--------------------------------------------------------------------------------
/src/Components/Place/PlacePresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { MutationFn } from "react-apollo";
3 | import styled from "../../typed-components";
4 |
5 | const Place = styled.div`
6 | margin: 15px 0;
7 | display: flex;
8 | align-items: center;
9 | & i {
10 | font-size: 12px;
11 | }
12 | `;
13 |
14 | const Container = styled.div`
15 | margin-left: 10px;
16 | `;
17 |
18 | const Name = styled.span`
19 | display: block;
20 | `;
21 |
22 | const Icon = styled.span`
23 | cursor: pointer;
24 | `;
25 |
26 | const Address = styled.span`
27 | color: ${props => props.theme.greyColor};
28 | font-size: 14px;
29 | `;
30 |
31 | interface IProps {
32 | fav: boolean;
33 | name: string;
34 | address: string;
35 | onStarPress: MutationFn;
36 | }
37 |
38 | const PlacePresenter: React.SFC = ({
39 | onStarPress,
40 | fav,
41 | name,
42 | address
43 | }) => (
44 |
45 | {fav ? "★" : "✩"}
46 |
47 | {name}
48 | {address}
49 |
50 |
51 | );
52 |
53 | export default PlacePresenter;
54 |
--------------------------------------------------------------------------------
/src/Components/Place/PlaceQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const EDIT_PLACE = gql`
4 | mutation editPlace($placeId: Int!, $isFav: Boolean) {
5 | EditPlace(placeId: $placeId, isFav: $isFav) {
6 | ok
7 | error
8 | }
9 | }
10 | `;
11 |
--------------------------------------------------------------------------------
/src/Components/Place/index.ts:
--------------------------------------------------------------------------------
1 | import PlaceContainer from "./PlaceContainer";
2 | export default PlaceContainer;
3 |
--------------------------------------------------------------------------------
/src/Components/RidePopUp/RidePopUp.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "../../typed-components";
3 | import Button from "../Button";
4 |
5 | interface IProps {
6 | pickUpAddress: string;
7 | dropOffAddress: string;
8 | price: number;
9 | distance: string;
10 | passengerName: string;
11 | passengerPhoto: string;
12 | acceptRideFn: any;
13 | id: number;
14 | }
15 |
16 | const Container = styled.div`
17 | background-color: white;
18 | position: absolute;
19 | margin: auto;
20 | top: 0;
21 | bottom: 0;
22 | left: 0;
23 | right: 0;
24 | width: 80%;
25 | height: 60%;
26 | z-index: 9;
27 | padding: 20px;
28 | `;
29 |
30 | const Title = styled.h4`
31 | font-weight: 800;
32 | margin-top: 30px;
33 | margin-bottom: 10px;
34 | &:first-child {
35 | margin-top: 0;
36 | }
37 | `;
38 |
39 | const Data = styled.span`
40 | color: ${props => props.theme.blueColor};
41 | `;
42 |
43 | const Img = styled.img`
44 | border-radius: 50%;
45 | margin-right: 20px;
46 | `;
47 |
48 | const Passenger = styled.div`
49 | display: flex;
50 | align-items: center;
51 | margin-bottom: 20px;
52 | `;
53 |
54 | const RidePopUp: React.SFC = ({
55 | pickUpAddress,
56 | dropOffAddress,
57 | price,
58 | distance,
59 | passengerName,
60 | passengerPhoto,
61 | acceptRideFn,
62 | id
63 | }) => (
64 |
65 | Pick Up Address
66 | {pickUpAddress}
67 | Drop Off Address
68 | {dropOffAddress}
69 | Price
70 | {price}
71 | Distance
72 | {distance}
73 | Distance
74 | {distance}
75 | Passenger:
76 |
77 |
78 | {passengerName}
79 |
80 |
85 | );
86 |
87 | export default RidePopUp;
88 |
--------------------------------------------------------------------------------
/src/Components/RidePopUp/index.ts:
--------------------------------------------------------------------------------
1 | import RidePopUp from "./RidePopUp";
2 | export default RidePopUp;
3 |
--------------------------------------------------------------------------------
/src/Routes/AddPlace/AddPlaceContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Mutation } from "react-apollo";
3 | import { RouteComponentProps } from "react-router-dom";
4 | import { toast } from "react-toastify";
5 | import { GET_PLACES } from "../../sharedQueries";
6 | import { addPlace, addPlaceVariables } from "../../types/api";
7 | import AddPlacePresenter from "./AddPlacePresenter";
8 | import { ADD_PLACE } from "./AddPlaceQuery";
9 |
10 | interface IState {
11 | address: string;
12 | name: string;
13 | lat: number;
14 | lng: number;
15 | }
16 |
17 | interface IProps extends RouteComponentProps {}
18 |
19 | class AddPlaceQuery extends Mutation {}
20 |
21 | class AddPlaceContainer extends React.Component {
22 | constructor(props: IProps) {
23 | super(props);
24 | const { location: { state = {} } = {} } = props;
25 | this.state = {
26 | address: state.address || "",
27 | lat: state.lat || 0,
28 | lng: state.lng || 0,
29 | name: ""
30 | };
31 | }
32 | public render() {
33 | const { address, name, lat, lng } = this.state;
34 | const { history } = this.props;
35 | return (
36 | {
39 | const { AddPlace } = data;
40 | if (AddPlace.ok) {
41 | toast.success("Place added!");
42 | setTimeout(() => {
43 | history.push("/places");
44 | }, 2000);
45 | } else {
46 | toast.error(AddPlace.error);
47 | }
48 | }}
49 | refetchQueries={[{ query: GET_PLACES }]}
50 | variables={{
51 | address,
52 | isFav: false,
53 | lat,
54 | lng,
55 | name
56 | }}
57 | >
58 | {(addPlaceFn, { loading }) => (
59 |
67 | )}
68 |
69 | );
70 | }
71 |
72 | public onInputChange: React.ChangeEventHandler<
73 | HTMLInputElement
74 | > = async event => {
75 | const {
76 | target: { name, value }
77 | } = event;
78 | this.setState({
79 | [name]: value
80 | } as any);
81 | };
82 | }
83 |
84 | export default AddPlaceContainer;
85 |
--------------------------------------------------------------------------------
/src/Routes/AddPlace/AddPlacePresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { MutationFn } from "react-apollo";
3 | import Helmet from "react-helmet";
4 | import { Link } from "react-router-dom";
5 | import Button from "../../Components/Button";
6 | import Form from "../../Components/Form";
7 | import Header from "../../Components/Header";
8 | import Input from "../../Components/Input";
9 | import styled from "../../typed-components";
10 |
11 | const Container = styled.div`
12 | padding: 0 40px;
13 | `;
14 |
15 | const ExtendedInput = styled(Input)`
16 | margin-bottom: 40px;
17 | `;
18 |
19 | const ExtendedLink = styled(Link)`
20 | text-decoration: underline;
21 | margin-bottom: 20px;
22 | display: block;
23 | `;
24 |
25 | interface IProps {
26 | address: string;
27 | name: string;
28 | onInputChange: (event: React.ChangeEvent) => void;
29 | loading: boolean;
30 | onSubmit: MutationFn;
31 | pickedAddress: boolean;
32 | }
33 |
34 | const AddPlacePresenter: React.SFC = ({
35 | onInputChange,
36 | address,
37 | name,
38 | loading,
39 | onSubmit,
40 | pickedAddress
41 | }) => (
42 |
43 |
44 | Add Place | Nuber
45 |
46 |
47 |
48 |
71 |
72 |
73 | );
74 |
75 | export default AddPlacePresenter;
76 |
--------------------------------------------------------------------------------
/src/Routes/AddPlace/AddPlaceQuery.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const ADD_PLACE = gql`
4 | mutation addPlace(
5 | $name: String!
6 | $lat: Float!
7 | $lng: Float!
8 | $address: String!
9 | $isFav: Boolean!
10 | ) {
11 | AddPlace(
12 | name: $name
13 | lat: $lat
14 | lng: $lng
15 | address: $address
16 | isFav: $isFav
17 | ) {
18 | ok
19 | error
20 | }
21 | }
22 | `;
23 |
--------------------------------------------------------------------------------
/src/Routes/AddPlace/index.tsx:
--------------------------------------------------------------------------------
1 | import AddPlaceContainer from "./AddPlaceContainer";
2 | export default AddPlaceContainer;
3 |
--------------------------------------------------------------------------------
/src/Routes/Chat/ChatContainer.tsx:
--------------------------------------------------------------------------------
1 | import { SubscribeToMoreOptions } from "apollo-client";
2 | import React from "react";
3 | import { Mutation, MutationFn, Query } from "react-apollo";
4 | import { RouteComponentProps } from "react-router-dom";
5 | import { USER_PROFILE } from "../../sharedQueries";
6 | import {
7 | getChat,
8 | getChatVariables,
9 | sendMessage,
10 | sendMessageVariables,
11 | userProfile
12 | } from "../../types/api";
13 | import ChatPresenter from "./ChatPresenter";
14 | import { GET_CHAT, SEND_MESSAGE, SUBSCRIBE_TO_MESSAGES } from "./ChatQueries";
15 |
16 | interface IProps extends RouteComponentProps {}
17 | interface IState {
18 | message: "";
19 | }
20 |
21 | class ProfileQuery extends Query {}
22 | class ChatQuery extends Query {}
23 | class SendMessageMutation extends Mutation {}
24 |
25 | class ChatContainer extends React.Component {
26 | public sendMessageFn: MutationFn;
27 | constructor(props: IProps) {
28 | super(props);
29 | if (!props.match.params.chatId) {
30 | props.history.push("/");
31 | }
32 | this.state = {
33 | message: ""
34 | };
35 | }
36 | public render() {
37 | const {
38 | match: {
39 | params: { chatId }
40 | }
41 | } = this.props;
42 | const { message } = this.state;
43 | return (
44 |
45 | {({ data: userData }) => (
46 |
47 | {({ data, loading, subscribeToMore }) => {
48 | const subscribeToMoreOptions: SubscribeToMoreOptions = {
49 | document: SUBSCRIBE_TO_MESSAGES,
50 | updateQuery: (prev, { subscriptionData }) => {
51 | if (!subscriptionData.data) {
52 | return prev;
53 | }
54 | const {
55 | data: { MessageSubscription }
56 | } = subscriptionData;
57 | const {
58 | GetChat: {
59 | chat: { messages }
60 | }
61 | } = prev;
62 | const newMessageId = MessageSubscription.id;
63 | const latestMessageId = messages[messages.length - 1].id;
64 |
65 | if (newMessageId === latestMessageId) {
66 | return;
67 | }
68 |
69 | const newObject = Object.assign({}, prev, {
70 | GetChat: {
71 | ...prev.GetChat,
72 | chat: {
73 | ...prev.GetChat.chat,
74 | messages: [
75 | ...prev.GetChat.chat.messages,
76 | MessageSubscription
77 | ]
78 | }
79 | }
80 | });
81 | return newObject;
82 | }
83 | };
84 | subscribeToMore(subscribeToMoreOptions);
85 | return (
86 |
87 | {sendMessageFn => {
88 | this.sendMessageFn = sendMessageFn;
89 | return (
90 |
98 | );
99 | }}
100 |
101 | );
102 | }}
103 |
104 | )}
105 |
106 | );
107 | }
108 | public onInputChange: React.ChangeEventHandler = event => {
109 | const {
110 | target: { name, value }
111 | } = event;
112 | this.setState({
113 | [name]: value
114 | } as any);
115 | };
116 | public onSubmit = () => {
117 | const { message } = this.state;
118 | const {
119 | match: {
120 | params: { chatId }
121 | }
122 | } = this.props;
123 | if (message !== "") {
124 | this.setState({
125 | message: ""
126 | });
127 | this.sendMessageFn({
128 | variables: {
129 | chatId,
130 | text: message
131 | }
132 | });
133 | }
134 | return;
135 | };
136 | }
137 |
138 | export default ChatContainer;
139 |
--------------------------------------------------------------------------------
/src/Routes/Chat/ChatPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Form from "../../Components/Form";
3 | import Header from "../../Components/Header";
4 | import Input from "../../Components/Input";
5 | import Message from "../../Components/Message";
6 | import styled from "../../typed-components";
7 | import { getChat, userProfile } from "../../types/api";
8 |
9 | const Container = styled.div``;
10 |
11 | const Chat = styled.div`
12 | height: 80vh;
13 | overflow: scroll;
14 | padding: 0 20px;
15 | display: flex;
16 | flex-direction: column;
17 | align-items: flex-start;
18 | `;
19 |
20 | const InputCont = styled.div`
21 | padding: 0 20px;
22 | `;
23 |
24 | interface IProps {
25 | data?: getChat;
26 | userData?: userProfile;
27 | loading: boolean;
28 | messageText: string;
29 | onSubmit: () => void;
30 | onInputChange: (event: React.ChangeEvent) => void;
31 | }
32 |
33 | const ChatPresenter: React.SFC = ({
34 | loading,
35 | data: { GetChat: { chat = null } = {} } = {},
36 | userData: { GetMyProfile: { user = null } = {} } = {},
37 | messageText,
38 | onInputChange,
39 | onSubmit
40 | }) => (
41 |
42 |
43 | {!loading &&
44 | chat &&
45 | user && (
46 |
47 |
48 | {chat.messages &&
49 | chat.messages.map(message => {
50 | if (message) {
51 | return (
52 |
57 | );
58 | }
59 | return null;
60 | })}
61 |
62 |
63 |
71 |
72 |
73 | )}
74 |
75 | );
76 |
77 | export default ChatPresenter;
78 |
--------------------------------------------------------------------------------
/src/Routes/Chat/ChatQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const GET_CHAT = gql`
4 | query getChat($chatId: Int!) {
5 | GetChat(chatId: $chatId) {
6 | ok
7 | error
8 | chat {
9 | passengerId
10 | driverId
11 | messages {
12 | id
13 | text
14 | userId
15 | }
16 | }
17 | }
18 | }
19 | `;
20 |
21 | export const SEND_MESSAGE = gql`
22 | mutation sendMessage($text: String!, $chatId: Int!) {
23 | SendChatMessage(text: $text, chatId: $chatId) {
24 | ok
25 | error
26 | message {
27 | id
28 | text
29 | userId
30 | }
31 | }
32 | }
33 | `;
34 |
35 | export const SUBSCRIBE_TO_MESSAGES = gql`
36 | subscription messageSubscription {
37 | MessageSubscription {
38 | id
39 | text
40 | userId
41 | }
42 | }
43 | `;
44 |
--------------------------------------------------------------------------------
/src/Routes/Chat/index.ts:
--------------------------------------------------------------------------------
1 | import ChatContainer from "./ChatContainer";
2 | export default ChatContainer;
3 |
--------------------------------------------------------------------------------
/src/Routes/EditAccount/EditAccountContainer.tsx:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import React from "react";
3 | import { Mutation, Query } from "react-apollo";
4 | import { RouteComponentProps } from "react-router-dom";
5 | import { toast } from "react-toastify";
6 | import { USER_PROFILE } from "../../sharedQueries";
7 | import {
8 | updateProfile,
9 | updateProfileVariables,
10 | userProfile
11 | } from "../../types/api";
12 | import EditAccountPresenter from "./EditAccountPresenter";
13 | import { UPDATE_PROFILE } from "./EditAccountQueries";
14 |
15 | interface IState {
16 | firstName: string;
17 | lastName: string;
18 | email: string;
19 | profilePhoto: string;
20 | uploading: boolean;
21 | }
22 |
23 | interface IProps extends RouteComponentProps {}
24 |
25 | class UpdateProfileMutation extends Mutation<
26 | updateProfile,
27 | updateProfileVariables
28 | > {}
29 |
30 | class ProfileQuery extends Query {}
31 |
32 | class EditAccountContainer extends React.Component {
33 | public state = {
34 | email: "",
35 | firstName: "",
36 | lastName: "",
37 | profilePhoto: "",
38 | uploading: false
39 | };
40 | public render() {
41 | const { email, firstName, lastName, profilePhoto, uploading } = this.state;
42 | return (
43 |
48 | {() => (
49 | {
53 | const { UpdateMyProfile } = data;
54 | if (UpdateMyProfile.ok) {
55 | toast.success("Profile updated!");
56 | } else if (UpdateMyProfile.error) {
57 | toast.error(UpdateMyProfile.error);
58 | }
59 | }}
60 | variables={{
61 | email,
62 | firstName,
63 | lastName,
64 | profilePhoto
65 | }}
66 | >
67 | {(updateProfileFn, { loading }) => (
68 |
78 | )}
79 |
80 | )}
81 |
82 | );
83 | }
84 | public onInputChange: React.ChangeEventHandler<
85 | HTMLInputElement
86 | > = async event => {
87 | const {
88 | target: { name, value, files }
89 | } = event;
90 | if (files) {
91 | this.setState({
92 | uploading: true
93 | });
94 | const formData = new FormData();
95 | formData.append("file", files[0]);
96 | formData.append("api_key", "811881451928618");
97 | formData.append("upload_preset", "tqecb16q");
98 | formData.append("timestamp", String(Date.now() / 1000));
99 | const {
100 | data: { secure_url }
101 | } = await axios.post(
102 | "https://api.cloudinary.com/v1_1/djjpx4ror/image/upload",
103 | formData
104 | );
105 | if (secure_url) {
106 | this.setState({
107 | profilePhoto: secure_url,
108 | uploading: false
109 | });
110 | }
111 | }
112 | this.setState({
113 | [name]: value
114 | } as any);
115 | };
116 |
117 | public updateFields = (data: {} | userProfile) => {
118 | if ("GetMyProfile" in data) {
119 | const {
120 | GetMyProfile: { user }
121 | } = data;
122 | if (user !== null) {
123 | const { firstName, lastName, email, profilePhoto } = user;
124 | this.setState({
125 | email,
126 | firstName,
127 | lastName,
128 | profilePhoto,
129 | uploaded: profilePhoto !== null
130 | } as any);
131 | }
132 | }
133 | };
134 | }
135 |
136 | export default EditAccountContainer;
137 |
--------------------------------------------------------------------------------
/src/Routes/EditAccount/EditAccountPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { MutationFn } from "react-apollo";
3 | import Helmet from "react-helmet";
4 | import Button from "../../Components/Button";
5 | import Form from "../../Components/Form";
6 | import Header from "../../Components/Header";
7 | import Input from "../../Components/Input";
8 | import PhotoInput from "../../Components/PhotoInput";
9 | import styled from "../../typed-components";
10 |
11 | const Container = styled.div``;
12 |
13 | const ExtendedForm = styled(Form)`
14 | padding: 0px 40px;
15 | `;
16 |
17 | const ExtendedInput = styled(Input)`
18 | margin-bottom: 30px;
19 | `;
20 |
21 | interface IProps {
22 | firstName: string;
23 | lastName: string;
24 | email: string;
25 | profilePhoto: string;
26 | onSubmit: MutationFn;
27 | onInputChange: (event: React.ChangeEvent) => void;
28 | loading: boolean;
29 | uploading: boolean;
30 | }
31 |
32 | const EditAccountPresenter: React.SFC = ({
33 | firstName,
34 | lastName,
35 | email,
36 | onSubmit,
37 | profilePhoto,
38 | onInputChange,
39 | loading,
40 | uploading
41 | }) => (
42 |
43 |
44 | Edit Account | Number
45 |
46 |
47 |
48 |
53 |
60 |
67 |
74 |
75 |
76 |
77 | );
78 |
79 | export default EditAccountPresenter;
80 |
--------------------------------------------------------------------------------
/src/Routes/EditAccount/EditAccountQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const UPDATE_PROFILE = gql`
4 | mutation updateProfile(
5 | $firstName: String!
6 | $lastName: String!
7 | $email: String!
8 | $profilePhoto: String!
9 | ) {
10 | UpdateMyProfile(
11 | firstName: $firstName
12 | lastName: $lastName
13 | email: $email
14 | profilePhoto: $profilePhoto
15 | ) {
16 | ok
17 | error
18 | }
19 | }
20 | `;
21 |
--------------------------------------------------------------------------------
/src/Routes/EditAccount/index.tsx:
--------------------------------------------------------------------------------
1 | import EditAccountContainer from "./EditAccountContainer";
2 | export default EditAccountContainer;
3 |
--------------------------------------------------------------------------------
/src/Routes/FindAddress/FindAddressContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { RouteComponentProps } from "react-router-dom";
4 | import { geoCode, reverseGeoCode } from "../../mapHelpers";
5 | import FindAddressPresenter from "./FindAddressPresenter";
6 |
7 | interface IState {
8 | lat: number;
9 | lng: number;
10 | address: string;
11 | }
12 |
13 | interface IProps extends RouteComponentProps {
14 | google: any;
15 | }
16 |
17 | class FindAddressContainer extends React.Component {
18 | public mapRef: any;
19 | public map: google.maps.Map;
20 | public state = {
21 | address: "",
22 | lat: 0,
23 | lng: 0
24 | };
25 | constructor(props) {
26 | super(props);
27 | this.mapRef = React.createRef();
28 | }
29 | public componentDidMount() {
30 | navigator.geolocation.getCurrentPosition(
31 | this.handleGeoSucces,
32 | this.handleGeoError
33 | );
34 | }
35 | public render() {
36 | const { address } = this.state;
37 | return (
38 |
45 | );
46 | }
47 | public handleGeoSucces: PositionCallback = (positon: Position) => {
48 | const {
49 | coords: { latitude, longitude }
50 | } = positon;
51 | this.setState({
52 | lat: latitude,
53 | lng: longitude
54 | });
55 | this.loadMap(latitude, longitude);
56 | this.reverseGeocodeAddress(latitude, longitude);
57 | };
58 | public handleGeoError: PositionErrorCallback = () => {
59 | console.log("No location");
60 | };
61 | public loadMap = (lat, lng) => {
62 | const { google } = this.props;
63 | const maps = google.maps;
64 | const mapNode = ReactDOM.findDOMNode(this.mapRef.current);
65 | const mapConfig: google.maps.MapOptions = {
66 | center: {
67 | lat,
68 | lng
69 | },
70 | disableDefaultUI: true,
71 | minZoom: 8,
72 | zoom: 11
73 | };
74 | this.map = new maps.Map(mapNode, mapConfig);
75 | this.map.addListener("dragend", this.handleDragEnd);
76 | };
77 | public handleDragEnd = () => {
78 | const newCenter = this.map.getCenter();
79 | const lat = newCenter.lat();
80 | const lng = newCenter.lng();
81 | this.setState({
82 | lat,
83 | lng
84 | });
85 | this.reverseGeocodeAddress(lat, lng);
86 | };
87 | public onInputChange = (event: React.ChangeEvent) => {
88 | const {
89 | target: { name, value }
90 | } = event;
91 | this.setState({
92 | [name]: value
93 | } as any);
94 | };
95 | public onInputBlur = async () => {
96 | const { address } = this.state;
97 | const result = await geoCode(address);
98 | if (result !== false) {
99 | const { lat, lng, formatted_address: formatedAddress } = result;
100 | this.setState({
101 | address: formatedAddress,
102 | lat,
103 | lng
104 | });
105 | this.map.panTo({ lat, lng });
106 | }
107 | };
108 | public reverseGeocodeAddress = async (lat: number, lng: number) => {
109 | const reversedAddress = await reverseGeoCode(lat, lng);
110 | if (reversedAddress !== false) {
111 | this.setState({
112 | address: reversedAddress
113 | });
114 | }
115 | };
116 | public onPickPlace = () => {
117 | const { address, lat, lng } = this.state;
118 | const { history } = this.props;
119 | history.push({
120 | pathname: "/add-place",
121 | state: {
122 | address,
123 | lat,
124 | lng
125 | }
126 | });
127 | };
128 | }
129 |
130 | export default FindAddressContainer;
131 |
--------------------------------------------------------------------------------
/src/Routes/FindAddress/FindAddressPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Helmet from "react-helmet";
3 | import AddressBar from "../../Components/AddressBar";
4 | import Button from "../../Components/Button";
5 | import styled from "../../typed-components";
6 |
7 | const Map = styled.div`
8 | position: absolute;
9 | top: 0;
10 | left: 0;
11 | height: 100%;
12 | width: 100%;
13 | z-index: 1;
14 | `;
15 |
16 | const Center = styled.div`
17 | position: absolute;
18 | width: 40px;
19 | height: 40px;
20 | z-index: 2;
21 | font-size: 30px;
22 | margin: auto;
23 | top: 0;
24 | left: 0;
25 | right: 0;
26 | bottom: 0;
27 | `;
28 |
29 | const ExtendedButton = styled(Button)`
30 | position: absolute;
31 | bottom: 50px;
32 | left: 0;
33 | right: 0;
34 | margin: auto;
35 | z-index: 10;
36 | height: auto;
37 | width: 80%;
38 | `;
39 |
40 | interface IProps {
41 | mapRef: any;
42 | address: string;
43 | onInputBlur: () => void;
44 | onPickPlace: () => void;
45 | onInputChange: (event: React.ChangeEvent) => void;
46 | }
47 |
48 | class FindAddressPresenter extends React.Component {
49 | public render() {
50 | const {
51 | mapRef,
52 | address,
53 | onInputChange,
54 | onInputBlur,
55 | onPickPlace
56 | } = this.props;
57 | return (
58 |
59 |
60 | Find Address | Nuber
61 |
62 |
68 |
69 |
📍
70 |
71 |
72 | );
73 | }
74 | }
75 |
76 | export default FindAddressPresenter;
77 |
--------------------------------------------------------------------------------
/src/Routes/FindAddress/index.tsx:
--------------------------------------------------------------------------------
1 | import { GoogleApiWrapper } from "google-maps-react";
2 | import { MAPS_KEY } from "../../keys";
3 | import FindAddressContainer from "./FindAddressContainer";
4 | export default GoogleApiWrapper({
5 | apiKey: MAPS_KEY
6 | })(FindAddressContainer);
7 |
--------------------------------------------------------------------------------
/src/Routes/Home/HomeContainer.tsx:
--------------------------------------------------------------------------------
1 | import { SubscribeToMoreOptions } from "apollo-client";
2 | import React from "react";
3 | import { graphql, Mutation, MutationFn, Query } from "react-apollo";
4 | import ReactDOM from "react-dom";
5 | import { RouteComponentProps } from "react-router-dom";
6 | import { toast } from "react-toastify";
7 | import { geoCode, reverseGeoCode } from "../../mapHelpers";
8 | import { USER_PROFILE } from "../../sharedQueries";
9 | import {
10 | acceptRide,
11 | acceptRideVariables,
12 | getDrivers,
13 | getRides,
14 | reportMovement,
15 | reportMovementVariables,
16 | requestRide,
17 | requestRideVariables,
18 | userProfile
19 | } from "../../types/api";
20 | import HomePresenter from "./HomePresenter";
21 | import {
22 | ACCEPT_RIDE,
23 | GET_NEARBY_DRIVERS,
24 | GET_NEARBY_RIDE,
25 | REPORT_LOCATION,
26 | REQUEST_RIDE,
27 | SUBSCRIBE_NEARBY_RIDES
28 | } from "./HomeQueries";
29 |
30 | interface IState {
31 | isMenuOpen: boolean;
32 | toAddress: string;
33 | toLat: number;
34 | toLng: number;
35 | lat: number;
36 | lng: number;
37 | distance: string;
38 | duration?: string;
39 | price?: string;
40 | fromAddress: string;
41 | isDriving: boolean;
42 | }
43 |
44 | interface IProps extends RouteComponentProps {
45 | google: any;
46 | reportLocation: MutationFn;
47 | }
48 |
49 | class ProfileQuery extends Query {}
50 | class NearbyQueries extends Query {}
51 | class RequestRideMutation extends Mutation {}
52 | class GetNearbyRides extends Query {}
53 | class AcceptRide extends Mutation {}
54 |
55 | class HomeContainer extends React.Component {
56 | public mapRef: any;
57 | public map: google.maps.Map;
58 | public userMarker: google.maps.Marker;
59 | public toMarker: google.maps.Marker;
60 | public directions: google.maps.DirectionsRenderer;
61 | public drivers: google.maps.Marker[];
62 | public state = {
63 | distance: "",
64 | duration: undefined,
65 | fromAddress: "",
66 | isDriving: true,
67 | isMenuOpen: false,
68 | lat: 0,
69 | lng: 0,
70 | price: undefined,
71 | toAddress:
72 | "Athens International Airport (ATH), Attiki Odos, Spata Artemida 190 04, Greece",
73 | toLat: 0,
74 | toLng: 0
75 | };
76 | constructor(props) {
77 | super(props);
78 | this.mapRef = React.createRef();
79 | this.drivers = [];
80 | }
81 | public componentDidMount() {
82 | navigator.geolocation.getCurrentPosition(
83 | this.handleGeoSucces,
84 | this.handleGeoError
85 | );
86 | }
87 | public render() {
88 | const {
89 | isMenuOpen,
90 | toAddress,
91 | price,
92 | distance,
93 | fromAddress,
94 | lat,
95 | lng,
96 | toLat,
97 | toLng,
98 | duration,
99 | isDriving
100 | } = this.state;
101 | return (
102 |
103 | {({ data, loading }) => (
104 |
110 | {() => (
111 |
126 | {requestRideFn => (
127 |
128 | {({ subscribeToMore, data: nearbyRide }) => {
129 | const rideSubscriptionOptions: SubscribeToMoreOptions = {
130 | document: SUBSCRIBE_NEARBY_RIDES,
131 | updateQuery: (prev, { subscriptionData }) => {
132 | if (!subscriptionData.data) {
133 | return prev;
134 | }
135 | const newObject = Object.assign({}, prev, {
136 | GetNearbyRide: {
137 | ...prev.GetNearbyRide,
138 | ride: subscriptionData.data.NearbyRideSubscription
139 | }
140 | });
141 | return newObject;
142 | }
143 | };
144 | if (isDriving) {
145 | subscribeToMore(rideSubscriptionOptions);
146 | }
147 | return (
148 |
152 | {acceptRideFn => (
153 |
167 | )}
168 |
169 | );
170 | }}
171 |
172 | )}
173 |
174 | )}
175 |
176 | )}
177 |
178 | );
179 | }
180 | public toggleMenu = () => {
181 | this.setState(state => {
182 | return {
183 | isMenuOpen: !state.isMenuOpen
184 | };
185 | });
186 | };
187 | public handleGeoSucces = (positon: Position) => {
188 | const {
189 | coords: { latitude, longitude }
190 | } = positon;
191 | this.setState({
192 | lat: latitude,
193 | lng: longitude
194 | });
195 | this.getFromAdress(latitude, longitude);
196 | this.loadMap(latitude, longitude);
197 | };
198 | public getFromAdress = async (lat: number, lng: number) => {
199 | const address = await reverseGeoCode(lat, lng);
200 | if (address) {
201 | this.setState({
202 | fromAddress: address
203 | });
204 | }
205 | };
206 | public loadMap = (lat, lng) => {
207 | const { google } = this.props;
208 | const maps = google.maps;
209 | const mapNode = ReactDOM.findDOMNode(this.mapRef.current);
210 | if (!mapNode) {
211 | this.loadMap(lat, lng);
212 | return;
213 | }
214 | const mapConfig: google.maps.MapOptions = {
215 | center: {
216 | lat,
217 | lng
218 | },
219 | disableDefaultUI: true,
220 | zoom: 13
221 | };
222 | this.map = new maps.Map(mapNode, mapConfig);
223 | const userMarkerOptions: google.maps.MarkerOptions = {
224 | icon: {
225 | path: maps.SymbolPath.CIRCLE,
226 | scale: 7
227 | },
228 | position: {
229 | lat,
230 | lng
231 | }
232 | };
233 | this.userMarker = new maps.Marker(userMarkerOptions);
234 | this.userMarker.setMap(this.map);
235 | const watchOptions: PositionOptions = {
236 | enableHighAccuracy: true
237 | };
238 | navigator.geolocation.watchPosition(
239 | this.handleGeoWatchSuccess,
240 | this.handleGeoWatchError,
241 | watchOptions
242 | );
243 | };
244 | public handleGeoWatchSuccess = (position: Position) => {
245 | const { reportLocation } = this.props;
246 | const {
247 | coords: { latitude, longitude }
248 | } = position;
249 | this.userMarker.setPosition({ lat: latitude, lng: longitude });
250 | this.map.panTo({ lat: latitude, lng: longitude });
251 | reportLocation({
252 | variables: {
253 | lat: parseFloat(latitude.toFixed(10)),
254 | lng: parseFloat(longitude.toFixed(10))
255 | }
256 | });
257 | };
258 | public handleGeoWatchError = () => {
259 | console.log("Error watching you");
260 | };
261 | public handleGeoError = () => {
262 | console.log("No location");
263 | };
264 | public onInputChange = (event: React.ChangeEvent) => {
265 | const {
266 | target: { name, value }
267 | } = event;
268 | this.setState({
269 | [name]: value
270 | } as any);
271 | };
272 | public onAddressSubmit = async () => {
273 | const { toAddress } = this.state;
274 | const { google } = this.props;
275 | const maps = google.maps;
276 | const result = await geoCode(toAddress);
277 | if (result !== false) {
278 | const { lat, lng, formatted_address: formatedAddress } = result;
279 | if (this.toMarker) {
280 | this.toMarker.setMap(null);
281 | }
282 | const toMarkerOptions: google.maps.MarkerOptions = {
283 | position: {
284 | lat,
285 | lng
286 | }
287 | };
288 | this.toMarker = new maps.Marker(toMarkerOptions);
289 | this.toMarker.setMap(this.map);
290 | const bounds = new maps.LatLngBounds();
291 | bounds.extend({ lat, lng });
292 | bounds.extend({ lat: this.state.lat, lng: this.state.lng });
293 | this.map.fitBounds(bounds);
294 | this.setState(
295 | {
296 | toAddress: formatedAddress,
297 | toLat: lat,
298 | toLng: lng
299 | },
300 | this.createPath
301 | );
302 | }
303 | };
304 | public createPath = () => {
305 | const { toLat, toLng, lat, lng } = this.state;
306 | if (this.directions) {
307 | this.directions.setMap(null);
308 | }
309 | const renderOptions: google.maps.DirectionsRendererOptions = {
310 | polylineOptions: {
311 | strokeColor: "#000"
312 | },
313 | suppressMarkers: true
314 | };
315 | this.directions = new google.maps.DirectionsRenderer(renderOptions);
316 | const directionsService: google.maps.DirectionsService = new google.maps.DirectionsService();
317 | const to = new google.maps.LatLng(toLat, toLng);
318 | const from = new google.maps.LatLng(lat, lng);
319 | const directionsOptions: google.maps.DirectionsRequest = {
320 | destination: to,
321 | origin: from,
322 | travelMode: google.maps.TravelMode.DRIVING
323 | };
324 | directionsService.route(directionsOptions, this.handleRouteRequest);
325 | };
326 | public handleRouteRequest = (
327 | result: google.maps.DirectionsResult,
328 | status: google.maps.DirectionsStatus
329 | ) => {
330 | if (status === google.maps.DirectionsStatus.OK) {
331 | const { routes } = result;
332 | const {
333 | distance: { text: distance },
334 | duration: { text: duration }
335 | } = routes[0].legs[0];
336 | this.directions.setDirections(result);
337 | this.directions.setMap(this.map);
338 | this.setState(
339 | {
340 | distance,
341 | duration
342 | },
343 | this.setPrice
344 | );
345 | } else {
346 | toast.error("There is no route there, you have to swim ");
347 | }
348 | };
349 | public setPrice = () => {
350 | const { distance } = this.state;
351 | if (distance) {
352 | this.setState({
353 | price: Number(parseFloat(distance.replace(",", "")) * 3).toFixed(2)
354 | });
355 | }
356 | };
357 | public handleNearbyDrivers = (data: {} | getDrivers) => {
358 | if ("GetNearbyDrivers" in data) {
359 | const {
360 | GetNearbyDrivers: { drivers, ok }
361 | } = data;
362 | if (ok && drivers) {
363 | for (const driver of drivers) {
364 | if (driver && driver.lastLat && driver.lastLng) {
365 | const exisitingDriver:
366 | | google.maps.Marker
367 | | undefined = this.drivers.find(
368 | (driverMarker: google.maps.Marker) => {
369 | const markerID = driverMarker.get("ID");
370 | return markerID === driver.id;
371 | }
372 | );
373 | if (exisitingDriver) {
374 | exisitingDriver.setPosition({
375 | lat: driver.lastLat,
376 | lng: driver.lastLng
377 | });
378 | exisitingDriver.setMap(this.map);
379 | } else {
380 | const markerOptions: google.maps.MarkerOptions = {
381 | icon: {
382 | path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW,
383 | scale: 5
384 | },
385 | position: {
386 | lat: driver.lastLat,
387 | lng: driver.lastLng
388 | }
389 | };
390 | const newMarker: google.maps.Marker = new google.maps.Marker(
391 | markerOptions
392 | );
393 | this.drivers.push(newMarker);
394 | newMarker.set("ID", driver.id);
395 | newMarker.setMap(this.map);
396 | }
397 | }
398 | }
399 | }
400 | }
401 | };
402 | public handleRideRequest = (data: requestRide) => {
403 | const { history } = this.props;
404 | const { RequestRide } = data;
405 | if (RequestRide.ok) {
406 | toast.success("Drive requested, finding a driver");
407 | history.push(`/ride/${RequestRide.ride!.id}`);
408 | } else {
409 | toast.error(RequestRide.error);
410 | }
411 | };
412 | public handleProfileQuery = (data: userProfile) => {
413 | const { GetMyProfile } = data;
414 | if (GetMyProfile.user) {
415 | const {
416 | user: { isDriving }
417 | } = GetMyProfile;
418 | this.setState({
419 | isDriving
420 | });
421 | }
422 | };
423 | public handleRideAcceptance = (data: acceptRide) => {
424 | const { history } = this.props;
425 | const { UpdateRideStatus } = data;
426 | if (UpdateRideStatus.ok) {
427 | history.push(`/ride/${UpdateRideStatus.rideId}`);
428 | }
429 | };
430 | }
431 |
432 | export default graphql(
433 | REPORT_LOCATION,
434 | {
435 | name: "reportLocation"
436 | }
437 | )(HomeContainer);
438 |
--------------------------------------------------------------------------------
/src/Routes/Home/HomePresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { MutationFn } from "react-apollo";
3 | import Helmet from "react-helmet";
4 | import Sidebar from "react-sidebar";
5 | import AddressBar from "../../Components/AddressBar";
6 | import Button from "../../Components/Button";
7 | import Menu from "../../Components/Menu";
8 | import RidePopUp from "../../Components/RidePopUp";
9 | import styled from "../../typed-components";
10 | import { getRides, userProfile } from "../../types/api";
11 |
12 | const Container = styled.div``;
13 |
14 | const MenuButton = styled.button`
15 | appearance: none;
16 | padding: 10px;
17 | position: absolute;
18 | top: 10px;
19 | left: 10px;
20 | text-align: center;
21 | font-weight: 800;
22 | border: 0;
23 | cursor: pointer;
24 | font-size: 20px;
25 | transform: rotate(90deg);
26 | z-index: 2;
27 | background-color: transparent;
28 | `;
29 |
30 | const Map = styled.div`
31 | position: absolute;
32 | height: 100%;
33 | width: 100%;
34 | `;
35 |
36 | const ExtendedButton = styled(Button)`
37 | position: absolute;
38 | bottom: 50px;
39 | left: 0;
40 | right: 0;
41 | margin: auto;
42 | z-index: 10;
43 | height: auto;
44 | width: 80%;
45 | `;
46 |
47 | const RequestButton = ExtendedButton.extend`
48 | bottom: 250px;
49 | `;
50 |
51 | interface IProps {
52 | isMenuOpen: boolean;
53 | toggleMenu: () => void;
54 | loading: boolean;
55 | mapRef: any;
56 | toAddress: string;
57 | onAddressSubmit: () => void;
58 | price?: string;
59 | data?: userProfile;
60 | onInputChange: (event: React.ChangeEvent) => void;
61 | requestRideFn?: MutationFn;
62 | acceptRideFn?: MutationFn;
63 | nearbyRide?: getRides;
64 | }
65 |
66 | const HomePresenter: React.SFC = ({
67 | isMenuOpen,
68 | toggleMenu,
69 | loading,
70 | toAddress,
71 | mapRef,
72 | onInputChange,
73 | onAddressSubmit,
74 | price,
75 | data: { GetMyProfile: { user = null } = {} } = {},
76 | nearbyRide: { GetNearbyRide: { ride = null } = {} } = {},
77 | requestRideFn,
78 | acceptRideFn
79 | }) => (
80 |
81 |
82 | Home | Number
83 |
84 | }
86 | open={isMenuOpen}
87 | onSetOpen={toggleMenu}
88 | styles={{
89 | sidebar: {
90 | backgroundColor: "white",
91 | width: "80%",
92 | zIndex: "10"
93 | }
94 | }}
95 | >
96 | {!loading && |||}
97 | {user &&
98 | !user.isDriving && (
99 |
100 |
106 |
111 |
112 | )}
113 | {price && (
114 |
119 | )}
120 | {ride && (
121 |
131 | )}
132 |
133 |
134 |
135 | );
136 |
137 | export default HomePresenter;
138 |
--------------------------------------------------------------------------------
/src/Routes/Home/HomeQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const REPORT_LOCATION = gql`
4 | mutation reportMovement($lat: Float!, $lng: Float!) {
5 | ReportMovement(lastLat: $lat, lastLng: $lng) {
6 | ok
7 | }
8 | }
9 | `;
10 |
11 | export const GET_NEARBY_DRIVERS = gql`
12 | query getDrivers {
13 | GetNearbyDrivers {
14 | ok
15 | drivers {
16 | id
17 | lastLat
18 | lastLng
19 | }
20 | }
21 | }
22 | `;
23 |
24 | export const REQUEST_RIDE = gql`
25 | mutation requestRide(
26 | $pickUpAddress: String!
27 | $pickUpLat: Float!
28 | $pickUpLng: Float!
29 | $dropOffAddress: String!
30 | $dropOffLat: Float!
31 | $dropOffLng: Float!
32 | $price: Float!
33 | $distance: String!
34 | $duration: String!
35 | ) {
36 | RequestRide(
37 | pickUpAddress: $pickUpAddress
38 | pickUpLat: $pickUpLat
39 | pickUpLng: $pickUpLng
40 | dropOffAddress: $dropOffAddress
41 | dropOffLat: $dropOffLat
42 | dropOffLng: $dropOffLng
43 | price: $price
44 | distance: $distance
45 | duration: $duration
46 | ) {
47 | ok
48 | error
49 | ride {
50 | id
51 | }
52 | }
53 | }
54 | `;
55 |
56 | export const GET_NEARBY_RIDE = gql`
57 | query getRides {
58 | GetNearbyRide {
59 | ok
60 | error
61 | ride {
62 | id
63 | pickUpAddress
64 | dropOffAddress
65 | price
66 | distance
67 | passenger {
68 | fullName
69 | profilePhoto
70 | }
71 | }
72 | }
73 | }
74 | `;
75 |
76 | export const ACCEPT_RIDE = gql`
77 | mutation acceptRide($rideId: Int!) {
78 | UpdateRideStatus(rideId: $rideId, status: ACCEPTED) {
79 | ok
80 | error
81 | rideId
82 | }
83 | }
84 | `;
85 |
86 | export const SUBSCRIBE_NEARBY_RIDES = gql`
87 | subscription nearbyRides {
88 | NearbyRideSubscription {
89 | id
90 | pickUpAddress
91 | dropOffAddress
92 | price
93 | distance
94 | passenger {
95 | fullName
96 | profilePhoto
97 | }
98 | }
99 | }
100 | `;
101 |
--------------------------------------------------------------------------------
/src/Routes/Home/index.tsx:
--------------------------------------------------------------------------------
1 | import { GoogleApiWrapper } from "google-maps-react";
2 | import { MAPS_KEY } from "../../keys";
3 | import HomeContainer from "./HomeContainer";
4 | export default GoogleApiWrapper({
5 | apiKey: MAPS_KEY
6 | })(HomeContainer);
7 |
--------------------------------------------------------------------------------
/src/Routes/Login/LoginPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Helmet from "react-helmet";
3 | import { Link, RouteComponentProps } from "react-router-dom";
4 | import bgImage from "../../images/bg.png";
5 | import styled from "../../typed-components";
6 |
7 | const Container = styled.div`
8 | height: 100vh;
9 | `;
10 |
11 | const Header = styled.header`
12 | height: 70%;
13 | background: linear-gradient(rgba(0, 153, 196, 0.5), rgba(0, 153, 196, 0.4)),
14 | url(${bgImage});
15 | display: flex;
16 | align-items: center;
17 | justify-content: center;
18 | `;
19 |
20 | const Logo = styled.div`
21 | width: 110px;
22 | height: 110px;
23 | background-color: white;
24 | display: flex;
25 | justify-content: center;
26 | align-items: center;
27 | box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 -14px 28px rgba(0, 0, 0, 0.22);
28 | text-transform: uppercase;
29 | font-weight: 500;
30 | font-size: 25px;
31 | `;
32 |
33 | const Title = styled.h1``;
34 |
35 | const Footer = styled.div``;
36 |
37 | const Subtitle = styled.h2`
38 | font-size: 30px;
39 | `;
40 |
41 | const FakeInput = styled.div`
42 | margin: 50px 0px;
43 | font-size: 25px;
44 | font-weight: 300;
45 | `;
46 |
47 | const PhoneLogin = styled.div`
48 | padding: 20px;
49 | cursor: pointer;
50 | `;
51 |
52 | const Grey = styled.span`
53 | color: ${props => props.theme.greyColor};
54 | margin-left: 10px;
55 | `;
56 |
57 | const SocialLogin = styled.div`
58 | border-top: 1px solid ${props => props.theme.greyColor};
59 | padding: 30px 20px;
60 | `;
61 |
62 | const SocialLink = styled.span`
63 | color: ${props => props.theme.blueColor};
64 | font-size: 20px;
65 | cursor: pointer;
66 | `;
67 |
68 | interface IProps extends RouteComponentProps {}
69 |
70 | const OutHomePresenter: React.SFC = () => (
71 |
72 |
73 | Login | Nuber
74 |
75 |
76 |
77 | Nuber
78 |
79 |
80 |
95 |
96 | );
97 |
98 | export default OutHomePresenter;
99 |
--------------------------------------------------------------------------------
/src/Routes/Login/index.tsx:
--------------------------------------------------------------------------------
1 | import LoginPresenter from "./LoginPresenter";
2 | export default LoginPresenter;
3 |
--------------------------------------------------------------------------------
/src/Routes/PhoneLogin/PhoneLoginContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Mutation, MutationFn } from "react-apollo";
3 | import { RouteComponentProps } from "react-router-dom";
4 | import { toast } from "react-toastify";
5 | import {
6 | startPhoneVerification,
7 | startPhoneVerificationVariables
8 | } from "../../types/api";
9 | import PhoneLoginPresenter from "./PhoneLoginPresenter";
10 | import { PHONE_SIGN_IN } from "./PhoneQueries";
11 |
12 | interface IState {
13 | countryCode: string;
14 | phoneNumber: string;
15 | }
16 |
17 | class PhoneSignInMutation extends Mutation<
18 | startPhoneVerification,
19 | startPhoneVerificationVariables
20 | > {}
21 |
22 | class PhoneLoginContainer extends React.Component<
23 | RouteComponentProps,
24 | IState
25 | > {
26 | public phoneMutation: MutationFn;
27 | public state = {
28 | countryCode: "+82",
29 | phoneNumber: ""
30 | };
31 |
32 | public render() {
33 | const { history } = this.props;
34 | const { countryCode, phoneNumber } = this.state;
35 | return (
36 | {
42 | const { StartPhoneVerification } = data;
43 | const phone = `${countryCode}${phoneNumber}`;
44 | if (StartPhoneVerification.ok) {
45 | toast.success("SMS Sent! Redirecting you...");
46 | setTimeout(() => {
47 | history.push({
48 | pathname: "/verify-phone",
49 | state: {
50 | phone
51 | }
52 | });
53 | }, 2000);
54 | } else {
55 | toast.error(StartPhoneVerification.error);
56 | }
57 | }}
58 | >
59 | {(phoneMutation, { loading }) => {
60 | this.phoneMutation = phoneMutation;
61 | return (
62 |
69 | );
70 | }}
71 |
72 | );
73 | }
74 |
75 | public onInputChange: React.ChangeEventHandler<
76 | HTMLInputElement | HTMLSelectElement
77 | > = event => {
78 | const {
79 | target: { name, value }
80 | } = event;
81 | this.setState({
82 | [name]: value
83 | } as any);
84 | };
85 |
86 | public onSubmit: React.FormEventHandler = event => {
87 | event.preventDefault();
88 | const { countryCode, phoneNumber } = this.state;
89 | const phone = `${countryCode}${phoneNumber}`;
90 | const isValid = /^\+[1-9]{1}[0-9]{7,11}$/.test(phone);
91 | if (isValid) {
92 | this.phoneMutation();
93 | } else {
94 | toast.error("Please write a valid phone number");
95 | }
96 | };
97 | }
98 |
99 | export default PhoneLoginContainer;
100 |
--------------------------------------------------------------------------------
/src/Routes/PhoneLogin/PhoneLoginPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Helmet from "react-helmet";
3 | import BackArrow from "../../Components/BackArrow";
4 | import Input from "../../Components/Input";
5 | import countries from "../../countries";
6 | import styled from "../../typed-components";
7 |
8 | const Container = styled.div`
9 | margin-top: 30px;
10 | padding: 50px 20px;
11 | `;
12 |
13 | const BackArrowExtended = styled(BackArrow)`
14 | position: absolute;
15 | top: 20px;
16 | left: 20px;
17 | `;
18 |
19 | const Title = styled.h2`
20 | font-size: 25px;
21 | margin-bottom: 40px;
22 | `;
23 |
24 | const CountrySelect = styled.select`
25 | font-size: 20px;
26 | color: "#2c3e50";
27 | -webkit-appearance: none;
28 | -moz-appearance: none;
29 | appearance: none;
30 | background-color: white;
31 | border: 0;
32 | font-family: "Maven Pro";
33 | margin-bottom: 20px;
34 | width: 90%;
35 | `;
36 |
37 | const CountryOption = styled.option``;
38 |
39 | const Form = styled.form``;
40 |
41 | const Button = styled.button`
42 | box-shadow: 0 18px 35px rgba(50, 50, 93, 0.1), 0 8px 15px rgba(0, 0, 0, 0.07);
43 | background-color: black;
44 | color: white;
45 | padding: 20px;
46 | border-radius: 50%;
47 | display: flex;
48 | align-items: center;
49 | justify-content: center;
50 | position: absolute;
51 | bottom: 50px;
52 | right: 50px;
53 | cursor: pointer;
54 | `;
55 |
56 | interface IProps {
57 | countryCode: string;
58 | phoneNumber: string;
59 | onInputChange: (
60 | event: React.ChangeEvent
61 | ) => void;
62 | onSubmit: (event: React.FormEvent) => void;
63 | loading: boolean;
64 | }
65 |
66 | const PhoneLoginPresenter: React.SFC = ({
67 | countryCode,
68 | phoneNumber,
69 | onInputChange,
70 | onSubmit,
71 | loading
72 | }) => (
73 |
74 |
75 | Phone Login | Number
76 |
77 |
78 | Enter your mobile number
79 |
84 | {countries.map((country, index) => (
85 |
86 | {country.flag} {country.name} ({country.dial_code})
87 |
88 | ))}
89 |
90 |
121 |
122 | );
123 |
124 | export default PhoneLoginPresenter;
125 |
--------------------------------------------------------------------------------
/src/Routes/PhoneLogin/PhoneQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const PHONE_SIGN_IN = gql`
4 | mutation startPhoneVerification($phoneNumber: String!) {
5 | StartPhoneVerification(phoneNumber: $phoneNumber) {
6 | ok
7 | error
8 | }
9 | }
10 | `;
11 |
--------------------------------------------------------------------------------
/src/Routes/PhoneLogin/index.tsx:
--------------------------------------------------------------------------------
1 | import PhoneLoginContainer from "./PhoneLoginContainer";
2 | export default PhoneLoginContainer;
3 |
--------------------------------------------------------------------------------
/src/Routes/Places/PlacesContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Query } from "react-apollo";
3 | import { GET_PLACES } from "../../sharedQueries";
4 | import { getPlaces } from "../../types/api";
5 | import PlacesPresenter from "./PlacesPresenter";
6 |
7 | class PlacesQuery extends Query {}
8 |
9 | class PlacesContainer extends React.Component {
10 | public render() {
11 | return (
12 |
13 | {({ data, loading }) => (
14 |
15 | )}
16 |
17 | );
18 | }
19 | }
20 | export default PlacesContainer;
21 |
--------------------------------------------------------------------------------
/src/Routes/Places/PlacesPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Helmet from "react-helmet";
3 | import { Link } from "react-router-dom";
4 | import Header from "../../Components/Header";
5 | import Place from "../../Components/Place";
6 | import styled from "../../typed-components";
7 | import { getPlaces } from "../../types/api";
8 |
9 | const Container = styled.div`
10 | padding: 0 40px;
11 | `;
12 |
13 | const SLink = styled(Link)`
14 | text-decoration: underline;
15 | `;
16 |
17 | interface IProps {
18 | data?: getPlaces;
19 | loading: boolean;
20 | }
21 |
22 | const PlacesPresenter: React.SFC = ({
23 | data: { GetMyPlaces: { places = null } = {} } = {},
24 | loading
25 | }) => (
26 |
27 |
28 | Places | Number
29 |
30 |
31 |
32 | {!loading && places && places.length === 0 && "You have no places"}
33 | {!loading &&
34 | places &&
35 | places.map(place => (
36 |
43 | ))}
44 | Add some places!
45 |
46 |
47 | );
48 |
49 | export default PlacesPresenter;
50 |
--------------------------------------------------------------------------------
/src/Routes/Places/index.tsx:
--------------------------------------------------------------------------------
1 | import PlacesContainer from "./PlacesContainer";
2 | export default PlacesContainer;
3 |
--------------------------------------------------------------------------------
/src/Routes/Ride/RideContainer.tsx:
--------------------------------------------------------------------------------
1 | import { SubscribeToMoreOptions } from "apollo-client";
2 | import React from "react";
3 | import { Mutation, Query } from "react-apollo";
4 | import { RouteComponentProps } from "react-router-dom";
5 | import { USER_PROFILE } from "../../sharedQueries";
6 | import {
7 | getRide,
8 | getRideVariables,
9 | updateRide,
10 | updateRideVariables,
11 | userProfile
12 | } from "../../types/api";
13 | import RidePresenter from "./RidePresenter";
14 | import { GET_RIDE, RIDE_SUBSCRIPTION, UPDATE_RIDE_STATUS } from "./RideQueries";
15 |
16 | class RideQuery extends Query {}
17 | class ProfileQuery extends Query {}
18 | class RideUpdate extends Mutation {}
19 |
20 | interface IProps extends RouteComponentProps {}
21 |
22 | class RideContainer extends React.Component {
23 | constructor(props: IProps) {
24 | super(props);
25 | if (!props.match.params.rideId) {
26 | props.history.push("/");
27 | }
28 | }
29 | public render() {
30 | const {
31 | match: {
32 | params: { rideId }
33 | }
34 | } = this.props;
35 | return (
36 |
37 | {({ data: userData }) => (
38 |
39 | {({ data, loading, subscribeToMore }) => {
40 | const subscribeOptions: SubscribeToMoreOptions = {
41 | document: RIDE_SUBSCRIPTION,
42 | updateQuery: (prev, { subscriptionData }) => {
43 | if (!subscriptionData.data) {
44 | return prev;
45 | }
46 | const {
47 | data: {
48 | RideStatusSubscription: { status }
49 | }
50 | } = subscriptionData;
51 | if (status === "FINISHED") {
52 | window.location.href = "/";
53 | }
54 | }
55 | };
56 | subscribeToMore(subscribeOptions);
57 | return (
58 |
59 | {updateRideFn => (
60 |
66 | )}
67 |
68 | );
69 | }}
70 |
71 | )}
72 |
73 | );
74 | }
75 | }
76 | export default RideContainer;
77 |
--------------------------------------------------------------------------------
/src/Routes/Ride/RidePresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { MutationFn } from "react-apollo";
3 | import { Link } from "react-router-dom";
4 | import Button from "../../Components/Button";
5 | import styled from "../../typed-components";
6 | import { getRide, userProfile } from "../../types/api";
7 |
8 | const Container = styled.div`
9 | padding: 40px;
10 | `;
11 |
12 | const Title = styled.h4`
13 | font-weight: 800;
14 | margin-top: 30px;
15 | margin-bottom: 10px;
16 | &:first-child {
17 | margin-top: 0;
18 | }
19 | `;
20 |
21 | const Data = styled.span`
22 | color: ${props => props.theme.blueColor};
23 | `;
24 |
25 | const Img = styled.img`
26 | border-radius: 50%;
27 | margin-right: 20px;
28 | max-width: 50px;
29 | height: 50px;
30 | `;
31 |
32 | const Passenger = styled.div`
33 | display: flex;
34 | align-items: center;
35 | margin-bottom: 20px;
36 | `;
37 |
38 | const Buttons = styled.div`
39 | margin: 30px 0px;
40 | `;
41 |
42 | const ExtendedButton = styled(Button)`
43 | margin-bottom: 30px;
44 | `;
45 |
46 | interface IProps {
47 | data?: getRide;
48 | userData?: userProfile;
49 | loading: boolean;
50 | updateRideFn: MutationFn;
51 | }
52 |
53 | const RidePresenter: React.SFC = ({
54 | data: { GetRide: { ride = null } = {} } = {},
55 | userData: { GetMyProfile: { user = null } = {} } = {},
56 | updateRideFn
57 | }) => (
58 |
59 | {ride &&
60 | user && (
61 |
62 | Passenger
63 |
64 |
65 | {ride.passenger.fullName!}
66 |
67 | {ride.driver && (
68 |
69 | Driver
70 |
71 |
72 | {ride.driver.fullName!}
73 |
74 |
75 | )}
76 | From
77 | {ride.pickUpAddress}
78 | To
79 | {ride.dropOffAddress}
80 | Price
81 | {ride.price}
82 | Distance
83 | {ride.distance}
84 | Duration
85 | {ride.duration}
86 | Status
87 | {ride.status}
88 |
89 | {ride.driver &&
90 | ride.driver.id === user.id &&
91 | ride.status === "ACCEPTED" && (
92 |
95 | updateRideFn({
96 | variables: {
97 | rideId: ride.id,
98 | status: "ONROUTE"
99 | }
100 | })
101 | }
102 | />
103 | )}
104 | {ride.driver &&
105 | ride.driver.id === user.id &&
106 | ride.status === "ONROUTE" && (
107 |
110 | updateRideFn({
111 | variables: {
112 | rideId: ride.id,
113 | status: "FINISHED"
114 | }
115 | })
116 | }
117 | />
118 | )}
119 |
120 | {ride.status !== "REQUESTING" && (
121 |
122 |
123 |
124 | )}
125 |
126 |
127 | )}
128 |
129 | );
130 |
131 | export default RidePresenter;
132 |
--------------------------------------------------------------------------------
/src/Routes/Ride/RideQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const GET_RIDE = gql`
4 | query getRide($rideId: Int!) {
5 | GetRide(rideId: $rideId) {
6 | ok
7 | error
8 | ride {
9 | id
10 | status
11 | pickUpAddress
12 | dropOffAddress
13 | price
14 | distance
15 | duration
16 | driver {
17 | id
18 | fullName
19 | profilePhoto
20 | }
21 | passenger {
22 | id
23 | fullName
24 | profilePhoto
25 | }
26 | chatId
27 | }
28 | }
29 | }
30 | `;
31 |
32 | export const RIDE_SUBSCRIPTION = gql`
33 | subscription rideUpdates {
34 | RideStatusSubscription {
35 | id
36 | status
37 | pickUpAddress
38 | dropOffAddress
39 | price
40 | distance
41 | duration
42 | driver {
43 | id
44 | fullName
45 | profilePhoto
46 | }
47 | passenger {
48 | id
49 | fullName
50 | profilePhoto
51 | }
52 | chatId
53 | }
54 | }
55 | `;
56 |
57 | export const UPDATE_RIDE_STATUS = gql`
58 | mutation updateRide($rideId: Int!, $status: StatusOptions!) {
59 | UpdateRideStatus(rideId: $rideId, status: $status) {
60 | ok
61 | error
62 | rideId
63 | }
64 | }
65 | `;
66 |
--------------------------------------------------------------------------------
/src/Routes/Ride/index.tsx:
--------------------------------------------------------------------------------
1 | import RideContainer from "./RideContainer";
2 | export default RideContainer;
3 |
--------------------------------------------------------------------------------
/src/Routes/Settings/SettingsContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Mutation, Query } from "react-apollo";
3 | import { GET_PLACES, USER_PROFILE } from "../../sharedQueries";
4 | import { LOG_USER_OUT } from "../../sharedQueries.local";
5 | import { getPlaces, userProfile } from "../../types/api";
6 | import SettingsPresenter from "./SettingsPresenter";
7 |
8 | class MiniProfileQuery extends Query {}
9 | class PlacesQuery extends Query {}
10 |
11 | class SettingsContainer extends React.Component {
12 | public render() {
13 | return (
14 |
15 | {logUserOut => (
16 |
17 | {({ data: userData, loading: userDataLoading }) => (
18 |
19 | {({ data: placesData, loading: placesLoading }) => (
20 |
27 | )}
28 |
29 | )}
30 |
31 | )}
32 |
33 | );
34 | }
35 | }
36 |
37 | export default SettingsContainer;
38 |
--------------------------------------------------------------------------------
/src/Routes/Settings/SettingsPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { MutationFn } from "react-apollo";
3 | import Helmet from "react-helmet";
4 | import { Link } from "react-router-dom";
5 | import Header from "../../Components/Header";
6 | import Place from "../../Components/Place";
7 | import styled from "../../typed-components";
8 | import { getPlaces, userProfile } from "../../types/api";
9 |
10 | const Container = styled.div`
11 | padding: 0px 40px;
12 | `;
13 |
14 | const Image = styled.img`
15 | height: 60px;
16 | width: 60px;
17 | border-radius: 50%;
18 | `;
19 |
20 | const GridLink = styled(Link)`
21 | display: grid;
22 | grid-template-columns: 1fr 4fr;
23 | grid-gap: 10px;
24 | margin-bottom: 10px;
25 | `;
26 |
27 | const Keys = styled.div``;
28 |
29 | const Key = styled.span`
30 | display: block;
31 | margin-bottom: 5px;
32 | `;
33 |
34 | const FakeLink = styled.span`
35 | text-decoration: underline;
36 | cursor: pointer;
37 | `;
38 |
39 | const SLink = styled(Link)`
40 | display: block;
41 | text-decoration: underline;
42 | margin: 20px 0px;
43 | `;
44 |
45 | interface IProps {
46 | logUserOut: MutationFn;
47 | userData?: userProfile;
48 | placesData?: getPlaces;
49 | userDataLoading: boolean;
50 | placesLoading: boolean;
51 | }
52 |
53 | const SettingsPresenter: React.SFC = ({
54 | logUserOut,
55 | userData: { GetMyProfile: { user = null } = {} } = {},
56 | placesData: { GetMyPlaces: { places = null } = {} } = {},
57 | userDataLoading,
58 | placesLoading
59 | }) => (
60 |
61 |
62 | Settings | Nuber
63 |
64 |
65 |
66 |
67 | {!userDataLoading &&
68 | user &&
69 | user.profilePhoto &&
70 | user.email &&
71 | user.fullName && (
72 |
73 |
74 |
75 | {user.fullName}
76 | {user.email}
77 |
78 |
79 | )}
80 |
81 | {!placesLoading &&
82 | places &&
83 | places.map(place => (
84 |
91 | ))}
92 | Go to Places
93 | Log Out
94 |
95 |
96 | );
97 |
98 | export default SettingsPresenter;
99 |
--------------------------------------------------------------------------------
/src/Routes/Settings/index.tsx:
--------------------------------------------------------------------------------
1 | import SettingsContainer from "./SettingsContainer";
2 | export default SettingsContainer;
3 |
--------------------------------------------------------------------------------
/src/Routes/SocialLogin/SocialLoginContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Mutation, MutationFn } from "react-apollo";
3 | import { RouteComponentProps } from "react-router-dom";
4 | import { toast } from "react-toastify";
5 | import { LOG_USER_IN } from "../../sharedQueries.local";
6 | import { facebookConnect, facebookConnectVariables } from "../../types/api";
7 | import SocialLoginPresenter from "./SocialLoginPresenter";
8 | import { FACEBOOK_CONNECT } from "./SocialLoginQueries";
9 |
10 | class LoginMutation extends Mutation<
11 | facebookConnect,
12 | facebookConnectVariables
13 | > {}
14 |
15 | interface IState {
16 | firstName: string;
17 | lastName: string;
18 | email?: string;
19 | fbId: string;
20 | }
21 |
22 | interface IProps extends RouteComponentProps {}
23 |
24 | class SocialLoginContainer extends React.Component {
25 | public state = {
26 | email: "",
27 | fbId: "",
28 | firstName: "",
29 | lastName: ""
30 | };
31 | public facebookMutation: MutationFn;
32 | public render() {
33 | return (
34 |
35 | {logUserIn => (
36 | {
39 | const { FacebookConnect } = data;
40 | if (FacebookConnect.ok) {
41 | logUserIn({
42 | variables: {
43 | token: FacebookConnect.token
44 | }
45 | });
46 | } else {
47 | toast.error(FacebookConnect.error);
48 | }
49 | }}
50 | >
51 | {(facebookMutation, { loading }) => {
52 | this.facebookMutation = facebookMutation;
53 | return (
54 |
55 | );
56 | }}
57 |
58 | )}
59 |
60 | );
61 | }
62 |
63 | public loginCallback = response => {
64 | const { name, first_name, last_name, email, id, accessToken } = response;
65 | if (accessToken) {
66 | toast.success(`Welcome ${name}!`);
67 | this.facebookMutation({
68 | variables: {
69 | email,
70 | fbId: id,
71 | firstName: first_name,
72 | lastName: last_name
73 | }
74 | });
75 | } else {
76 | toast.error("Could not log you in 😔");
77 | }
78 | };
79 | }
80 |
81 | export default SocialLoginContainer;
82 |
--------------------------------------------------------------------------------
/src/Routes/SocialLogin/SocialLoginPresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import FacebookLogin from "react-facebook-login/dist/facebook-login-render-props";
3 | import Helmet from "react-helmet";
4 | import BackArrow from "../../Components/BackArrow";
5 | import styled from "../../typed-components";
6 |
7 | const Container = styled.div`
8 | margin-top: 30px;
9 | padding: 50px 20px;
10 | `;
11 |
12 | const Title = styled.h2`
13 | font-size: 25px;
14 | margin-bottom: 40px;
15 | `;
16 |
17 | const Link = styled.span`
18 | display: flex;
19 | align-items: center;
20 | cursor: pointer;
21 | `;
22 |
23 | const Icon = styled.span`
24 | margin-right: 10px;
25 | `;
26 |
27 | const BackArrowExtended = styled(BackArrow)`
28 | position: absolute;
29 | top: 20px;
30 | left: 20px;
31 | `;
32 |
33 | interface IProps {
34 | loginCallback: (response) => void;
35 | }
36 |
37 | const SocialLoginPresenter: React.SFC = ({ loginCallback }) => (
38 |
39 |
40 | Social Login | Nuber
41 |
42 | Choose an account
43 |
44 | (
50 |
51 |
52 |
61 |
62 | Facebook
63 |
64 | )}
65 | />
66 |
67 | );
68 |
69 | export default SocialLoginPresenter;
70 |
--------------------------------------------------------------------------------
/src/Routes/SocialLogin/SocialLoginQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const FACEBOOK_CONNECT = gql`
4 | mutation facebookConnect(
5 | $firstName: String!
6 | $lastName: String!
7 | $email: String
8 | $fbId: String!
9 | ) {
10 | FacebookConnect(
11 | firstName: $firstName
12 | lastName: $lastName
13 | email: $email
14 | fbId: $fbId
15 | ) {
16 | ok
17 | error
18 | token
19 | }
20 | }
21 | `;
22 |
--------------------------------------------------------------------------------
/src/Routes/SocialLogin/index.tsx:
--------------------------------------------------------------------------------
1 | import SocialLoginContainer from "./SocialLoginContainer";
2 | export default SocialLoginContainer;
3 |
--------------------------------------------------------------------------------
/src/Routes/VerifyPhone/VerifyPhoneContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Mutation } from "react-apollo";
3 | import { RouteComponentProps } from "react-router-dom";
4 | import { toast } from "react-toastify";
5 | import { LOG_USER_IN } from "../../sharedQueries.local";
6 | import { verifyPhone, verifyPhoneVariables } from "../../types/api";
7 | import VerifyPhonePresenter from "./VerifyPhonePresenter";
8 | import { VERIFY_PHONE } from "./VerifyPhoneQueries";
9 |
10 | interface IState {
11 | verificationKey: string;
12 | phoneNumber: string;
13 | }
14 |
15 | interface IProps extends RouteComponentProps {}
16 |
17 | class VerifyMutation extends Mutation {}
18 |
19 | class VerifyPhoneContainer extends React.Component {
20 | constructor(props: IProps) {
21 | super(props);
22 | if (!props.location.state) {
23 | props.history.push("/");
24 | }
25 | this.state = {
26 | phoneNumber: props.location.state.phone,
27 | verificationKey: ""
28 | };
29 | }
30 | public render() {
31 | const { verificationKey, phoneNumber } = this.state;
32 | return (
33 |
34 | {logUserIn => (
35 | {
42 | const { CompletePhoneVerification } = data;
43 | if (CompletePhoneVerification.ok) {
44 | if (CompletePhoneVerification.token) {
45 | logUserIn({
46 | variables: {
47 | token: CompletePhoneVerification.token
48 | }
49 | });
50 | }
51 | toast.success("You're verified, loggin in now");
52 | } else {
53 | toast.error(CompletePhoneVerification.error);
54 | }
55 | }}
56 | >
57 | {(mutation, { loading }) => (
58 |
64 | )}
65 |
66 | )}
67 |
68 | );
69 | }
70 |
71 | public onInputChange: React.ChangeEventHandler = event => {
72 | const {
73 | target: { name, value }
74 | } = event;
75 | this.setState({
76 | [name]: value
77 | } as any);
78 | };
79 | }
80 |
81 | export default VerifyPhoneContainer;
82 |
--------------------------------------------------------------------------------
/src/Routes/VerifyPhone/VerifyPhonePresenter.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { MutationFn } from "react-apollo";
3 | import Helmet from "react-helmet";
4 | import Button from "../../Components/Button";
5 | import Form from "../../Components/Form";
6 | import Header from "../../Components/Header";
7 | import Input from "../../Components/Input";
8 | import styled from "../../typed-components";
9 |
10 | const Container = styled.div``;
11 |
12 | const ExtendedForm = styled(Form)`
13 | padding: 0px 40px;
14 | `;
15 |
16 | const ExtendedInput = styled(Input)`
17 | margin-bottom: 20px;
18 | `;
19 |
20 | interface IProps {
21 | verificationKey: string;
22 | onChange: (event: React.ChangeEvent) => void;
23 | onSubmit: MutationFn;
24 | loading: boolean;
25 | }
26 |
27 | const VerifyPhonePresenter: React.SFC = ({
28 | verificationKey,
29 | onChange,
30 | onSubmit,
31 | loading
32 | }) => (
33 |
34 |
35 | Verify Phone | Number
36 |
37 |
38 |
39 |
45 |
50 |
51 |
52 | );
53 |
54 | export default VerifyPhonePresenter;
55 |
--------------------------------------------------------------------------------
/src/Routes/VerifyPhone/VerifyPhoneQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const VERIFY_PHONE = gql`
4 | mutation verifyPhone($key: String!, $phoneNumber: String!) {
5 | CompletePhoneVerification(key: $key, phoneNumber: $phoneNumber) {
6 | ok
7 | error
8 | token
9 | }
10 | }
11 | `;
12 |
--------------------------------------------------------------------------------
/src/Routes/VerifyPhone/index.tsx:
--------------------------------------------------------------------------------
1 | import VerifyPhoneContainer from "./VerifyPhoneContainer";
2 | export default VerifyPhoneContainer;
3 |
--------------------------------------------------------------------------------
/src/apollo.ts:
--------------------------------------------------------------------------------
1 | import { InMemoryCache } from "apollo-cache-inmemory";
2 | import { ApolloClient } from "apollo-client";
3 | import { ApolloLink, concat, Operation, split } from "apollo-link";
4 | import { onError } from "apollo-link-error";
5 | import { HttpLink } from "apollo-link-http";
6 | import { withClientState } from "apollo-link-state";
7 | import { WebSocketLink } from "apollo-link-ws";
8 | import { getMainDefinition } from "apollo-utilities";
9 | import { toast } from "react-toastify";
10 |
11 | const isDev = process.env.NODE_ENV === "development";
12 | console.log(isDev);
13 |
14 | const getToken = () => {
15 | const token = localStorage.getItem("jwt");
16 | if (token) {
17 | return token;
18 | } else {
19 | return "";
20 | }
21 | };
22 |
23 | const cache = new InMemoryCache();
24 |
25 | const authMiddleware = new ApolloLink((operation: Operation, forward: any) => {
26 | operation.setContext({
27 | headers: {
28 | "X-JWT": getToken()
29 | }
30 | });
31 | return forward(operation);
32 | });
33 |
34 | const httpLink = new HttpLink({
35 | uri: isDev
36 | ? "http://localhost:4000/graphql"
37 | : "https://nuberserver.now.sh/graphql"
38 | });
39 |
40 | const wsLink = new WebSocketLink({
41 | options: {
42 | connectionParams: {
43 | "X-JWT": getToken()
44 | },
45 | reconnect: true
46 | },
47 | uri: isDev
48 | ? "ws://localhost:4000/subscription"
49 | : "ws://nuberserver.now.sh/subscription"
50 | });
51 |
52 | const combinedLinks = split(
53 | ({ query }) => {
54 | const { kind, operation }: any = getMainDefinition(query);
55 | return kind === "OperationDefinition" && operation === "subscription";
56 | },
57 | wsLink,
58 | httpLink
59 | );
60 |
61 | const errorLink = onError(({ graphQLErrors, networkError }) => {
62 | if (graphQLErrors) {
63 | graphQLErrors.map(({ message }) => {
64 | toast.error(`Unexpected error: ${message}`);
65 | });
66 | }
67 | if (networkError) {
68 | toast.error(`Network error: ${networkError}`);
69 | }
70 | });
71 |
72 | const localStateLink = withClientState({
73 | cache,
74 | defaults: {
75 | auth: {
76 | __typename: "Auth",
77 | isLoggedIn: Boolean(localStorage.getItem("jwt"))
78 | }
79 | },
80 | resolvers: {
81 | Mutation: {
82 | logUserIn: (_, { token }, { cache: appCache }) => {
83 | localStorage.setItem("jwt", token);
84 | appCache.writeData({
85 | data: {
86 | auth: {
87 | __typename: "Auth",
88 | isLoggedIn: true
89 | }
90 | }
91 | });
92 | return null;
93 | },
94 | logUserOut: (_, __, { cache: appCache }) => {
95 | localStorage.removeItem("jwt");
96 | appCache.writeData({
97 | data: {
98 | auth: {
99 | __typename: "Auth",
100 | isLoggedIn: false
101 | }
102 | }
103 | });
104 | return null;
105 | }
106 | }
107 | }
108 | });
109 |
110 | const client = new ApolloClient({
111 | cache,
112 | link: ApolloLink.from([
113 | errorLink,
114 | localStateLink,
115 | concat(authMiddleware, combinedLinks)
116 | ])
117 | });
118 |
119 | export default client;
120 |
--------------------------------------------------------------------------------
/src/countries.ts:
--------------------------------------------------------------------------------
1 | const countries = [
2 | { name: "Afghanistan", dial_code: "+93", code: "AF", flag: "🇦🇫" },
3 | { name: "Åland Islands", dial_code: "+358", code: "AX", flag: "🇦🇽" },
4 | { name: "Albania", dial_code: "+355", code: "AL", flag: "🇦🇱" },
5 | { name: "Algeria", dial_code: "+213", code: "DZ", flag: "🇩🇿" },
6 | {
7 | name: "American Samoa",
8 | flag: "🇺🇸",
9 | dial_code: "+1684",
10 | code: "AS"
11 | },
12 | { name: "Andorra", dial_code: "+376", code: "AD", flag: "🇦🇩" },
13 | { name: "Angola", dial_code: "+244", code: "AO", flag: "🇦🇴" },
14 | { name: "Anguilla", dial_code: "+1264", code: "AI", flag: "🇦🇮" },
15 | { name: "Antarctica", dial_code: "+672", code: "AQ", flag: "🇦🇶" },
16 | {
17 | name: "Antigua and Barbuda",
18 | dial_code: "+1268",
19 | code: "AG",
20 | flag: "🇦🇬"
21 | },
22 | { name: "Argentina", dial_code: "+54", code: "AR", flag: "🇦🇷" },
23 | { name: "Armenia", dial_code: "+374", code: "AM", flag: "🇦🇲" },
24 | { name: "Aruba", dial_code: "+297", code: "AW", flag: "🇦🇼" },
25 | { name: "Australia", dial_code: "+61", code: "AU", flag: "🇦🇺" },
26 | { name: "Austria", dial_code: "+43", code: "AT", flag: "🇦🇹" },
27 | { name: "Azerbaijan", dial_code: "+994", code: "AZ", flag: "🇦🇿" },
28 | { name: "Bahamas", dial_code: "+1242", code: "BS", flag: "🇧🇸" },
29 | { name: "Bahrain", dial_code: "+973", code: "BH", flag: "🇧🇸" },
30 | { name: "Bangladesh", dial_code: "+880", code: "BD", flag: "🇧🇩" },
31 | { name: "Barbados", dial_code: "+1246", code: "BB", flag: "🇧🇧" },
32 | { name: "Belarus", dial_code: "+375", code: "BY", flag: "🇧🇾" },
33 | { name: "Belgium", dial_code: "+32", code: "BE", flag: "🇧🇪" },
34 | { name: "Belize", dial_code: "+501", code: "BZ", flag: "🇧🇿" },
35 | { name: "Benin", dial_code: "+229", code: "BJ", flag: "🇧🇯" },
36 | { name: "Bermuda", dial_code: "+1441", code: "BM", flag: "🇧🇲" },
37 | { name: "Bhutan", dial_code: "+975", code: "BT", flag: "🇧🇹" },
38 | {
39 | name: "Bolivia, Plurinational State of bolivia",
40 | dial_code: "+591",
41 | code: "BO",
42 | flag: "🇧🇴"
43 | },
44 | {
45 | name: "Bosnia and Herzegovina",
46 | dial_code: "+387",
47 | code: "BA",
48 | flag: "🇧🇦"
49 | },
50 | { name: "Botswana", dial_code: "+267", code: "BW", flag: "🇧🇼" },
51 | { name: "Bouvet Island", dial_code: "+47", code: "BV", flag: "🏳" },
52 | { name: "Brazil", dial_code: "+55", code: "BR", flag: "🇧🇷" },
53 | {
54 | name: "British Indian Ocean Territory",
55 | dial_code: "+246",
56 | code: "IO",
57 | flag: "🇮🇴"
58 | },
59 | {
60 | name: "Brunei Darussalam",
61 | dial_code: "+673",
62 | code: "BN",
63 | flag: "🇧🇳"
64 | },
65 | { name: "Bulgaria", dial_code: "+359", code: "BG", flag: "🇧🇬" },
66 | { name: "Burkina Faso", dial_code: "+226", code: "BF", flag: "🇧🇫" },
67 | { name: "Burundi", dial_code: "+257", code: "BI", flag: "🇧🇮" },
68 | { name: "Cambodia", dial_code: "+855", code: "KH", flag: "🇰🇭" },
69 | { name: "Cameroon", dial_code: "+237", code: "CM", flag: "🇨🇲" },
70 | { name: "Canada", dial_code: "+1", code: "CA", flag: "🇨🇦" },
71 | { name: "Cape Verde", dial_code: "+238", code: "CV", flag: "🇨🇻" },
72 | {
73 | name: "Cayman Islands",
74 | dial_code: "+ 345",
75 | code: "KY",
76 | flag: "🇰🇾"
77 | },
78 | {
79 | name: "Central African Republic",
80 | dial_code: "+236",
81 | code: "CF",
82 | flag: "🇨🇫"
83 | },
84 | { name: "Chad", dial_code: "+235", code: "TD", flag: "🇹🇩" },
85 | { name: "Chile", dial_code: "+56", code: "CL", flag: "🇨🇱" },
86 | { name: "China", dial_code: "+86", code: "CN", flag: "🇨🇳" },
87 | {
88 | name: "Christmas Island",
89 | dial_code: "+61",
90 | code: "CX",
91 | flag: "🇨🇽"
92 | },
93 | {
94 | name: "Cocos (Keeling) Islands",
95 | dial_code: "+61",
96 | code: "CC",
97 | flag: "🇨🇨"
98 | },
99 | { name: "Colombia", dial_code: "+57", code: "CO", flag: "" },
100 | { name: "Comoros", dial_code: "+269", code: "KM", flag: "🇰🇲" },
101 | { name: "Congo", dial_code: "+242", code: "CG", flag: "🇨🇬" },
102 | {
103 | name: "Congo, The Democratic Republic of the Congo",
104 | dial_code: "+243",
105 | code: "CD",
106 | flag: "🇨🇩"
107 | },
108 | { name: "Cook Islands", dial_code: "+682", code: "CK", flag: "🇨🇰" },
109 | { name: "Costa Rica", dial_code: "+506", code: "CR", flag: "🇨🇷" },
110 | { name: "Cote d'Ivoire", dial_code: "+225", code: "CI", flag: "🇨🇮" },
111 | { name: "Croatia", dial_code: "+385", code: "HR", flag: "🇭🇷" },
112 | { name: "Cuba", dial_code: "+53", code: "CU", flag: "🇨🇺" },
113 | { name: "Cyprus", dial_code: "+357", code: "CY", flag: "🇨🇾" },
114 | { name: "Czech Republic", dial_code: "+420", code: "CZ", flag: "🇨🇿" },
115 | { name: "Denmark", dial_code: "+45", code: "DK", flag: "🇩🇰" },
116 | { name: "Djibouti", dial_code: "+253", code: "DJ", flag: "🇩🇯" },
117 | { name: "Dominica", dial_code: "+1767", code: "DM", flag: "🇩🇲" },
118 | {
119 | name: "Dominican Republic",
120 | dial_code: "+1849",
121 | code: "DO",
122 | flag: "🇨🇩"
123 | },
124 | { name: "Ecuador", dial_code: "+593", code: "EC", flag: "🇪🇨" },
125 | { name: "Egypt", dial_code: "+20", code: "EG", flag: "🇪🇬" },
126 | { name: "El Salvador", dial_code: "+503", code: "SV", flag: "🇸🇻" },
127 | {
128 | name: "Equatorial Guinea",
129 | dial_code: "+240",
130 | code: "GQ",
131 | flag: "🇬🇶"
132 | },
133 | { name: "Eritrea", dial_code: "+291", code: "ER", flag: "🇪🇷" },
134 | { name: "Estonia", dial_code: "+372", code: "EE", flag: "🇪🇪" },
135 | { name: "Ethiopia", dial_code: "+251", code: "ET", flag: "🇪🇹" },
136 | {
137 | name: "Falkland Islands (Malvinas)",
138 | dial_code: "+500",
139 | code: "FK",
140 | flag: "🇫🇰"
141 | },
142 | { name: "Faroe Islands", dial_code: "+298", code: "FO", flag: "" },
143 | { name: "Fiji", dial_code: "+679", code: "FJ", flag: "🇫🇯" },
144 | { name: "Finland", dial_code: "+358", code: "FI", flag: "🇫🇮" },
145 | { name: "France", dial_code: "+33", code: "FR", flag: "🇫🇷" },
146 | { name: "French Guiana", dial_code: "+594", code: "GF", flag: "🇬🇫" },
147 | {
148 | name: "French Polynesia",
149 | dial_code: "+689",
150 | code: "PF",
151 | flag: "🇵🇫"
152 | },
153 | {
154 | name: "French Southern Territories",
155 | dial_code: "+262",
156 | code: "TF",
157 | flag: "🇹🇫"
158 | },
159 | { name: "Gabon", dial_code: "+241", code: "GA", flag: "🇬🇦" },
160 | { name: "Gambia", dial_code: "+220", code: "GM", flag: "🇬🇲" },
161 | { name: "Georgia", dial_code: "+995", code: "GE", flag: "🇬🇪" },
162 | { name: "Germany", dial_code: "+49", code: "DE", flag: "🇩🇪" },
163 | { name: "Ghana", dial_code: "+233", code: "GH", flag: "🇬🇭" },
164 | { name: "Gibraltar", dial_code: "+350", code: "GI", flag: "🇬🇮" },
165 | { name: "Greece", dial_code: "+30", code: "GR", flag: "🇬🇷" },
166 | { name: "Greenland", dial_code: "+299", code: "GL", flag: "🇬🇱" },
167 | { name: "Grenada", dial_code: "+1473", code: "GD", flag: "🇬🇩" },
168 | { name: "Guadeloupe", dial_code: "+590", code: "GP", flag: "🇬🇵" },
169 | { name: "Guam", dial_code: "+1671", code: "GU", flag: "🇬🇺" },
170 | { name: "Guatemala", dial_code: "+502", code: "GT", flag: "🇬🇹" },
171 | { name: "Guernsey", dial_code: "+44", code: "GG", flag: "🇬🇬" },
172 | { name: "Guinea", dial_code: "+224", code: "GN", flag: "🇬🇳" },
173 | { name: "Guinea-Bissau", dial_code: "+245", code: "GW", flag: "🇬🇼" },
174 | { name: "Guyana", dial_code: "+592", code: "GY", flag: "🇬🇾" },
175 | { name: "Haiti", dial_code: "+509", code: "HT", flag: "🇭🇹" },
176 | {
177 | name: "Heard Island and Mcdonald Islands",
178 | dial_code: "+0",
179 | code: "HM",
180 | flag: "🏳"
181 | },
182 | {
183 | name: "Holy See (Vatican City State)",
184 | dial_code: "+379",
185 | code: "VA",
186 | flag: "🇻🇦"
187 | },
188 | { name: "Honduras", dial_code: "+504", code: "HN", flag: "🇭🇳" },
189 | { name: "Hong Kong", dial_code: "+852", code: "HK", flag: "🇭🇰" },
190 | { name: "Hungary", dial_code: "+36", code: "HU", flag: "🇭🇺" },
191 | { name: "Iceland", dial_code: "+354", code: "IS", flag: "🇮🇸" },
192 | { name: "India", dial_code: "+91", code: "IN", flag: "🇮🇳" },
193 | { name: "Indonesia", dial_code: "+62", code: "ID", flag: "🇮🇩" },
194 | {
195 | name: "Iran, Islamic Republic of Persian Gulf",
196 | dial_code: "+98",
197 | code: "IR",
198 | flag: "🇮🇷"
199 | },
200 | { name: "Iraq", dial_code: "+964", code: "IQ", flag: "🇮🇶" },
201 | { name: "Ireland", dial_code: "+353", code: "IE", flag: "🇮🇪" },
202 | { name: "Isle of Man", dial_code: "+44", code: "IM", flag: "🇮🇲" },
203 | { name: "Israel", dial_code: "+972", code: "IL", flag: "🇮🇱" },
204 | { name: "Italy", dial_code: "+39", code: "IT", flag: "🇮🇹" },
205 | { name: "Jamaica", dial_code: "+1876", code: "JM", flag: "🇯🇲" },
206 | { name: "Japan", dial_code: "+81", code: "JP", flag: "🇯🇵" },
207 | { name: "Jersey", dial_code: "+44", code: "JE", flag: "🇯🇪" },
208 | { name: "Jordan", dial_code: "+962", code: "JO", flag: "🇯🇴" },
209 | { name: "Kazakhstan", dial_code: "+7", code: "KZ", flag: "🇰🇿" },
210 | { name: "Kenya", dial_code: "+254", code: "KE", flag: "🇰🇪" },
211 | { name: "Kiribati", dial_code: "+686", code: "KI", flag: "🇰🇮" },
212 | {
213 | name: "Korea, Democratic People's Republic of Korea",
214 | dial_code: "+850",
215 | code: "KP",
216 | flag: "🇰🇵"
217 | },
218 | {
219 | name: "Korea, Republic of South Korea",
220 | dial_code: "+82",
221 | code: "KR",
222 | flag: "🇰🇷"
223 | },
224 | { name: "Kosovo", dial_code: "+383", code: "XK", flag: "🇽🇰" },
225 | { name: "Kuwait", dial_code: "+965", code: "KW", flag: "🇰🇼" },
226 | { name: "Kyrgyzstan", dial_code: "+996", code: "KG", flag: "🇰🇬" },
227 | { name: "Laos", dial_code: "+856", code: "LA", flag: "🇱🇦" },
228 | { name: "Latvia", dial_code: "+371", code: "LV", flag: "🇱🇻" },
229 | { name: "Lebanon", dial_code: "+961", code: "LB", flag: "🇱🇧" },
230 | { name: "Lesotho", dial_code: "+266", code: "LS", flag: "🇱🇸" },
231 | { name: "Liberia", dial_code: "+231", code: "LR", flag: "🇱🇷" },
232 | {
233 | name: "Libyan Arab Jamahiriya",
234 | dial_code: "+218",
235 | code: "LY",
236 | flag: "🇱🇾"
237 | },
238 | { name: "Liechtenstein", dial_code: "+423", code: "LI", flag: "🇱🇮" },
239 | { name: "Lithuania", dial_code: "+370", code: "LT", flag: "🇱🇹" },
240 | { name: "Luxembourg", dial_code: "+352", code: "LU", flag: "🇱🇺" },
241 | { name: "Macao", dial_code: "+853", code: "MO", flag: "🇲🇴" },
242 | { name: "Macedonia", dial_code: "+389", code: "MK", flag: "🇲🇰" },
243 | { name: "Madagascar", dial_code: "+261", code: "MG", flag: "🇲🇬" },
244 | { name: "Malawi", dial_code: "+265", code: "MW", flag: "🇲🇼" },
245 | { name: "Malaysia", dial_code: "+60", code: "MY", flag: "🇲🇾" },
246 | { name: "Maldives", dial_code: "+960", code: "MV", flag: "🇲🇻" },
247 | { name: "Mali", dial_code: "+223", code: "ML", flag: "🇲🇱" },
248 | { name: "Malta", dial_code: "+356", code: "MT", flag: "🇲🇹" },
249 | {
250 | name: "Marshall Islands",
251 | dial_code: "+692",
252 | code: "MH",
253 | flag: "🇲🇭"
254 | },
255 | { name: "Martinique", dial_code: "+596", code: "MQ", flag: "🇲🇶" },
256 | { name: "Mauritania", dial_code: "+222", code: "MR", flag: "🇲🇷" },
257 | { name: "Mauritius", dial_code: "+230", code: "MU", flag: "🇲🇺" },
258 | { name: "Mayotte", dial_code: "+262", code: "YT", flag: "🇾🇹" },
259 | { name: "Mexico", dial_code: "+52", code: "MX", flag: "🇲🇽" },
260 | {
261 | name: "Micronesia, Federated States of Micronesia",
262 | dial_code: "+691",
263 | code: "FM",
264 | flag: "🇫🇲"
265 | },
266 | { name: "Moldova", dial_code: "+373", code: "MD", flag: "🇲🇩" },
267 | { name: "Monaco", dial_code: "+377", code: "MC", flag: "🇲🇨" },
268 | { name: "Mongolia", dial_code: "+976", code: "MN", flag: "🇲🇳" },
269 | { name: "Montenegro", dial_code: "+382", code: "ME", flag: "🇲🇪" },
270 | { name: "Montserrat", dial_code: "+1664", code: "MS", flag: "🇲🇸" },
271 | { name: "Morocco", dial_code: "+212", code: "MA", flag: "🇲🇦" },
272 | { name: "Mozambique", dial_code: "+258", code: "MZ", flag: "🇲🇿" },
273 | { name: "Myanmar", dial_code: "+95", code: "MM", flag: "🇲🇲" },
274 | { name: "Namibia", dial_code: "+264", code: "NA", flag: "🇳🇦" },
275 | { name: "Nauru", dial_code: "+674", code: "NR", flag: "🇳🇷" },
276 | { name: "Nepal", dial_code: "+977", code: "NP", flag: "🇳🇵" },
277 | { name: "Netherlands", dial_code: "+31", code: "NL", flag: "🇳🇱" },
278 | {
279 | name: "Netherlands Antilles",
280 | dial_code: "+599",
281 | code: "AN",
282 | flag: "🇳🇱"
283 | },
284 | { name: "New Caledonia", dial_code: "+687", code: "NC", flag: "🇳🇨" },
285 | { name: "New Zealand", dial_code: "+64", code: "NZ", flag: "🇳🇿" },
286 | { name: "Nicaragua", dial_code: "+505", code: "NI", flag: "🇳🇮" },
287 | { name: "Niger", dial_code: "+227", code: "NE", flag: "🇳🇪" },
288 | { name: "Nigeria", dial_code: "+234", code: "NG", flag: "🇳🇬" },
289 | { name: "Niue", dial_code: "+683", code: "NU", flag: "🇳🇺" },
290 | { name: "Norfolk Island", dial_code: "+672", code: "NF", flag: "🇳🇫" },
291 | {
292 | name: "Northern Mariana Islands",
293 | dial_code: "+1670",
294 | code: "MP",
295 | flag: "🏳"
296 | },
297 | { name: "Norway", dial_code: "+47", code: "NO", flag: "🇳🇴" },
298 | { name: "Oman", dial_code: "+968", code: "OM", flag: "🇴🇲" },
299 | { name: "Pakistan", dial_code: "+92", code: "PK", flag: "🇵🇰" },
300 | { name: "Palau", dial_code: "+680", code: "PW", flag: "🇵🇼" },
301 | {
302 | name: "Palestinian Territory, Occupied",
303 | dial_code: "+970",
304 | code: "PS",
305 | flag: "🇵🇸"
306 | },
307 | { name: "Panama", dial_code: "+507", code: "PA", flag: "🇵🇦" },
308 | {
309 | name: "Papua New Guinea",
310 | dial_code: "+675",
311 | code: "PG",
312 | flag: "🇵🇬"
313 | },
314 | { name: "Paraguay", dial_code: "+595", code: "PY", flag: "🇵🇾" },
315 | { name: "Peru", dial_code: "+51", code: "PE", flag: "🇵🇪" },
316 | { name: "Philippines", dial_code: "+63", code: "PH", flag: "🇵🇭" },
317 | { name: "Pitcairn", dial_code: "+64", code: "PN", flag: "🇵🇳" },
318 | { name: "Poland", dial_code: "+48", code: "PL", flag: "🇵🇱" },
319 | { name: "Portugal", dial_code: "+351", code: "PT", flag: "🇵🇹" },
320 | { name: "Puerto Rico", dial_code: "+1939", code: "PR", flag: "🇵🇷" },
321 | { name: "Qatar", dial_code: "+974", code: "QA", flag: "🇶🇦" },
322 | { name: "Romania", dial_code: "+40", code: "RO", flag: "🇷🇴" },
323 | { name: "Russia", dial_code: "+7", code: "RU", flag: "🇷🇺" },
324 | { name: "Rwanda", dial_code: "+250", code: "RW", flag: "🇷🇼" },
325 | { name: "Reunion", dial_code: "+262", code: "RE", flag: "🇫🇷" },
326 | {
327 | name: "Saint Barthelemy",
328 | dial_code: "+590",
329 | code: "BL",
330 | flag: "🇧🇱"
331 | },
332 | {
333 | name: "Saint Helena, Ascension and Tristan Da Cunha",
334 | dial_code: "+290",
335 | code: "SH",
336 | flag: "🇸🇭"
337 | },
338 | {
339 | name: "Saint Kitts and Nevis",
340 | dial_code: "+1869",
341 | code: "KN",
342 | flag: "🇰🇳"
343 | },
344 | { name: "Saint Lucia", dial_code: "+1758", code: "LC", flag: "🇱🇨" },
345 | { name: "Saint Martin", dial_code: "+590", code: "MF", flag: "🏳" },
346 | {
347 | name: "Saint Pierre and Miquelon",
348 | dial_code: "+508",
349 | code: "PM",
350 | flag: "🇵🇲"
351 | },
352 | {
353 | name: "Saint Vincent and the Grenadines",
354 | dial_code: "+1784",
355 | code: "VC",
356 | flag: "🇻🇨"
357 | },
358 | { name: "Samoa", dial_code: "+685", code: "WS", flag: "🇼🇸" },
359 | { name: "San Marino", dial_code: "+378", code: "SM", flag: "🇸🇲" },
360 | {
361 | name: "Sao Tome and Principe",
362 | dial_code: "+239",
363 | code: "ST",
364 | flag: "🇸🇹"
365 | },
366 | { name: "Saudi Arabia", dial_code: "+966", code: "SA", flag: "🇸🇦" },
367 | { name: "Senegal", dial_code: "+221", code: "SN", flag: "🇸🇳" },
368 | { name: "Serbia", dial_code: "+381", code: "RS", flag: "🇷🇸" },
369 | { name: "Seychelles", dial_code: "+248", code: "SC", flag: "🇸🇨" },
370 | { name: "Sierra Leone", dial_code: "+232", code: "SL", flag: "🇸🇱" },
371 | { name: "Singapore", dial_code: "+65", code: "SG", flag: "🇸🇬" },
372 | { name: "Slovakia", dial_code: "+421", code: "SK", flag: "🇸🇰" },
373 | { name: "Slovenia", dial_code: "+386", code: "SI", flag: "🇸🇮" },
374 | {
375 | name: "Solomon Islands",
376 | dial_code: "+677",
377 | code: "SB",
378 | flag: "🇸🇧"
379 | },
380 | { name: "Somalia", dial_code: "+252", code: "SO", flag: "🇸🇴" },
381 | { name: "South Africa", dial_code: "+27", code: "ZA", flag: "🇿🇦" },
382 | { name: "South Sudan", dial_code: "+211", code: "SS", flag: "🇸🇸" },
383 | {
384 | name: "South Georgia and the South Sandwich Islands",
385 | dial_code: "+500",
386 | code: "GS",
387 | flag: "🇬🇸"
388 | },
389 | { name: "Spain", dial_code: "+34", code: "ES", flag: "🇪🇸" },
390 | { name: "Sri Lanka", dial_code: "+94", code: "LK", flag: "🇱🇰" },
391 | { name: "Sudan", dial_code: "+249", code: "SD", flag: "🇸🇩" },
392 | { name: "Suriname", dial_code: "+597", code: "SR", flag: "🇸🇷" },
393 | {
394 | name: "Svalbard and Jan Mayen",
395 | dial_code: "+47",
396 | code: "SJ",
397 | flag: "🇩🇰"
398 | },
399 | { name: "Swaziland", dial_code: "+268", code: "SZ", flag: "🇸🇿" },
400 | { name: "Sweden", dial_code: "+46", code: "SE", flag: "🇸🇪" },
401 | { name: "Switzerland", dial_code: "+41", code: "CH", flag: "🇨🇭" },
402 | {
403 | name: "Syrian Arab Republic",
404 | dial_code: "+963",
405 | code: "SY",
406 | flag: "🇸🇾"
407 | },
408 | { name: "Taiwan", dial_code: "+886", code: "TW", flag: "🇹🇼" },
409 | { name: "Tajikistan", dial_code: "+992", code: "TJ", flag: "🇹🇯" },
410 | {
411 | name: "Tanzania, United Republic of Tanzania",
412 | dial_code: "+255",
413 | code: "TZ",
414 | flag: "🇹🇿"
415 | },
416 | { name: "Thailand", dial_code: "+66", code: "TH", flag: "🇹🇭" },
417 | { name: "Timor-Leste", dial_code: "+670", code: "TL", flag: "🇹🇱" },
418 | { name: "Togo", dial_code: "+228", code: "TG", flag: "🇹🇬" },
419 | { name: "Tokelau", dial_code: "+690", code: "TK", flag: "🇹🇰" },
420 | { name: "Tonga", dial_code: "+676", code: "TO", flag: "🇹🇴" },
421 | {
422 | name: "Trinidad and Tobago",
423 | dial_code: "+1868",
424 | code: "TT",
425 | flag: "🇹🇹"
426 | },
427 | { name: "Tunisia", dial_code: "+216", code: "TN", flag: "🇹🇳" },
428 | { name: "Turkey", dial_code: "+90", code: "TR", flag: "🇹🇷" },
429 | { name: "Turkmenistan", dial_code: "+993", code: "TM", flag: "🇹🇲" },
430 | {
431 | name: "Turks and Caicos Islands",
432 | dial_code: "+1649",
433 | code: "TC",
434 | flag: "🇹🇨"
435 | },
436 | { name: "Tuvalu", dial_code: "+688", code: "TV", flag: "🇹🇻" },
437 | { name: "Uganda", dial_code: "+256", code: "UG", flag: "🇺🇬" },
438 | { name: "Ukraine", dial_code: "+380", code: "UA", flag: "🇺🇦" },
439 | {
440 | name: "United Arab Emirates",
441 | dial_code: "+971",
442 | code: "AE",
443 | flag: "🇦🇪"
444 | },
445 | { name: "United Kingdom", dial_code: "+44", code: "GB", flag: "🇬🇧" },
446 | { name: "United States", dial_code: "+1", code: "US", flag: "🇺🇸" },
447 | { name: "Uruguay", dial_code: "+598", code: "UY", flag: "🇺🇾" },
448 | { name: "Uzbekistan", dial_code: "+998", code: "UZ", flag: "🇺🇿" },
449 | { name: "Vanuatu", dial_code: "+678", code: "VU", flag: "🇻🇺" },
450 | {
451 | name: "Venezuela, Bolivarian Republic of Venezuela",
452 | dial_code: "+58",
453 | code: "VE",
454 | flag: "🇻🇪"
455 | },
456 | { name: "Vietnam", dial_code: "+84", code: "VN", flag: "🇻🇳" },
457 | {
458 | name: "Virgin Islands, British",
459 | dial_code: "+1284",
460 | code: "VG",
461 | flag: "🇻🇬"
462 | },
463 | {
464 | name: "Virgin Islands, U.S.",
465 | dial_code: "+1340",
466 | code: "VI",
467 | flag: "🇻🇮"
468 | },
469 | {
470 | name: "Wallis and Futuna",
471 | dial_code: "+681",
472 | code: "WF",
473 | flag: "🇼🇫"
474 | },
475 | { name: "Yemen", dial_code: "+967", code: "YE", flag: "🇾🇪" },
476 | { name: "Zambia", dial_code: "+260", code: "ZM", flag: "🇿🇲" },
477 | { name: "Zimbabwe", dial_code: "+263", code: "ZW", flag: "🇿🇼" }
478 | ];
479 |
480 | export default countries;
481 |
--------------------------------------------------------------------------------
/src/global-styles.ts:
--------------------------------------------------------------------------------
1 | import reset from "styled-reset";
2 | import { injectGlobal } from "./typed-components";
3 |
4 | // tslint:disable-next-line
5 | injectGlobal`
6 | @import url('https://fonts.googleapis.com/css?family=Maven+Pro');
7 | ${reset};
8 | * {
9 | box-sizing: border-box;
10 | }
11 | body{
12 | font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
13 | }
14 | a{
15 | color:inherit;
16 | text-decoration:none;
17 | }
18 | input,
19 | button{&:focus,&:active{outline:none}
20 | }
21 | h1,h2,h3,h4,h5,h6{
22 | font-family:'Maven Pro', sans-serif;
23 | }
24 | `;
25 |
--------------------------------------------------------------------------------
/src/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nomadcoders/nuber-client/2cf12dfcc2eed3c3fc3e5c8c4765a656b5015dcc/src/images/bg.png
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { ApolloProvider } from "react-apollo";
3 | import ReactDOM from "react-dom";
4 | import client from "./apollo";
5 | import App from "./Components/App";
6 | import "./global-styles";
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById("root") as HTMLElement
13 | );
14 |
--------------------------------------------------------------------------------
/src/keys.ts:
--------------------------------------------------------------------------------
1 | export const MAPS_KEY = "AIzaSyAZItvexravC2icdhsjSGHA6Ovbk2ROwmc";
2 |
--------------------------------------------------------------------------------
/src/mapHelpers.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { toast } from "react-toastify";
3 | import { MAPS_KEY } from "./keys";
4 |
5 | export const geoCode = async (address: string) => {
6 | const URL = `https://maps.googleapis.com/maps/api/geocode/json?address=${address}&key=${MAPS_KEY}`;
7 | const { data } = await axios(URL);
8 | if (!data.error_message) {
9 | const { results } = data;
10 | const firstPlace = results[0];
11 | const {
12 | formatted_address,
13 | geometry: {
14 | location: { lat, lng }
15 | }
16 | } = firstPlace;
17 | return { formatted_address, lat, lng };
18 | } else {
19 | toast.error(data.error_message);
20 | return false;
21 | }
22 | };
23 |
24 | export const reverseGeoCode = async (lat: number, lng: number) => {
25 | const URL = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=${MAPS_KEY}`;
26 | const { data } = await axios(URL);
27 | if (!data.error_message) {
28 | const { results } = data;
29 | const firstPlace = results[0];
30 | if (!firstPlace) {
31 | return false;
32 | }
33 | const address = firstPlace.formatted_address;
34 | return address;
35 | } else {
36 | toast.error(data.error_message);
37 | return false;
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/src/sharedQueries.local.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const LOG_USER_IN = gql`
4 | mutation logUserIn($token: String!) {
5 | logUserIn(token: $token) @client
6 | }
7 | `;
8 |
9 | export const LOG_USER_OUT = gql`
10 | mutation logUserOut {
11 | logUserOut @client
12 | }
13 | `;
14 |
--------------------------------------------------------------------------------
/src/sharedQueries.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const USER_PROFILE = gql`
4 | query userProfile {
5 | GetMyProfile {
6 | ok
7 | error
8 | user {
9 | id
10 | profilePhoto
11 | firstName
12 | lastName
13 | email
14 | fullName
15 | isDriving
16 | }
17 | }
18 | }
19 | `;
20 |
21 | export const GET_PLACES = gql`
22 | query getPlaces {
23 | GetMyPlaces {
24 | ok
25 | error
26 | places {
27 | id
28 | name
29 | address
30 | isFav
31 | }
32 | }
33 | }
34 | `;
35 |
--------------------------------------------------------------------------------
/src/theme.ts:
--------------------------------------------------------------------------------
1 | const theme = {
2 | blueColor: "#3498db",
3 | greenColor: "#1abc9c",
4 | greyColor: "#7f8c8d",
5 | yellowColor: "#f1c40f"
6 | };
7 |
8 | export default theme;
9 |
--------------------------------------------------------------------------------
/src/typed-components.ts:
--------------------------------------------------------------------------------
1 | import * as styledComponents from "styled-components";
2 | import { ThemedStyledComponentsModule } from "styled-components";
3 |
4 | interface IThemeInterface {
5 | blueColor: string;
6 | greyColor: string;
7 | yellowColor: string;
8 | greenColor: string;
9 | }
10 |
11 | const {
12 | default: styled,
13 | css,
14 | injectGlobal,
15 | keyframes,
16 | ThemeProvider
17 | } = styledComponents as ThemedStyledComponentsModule;
18 |
19 | export { css, injectGlobal, keyframes, ThemeProvider };
20 | export default styled;
21 |
--------------------------------------------------------------------------------
/src/types/api.d.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 | /* tslint:disable */
4 | // This file was automatically generated and should not be edited.
5 |
6 | // ====================================================
7 | // GraphQL mutation operation: toggleDriving
8 | // ====================================================
9 |
10 | export interface toggleDriving_ToggleDrivingMode {
11 | __typename: "ToggleDrivingModeResponse";
12 | ok: boolean;
13 | error: string | null;
14 | }
15 |
16 | export interface toggleDriving {
17 | ToggleDrivingMode: toggleDriving_ToggleDrivingMode;
18 | }
19 |
20 |
21 | /* tslint:disable */
22 | // This file was automatically generated and should not be edited.
23 |
24 | // ====================================================
25 | // GraphQL mutation operation: editPlace
26 | // ====================================================
27 |
28 | export interface editPlace_EditPlace {
29 | __typename: "EditPlaceResponse";
30 | ok: boolean;
31 | error: string | null;
32 | }
33 |
34 | export interface editPlace {
35 | EditPlace: editPlace_EditPlace;
36 | }
37 |
38 | export interface editPlaceVariables {
39 | placeId: number;
40 | isFav?: boolean | null;
41 | }
42 |
43 |
44 | /* tslint:disable */
45 | // This file was automatically generated and should not be edited.
46 |
47 | // ====================================================
48 | // GraphQL mutation operation: addPlace
49 | // ====================================================
50 |
51 | export interface addPlace_AddPlace {
52 | __typename: "AddPlaceResponse";
53 | ok: boolean;
54 | error: string | null;
55 | }
56 |
57 | export interface addPlace {
58 | AddPlace: addPlace_AddPlace;
59 | }
60 |
61 | export interface addPlaceVariables {
62 | name: string;
63 | lat: number;
64 | lng: number;
65 | address: string;
66 | isFav: boolean;
67 | }
68 |
69 |
70 | /* tslint:disable */
71 | // This file was automatically generated and should not be edited.
72 |
73 | // ====================================================
74 | // GraphQL query operation: getChat
75 | // ====================================================
76 |
77 | export interface getChat_GetChat_chat_messages {
78 | __typename: "Message";
79 | id: number;
80 | text: string;
81 | userId: number | null;
82 | }
83 |
84 | export interface getChat_GetChat_chat {
85 | __typename: "Chat";
86 | passengerId: number;
87 | driverId: number | null;
88 | messages: (getChat_GetChat_chat_messages | null)[] | null;
89 | }
90 |
91 | export interface getChat_GetChat {
92 | __typename: "GetChatResponse";
93 | ok: boolean;
94 | error: string | null;
95 | chat: getChat_GetChat_chat | null;
96 | }
97 |
98 | export interface getChat {
99 | GetChat: getChat_GetChat;
100 | }
101 |
102 | export interface getChatVariables {
103 | chatId: number;
104 | }
105 |
106 |
107 | /* tslint:disable */
108 | // This file was automatically generated and should not be edited.
109 |
110 | // ====================================================
111 | // GraphQL mutation operation: sendMessage
112 | // ====================================================
113 |
114 | export interface sendMessage_SendChatMessage_message {
115 | __typename: "Message";
116 | id: number;
117 | text: string;
118 | userId: number | null;
119 | }
120 |
121 | export interface sendMessage_SendChatMessage {
122 | __typename: "SendChatMessageResponse";
123 | ok: boolean;
124 | error: string | null;
125 | message: sendMessage_SendChatMessage_message | null;
126 | }
127 |
128 | export interface sendMessage {
129 | SendChatMessage: sendMessage_SendChatMessage;
130 | }
131 |
132 | export interface sendMessageVariables {
133 | text: string;
134 | chatId: number;
135 | }
136 |
137 |
138 | /* tslint:disable */
139 | // This file was automatically generated and should not be edited.
140 |
141 | // ====================================================
142 | // GraphQL mutation operation: updateProfile
143 | // ====================================================
144 |
145 | export interface updateProfile_UpdateMyProfile {
146 | __typename: "UpdateMyProfileResponse";
147 | ok: boolean;
148 | error: string | null;
149 | }
150 |
151 | export interface updateProfile {
152 | UpdateMyProfile: updateProfile_UpdateMyProfile;
153 | }
154 |
155 | export interface updateProfileVariables {
156 | firstName: string;
157 | lastName: string;
158 | email: string;
159 | profilePhoto: string;
160 | }
161 |
162 |
163 | /* tslint:disable */
164 | // This file was automatically generated and should not be edited.
165 |
166 | // ====================================================
167 | // GraphQL mutation operation: reportMovement
168 | // ====================================================
169 |
170 | export interface reportMovement_ReportMovement {
171 | __typename: "ReportMovementResponse";
172 | ok: boolean;
173 | }
174 |
175 | export interface reportMovement {
176 | ReportMovement: reportMovement_ReportMovement;
177 | }
178 |
179 | export interface reportMovementVariables {
180 | lat: number;
181 | lng: number;
182 | }
183 |
184 |
185 | /* tslint:disable */
186 | // This file was automatically generated and should not be edited.
187 |
188 | // ====================================================
189 | // GraphQL query operation: getDrivers
190 | // ====================================================
191 |
192 | export interface getDrivers_GetNearbyDrivers_drivers {
193 | __typename: "User";
194 | id: number;
195 | lastLat: number | null;
196 | lastLng: number | null;
197 | }
198 |
199 | export interface getDrivers_GetNearbyDrivers {
200 | __typename: "GetNearbyDriversResponse";
201 | ok: boolean;
202 | drivers: (getDrivers_GetNearbyDrivers_drivers | null)[] | null;
203 | }
204 |
205 | export interface getDrivers {
206 | GetNearbyDrivers: getDrivers_GetNearbyDrivers;
207 | }
208 |
209 |
210 | /* tslint:disable */
211 | // This file was automatically generated and should not be edited.
212 |
213 | // ====================================================
214 | // GraphQL mutation operation: requestRide
215 | // ====================================================
216 |
217 | export interface requestRide_RequestRide_ride {
218 | __typename: "Ride";
219 | id: number;
220 | }
221 |
222 | export interface requestRide_RequestRide {
223 | __typename: "RequestRideResponse";
224 | ok: boolean;
225 | error: string | null;
226 | ride: requestRide_RequestRide_ride | null;
227 | }
228 |
229 | export interface requestRide {
230 | RequestRide: requestRide_RequestRide;
231 | }
232 |
233 | export interface requestRideVariables {
234 | pickUpAddress: string;
235 | pickUpLat: number;
236 | pickUpLng: number;
237 | dropOffAddress: string;
238 | dropOffLat: number;
239 | dropOffLng: number;
240 | price: number;
241 | distance: string;
242 | duration: string;
243 | }
244 |
245 |
246 | /* tslint:disable */
247 | // This file was automatically generated and should not be edited.
248 |
249 | // ====================================================
250 | // GraphQL query operation: getRides
251 | // ====================================================
252 |
253 | export interface getRides_GetNearbyRide_ride_passenger {
254 | __typename: "User";
255 | fullName: string | null;
256 | profilePhoto: string | null;
257 | }
258 |
259 | export interface getRides_GetNearbyRide_ride {
260 | __typename: "Ride";
261 | id: number;
262 | pickUpAddress: string;
263 | dropOffAddress: string;
264 | price: number;
265 | distance: string;
266 | passenger: getRides_GetNearbyRide_ride_passenger;
267 | }
268 |
269 | export interface getRides_GetNearbyRide {
270 | __typename: "GetNearbyRideResponse";
271 | ok: boolean;
272 | error: string | null;
273 | ride: getRides_GetNearbyRide_ride | null;
274 | }
275 |
276 | export interface getRides {
277 | GetNearbyRide: getRides_GetNearbyRide;
278 | }
279 |
280 |
281 | /* tslint:disable */
282 | // This file was automatically generated and should not be edited.
283 |
284 | // ====================================================
285 | // GraphQL mutation operation: acceptRide
286 | // ====================================================
287 |
288 | export interface acceptRide_UpdateRideStatus {
289 | __typename: "UpdateRideStatusResponse";
290 | ok: boolean;
291 | error: string | null;
292 | rideId: number | null;
293 | }
294 |
295 | export interface acceptRide {
296 | UpdateRideStatus: acceptRide_UpdateRideStatus;
297 | }
298 |
299 | export interface acceptRideVariables {
300 | rideId: number;
301 | }
302 |
303 |
304 | /* tslint:disable */
305 | // This file was automatically generated and should not be edited.
306 |
307 | // ====================================================
308 | // GraphQL subscription operation: nearbyRides
309 | // ====================================================
310 |
311 | export interface nearbyRides_NearbyRideSubscription_passenger {
312 | __typename: "User";
313 | fullName: string | null;
314 | profilePhoto: string | null;
315 | }
316 |
317 | export interface nearbyRides_NearbyRideSubscription {
318 | __typename: "Ride";
319 | id: number;
320 | pickUpAddress: string;
321 | dropOffAddress: string;
322 | price: number;
323 | distance: string;
324 | passenger: nearbyRides_NearbyRideSubscription_passenger;
325 | }
326 |
327 | export interface nearbyRides {
328 | NearbyRideSubscription: nearbyRides_NearbyRideSubscription | null;
329 | }
330 |
331 |
332 | /* tslint:disable */
333 | // This file was automatically generated and should not be edited.
334 |
335 | // ====================================================
336 | // GraphQL mutation operation: startPhoneVerification
337 | // ====================================================
338 |
339 | export interface startPhoneVerification_StartPhoneVerification {
340 | __typename: "StartPhoneVerificationResponse";
341 | ok: boolean;
342 | error: string | null;
343 | }
344 |
345 | export interface startPhoneVerification {
346 | StartPhoneVerification: startPhoneVerification_StartPhoneVerification;
347 | }
348 |
349 | export interface startPhoneVerificationVariables {
350 | phoneNumber: string;
351 | }
352 |
353 |
354 | /* tslint:disable */
355 | // This file was automatically generated and should not be edited.
356 |
357 | // ====================================================
358 | // GraphQL query operation: getRide
359 | // ====================================================
360 |
361 | export interface getRide_GetRide_ride_driver {
362 | __typename: "User";
363 | id: number;
364 | fullName: string | null;
365 | profilePhoto: string | null;
366 | }
367 |
368 | export interface getRide_GetRide_ride_passenger {
369 | __typename: "User";
370 | id: number;
371 | fullName: string | null;
372 | profilePhoto: string | null;
373 | }
374 |
375 | export interface getRide_GetRide_ride {
376 | __typename: "Ride";
377 | id: number;
378 | status: string;
379 | pickUpAddress: string;
380 | dropOffAddress: string;
381 | price: number;
382 | distance: string;
383 | duration: string;
384 | driver: getRide_GetRide_ride_driver | null;
385 | passenger: getRide_GetRide_ride_passenger;
386 | chatId: number | null;
387 | }
388 |
389 | export interface getRide_GetRide {
390 | __typename: "GetRideResponse";
391 | ok: boolean;
392 | error: string | null;
393 | ride: getRide_GetRide_ride | null;
394 | }
395 |
396 | export interface getRide {
397 | GetRide: getRide_GetRide;
398 | }
399 |
400 | export interface getRideVariables {
401 | rideId: number;
402 | }
403 |
404 |
405 | /* tslint:disable */
406 | // This file was automatically generated and should not be edited.
407 |
408 | // ====================================================
409 | // GraphQL subscription operation: rideUpdates
410 | // ====================================================
411 |
412 | export interface rideUpdates_RideStatusSubscription_driver {
413 | __typename: "User";
414 | id: number;
415 | fullName: string | null;
416 | profilePhoto: string | null;
417 | }
418 |
419 | export interface rideUpdates_RideStatusSubscription_passenger {
420 | __typename: "User";
421 | id: number;
422 | fullName: string | null;
423 | profilePhoto: string | null;
424 | }
425 |
426 | export interface rideUpdates_RideStatusSubscription {
427 | __typename: "Ride";
428 | id: number;
429 | status: string;
430 | pickUpAddress: string;
431 | dropOffAddress: string;
432 | price: number;
433 | distance: string;
434 | duration: string;
435 | driver: rideUpdates_RideStatusSubscription_driver | null;
436 | passenger: rideUpdates_RideStatusSubscription_passenger;
437 | chatId: number | null;
438 | }
439 |
440 | export interface rideUpdates {
441 | RideStatusSubscription: rideUpdates_RideStatusSubscription | null;
442 | }
443 |
444 |
445 | /* tslint:disable */
446 | // This file was automatically generated and should not be edited.
447 |
448 | // ====================================================
449 | // GraphQL mutation operation: updateRide
450 | // ====================================================
451 |
452 | export interface updateRide_UpdateRideStatus {
453 | __typename: "UpdateRideStatusResponse";
454 | ok: boolean;
455 | error: string | null;
456 | rideId: number | null;
457 | }
458 |
459 | export interface updateRide {
460 | UpdateRideStatus: updateRide_UpdateRideStatus;
461 | }
462 |
463 | export interface updateRideVariables {
464 | rideId: number;
465 | status: StatusOptions;
466 | }
467 |
468 |
469 | /* tslint:disable */
470 | // This file was automatically generated and should not be edited.
471 |
472 | // ====================================================
473 | // GraphQL mutation operation: facebookConnect
474 | // ====================================================
475 |
476 | export interface facebookConnect_FacebookConnect {
477 | __typename: "FacebookConnectResponse";
478 | ok: boolean;
479 | error: string | null;
480 | token: string | null;
481 | }
482 |
483 | export interface facebookConnect {
484 | FacebookConnect: facebookConnect_FacebookConnect;
485 | }
486 |
487 | export interface facebookConnectVariables {
488 | firstName: string;
489 | lastName: string;
490 | email?: string | null;
491 | fbId: string;
492 | }
493 |
494 |
495 | /* tslint:disable */
496 | // This file was automatically generated and should not be edited.
497 |
498 | // ====================================================
499 | // GraphQL mutation operation: verifyPhone
500 | // ====================================================
501 |
502 | export interface verifyPhone_CompletePhoneVerification {
503 | __typename: "CompletePhoneVerificationResponse";
504 | ok: boolean;
505 | error: string | null;
506 | token: string | null;
507 | }
508 |
509 | export interface verifyPhone {
510 | CompletePhoneVerification: verifyPhone_CompletePhoneVerification;
511 | }
512 |
513 | export interface verifyPhoneVariables {
514 | key: string;
515 | phoneNumber: string;
516 | }
517 |
518 |
519 | /* tslint:disable */
520 | // This file was automatically generated and should not be edited.
521 |
522 | // ====================================================
523 | // GraphQL query operation: userProfile
524 | // ====================================================
525 |
526 | export interface userProfile_GetMyProfile_user {
527 | __typename: "User";
528 | id: number;
529 | profilePhoto: string | null;
530 | firstName: string;
531 | lastName: string;
532 | email: string | null;
533 | fullName: string | null;
534 | isDriving: boolean;
535 | }
536 |
537 | export interface userProfile_GetMyProfile {
538 | __typename: "GetMyProfileResponse";
539 | ok: boolean;
540 | error: string | null;
541 | user: userProfile_GetMyProfile_user | null;
542 | }
543 |
544 | export interface userProfile {
545 | GetMyProfile: userProfile_GetMyProfile;
546 | }
547 |
548 |
549 | /* tslint:disable */
550 | // This file was automatically generated and should not be edited.
551 |
552 | // ====================================================
553 | // GraphQL query operation: getPlaces
554 | // ====================================================
555 |
556 | export interface getPlaces_GetMyPlaces_places {
557 | __typename: "Place";
558 | id: number;
559 | name: string;
560 | address: string;
561 | isFav: boolean;
562 | }
563 |
564 | export interface getPlaces_GetMyPlaces {
565 | __typename: "GetMyPlacesResponse";
566 | ok: boolean;
567 | error: string | null;
568 | places: (getPlaces_GetMyPlaces_places | null)[] | null;
569 | }
570 |
571 | export interface getPlaces {
572 | GetMyPlaces: getPlaces_GetMyPlaces;
573 | }
574 |
575 | /* tslint:disable */
576 | // This file was automatically generated and should not be edited.
577 |
578 | //==============================================================
579 | // START Enums and Input Objects
580 | //==============================================================
581 |
582 | export enum StatusOptions {
583 | ACCEPTED = "ACCEPTED",
584 | CANCELED = "CANCELED",
585 | FINISHED = "FINISHED",
586 | ONROUTE = "ONROUTE",
587 | REQUESTING = "REQUESTING",
588 | }
589 |
590 | //==============================================================
591 | // END Enums and Input Objects
592 | //==============================================================
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "outDir": "build/dist",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "lib": ["es6", "dom", "esnext.asynciterable"],
8 | "sourceMap": true,
9 | "allowJs": true,
10 | "jsx": "react",
11 | "moduleResolution": "node",
12 | "rootDir": "src",
13 | "forceConsistentCasingInFileNames": true,
14 | "noImplicitReturns": true,
15 | "noImplicitThis": true,
16 | "noImplicitAny": false,
17 | "strictNullChecks": true,
18 | "suppressImplicitAnyIndexErrors": true,
19 | "noUnusedLocals": true,
20 | "esModuleInterop": true,
21 | "skipLibCheck": true
22 | },
23 | "exclude": [
24 | "node_modules",
25 | "build",
26 | "scripts",
27 | "acceptance-tests",
28 | "webpack",
29 | "jest",
30 | "src/setupTests.ts"
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/tsconfig.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json"
3 | }
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
3 | "rules": {
4 | "max-classes-per-file": false,
5 | "jsx-no-lambda": false,
6 | "no-console": false
7 | },
8 | "linterOptions": {
9 | "exclude": [
10 | "config/**/*.js",
11 | "node_modules/**/*.ts",
12 | "coverage/lcov-report/*.js",
13 | "src/countries.ts"
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------