mirror of
https://github.com/docmost/docmost.git
synced 2025-11-16 23:31:12 +10:00
websocket updates
* sync page title on icon via websocket * sync on page tree too
This commit is contained in:
4
apps/client/src/features/websocket/atoms/socket-atom.ts
Normal file
4
apps/client/src/features/websocket/atoms/socket-atom.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { atom } from "jotai";
|
||||
import { Socket } from "socket.io-client";
|
||||
|
||||
export const socketAtom = atom<Socket | null>(null);
|
||||
3
apps/client/src/features/websocket/types/constants.ts
Normal file
3
apps/client/src/features/websocket/types/constants.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const SOCKET_URL = import.meta.env.DEV
|
||||
? "http://localhost:3000"
|
||||
: undefined;
|
||||
2
apps/client/src/features/websocket/types/index.ts
Normal file
2
apps/client/src/features/websocket/types/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./types.ts";
|
||||
export * from "./constants.ts";
|
||||
14
apps/client/src/features/websocket/types/types.ts
Normal file
14
apps/client/src/features/websocket/types/types.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export type InvalidateEvent = {
|
||||
operation: "invalidate";
|
||||
entity: Array<string>;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export type UpdateEvent = {
|
||||
operation: "updateOne";
|
||||
entity: Array<string>;
|
||||
id: string;
|
||||
payload: Partial<any>;
|
||||
};
|
||||
|
||||
export type WebSocketEvent = InvalidateEvent | UpdateEvent;
|
||||
11
apps/client/src/features/websocket/use-query-emit.ts
Normal file
11
apps/client/src/features/websocket/use-query-emit.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { socketAtom } from "@/features/websocket/atoms/socket-atom.ts";
|
||||
import { useAtom } from "jotai";
|
||||
import { WebSocketEvent } from "@/features/websocket/types";
|
||||
|
||||
export const useQueryEmit = () => {
|
||||
const [socket] = useAtom(socketAtom);
|
||||
|
||||
return (input: WebSocketEvent) => {
|
||||
socket?.emit("message", input);
|
||||
};
|
||||
};
|
||||
43
apps/client/src/features/websocket/use-query-subscription.ts
Normal file
43
apps/client/src/features/websocket/use-query-subscription.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
import { socketAtom } from "@/features/websocket/atoms/socket-atom.ts";
|
||||
import { useAtom } from "jotai";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { WebSocketEvent } from "@/features/websocket/types";
|
||||
|
||||
export const useQuerySubscription = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const [socket] = useAtom(socketAtom);
|
||||
|
||||
React.useEffect(() => {
|
||||
socket?.on("message", (event) => {
|
||||
const data: WebSocketEvent = event;
|
||||
|
||||
switch (data.operation) {
|
||||
case "invalidate":
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...data.entity, data.id].filter(Boolean),
|
||||
});
|
||||
break;
|
||||
case "updateOne":
|
||||
queryClient.setQueryData([...data.entity, data.id], {
|
||||
...queryClient.getQueryData([...data.entity, data.id]),
|
||||
...data.payload,
|
||||
});
|
||||
|
||||
/*
|
||||
queryClient.setQueriesData(
|
||||
{ queryKey: [data.entity, data.id] },
|
||||
(oldData: any) => {
|
||||
const update = (entity: Record<string, unknown>) =>
|
||||
entity.id === data.id ? { ...entity, ...data.payload } : entity;
|
||||
return Array.isArray(oldData)
|
||||
? oldData.map(update)
|
||||
: update(oldData as Record<string, unknown>);
|
||||
},
|
||||
);
|
||||
*/
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, [queryClient, socket]);
|
||||
};
|
||||
62
apps/client/src/features/websocket/use-tree-socket.ts
Normal file
62
apps/client/src/features/websocket/use-tree-socket.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { socketAtom } from "@/features/websocket/atoms/socket-atom.ts";
|
||||
import { useAtom } from "jotai";
|
||||
import { treeDataAtom } from "@/features/page/tree/atoms/tree-data-atom.ts";
|
||||
import {
|
||||
updateTreeNodeIcon,
|
||||
updateTreeNodeName,
|
||||
} from "@/features/page/tree/utils";
|
||||
import { WebSocketEvent } from "@/features/websocket/types";
|
||||
import { SpaceTreeNode } from "@/features/page/tree/types.ts";
|
||||
|
||||
export const useTreeSocket = () => {
|
||||
const [socket] = useAtom(socketAtom);
|
||||
const [treeData, setTreeData] = useAtom(treeDataAtom);
|
||||
|
||||
const initialTreeData = useRef(treeData);
|
||||
|
||||
useEffect(() => {
|
||||
initialTreeData.current = treeData;
|
||||
}, [treeData]);
|
||||
|
||||
useEffect(() => {
|
||||
socket?.on("message", (event) => {
|
||||
const data: WebSocketEvent = event;
|
||||
|
||||
const initialData = initialTreeData.current;
|
||||
switch (data.operation) {
|
||||
case "invalidate":
|
||||
// nothing to do here
|
||||
break;
|
||||
case "updateOne":
|
||||
// Get the initial value of treeData
|
||||
if (initialData && initialData.length > 0) {
|
||||
let newTreeData: SpaceTreeNode[];
|
||||
|
||||
if (data.entity[0] === "pages") {
|
||||
if (data.payload?.title !== undefined) {
|
||||
newTreeData = updateTreeNodeName(
|
||||
initialData,
|
||||
data.id,
|
||||
data.payload.title,
|
||||
);
|
||||
}
|
||||
|
||||
if (data.payload?.icon !== undefined) {
|
||||
newTreeData = updateTreeNodeIcon(
|
||||
initialData,
|
||||
data.id,
|
||||
data.payload.icon,
|
||||
);
|
||||
}
|
||||
|
||||
if (newTreeData && newTreeData.length > 0) {
|
||||
setTreeData(newTreeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, [socket]);
|
||||
};
|
||||
Reference in New Issue
Block a user