-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/meditor 896 show collaborators for given document #63
base: main
Are you sure you want to change the base?
Feature/meditor 896 show collaborators for given document #63
Conversation
import type { User } from 'auth/types' | ||
import type { Collaborator } from './types' | ||
|
||
export function adaptUserToCollaborator( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Receives a user and returns a collaborator, which is similar to a user, but has additional fields specifically for the collaboration feature.
packages/app/collaboration/db.ts
Outdated
class CollaboratorsDb { | ||
#COLLECTION = 'Collaborators' | ||
#db: Db | ||
#durationSeconds = 60 * 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used to set the Time To Live (TTL) index for MongoDB.
}) | ||
|
||
if (!index) { | ||
await this.#db.collection(this.#COLLECTION).createIndex( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This TTL index is created only if it doesn't already exist. It is based on the updatedAt
field, which is a BSON ISODate
.
documentTitle: string, | ||
modelName: string | ||
) { | ||
const query = { uid: collaborator.uid } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we query on the uid
as our PK, each user may only be collaborating on one document at a time, which is recorded in documentModelTitle
. If users have multiple document open, and each is sending an HTTP request to the API, I could see this getting weird. I should probably add (to the document page) something to check which tab is active...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 97c6f5c.
const DocumentHeader = ({ | ||
activePanel = null, | ||
document = null, | ||
isJsonPanelOpen = false, | ||
model, | ||
version = null, | ||
toggleJsonDiffer, | ||
togglePanelOpen, | ||
toggleJsonDiffer = () => {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically a bit of a guard against not knowing if we've passed these functions in. Since adding the PropsType
above, I wasn't able to get the app to pass the production ESLint check / tsc
check without marking the toggle fns optional. On the new document page, we now do not pass in these undefined functions.
modelName | ||
) | ||
|
||
consecutiveErrorCount = error ? consecutiveErrorCount + 1 : 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only increment for consecutive errors.
|
||
consecutiveErrorCount = error ? consecutiveErrorCount + 1 : 0 | ||
|
||
setClientCollaborators(filterActiveUser(collaborators, user)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filter
returns an array, so if there are no active collaborators, we still keep the same data type.
@@ -419,11 +475,18 @@ export async function getServerSideProps(ctx: NextPageContext) { | |||
decodeURIComponent(modelName.toString()) | |||
) | |||
|
|||
// We only get the document collaborators here, because the browser's heuristics are needed to determine hasBeenActive and isActive. | |||
const [collaboratorsError, collaborators] = await getDocumentCollaborators( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can load the page with a list of collaborators, but don't poll the API to set collaborators or get collaborators unless the user's browser has the userActivation
API. I suppose this could be worked around by adding an else
to the userActivation
check, where we could still get collaborators on long poll.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 97c6f5c.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I struggled a bit with the path for this API. Right now, it's collaborators/models/[modelName]/documents/[documentTitle]/index.ts
Collaboration is it's own store, which seems appropriate (it's not the same as a document
resource)...but if you think of a better path, please let me know! We do need both the modelName
and documentTitle
to generate a unique document. So, anyway, there are a handful of files like this that disable the constituent endpoints until we reach the only working endpoint at the full path.
|
||
switch (req.method) { | ||
case 'GET': { | ||
// Unlike most GET endpoints, unauthenticated users should not have access to an authenicated user's actions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is worth noting. Also, I'm noodling over how to differentiate between userCanAccessModel
and these !user
checks. I feel like we could rename those functions to be clearer about their restrictions.
When a user opens a tab to collaborate but then closes the document without opening another, they should be removed fairly quickly.
Purpose
This PR adds the ability to see collaborators on a given unique document (model name + document title).
edit
privileges are considered collaborators.isActive
(blue),hasBeenActive
(dark grey), whose states are determined byglobalThis.navigator.userActivation
(docs). If neither are true (I opened the page but never interacted with it), a tertiary stale UI state (light grey) is shown.title
attribute to show additional information about each collaborator: full name, description of what the UI state means.+n
UI component. If there are more than nine additional collaborators, the UI component displays++
.Testing
I owe some tests for this; I built this feature in the tail end of an IP sprint.
Visual Changes
I don't have a screenshot at the moment, but collaborators will show up in the document's header towards the right.