├── .eslintignore
├── .eslintrc
├── .firebaserc
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .prettierrc
├── .vscode
├── cpx.json
└── settings.json
├── CRA.md
├── LICENSE
├── README.md
├── branding
├── ads
│ ├── poster-01.png
│ └── poster.ai
├── fonts
│ ├── PT Serif
│ │ ├── OFL.txt
│ │ ├── PTSerif-Bold.ttf
│ │ ├── PTSerif-BoldItalic.ttf
│ │ ├── PTSerif-Italic.ttf
│ │ ├── PTSerif-Regular.ttf
│ │ └── PT_Serif.zip
│ └── Poppins
│ │ ├── OFL.txt
│ │ ├── Poppins-Black.ttf
│ │ ├── Poppins-BlackItalic.ttf
│ │ ├── Poppins-Bold.ttf
│ │ ├── Poppins-BoldItalic.ttf
│ │ ├── Poppins-ExtraBold.ttf
│ │ ├── Poppins-ExtraBoldItalic.ttf
│ │ ├── Poppins-ExtraLight.ttf
│ │ ├── Poppins-ExtraLightItalic.ttf
│ │ ├── Poppins-Italic.ttf
│ │ ├── Poppins-Light.ttf
│ │ ├── Poppins-LightItalic.ttf
│ │ ├── Poppins-Medium.ttf
│ │ ├── Poppins-MediumItalic.ttf
│ │ ├── Poppins-Regular.ttf
│ │ ├── Poppins-SemiBold.ttf
│ │ ├── Poppins-SemiBoldItalic.ttf
│ │ ├── Poppins-Thin.ttf
│ │ ├── Poppins-ThinItalic.ttf
│ │ └── Poppins.zip
└── logo
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── g-logo-01.png
│ ├── g-logo-inverse-01.png
│ ├── grouber-192.ai
│ ├── grouber-512.ai
│ ├── grouber-logo-01.png
│ ├── grouber-logo-01.svg
│ ├── grouber-logo-inverse-01.png
│ ├── grouber-logo.ai
│ ├── logo192.png
│ ├── logo512.png
│ └── site.webmanifest
├── database.rules.json
├── firebase.json
├── functions
├── .gitignore
├── lib
│ ├── DistanceMatrix.js
│ ├── index.js
│ └── priorityQueue.js
├── package-lock.json
├── package.json
├── src
│ ├── DistanceMatrix.ts
│ ├── index.ts
│ └── priorityQueue.ts
├── tsconfig.json
└── tslint.json
├── images
├── RSVPPage.jpg
├── appMap.jpg
├── branding.png
├── dashboard.jpg
├── homescreen.jpg
└── homescreen.png
├── package-lock.json
├── package.json
├── public
├── cover.png
├── favicon.ico
├── index.html
├── login_art.png
├── logo.png
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.tsx
├── _types
│ ├── address.d.ts
│ ├── event.d.ts
│ └── people.d.ts
├── components
│ ├── DemoCard.tsx
│ ├── EventForm.tsx
│ ├── EventList.tsx
│ ├── Footer.tsx
│ ├── GuestForm.tsx
│ ├── Header.tsx
│ ├── LinkDisplay.tsx
│ ├── LinkSharing.tsx
│ ├── LoginForm.tsx
│ ├── Map
│ │ ├── id-to-lat-lng.ts
│ │ ├── index.tsx
│ │ └── theme.ts
│ ├── Notification.tsx
│ └── PeopleList.tsx
├── hooks
│ ├── UseAutocompletePlaces.tsx
│ ├── UseFetch.tsx
│ └── useEventPeople.tsx
├── index.tsx
├── pages
│ ├── 404.tsx
│ ├── Home.tsx
│ ├── dashboard.tsx
│ ├── form.tsx
│ └── login.tsx
├── react-app-env.d.ts
├── serviceWorker.ts
├── setupTests.ts
├── styles
│ ├── App.module.scss
│ ├── constants.scss
│ ├── index.scss
│ └── theme.ts
└── utils
│ └── sassHelper.tsx
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
3 | coverage/
4 | public/
5 | cypress/
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "parserOptions": {
5 | "ecmaVersion": 2020, // Allows for the parsing of modern ECMAScript features
6 | "sourceType": "module", // Allows for the use of imports
7 | "ecmaFeatures": {
8 | "jsx": true // Allows for the parsing of JSX
9 | }
10 | },
11 | "settings": {
12 | "react": {
13 | "version": "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
14 | }
15 | },
16 | "plugins": [
17 | "@typescript-eslint",
18 | "react"
19 | ],
20 | "extends": [
21 | "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
22 | "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
23 | "prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
24 | "plugin:prettier/recommended" // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
25 | ],
26 | "rules": {
27 | "@typescript-eslint/explicit-module-boundary-types": "off"
28 | }
29 | }
--------------------------------------------------------------------------------
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "find-my-carpool"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 | on:
3 | push:
4 | branches:
5 | - master
6 |
7 | jobs:
8 | build:
9 | name: Build and Deploy
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout Repo
13 | uses: actions/checkout@master
14 | - name: Install Dependencies
15 | run: npm install
16 | - name: Build
17 | run: CI=false npm run build
18 | - name: Deploy to Firebase Hosting
19 | uses: w9jds/firebase-action@master
20 | with:
21 | args: deploy --only hosting
22 | env:
23 | FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
24 | - name: Install Dependencies for Firebase Functions
25 | run: npm install
26 | working-directory: ./functions
27 | - name: Deploy to Firebase Functions
28 | uses: w9jds/firebase-action@master
29 | with:
30 | args: deploy --only functions
31 | env:
32 | FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | *.log
4 |
5 | # dependencies
6 | /node_modules
7 | /.pnp
8 | .pnp.js
9 |
10 | # testing
11 | /coverage
12 |
13 | # production
14 | /build
15 |
16 | # misc
17 | .DS_Store
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "trailingComma": "all",
4 | "singleQuote": true,
5 | "printWidth": 120,
6 | "tabWidth": 4
7 | }
--------------------------------------------------------------------------------
/.vscode/cpx.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.codeActionsOnSave": {
3 | "source.fixAll.eslint": true
4 | },
5 | "python.linting.pylintArgs": [
6 | "--init-hook",
7 | "import sys; sys.path.extend([\"\"c:\\\\Users\\\\lili3\\\\.vscode\\\\extensions\\\\ms-python.devicesimulatorexpress-2020.0.36321\\\\out\"\",\"\"c:\\\\Users\\\\lili3\\\\.vscode\\\\extensions\\\\ms-python.devicesimulatorexpress-2020.0.36321\\\\out\\\\micropython\"\",\"\"c:\\\\Users\\\\lili3\\\\.vscode\\\\extensions\\\\ms-python.devicesimulatorexpress-2020.0.36321\\\\out\\\\clue\"\",\"\"c:\\\\Users\\\\lili3\\\\.vscode\\\\extensions\\\\ms-python.devicesimulatorexpress-2020.0.36321\\\\out\\\\base_circuitpython\"\"])"
8 | ],
9 | }
--------------------------------------------------------------------------------
/CRA.md:
--------------------------------------------------------------------------------
1 |
2 | # Create React App
3 |
4 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
5 |
6 | ## Available Scripts
7 |
8 | In the project directory, you can run:
9 |
10 | ### `npm start`
11 |
12 | Runs the app in the development mode.
13 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
14 |
15 | The page will reload if you make edits.
16 | You will also see any lint errors in the console.
17 |
18 | ### `npm test`
19 |
20 | Launches the test runner in the interactive watch mode.
21 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
22 |
23 | ### `npm run build`
24 |
25 | Builds the app for production to the `build` folder.
26 | It correctly bundles React in production mode and optimizes the build for the best performance.
27 |
28 | The build is minified and the filenames include the hashes.
29 | Your app is ready to be deployed!
30 |
31 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
32 |
33 | ### `npm run eject`
34 |
35 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
36 |
37 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
38 |
39 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
40 |
41 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
42 |
43 | ## Learn More
44 |
45 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
46 |
47 | To learn React, check out the [React documentation](https://reactjs.org/).
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Michael DeMarco
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
This repository is now deprecated, and development of groUber, now called rooter, is being continued here.
2 |
3 |
4 |
5 |
6 |
7 | # groUber: schedule carpools, without the headache
8 |
9 | Hello, world! We’re groUber, an app for organizing events in the 21st century.
10 |
11 | groups + Uber = [groUber](https://grouber.online)
12 |
13 | ## About
14 |
15 | groUber is aimed to help event planners create carpools, and is being built for To the Moon and Hack. If you're going to use this project to plan your event, remember to stay 6ft apart!
16 |
17 | ### Motivation
18 |
19 | This project was built by a group of 5 students from UBC in Vancouver, BC who love automating things. And one horrific task all of us have run into when planning our events is creating a workable carpool schedule.
20 |
21 | It’s a great option for getting your group together: whether it be parents figuring out how best to get their kids to soccer practice, or friends accommodating those without access to a car, carpooling is common, but creating a plan can be painful, to say the least.
22 |
23 | You finally come up with a workable schedule: everyone can make it to the event, no driver has to go in annoying, wasteful loops, and everything can start on time.
24 |
25 | Then a driver with 4 seats drops out. And you have to do it all over again. No, thanks.
26 |
27 | ### Introducing: groUber
28 |
29 | With groUber, never go through that headache again. As an event organizer, create your event, send an invite link to your friends, and create a carpool schedule with one click. As a participant, simply receive a link, RSVP, and inform the organizer of how many seats you have available. You’ll receive a schedule on the day-of.
30 |
31 | Using the Radar API and the Google Maps API, along with a bit of algo-magic, our app will create the most optimal carpool schedule for everyone involved. We were hesitant to do this project at first; the idea of designing an algorithm to find the “best” carpool strategy was intimidating to say the least. After some research, it turns out this is actually classified an NP-hard problem. We didn't need to solve the problem generally though, and were able to design a heuristic algorithm to be able to compute this with fairly good results. Here are a [few](https://www.sciencedirect.com/science/article/pii/S1877050914006334) [examples](https://arxiv.org/pdf/1604.05609.pdf) of scholarly work in this area. It took great teamwork, persistence, and a decent amount of caffeine to get this working.
32 |
33 | Now, drivers won’t have to waste gas, and everyone will get there on time. Someone drops out? No problem, our app will allow you to adjust your schedule, painlessly.
34 |
35 | groUber is ride-sharing for your group of friends, without all the expenses and overhead. Do a favor for the environment, and for your stress-levels, and start using groUber today.
36 |
37 | Here's our [whitepaper](https://docs.google.com/document/d/e/2PACX-1vSLo8DhZ7p-VgPnmieasD01zZ2_76uMxPxSwVErq3_gQPvpokrFI4G5SHA1Pxrc9b9ouhK5yvL-4X3t/pub) where you can find a more in-depth justification for this hack! We also have a [slide deck](https://docs.google.com/presentation/d/e/2PACX-1vRY4A2dxP3vd1L7tY9gNC1Y0oegweKaiE9ZRHzulQArcLDld2zxErJQPk2TWvtwONXqdFWSECFlWEhh/pub?start=false&loop=false&delayms=15000) for the visual learners out there.
38 |
39 | ## Installation
40 |
41 | ### Stack
42 |
43 | - TypeScript, React
44 | - Node.js, npm
45 | - Firebase, including authentication, hosting, and Firestore
46 |
47 | ### Get it running
48 |
49 | `npm install`
50 | Install dependencies.
51 |
52 | `npm start`
53 | Run for development.
54 |
55 | `npm lint`
56 | Run linting over the project repository.
57 |
58 | Deployment happens entirely via GitHub Actions; on any pushes to master, the app will be re-deployed to Firebase hosting.
59 |
60 | ## Usage
61 |
62 | Head to [grouber.online](https://grouber.online). Sign-up using your Google account.
63 |
64 | Create your event with all key details, and send an invite to your to-be attendees. Once they RSVP, you'll see their details on your event dashboard. Then, generate your event's carpool schedule in one click!
65 |
66 | Participants must submit their address, if they're driving, and if so, how many seats they have available in their vehicle.
67 |
68 | ### Examples
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | ## Contributing
89 |
90 | This will be updated after the hackathon! Stay posted for more.
91 |
--------------------------------------------------------------------------------
/branding/ads/poster-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/ads/poster-01.png
--------------------------------------------------------------------------------
/branding/ads/poster.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/ads/poster.ai
--------------------------------------------------------------------------------
/branding/fonts/PT Serif/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010, ParaType Ltd. (http://www.paratype.com/public),
2 | with Reserved Font Names "PT Sans", "PT Serif" and "ParaType".
3 |
4 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
5 | This license is copied below, and is also available with a FAQ at:
6 | http://scripts.sil.org/OFL
7 |
8 |
9 | -----------------------------------------------------------
10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11 | -----------------------------------------------------------
12 |
13 | PREAMBLE
14 | The goals of the Open Font License (OFL) are to stimulate worldwide
15 | development of collaborative font projects, to support the font creation
16 | efforts of academic and linguistic communities, and to provide a free and
17 | open framework in which fonts may be shared and improved in partnership
18 | with others.
19 |
20 | The OFL allows the licensed fonts to be used, studied, modified and
21 | redistributed freely as long as they are not sold by themselves. The
22 | fonts, including any derivative works, can be bundled, embedded,
23 | redistributed and/or sold with any software provided that any reserved
24 | names are not used by derivative works. The fonts and derivatives,
25 | however, cannot be released under any other type of license. The
26 | requirement for fonts to remain under this license does not apply
27 | to any document created using the fonts or their derivatives.
28 |
29 | DEFINITIONS
30 | "Font Software" refers to the set of files released by the Copyright
31 | Holder(s) under this license and clearly marked as such. This may
32 | include source files, build scripts and documentation.
33 |
34 | "Reserved Font Name" refers to any names specified as such after the
35 | copyright statement(s).
36 |
37 | "Original Version" refers to the collection of Font Software components as
38 | distributed by the Copyright Holder(s).
39 |
40 | "Modified Version" refers to any derivative made by adding to, deleting,
41 | or substituting -- in part or in whole -- any of the components of the
42 | Original Version, by changing formats or by porting the Font Software to a
43 | new environment.
44 |
45 | "Author" refers to any designer, engineer, programmer, technical
46 | writer or other person who contributed to the Font Software.
47 |
48 | PERMISSION & CONDITIONS
49 | Permission is hereby granted, free of charge, to any person obtaining
50 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
51 | redistribute, and sell modified and unmodified copies of the Font
52 | Software, subject to the following conditions:
53 |
54 | 1) Neither the Font Software nor any of its individual components,
55 | in Original or Modified Versions, may be sold by itself.
56 |
57 | 2) Original or Modified Versions of the Font Software may be bundled,
58 | redistributed and/or sold with any software, provided that each copy
59 | contains the above copyright notice and this license. These can be
60 | included either as stand-alone text files, human-readable headers or
61 | in the appropriate machine-readable metadata fields within text or
62 | binary files as long as those fields can be easily viewed by the user.
63 |
64 | 3) No Modified Version of the Font Software may use the Reserved Font
65 | Name(s) unless explicit written permission is granted by the corresponding
66 | Copyright Holder. This restriction only applies to the primary font name as
67 | presented to the users.
68 |
69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
70 | Software shall not be used to promote, endorse or advertise any
71 | Modified Version, except to acknowledge the contribution(s) of the
72 | Copyright Holder(s) and the Author(s) or with their explicit written
73 | permission.
74 |
75 | 5) The Font Software, modified or unmodified, in part or in whole,
76 | must be distributed entirely under this license, and must not be
77 | distributed under any other license. The requirement for fonts to
78 | remain under this license does not apply to any document created
79 | using the Font Software.
80 |
81 | TERMINATION
82 | This license becomes null and void if any of the above conditions are
83 | not met.
84 |
85 | DISCLAIMER
86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
94 | OTHER DEALINGS IN THE FONT SOFTWARE.
95 |
--------------------------------------------------------------------------------
/branding/fonts/PT Serif/PTSerif-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/PT Serif/PTSerif-Bold.ttf
--------------------------------------------------------------------------------
/branding/fonts/PT Serif/PTSerif-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/PT Serif/PTSerif-BoldItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/PT Serif/PTSerif-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/PT Serif/PTSerif-Italic.ttf
--------------------------------------------------------------------------------
/branding/fonts/PT Serif/PTSerif-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/PT Serif/PTSerif-Regular.ttf
--------------------------------------------------------------------------------
/branding/fonts/PT Serif/PT_Serif.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/PT Serif/PT_Serif.zip
--------------------------------------------------------------------------------
/branding/fonts/Poppins/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-Black.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-BlackItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-BoldItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-ExtraBold.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-ExtraLight.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-Italic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-Light.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-LightItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-MediumItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-Thin.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins-ThinItalic.ttf
--------------------------------------------------------------------------------
/branding/fonts/Poppins/Poppins.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/fonts/Poppins/Poppins.zip
--------------------------------------------------------------------------------
/branding/logo/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/android-chrome-192x192.png
--------------------------------------------------------------------------------
/branding/logo/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/android-chrome-512x512.png
--------------------------------------------------------------------------------
/branding/logo/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/apple-touch-icon.png
--------------------------------------------------------------------------------
/branding/logo/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/favicon-16x16.png
--------------------------------------------------------------------------------
/branding/logo/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/favicon-32x32.png
--------------------------------------------------------------------------------
/branding/logo/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/favicon.ico
--------------------------------------------------------------------------------
/branding/logo/g-logo-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/g-logo-01.png
--------------------------------------------------------------------------------
/branding/logo/g-logo-inverse-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/g-logo-inverse-01.png
--------------------------------------------------------------------------------
/branding/logo/grouber-192.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/grouber-192.ai
--------------------------------------------------------------------------------
/branding/logo/grouber-512.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/grouber-512.ai
--------------------------------------------------------------------------------
/branding/logo/grouber-logo-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/grouber-logo-01.png
--------------------------------------------------------------------------------
/branding/logo/grouber-logo-01.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/branding/logo/grouber-logo-inverse-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/grouber-logo-inverse-01.png
--------------------------------------------------------------------------------
/branding/logo/grouber-logo.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/grouber-logo.ai
--------------------------------------------------------------------------------
/branding/logo/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/logo192.png
--------------------------------------------------------------------------------
/branding/logo/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelfromyeg/groUber/ceee9ce08cf1da37904944c9ac5ca34afc6476fe/branding/logo/logo512.png
--------------------------------------------------------------------------------
/branding/logo/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
--------------------------------------------------------------------------------
/database.rules.json:
--------------------------------------------------------------------------------
1 | {
2 | /* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
3 | "rules": {
4 | ".read": false,
5 | ".write": false
6 | }
7 | }
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "rules": "database.rules.json"
4 | },
5 | "functions": {
6 | "predeploy": [
7 | "npm --prefix \"$RESOURCE_DIR\" run lint",
8 | "npm --prefix \"$RESOURCE_DIR\" run build"
9 | ]
10 | },
11 | "hosting": {
12 | "public": "build",
13 | "ignore": [
14 | "firebase.json",
15 | "**/.*",
16 | "**/node_modules/**"
17 | ],
18 | "rewrites": [
19 | {
20 | "source": "**",
21 | "destination": "/index.html"
22 | }
23 | ]
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/functions/.gitignore:
--------------------------------------------------------------------------------
1 | ## Compiled JavaScript files
2 | **/*.js.map
3 |
4 | # Typescript v1 declaration files
5 | typings/
6 |
7 | node_modules/
--------------------------------------------------------------------------------
/functions/lib/DistanceMatrix.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | exports.CustomMap = exports.DistanceMatrix = void 0;
7 | const axios_1 = __importDefault(require("axios"));
8 | class DistanceMatrix {
9 | /**
10 | * @param {People[]} people
11 | * @example distanceMatrix = new DistanceMatrix(people)
12 | */
13 | constructor(people) {
14 | this.data = new CustomMap(); // number is the distance
15 | this.people = people;
16 | }
17 | /** generates data which is our map given a list of people.
18 | *
19 | * @example await distanceMatrix.init();
20 | */
21 | async init() {
22 | const apiData = await this.grabMatrixDataFromRadar(this.people);
23 | const origins = apiData.origins;
24 | const destinations = apiData.destinations;
25 | const matrix = apiData.matrix;
26 | for (let i = 0; i < origins.length; i++) {
27 | const distances = new CustomMap();
28 | const startLoc = {
29 | latitude: this.people[i].location.latlng.lat,
30 | longitude: this.people[i].location.latlng.lng,
31 | };
32 | for (let j = 0; j < destinations.length; j++) {
33 | const endLoc = {
34 | latitude: this.people[j].location.latlng.lat,
35 | longitude: this.people[j].location.latlng.lng,
36 | };
37 | distances.set(endLoc, matrix[i][j].distance.value);
38 | }
39 | this.data.set(startLoc, distances);
40 | }
41 | }
42 | async grabMatrixDataFromRadar(people) {
43 | const locations = people.map((person) => {
44 | return `${person.location.latlng.lat},${person.location.latlng.lng}`;
45 | });
46 | const result = await axios_1.default.get('https://api.radar.io/v1/route/matrix', {
47 | params: {
48 | origins: locations.join('|'),
49 | destinations: locations.join('|'),
50 | mode: 'car',
51 | units: 'metric',
52 | },
53 | headers: {
54 | Authorization: `prj_live_pk_7a9bbe078da0cfa051f77e2c9d9d0f929b9e5955`,
55 | },
56 | });
57 | return result.data;
58 | }
59 | }
60 | exports.DistanceMatrix = DistanceMatrix;
61 | class CustomMap {
62 | constructor() {
63 | // fields go here
64 | // id: string
65 | this.keys = [];
66 | this.values = [];
67 | }
68 | set(key, value) {
69 | const index = this.findIndex(key);
70 | if (index == -1) {
71 | this.keys.push(key);
72 | this.values.push(value);
73 | }
74 | else {
75 | this.values[index] = value;
76 | }
77 | }
78 | get(key) {
79 | return this.values[this.findIndex(key)];
80 | }
81 | findIndex(key) {
82 | for (let i = 0; i < this.keys.length; i++) {
83 | const item = this.keys[i];
84 | if (item.latitude === key.latitude && item.longitude === key.longitude) {
85 | return i;
86 | }
87 | }
88 | return -1;
89 | }
90 | }
91 | exports.CustomMap = CustomMap;
92 | //# sourceMappingURL=DistanceMatrix.js.map
--------------------------------------------------------------------------------
/functions/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 | if (k2 === undefined) k2 = k;
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5 | }) : (function(o, m, k, k2) {
6 | if (k2 === undefined) k2 = k;
7 | o[k2] = m[k];
8 | }));
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
11 | }) : function(o, v) {
12 | o["default"] = v;
13 | });
14 | var __importStar = (this && this.__importStar) || function (mod) {
15 | if (mod && mod.__esModule) return mod;
16 | var result = {};
17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 | __setModuleDefault(result, mod);
19 | return result;
20 | };
21 | var __importDefault = (this && this.__importDefault) || function (mod) {
22 | return (mod && mod.__esModule) ? mod : { "default": mod };
23 | };
24 | Object.defineProperty(exports, "__esModule", { value: true });
25 | exports.solve = exports.directions = void 0;
26 | const functions = __importStar(require("firebase-functions"));
27 | const DistanceMatrix_1 = require("./DistanceMatrix");
28 | const firebase = __importStar(require("firebase"));
29 | const admin = __importStar(require("firebase-admin"));
30 | const priorityqueue_1 = __importDefault(require("priorityqueue"));
31 | require("firebase/firestore");
32 | const axios_1 = __importDefault(require("axios"));
33 | // eslint-disable-next-line @typescript-eslint/no-var-requires
34 | const cors = require('cors')();
35 | const firebaseConfig = {
36 | apiKey: 'AIzaSyDvCT-243TWt9Dwb9ChTOgfkFMUhIjTlRc',
37 | authDomain: 'find-my-carpool.firebaseapp.com',
38 | databaseURL: 'https://find-my-carpool.firebaseio.com',
39 | projectId: 'find-my-carpool',
40 | storageBucket: 'find-my-carpool.appspot.com',
41 | messagingSenderId: '470237283855',
42 | appId: '1:470237283855:web:d3aa289ca316787e4a457a',
43 | measurementId: 'G-YSVSBWXT23',
44 | };
45 | firebase.initializeApp(firebaseConfig);
46 | admin.initializeApp(firebaseConfig);
47 | exports.directions = functions.https.onRequest(async (request, response) => {
48 | cors(request, response, async () => {
49 | response.set('Access-Control-Allow-Origin', '*');
50 | const key = functions.config().directions.key;
51 | if (request.method !== 'POST') {
52 | response.status(405).json({ message: 'Method Not Allowed.' });
53 | return;
54 | }
55 | else if (!request.get('Access-Token')) {
56 | response.status(401).json({ message: 'Unauthorized.' });
57 | return;
58 | }
59 | else if (!key) {
60 | response.status(403).json({ message: 'Service Unavailable' });
61 | return;
62 | }
63 | else {
64 | try {
65 | const verify = await admin.auth().verifyIdToken(request.get('Access-Token'));
66 | if (!verify.uid) {
67 | response.status(403).json({ message: 'Service Unavailable' });
68 | return;
69 | }
70 | else {
71 | console.log(request.body.destination);
72 | const axiosResult = await axios_1.default({
73 | method: 'GET',
74 | url: `https://maps.googleapis.com/maps/api/directions/json?destination=${request.body.destination}&key=${key}&origin=${request.body.origin}&waypoints=${request.body.waypoints}`,
75 | });
76 | response.json(axiosResult.data);
77 | return;
78 | }
79 | }
80 | catch (err) {
81 | response.status(401).json({ message: err.message });
82 | return;
83 | }
84 | }
85 | });
86 | });
87 | exports.solve = functions.https.onRequest(async (request, response) => {
88 | response.set('Access-Control-Allow-Origin', '*');
89 | functions.logger.info('Hello logs!', { structuredData: true });
90 | // grab event first
91 | const result = await firebase.firestore().collection('events').doc(request.query.eventId).get();
92 | const event = result.data();
93 | if (!event) {
94 | response.json('error couldnt find event');
95 | return;
96 | }
97 | const people = await Promise.all(event.people.map(async (person) => {
98 | const personRef = await person.get();
99 | return Object.assign({ id: person.id }, personRef.data());
100 | }));
101 | event.people = people;
102 | response.json(await solveCarpoolProblem(event));
103 | });
104 | /**
105 | * Solves the carpool problem. Given an event (containing a list of people)
106 | * return a hashmap where the key is a driver id, and the value is a list of
107 | * passsenger ids that driver can drive
108 | *
109 | * @param {Event} event
110 | * @returns Map>
111 | */
112 | async function solveCarpoolProblem(event) {
113 | const people = event.people;
114 | let remainingDrivers = getDrivers(people);
115 | let remainingPassengers = getPassengers(people);
116 | const distanceMatrix = new DistanceMatrix_1.DistanceMatrix(people);
117 | await distanceMatrix.init();
118 | const solution = new Map();
119 | while (!isDone(remainingDrivers, remainingPassengers)) {
120 | if (remainingPassengers.length > 0 && remainingDrivers.length === 0)
121 | break;
122 | // const distances = calculatePassengerDistances(remainingDrivers, remainingPassengers);
123 | if (remainingPassengers.length > 0) {
124 | const closestDriverToPassengers = new Map();
125 | for (const passenger of remainingPassengers) {
126 | let minDriverDistance = Infinity;
127 | let minDriverId = '';
128 | for (const driver of remainingDrivers) {
129 | // // calc distance between driver and passenger
130 | // console.log("this shit ran")
131 | // console.log(distanceMatrix.data.keys, distanceMatrix.data.values)
132 | // console.log("this is the key were trying to get", {
133 | // latitude: driver.location.latlng.lat,
134 | // longitude: driver.location.latlng.lng
135 | // })
136 | const distanceMap = distanceMatrix.data.get({
137 | latitude: driver.location.latlng.lat,
138 | longitude: driver.location.latlng.lng,
139 | });
140 | const distance = distanceMap.get({
141 | latitude: passenger.location.latlng.lat,
142 | longitude: passenger.location.latlng.lng,
143 | });
144 | console.log(distance, ' from ', passenger.name, driver.name);
145 | // const distance = haversine({
146 | // latitude: driver.location.latlng.lat,
147 | // longitude: driver.location.latlng.lng,
148 | // }, {
149 | // latitude: passenger.location.latlng.lat,
150 | // longitude: passenger.location.latlng.lng,
151 | // }, {unit: 'meter'})
152 | if (distance < minDriverDistance) {
153 | minDriverDistance = distance;
154 | minDriverId = driver.id;
155 | }
156 | }
157 | closestDriverToPassengers.set(passenger.id, [minDriverId, minDriverDistance]);
158 | }
159 | const numericCompare = (a, b) => (a < b ? 1 : a > b ? -1 : 0);
160 | const comparator = (a, b) => {
161 | return numericCompare(a.minDriverDistance, b.minDriverDistance);
162 | };
163 | const pq = new priorityqueue_1.default({ comparator });
164 | for (const passenger of remainingPassengers) {
165 | const array = closestDriverToPassengers.get(passenger.id);
166 | if (!array)
167 | continue;
168 | const [minDriverId, minDriverDistance] = array;
169 | const node = {
170 | passengerId: passenger.id,
171 | minDriverId,
172 | minDriverDistance,
173 | };
174 | pq.enqueue(node);
175 | }
176 | const nextPassengerNode = pq.dequeue();
177 | if (!nextPassengerNode)
178 | continue;
179 | const nextPassenger = getPersonById(people, nextPassengerNode.passengerId);
180 | const minDriver = getPersonById(people, nextPassengerNode.minDriverId);
181 | if (!nextPassenger || !minDriver)
182 | continue;
183 | minDriver.location.latlng = {
184 | lat: nextPassenger === null || nextPassenger === void 0 ? void 0 : nextPassenger.location.latlng.lat,
185 | lng: nextPassenger === null || nextPassenger === void 0 ? void 0 : nextPassenger.location.latlng.lng,
186 | };
187 | const passengerIds = solution.get(minDriver.id) || [];
188 | passengerIds.push(nextPassenger.id);
189 | solution.set(minDriver.id, passengerIds);
190 | minDriver.seats--;
191 | if (minDriver.seats === 0) {
192 | remainingDrivers = removePerson(remainingDrivers, minDriver.id);
193 | }
194 | remainingPassengers = removePerson(remainingPassengers, nextPassenger.id);
195 | // TODO add to database for visualization of this algorithm
196 | }
197 | // no more remaining passengers but still have drivers
198 | if (remainingDrivers.length === 1 && remainingPassengers.length === 0) {
199 | if (!solution.has(remainingDrivers[0].id))
200 | solution.set(remainingDrivers[0].id, []);
201 | remainingDrivers = removePerson(remainingDrivers, remainingDrivers[0].id);
202 | }
203 | if (remainingPassengers.length === 0) {
204 | const driverToBeConverted = getSomeoneDrivingNoOne(remainingDrivers, solution);
205 | if (driverToBeConverted) {
206 | // exists
207 | remainingPassengers.push(driverToBeConverted);
208 | removePerson(remainingDrivers, driverToBeConverted.id);
209 | }
210 | else {
211 | break;
212 | }
213 | }
214 | }
215 | return strMapToObj(solution);
216 | }
217 | function strMapToObj(strMap) {
218 | const obj = Object.create(null);
219 | for (const [k, v] of strMap) {
220 | // We don’t escape the key '__proto__'
221 | // which can cause problems on older engines
222 | obj[k] = v;
223 | }
224 | return obj;
225 | }
226 | function getSomeoneDrivingNoOne(drivers, solution) {
227 | for (const driver of drivers) {
228 | if (!solution.has(driver.id)) {
229 | return driver;
230 | }
231 | }
232 | return undefined;
233 | }
234 | function removePerson(people, id) {
235 | for (let i = 0; i < people.length; i++) {
236 | const driver = people[i];
237 | if (driver.id === id) {
238 | people.splice(i, 1);
239 | }
240 | }
241 | return people;
242 | }
243 | function getPersonById(people, id) {
244 | for (const person of people) {
245 | if (person.id === id) {
246 | return person;
247 | }
248 | }
249 | return undefined;
250 | }
251 | /**
252 | * returns whether the solution contains all the people in an event
253 | *
254 | * @param {People} people
255 | * @param { Map>} solution
256 | * @returns boolean
257 | */
258 | function isDone(drivers, passengers) {
259 | return drivers.length === 0 && passengers.length === 0;
260 | }
261 | /**
262 | * Return all drivers from a list of people
263 | *
264 | * @param {People[]} people
265 | * @returns People[]
266 | */
267 | function getDrivers(people) {
268 | return people.filter((person) => {
269 | return person.canDrive;
270 | });
271 | }
272 | /**
273 | * Return all passengers from a list of people
274 | *
275 | * @param {People[]} people
276 | * @returns People[]
277 | */
278 | function getPassengers(people) {
279 | return people.filter((person) => {
280 | return !person.canDrive;
281 | });
282 | }
283 | // export const getEncoding = functions.https.onRequest(async (request: any, response: any) => {
284 | // const { route } = request.body
285 | // await Axios.get(
286 | // `https://maps.googleapis.com/maps/api/directions/json?origin=${origin.lat},${origin.lng}&destination=${destination.lat},${destination.lng}&${waypoint}&key=${key}`,
287 | // );
288 | // const origin = route[0];
289 | // const destination = route[route.length - 1];
290 | // if (route.length > 3) {
291 | // let waypoint = 'waypoints=';
292 | // // , === %2C
293 | // // | seperate locations
294 | // for (let i = 1; i < route.length - 1; i++) {
295 | // if (i == route.length - 2) {
296 | // waypoint = waypoint + route[i].lat + '%2C' + route[i].lat;
297 | // } else {
298 | // waypoint = waypoint + route[i].lat + '%2C' + route[i].lat + '|';
299 | // }
300 | // }
301 | // const response = await axios.get(
302 | // `https://maps.googleapis.com/maps/api/directions/json?origin=${origin.lat},${origin.lng}&destination=${destination.lat},${destination.lng}&${waypoint}&key=${key}`,
303 | // );
304 | // // console.log(response);
305 | // } else {
306 | // // const response = await axios.get(
307 | // // `https://maps.googleapis.com/maps/api/directions/json?origin=${origin.lat},${origin.lng}&destination=${destination.lat},${destination.lng}&key=${key}`,
308 | // // );
309 | // // console.log(response);
310 | // }
311 | // }
312 | //# sourceMappingURL=index.js.map
--------------------------------------------------------------------------------
/functions/lib/priorityQueue.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // export class Node{
3 | // value: any;
4 | // priority: number
5 | // constructor(value: any, priority: number){
6 | // this.value = value
7 | // this.priority = priority
8 | // }
9 | // }
10 | // export class PriorityQueue{
11 | // values: Node[]
12 | // constructor(){
13 | // this.values = []
14 | // }
15 | // //helper method that swaps the values and two indexes of an array
16 | // swap(index1: number, index2: number){
17 | // let temp = this.values[index1];
18 | // this.values[index1] = this.values[index2];
19 | // this.values[index2] = temp;
20 | // return this.values;
21 | // }
22 | // //helper methods that bubbles up values from end
23 | // bubbleUp(){
24 | // //get index of inserted element
25 | // let index = this.values.length - 1
26 | // //loop while index is not 0 or element no loger needs to bubble
27 | // while(index > 0){
28 | // //get parent index via formula
29 | // let parentIndex = Math.floor((index - 1)/2);
30 | // //if values is greater than parent, swap the two
31 | // if(this.values[parentIndex].priority > this.values[index].priority){
32 | // //swap with helper method
33 | // this.swap(index, parentIndex);
34 | // //change current index to parent index
35 | // index = parentIndex;
36 | // } else{
37 | // break;
38 | // }
39 | // }
40 | // return 0;
41 | // }
42 | // // method that pushes new value onto the end and calls the bubble helper
43 | // enqueue(value: Node){
44 | // this.values.push(value)
45 | // //calculate parent, if parent is greater swap
46 | // //while loop or recurse
47 | // this.bubbleUp();
48 | // return this.values
49 | // }
50 | // bubbleDown(){
51 | // let parentIndex = 0;
52 | // const length = this.values.length;
53 | // const elementPriority = this.values[0].priority;
54 | // //loop breaks if no swaps are needed
55 | // while (true){
56 | // //get indexes of child elements by following formula
57 | // let leftChildIndex = (2 * parentIndex) + 1;
58 | // let rightChildIndex = (2 * parentIndex) + 2;
59 | // let leftChildPriority, rightChildPriority;
60 | // let indexToSwap = null;
61 | // // if left child exists, and is greater than the element, plan to swap with the left child index
62 | // if(leftChildIndex < length){
63 | // leftChildPriority = this.values[leftChildIndex].priority
64 | // if(leftChildPriority < elementPriority){
65 | // indexToSwap = leftChildIndex;
66 | // }
67 | // }
68 | // //if right child exists
69 | // if(rightChildIndex < length){
70 | // rightChildPriority = this.values[rightChildIndex].priority
71 | // if(
72 | // //if right child is greater than element and there are no plans to swap
73 | // (rightChildPriority < elementPriority && indexToSwap === null) ||
74 | // //OR if right child is greater than left child and there ARE plans to swap
75 | // (rightChildPriority < leftChildPriority && indexToSwap !== null))
76 | // {
77 | // //plan to swap with the right child
78 | // indexToSwap = rightChildIndex
79 | // }
80 | // }
81 | // //if there are no plans to swap, break out of the loop
82 | // if(indexToSwap === null){
83 | // break;
84 | // }
85 | // //swap with planned element
86 | // this.swap(parentIndex, indexToSwap);
87 | // //starting index is now index that we swapped with
88 | // parentIndex = indexToSwap;
89 | // }
90 | // }
91 | // dequeue(){
92 | // //swap first and last element
93 | // this.swap(0, this.values.length - 1);
94 | // //pop max value off of values
95 | // let poppedNode = this.values.pop();
96 | // //re-adjust heap if length is greater than 1
97 | // if(this.values.length > 1){
98 | // this.bubbleDown();
99 | // }
100 | // return poppedNode;
101 | // }
102 | // }
103 | //# sourceMappingURL=priorityQueue.js.map
--------------------------------------------------------------------------------
/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "scripts": {
4 | "lint": "tslint --project tsconfig.json",
5 | "build": "tsc",
6 | "serve": "npm run build && firebase emulators:start --only functions",
7 | "shell": "npm run build && firebase functions:shell",
8 | "start": "npm run shell",
9 | "deploy": "firebase deploy --only functions",
10 | "logs": "firebase functions:log"
11 | },
12 | "engines": {
13 | "node": "10"
14 | },
15 | "main": "lib/index.js",
16 | "dependencies": {
17 | "@types/cors": "^2.8.7",
18 | "@types/node": "^14.0.27",
19 | "axios": "^1.6.0",
20 | "cors": "^2.8.5",
21 | "firebase": "^7.24.0",
22 | "firebase-admin": "^11.3.0",
23 | "firebase-functions": "^3.6.1",
24 | "haversine": "^1.1.1",
25 | "priorityqueue": "^1.0.0",
26 | "radar-sdk-js": "^3.0.1",
27 | "tslint": "^5.20.1",
28 | "typescript": "^3.8.0"
29 | },
30 | "devDependencies": {
31 | "firebase-functions-test": "^0.2.0"
32 | },
33 | "private": true
34 | }
35 |
--------------------------------------------------------------------------------
/functions/src/DistanceMatrix.ts:
--------------------------------------------------------------------------------
1 | import { People } from '../../src/_types/people';
2 | import axios from 'axios';
3 |
4 | export interface Location {
5 | latitude: number;
6 | longitude: number;
7 | }
8 |
9 | export class DistanceMatrix {
10 | people: People[];
11 | data: CustomMap> = new CustomMap(); // number is the distance
12 | /**
13 | * @param {People[]} people
14 | * @example distanceMatrix = new DistanceMatrix(people)
15 | */
16 | constructor(people: People[]) {
17 | this.people = people;
18 | }
19 | /** generates data which is our map given a list of people.
20 | *
21 | * @example await distanceMatrix.init();
22 | */
23 | async init() {
24 | const apiData = await this.grabMatrixDataFromRadar(this.people);
25 | const origins = apiData.origins;
26 | const destinations = apiData.destinations;
27 | const matrix = apiData.matrix;
28 |
29 | for (let i = 0; i < origins.length; i++) {
30 | const distances: CustomMap = new CustomMap();
31 |
32 | const startLoc: Location = {
33 | latitude: this.people[i].location.latlng.lat,
34 | longitude: this.people[i].location.latlng.lng,
35 | };
36 | for (let j = 0; j < destinations.length; j++) {
37 | const endLoc: Location = {
38 | latitude: this.people[j].location.latlng.lat,
39 | longitude: this.people[j].location.latlng.lng,
40 | };
41 | distances.set(endLoc, matrix[i][j].distance.value);
42 | }
43 | this.data.set(startLoc, distances);
44 | }
45 | }
46 |
47 | async grabMatrixDataFromRadar(people: People[]): Promise {
48 | const locations: string[] = people.map((person) => {
49 | return `${person.location.latlng.lat},${person.location.latlng.lng}`;
50 | });
51 |
52 | const result = await axios.get('https://api.radar.io/v1/route/matrix', {
53 | params: {
54 | origins: locations.join('|'),
55 | destinations: locations.join('|'),
56 | mode: 'car',
57 | units: 'metric',
58 | },
59 | headers: {
60 | Authorization: `prj_live_pk_7a9bbe078da0cfa051f77e2c9d9d0f929b9e5955`,
61 | },
62 | });
63 |
64 | return result.data;
65 | }
66 | }
67 |
68 | export class CustomMap {
69 | // fields go here
70 | // id: string
71 |
72 | keys: Array = [];
73 | values: Array = [];
74 |
75 | set(key: Location, value: B) {
76 | const index = this.findIndex(key);
77 | if (index == -1) {
78 | this.keys.push(key);
79 | this.values.push(value);
80 | } else {
81 | this.values[index] = value;
82 | }
83 | }
84 |
85 | get(key: Location) {
86 | return this.values[this.findIndex(key)];
87 | }
88 |
89 | findIndex(key: Location): number {
90 | for (let i = 0; i < this.keys.length; i++) {
91 | const item = this.keys[i];
92 | if (item.latitude === key.latitude && item.longitude === key.longitude) {
93 | return i;
94 | }
95 | }
96 | return -1;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/functions/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as functions from 'firebase-functions';
2 | import { Event } from '../../src/_types/event';
3 | import { People } from '../../src/_types/people';
4 |
5 | import { DistanceMatrix } from './DistanceMatrix';
6 | import * as firebase from 'firebase';
7 | import * as admin from 'firebase-admin';
8 | import PriorityQueue from 'priorityqueue';
9 | import 'firebase/firestore';
10 | import axios from 'axios';
11 |
12 | // eslint-disable-next-line @typescript-eslint/no-var-requires
13 | const cors = require('cors')();
14 |
15 | const firebaseConfig = {
16 | apiKey: 'AIzaSyDvCT-243TWt9Dwb9ChTOgfkFMUhIjTlRc',
17 | authDomain: 'find-my-carpool.firebaseapp.com',
18 | databaseURL: 'https://find-my-carpool.firebaseio.com',
19 | projectId: 'find-my-carpool',
20 | storageBucket: 'find-my-carpool.appspot.com',
21 | messagingSenderId: '470237283855',
22 | appId: '1:470237283855:web:d3aa289ca316787e4a457a',
23 | measurementId: 'G-YSVSBWXT23',
24 | };
25 | firebase.initializeApp(firebaseConfig);
26 |
27 | admin.initializeApp(firebaseConfig);
28 |
29 | export const directions = functions.https.onRequest(async (request: any, response: any) => {
30 | cors(request, response, async () => {
31 | response.set('Access-Control-Allow-Origin', '*');
32 | const key = functions.config().directions.key;
33 | if (request.method !== 'POST') {
34 | response.status(405).json({ message: 'Method Not Allowed.' });
35 | return;
36 | } else if (!request.get('Access-Token')) {
37 | response.status(401).json({ message: 'Unauthorized.' });
38 | return;
39 | } else if (!key) {
40 | response.status(403).json({ message: 'Service Unavailable' });
41 | return;
42 | } else {
43 | try {
44 | const verify = await admin.auth().verifyIdToken(request.get('Access-Token'));
45 | if (!verify.uid) {
46 | response.status(403).json({ message: 'Service Unavailable' });
47 | return;
48 | } else {
49 | console.log(request.body);
50 | const axiosResult = await axios({
51 | method: 'GET',
52 | url: `https://maps.googleapis.com/maps/api/directions/json?destination=${
53 | request.body.destination
54 | }&key=${key}&origin=${request.body.origin}${
55 | request.body.waypoints && `&waypoints=${request.body.waypoints}`
56 | }`,
57 | });
58 | response.json(axiosResult.data);
59 | return;
60 | }
61 | } catch (err) {
62 | response.status(401).json({ message: err.message });
63 | return;
64 | }
65 | }
66 | });
67 | });
68 |
69 | export const solve = functions.https.onRequest(async (request: any, response: any) => {
70 | response.set('Access-Control-Allow-Origin', '*');
71 | functions.logger.info('Hello logs!', { structuredData: true });
72 |
73 | // grab event first
74 | const result = await firebase.firestore().collection('events').doc(request.query.eventId).get();
75 |
76 | const event = result.data();
77 |
78 | if (!event) {
79 | response.json('error couldnt find event');
80 | return;
81 | }
82 |
83 | const people = await Promise.all(
84 | event.people.map(async (person: firebase.firestore.DocumentReference) => {
85 | const personRef = await person.get();
86 | return {
87 | id: person.id,
88 | ...personRef.data(),
89 | };
90 | }),
91 | );
92 |
93 | event.people = people;
94 |
95 | response.json(await solveCarpoolProblem(event as Event));
96 | });
97 |
98 | /**
99 | * Solves the carpool problem. Given an event (containing a list of people)
100 | * return a hashmap where the key is a driver id, and the value is a list of
101 | * passsenger ids that driver can drive
102 | *
103 | * @param {Event} event
104 | * @returns Map>
105 | */
106 | async function solveCarpoolProblem(event: Event): Promise