* 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>
72 lines
1.8 KiB
TypeScript
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();
|
|
}
|
|
}
|