Skip to content

Commit

Permalink
feat: add useChildrenUpdates and useSharedItemsUpdates hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
codeofmochi committed Jun 22, 2021
1 parent 468999d commit 167e313
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 10 deletions.
5 changes: 2 additions & 3 deletions src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,14 @@ export default (config: Partial<QueryClientConfig>) => {
const hooks = configureHooks(queryClient, queryConfig);

// set up websocket client and hooks given config
if (queryConfig.enableWebsocket) {
configureWebsockets(queryClient, queryConfig);
}
const ws = (queryConfig.enableWebsocket) ? { ws: configureWebsockets(queryClient, queryConfig) } : {};

// returns the queryClient and relative instances
return {
queryClient,
QueryClientProvider,
hooks,
...ws,
useMutation,
ReactQueryDevtools,
};
Expand Down
103 changes: 103 additions & 0 deletions src/ws/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Graasp websocket client
* React effect hooks to subscribe to real-time updates and mutate query client
*
* @author Alexandre CHAU
*/

import { ServerMessage } from "graasp-websockets/src/interfaces/message";
import { List } from "immutable";
import { useEffect } from "react";
import { QueryClient } from "react-query";
import { buildItemChildrenKey, buildItemKey, SHARED_ITEMS_KEY } from "../config/keys";
import { Item, UUID } from "../types";
import { Channel, GraaspWebsocketClient } from "./ws-client";

const ITEM_ENTITY_TYPE = "item"
const MEMBER_ENTITY_TYPE = "member"

export default (websocketClient: GraaspWebsocketClient, queryClient: QueryClient) => ({
/**
* React hook to subscribe to the children updates of the give parent item ID
*
* @param parentId The ID of the parent on which to observe children updates
*/
useChildrenUpdates: (parentId: UUID) => {
useEffect(() => {
if (!parentId) {
return;
}

const channel: Channel = { name: parentId, entity: ITEM_ENTITY_TYPE };
const parentChildrenKey = buildItemChildrenKey(parentId);

const handler = (data: ServerMessage) => {
if (data.type === "update" && data.body.kind === "childItem" && data.body.entity === ITEM_ENTITY_TYPE) {
const current: List<Item> | undefined = queryClient.getQueryData(parentChildrenKey);
const value = data.body.value;
let mutation;
switch (data.body.op) {
case "create": {
if (current && !current.find(i => i.id === value.id)) {
mutation = current.push(value);
queryClient.setQueryData(parentChildrenKey, mutation);
queryClient.setQueryData(buildItemKey(value.id), value);
}
break;
}
case "delete": {
mutation = current?.filter(i => i.id !== value.id);
queryClient.setQueryData(parentChildrenKey, mutation);
break;
}
}
}
};

websocketClient.subscribe(channel, handler);

return function cleanup() {
websocketClient.unsubscribe(channel, handler);
};
}, [parentId]);
},

useSharedItemsUpdates: (userId: UUID) => {
useEffect(() => {
if (!userId) {
return;
}

const channel: Channel = { name: userId, entity: MEMBER_ENTITY_TYPE };

const handler = (data: ServerMessage) => {
if (data.type === "update" && data.body.kind === "sharedWith" && data.body.entity === MEMBER_ENTITY_TYPE) {
const current: List<Item> | undefined = queryClient.getQueryData(SHARED_ITEMS_KEY);
const value = data.body.value;
let mutation;
switch (data.body.op) {
case "create": {
if (current && !current.find(i => i.id === value.id)) {
mutation = current.push(value);
queryClient.setQueryData(SHARED_ITEMS_KEY, mutation);
queryClient.setQueryData(buildItemKey(value.id), value);
}
break;
}
case "delete": {
mutation = current?.filter(i => i.id !== value.id);
queryClient.setQueryData(SHARED_ITEMS_KEY, mutation);
break;
}
}
}
};

websocketClient.subscribe(channel, handler);

return function cleanup() {
websocketClient.unsubscribe(channel, handler);
}
}, [userId]);
},
});
22 changes: 17 additions & 5 deletions src/ws/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
/**
* Graasp websocket client top-level file
* Entry point to use the Graasp WebSocket client in front-end applications
*
* @author Alexandre CHAU
*/

import { QueryClient } from "react-query";
import { QueryClientConfig } from "../types";
import configureWebsocketHooks from "./hooks";
import { configureWebsocketClient } from "./ws-client";

const configureWebsockets = (
// to be called by the main query client configurator
export default (
queryClient: QueryClient,
queryConfig: QueryClientConfig,
) => ({

});
) => {
const websocketClient = configureWebsocketClient(queryConfig);

export default configureWebsockets;
return {
hooks: configureWebsocketHooks(websocketClient, queryClient),
};
};
23 changes: 21 additions & 2 deletions src/ws/ws-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { ClientMessage, ServerMessage } from "graasp-websockets/src/interfaces/message";
import { QueryClientConfig } from "../types";

type Channel = {
export type Channel = {
entity: "item" | "member";
name: string,
};
Expand Down Expand Up @@ -38,7 +38,26 @@ function addToMappedArray<S, T>(map: Map<S, Array<T>>, key: S, value: T) {
}
}

export const configureWebsocketClient = (config: QueryClientConfig) => {
/**
* Websocket client for the graasp-websockets protocol
*/
export interface GraaspWebsocketClient {
/**
* Subscribe a handler to a given channel
* @param channel Channel to which to subscribe to
* @param handler Handler function to register
*/
subscribe(channel: Channel, handler: UpdateHandlerFn): void

/**
* Unsubscribe a handler from a channel, THE HANDLER MUST === THE ONE PASSED TO SUBSCRIBE
* @param channel Channel from wihch to unsubscribe the provided handler from
* @param handler Handler function to unregster, MUST BE EQUAL (===) TO PREVIOUSLY REGISTERED HANDLE WITH @see subscribe !
*/
unsubscribe(channel: Channel, handler: UpdateHandlerFn): void
}

export const configureWebsocketClient = (config: QueryClientConfig): GraaspWebsocketClient => {
// native client WebSocket instance
const ws = new WebSocket(config.WS_HOST);

Expand Down

0 comments on commit 167e313

Please sign in to comment.