Skip to content
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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

benforshey
Copy link
Contributor

Purpose

This PR adds the ability to see collaborators on a given unique document (model name + document title).

  • Only users with edit privileges are considered collaborators.
  • Collaborators have UI for isActive (blue), hasBeenActive (dark grey), whose states are determined by globalThis.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.
  • Collaborators are removed after 10 minutes of inactivity (they are no longer have a UI element representing them on the page). E.g., I'm editing a page, but then leave it. My browser may put the tab into "sleep mode" and stop sending updates. After 10 minutes, I'd no longer be considered a collaborator, and the UI for my user as a collaborator would disappear. Another example: I leave the page open and my browser doesn't suspend the tab. Because it is still sending requests to the collaborator API, I will still be shown as a collaborator. Let's discuss this if there's something more optimal that we could do!
  • I used the title attribute to show additional information about each collaborator: full name, description of what the UI state means.
  • Even if you're not an editor for a given document, you will still see collaborators.
  • Four collaborators will be displayed at a time, with up to nine additional collaborators being put into a +n UI component. If there are more than nine additional collaborators, the UI component displays ++.
  • Collaborators are sorted by state, so active collaborators show up first, followed by inactive collaborators, followed by collaborators who haven't yet interacted with the page.

Testing

I owe some tests for this; I built this feature in the tail end of an IP sprint.

  • Run the existing tests to see if I've broken anything.
  • Find someone (or lots of people) who have edit privileges (maybe some who don't) and visit a document.

Visual Changes

I don't have a screenshot at the moment, but collaborators will show up in the document's header towards the right.

@benforshey benforshey added the enhancement New feature or request label Sep 20, 2024
@benforshey benforshey self-assigned this Sep 20, 2024
import type { User } from 'auth/types'
import type { Collaborator } from './types'

export function adaptUserToCollaborator(
Copy link
Contributor Author

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.

class CollaboratorsDb {
#COLLECTION = 'Collaborators'
#db: Db
#durationSeconds = 60 * 10
Copy link
Contributor Author

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(
Copy link
Contributor Author

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 }
Copy link
Contributor Author

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...

Copy link
Contributor Author

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 = () => {},
Copy link
Contributor Author

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
Copy link
Contributor Author

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))
Copy link
Contributor Author

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(
Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 97c6f5c.

Copy link
Contributor Author

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.
Copy link
Contributor Author

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant