-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
106 changed files
with
15,235 additions
and
4,498 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"singleQuote": true, | ||
"printWidth": 100 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"type": "pwa-chrome", | ||
"request": "launch", | ||
"name": "Launch Chrome against localhost", | ||
"url": "http://localhost:3000", | ||
"webRoot": "${workspaceFolder}" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,27 @@ | ||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). | ||
# Pairist 2 | ||
|
||
## Available Scripts | ||
This is a re-imagining of the Pairist project. Pairist is a web app for teams that pair. It helps you keep track of your pairs and pairing history, the work you have in progress, standup notes, and action items. It can recommend pairs each day based on who has context on what and how recently people have paired with one another. | ||
|
||
In the project directory, you can run: | ||
## What's new? | ||
|
||
### `yarn start` | ||
Pairist 2 makes several improvements and changes to original Pairist: | ||
|
||
Runs the app in the development mode.<br /> | ||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. | ||
### 🔐 You log in as a _person_, not a team | ||
|
||
The page will reload if you make edits.<br /> | ||
You will also see any lint errors in the console. | ||
Previously, Pairist only supported basic authentication (username/password). Login information was shared for a whole team. Pairist 2 uses Google authentication, and you authenticate as yourself (rather than as your team). | ||
|
||
### `yarn test` | ||
Now that you're logged in as a _person_, not a team, you can be a member of multiple teams! | ||
|
||
Launches the test runner in the interactive watch mode.<br /> | ||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. | ||
A team's "people" are now real users who have been added to the team. Team members can be easily added to/removed from teams to support frequent team-switching. | ||
|
||
### `yarn build` | ||
### 🎨 New & improved UI | ||
|
||
Builds the app for production to the `build` folder.<br /> | ||
It correctly bundles React in production mode and optimizes the build for the best performance. | ||
The UI has been updated to be more accessible and more mobile-friendly. We moved away from the Material UI-style components in the original Pairist. Still, the interface should be familiar to anyone who has used the original Pairist. | ||
|
||
The build is minified and the filenames include the hashes.<br /> | ||
Your app is ready to be deployed! | ||
A few long-standing UI bugs were addressed, especially around text editing for list items and elements getting cut off in smaller browser windows. | ||
|
||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. | ||
### ⚛️ Rewritten using React, TypeScript, and Cloud Firestore | ||
|
||
### `yarn eject` | ||
The original version was written in Vue and regular JavaScript. Though this was working fine, in the spirit of experimentation we decided to migrate to React (for fun) and TypeScript (to benefit from static type-checking). | ||
|
||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!** | ||
|
||
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. | ||
|
||
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. | ||
|
||
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. | ||
|
||
## Learn More | ||
|
||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). | ||
|
||
To learn React, check out the [React documentation](https://reactjs.org/). | ||
The back-end previously used Firebase's Realtime Database. Pairist 2 still uses Firebase, but has switched to the newer Cloud Firestore offering. [Learn more about the difference.](https://firebase.google.com/docs/database/rtdb-vs-firestore) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
module.exports = { | ||
reactScriptsVersion: 'react-scripts', | ||
webpack: { | ||
configure: (webpackConfig) => { | ||
webpackConfig.module.rules.push({ | ||
test: /\.(js|mjs|jsx|ts|tsx)$/, | ||
use: [ | ||
{ | ||
loader: 'astroturf/loader', | ||
options: { extension: '.module.scss' }, | ||
}, | ||
], | ||
}); | ||
|
||
return webpackConfig; | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"firestore": { | ||
"rules": "firestore.rules", | ||
"indexes": "firestore.indexes.json" | ||
}, | ||
"functions": { | ||
"predeploy": ["yarn --cwd \"$RESOURCE_DIR\" install", "yarn --cwd \"$RESOURCE_DIR\" build"] | ||
}, | ||
"emulators": { | ||
"firestore": { | ||
"port": 4700 | ||
} | ||
}, | ||
"hosting": { | ||
"public": "build", | ||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"], | ||
"rewrites": [ | ||
{ | ||
"source": "**", | ||
"destination": "/index.html" | ||
} | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"indexes": [], | ||
"fieldOverrides": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
rules_version = '2'; | ||
|
||
service cloud.firestore { | ||
match /databases/{database}/documents { | ||
function authenticated() { | ||
return request.auth.uid != null; | ||
} | ||
|
||
function isTeamMember(teamId) { | ||
return request.auth.uid in get(/databases/$(database)/documents/teamMembers/$(teamId)).data; | ||
} | ||
|
||
function teamExists(teamId) { | ||
return exists(/databases/$(database)/documents/teams/$(teamId)); | ||
} | ||
|
||
function isPublicTeam() { | ||
return resource.data.isPublic == true && resource.data.isArchived != true; | ||
} | ||
|
||
match /{document=**} { | ||
allow read, write: if false; | ||
} | ||
|
||
match /teamMembers/{teamId} { | ||
allow read: if authenticated() && isTeamMember(teamId); | ||
allow write: if false; | ||
} | ||
|
||
match /memberTeams/{userId} { | ||
allow read: if authenticated() && userId == request.auth.uid; | ||
allow write: if false; | ||
} | ||
|
||
match /teamHistories/{teamId} { | ||
allow read: if authenticated() && isTeamMember(teamId); | ||
allow write: if false; | ||
} | ||
|
||
match /teams/{teamId} { | ||
function isValidTeamId(teamId) { | ||
return teamId.matches('[a-z0-9_-]+') | ||
} | ||
|
||
function isValidUpdateRequest() { | ||
return !('id' in request.resource.data); | ||
} | ||
|
||
allow read: if isPublicTeam() || (authenticated() && isTeamMember(teamId)); | ||
allow create: if authenticated() && !teamExists(teamId) && isValidTeamId(teamId); | ||
allow update: if authenticated() && isTeamMember(teamId) && isValidUpdateRequest(); | ||
allow delete: if false; | ||
} | ||
|
||
match /teams/{teamId}/lists/{document=**} { | ||
allow read: if isPublicTeam() || (authenticated() && isTeamMember(teamId)); | ||
allow write: if authenticated() && isTeamMember(teamId); | ||
} | ||
|
||
match /teams/{teamId}/tracks/{document=**} { | ||
allow read: if isPublicTeam() || (authenticated() && isTeamMember(teamId)); | ||
allow write: if authenticated() && isTeamMember(teamId); | ||
} | ||
|
||
match /teams/{teamId}/roles/{document=**} { | ||
allow read: if isPublicTeam() || (authenticated() && isTeamMember(teamId)); | ||
allow write: if authenticated() && isTeamMember(teamId); | ||
} | ||
|
||
match /teams/{teamId}/people/{document=**} { | ||
allow read: if isPublicTeam() || (authenticated() && isTeamMember(teamId)); | ||
allow write: if authenticated() && isTeamMember(teamId); | ||
} | ||
|
||
match /teams/{teamId}/lanes/{document=**} { | ||
allow read: if isPublicTeam() || (authenticated() && isTeamMember(teamId)); | ||
allow write: if authenticated() && isTeamMember(teamId); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "functions", | ||
"private": true, | ||
"scripts": { | ||
"build": "tsc", | ||
"serve": "yarn build && firebase serve --only functions", | ||
"shell": "yarn build && firebase functions:shell", | ||
"start": "yarn shell", | ||
"deploy": "firebase deploy --only functions", | ||
"logs": "firebase functions:log" | ||
}, | ||
"engines": { | ||
"node": "10" | ||
}, | ||
"main": "lib/index.js", | ||
"dependencies": { | ||
"firebase-admin": "^9.4.1", | ||
"firebase-functions": "^3.11.0" | ||
}, | ||
"devDependencies": { | ||
"typescript": "4.1.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import * as admin from 'firebase-admin'; | ||
import * as functions from 'firebase-functions'; | ||
|
||
const db = admin.firestore(); | ||
const auth = admin.auth(); | ||
|
||
export const addTeamMember = functions.https.onCall(async (data, context) => { | ||
if (!context.auth || !context.auth.uid) { | ||
throw new functions.https.HttpsError('unauthenticated', 'Unauthenticated.'); | ||
} | ||
|
||
if (!context.auth.token.email_verified) { | ||
throw new functions.https.HttpsError('unauthenticated', 'Unverified.'); | ||
} | ||
|
||
const { uid } = context.auth; | ||
|
||
const { teamId, teamName, memberEmail } = data; | ||
|
||
const membersSnapshot = await db.collection('teamMembers').doc(teamId).get(); | ||
const currentMembers = membersSnapshot.data() || {}; | ||
|
||
if (!(uid in currentMembers)) { | ||
throw new functions.https.HttpsError( | ||
'failed-precondition', | ||
'Not authenticated as a member of this team.' | ||
); | ||
} | ||
|
||
console.log(`Adding user ${memberEmail} to team ${teamName} (teamId: ${teamId})`); | ||
|
||
let userToAdd: admin.auth.UserRecord; | ||
try { | ||
userToAdd = await auth.getUserByEmail(memberEmail); | ||
} catch (err) { | ||
throw new functions.https.HttpsError('not-found', 'User not found.'); | ||
} | ||
|
||
if (!userToAdd.emailVerified) { | ||
throw new functions.https.HttpsError('failed-precondition', 'Cannot add unverified user.'); | ||
} | ||
|
||
const batch = db.batch(); | ||
|
||
batch.set( | ||
db.collection('teamMembers').doc(teamId), | ||
{ | ||
[userToAdd.uid]: { | ||
displayName: userToAdd.displayName || '', | ||
email: userToAdd.email || '', | ||
photoURL: userToAdd.photoURL || '', | ||
}, | ||
}, | ||
{ merge: true } | ||
); | ||
|
||
batch.set( | ||
db.collection('memberTeams').doc(userToAdd.uid), | ||
{ | ||
[teamId]: teamName, | ||
}, | ||
{ merge: true } | ||
); | ||
|
||
await batch.commit(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import * as admin from 'firebase-admin'; | ||
import * as functions from 'firebase-functions'; | ||
|
||
const db = admin.firestore(); | ||
|
||
export const createTeam = functions.https.onCall(async (data, context) => { | ||
if (!context.auth || !context.auth.uid) { | ||
throw new functions.https.HttpsError('unauthenticated', 'Unauthenticated.'); | ||
} | ||
|
||
if (!context.auth.token.email_verified) { | ||
throw new functions.https.HttpsError('unauthenticated', 'Unverified.'); | ||
} | ||
|
||
const { uid } = context.auth; | ||
|
||
let { teamId, teamName, isPublic, userDisplayName, userPhotoURL } = data; | ||
|
||
teamId = teamId.toLowerCase(); | ||
|
||
console.log(`Creating team ${teamName} (id: ${teamId}) as user ${uid}`); | ||
|
||
const teamSnapshot = await db.collection('teams').doc(teamId).get(); | ||
|
||
if (teamSnapshot.exists) { | ||
throw new functions.https.HttpsError( | ||
'failed-precondition', | ||
'Team with this name already exists.' | ||
); | ||
} | ||
|
||
const batch = db.batch(); | ||
|
||
batch.set(db.collection('teams').doc(teamId), { | ||
created: admin.firestore.FieldValue.serverTimestamp(), | ||
isPublic: isPublic || false, | ||
teamName, | ||
}); | ||
|
||
batch.set(db.collection('teams').doc(teamId).collection('people').doc(uid), { | ||
laneId: '', | ||
}); | ||
|
||
batch.set(db.collection('teamMembers').doc(teamId), { | ||
[uid]: { | ||
displayName: userDisplayName, | ||
photoURL: userPhotoURL, | ||
}, | ||
}); | ||
|
||
batch.set(db.collection('memberTeams').doc(uid), { | ||
[teamId]: teamName, | ||
}); | ||
|
||
await batch.commit(); | ||
}); |
Oops, something went wrong.