Files
outline/server/collaboration/ViewsExtension.ts
Apoorv Mishra 7e61a519f1 Type server models (#6326)
* fix: type server models

* fix: make ParanoidModel generic

* fix: ApiKey

* fix: Attachment

* fix: AuthenticationProvider

* fix: Backlink

* fix: Collection

* fix: Comment

* fix: Document

* fix: FileOperation

* fix: Group

* fix: GroupPermission

* fix: GroupUser

* fix: Integration

* fix: IntegrationAuthentication

* fix: Notification

* fix: Pin

* fix: Revision

* fix: SearchQuery

* fix: Share

* fix: Star

* fix: Subscription

* fix: TypeError

* fix: Imports

* fix: Team

* fix: TeamDomain

* fix: User

* fix: UserAuthentication

* fix: UserPermission

* fix: View

* fix: WebhookDelivery

* fix: WebhookSubscription

* Remove type duplication

---------

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2024-01-12 22:33:05 +05:30

72 lines
1.8 KiB
TypeScript

import {
Extension,
onDisconnectPayload,
onChangePayload,
} from "@hocuspocus/server";
import { Minute } from "@shared/utils/time";
import Logger from "@server/logging/Logger";
import { trace } from "@server/logging/tracing";
import { View } from "@server/models";
import { withContext } from "./types";
@trace()
export class ViewsExtension implements Extension {
/**
* Map of last view recorded by socket
*/
lastViewBySocket: Map<string, Date> = new Map();
/**
* onChange hook. When a user changes a document, we update their "viewedAt"
* timestamp if it's been more than a minute since their last change.
*
* @param data The change payload
*/
async onChange({
documentName,
context,
socketId,
}: withContext<onChangePayload>) {
if (!context.user) {
return;
}
const lastUpdate = this.lastViewBySocket.get(socketId);
const [, documentId] = documentName.split(".");
if (!lastUpdate || Date.now() - lastUpdate.getTime() > Minute) {
this.lastViewBySocket.set(socketId, new Date());
Logger.debug(
"multiplayer",
`User ${context.user.id} viewed "${documentName}"`
);
await Promise.all([
View.touch(documentId, context.user.id, true),
context.user.update({ lastActiveAt: new Date() }),
]);
}
}
/**
* onDisconnect hook. When a user disconnects, we remove their socket from
* the lastViewBySocket map to cleanup memory.
*
* @param data The disconnect payload
*/
async onDisconnect({ socketId }: onDisconnectPayload) {
const interval = this.lastViewBySocket.get(socketId);
if (interval) {
this.lastViewBySocket.delete(socketId);
}
}
/**
* onDestroy hook
* @param data The destroy payload
*/
async onDestroy() {
this.lastViewBySocket = new Map();
}
}