Skip to content

Commit

Permalink
feat: refactor websocket client, add new real-time behaviours
Browse files Browse the repository at this point in the history
  • Loading branch information
codeofmochi committed Aug 3, 2021
1 parent 35b96c4 commit e836e7c
Show file tree
Hide file tree
Showing 20 changed files with 717 additions and 651 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"@babel/preset-typescript": "7.14.5",
"@commitlint/cli": "12.1.4",
"@commitlint/config-conventional": "12.1.4",
"@graasp/websockets": "git://github.com/graasp/graasp-websockets.git#master",
"@testing-library/jest-dom": "5.11.6",
"@testing-library/react": "11.2.2",
"@testing-library/react-hooks": "7.0.0",
Expand Down
30 changes: 30 additions & 0 deletions src/api/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { PartialChatMessage, QueryClientConfig, UUID } from '../types';
import { buildGetItemChatRoute, buildPostItemChatMessageRoute } from './routes';
import { DEFAULT_GET, DEFAULT_POST, failOnError } from './utils';

export const getItemChat = async (
id: UUID,
{ API_HOST }: QueryClientConfig,
) => {
const res = await fetch(
`${API_HOST}/${buildGetItemChatRoute(id)}`,
DEFAULT_GET,
).then(failOnError);
const itemChat = await res.json();
return itemChat;
};

export const postItemChatMessage = async (
{ chatId, body }: PartialChatMessage,
{ API_HOST }: QueryClientConfig,
) => {
const res = await fetch(
`${API_HOST}/${buildPostItemChatMessageRoute(chatId)}`,
{
...DEFAULT_POST,
body: JSON.stringify({ body }),
},
).then(failOnError);
const publishedMessage = await res.json();
return publishedMessage;
};
1 change: 1 addition & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './authentication';
export * from './itemTag';
export * from './itemLogin';
export * from './itemFlag';
export * from './chat';
3 changes: 3 additions & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export const buildShareItemWithRoute = (id: UUID) =>
`item-memberships?itemId=${id}`;
export const buildGetItemMembershipsForItemRoute = (id: UUID) =>
`item-memberships?itemId=${id}`;
export const buildGetItemChatRoute = (id: UUID) => `${ITEMS_ROUTE}/${id}/chat`;
export const buildPostItemChatMessageRoute = (id: UUID) =>
`${ITEMS_ROUTE}/${id}/chat`;

export const buildGetMemberBy = (email: string) =>
`${MEMBERS_ROUTE}?email=${email}`;
Expand Down
3 changes: 3 additions & 0 deletions src/config/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const CURRENT_MEMBER_KEY = 'currentMember';
export const MEMBERS_KEY = 'members';
export const buildMemberKey = (id: UUID) => [MEMBERS_KEY, id];
export const buildItemParentsKey = (id: UUID) => [ITEMS_KEY, id, 'parents'];
export const CHATS_KEY = 'chats';
export const buildItemChatKey = (id: UUID) => [CHATS_KEY, id];

export const getKeyForParentId = (parentId: UUID | null) =>
parentId ? buildItemChildrenKey(parentId) : OWN_ITEMS_KEY;
Expand Down Expand Up @@ -48,4 +50,5 @@ export const MUTATION_KEYS = {
POST_ITEM_FLAG: 'postItemFlag',
EDIT_ITEM_MEMBERSHIP: 'editItemMembership',
DELETE_ITEM_MEMBERSHIP: 'deleteItemMembership',
POST_ITEM_CHAT_MESSAGE: 'postChatMessage',
};
24 changes: 24 additions & 0 deletions src/hooks/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useQuery } from 'react-query';
import * as Api from '../api';
import { buildItemChatKey } from '../config/keys';
import { QueryClientConfig, UUID } from '../types';

export default (queryConfig: QueryClientConfig) => {
const { retry, cacheTime, staleTime } = queryConfig;
const defaultOptions = {
retry,
cacheTime,
staleTime,
};

return {
useItemChat: (itemId: UUID) =>
useQuery({
queryKey: buildItemChatKey(itemId),
queryFn: () =>
Api.getItemChat(itemId, queryConfig).then((data) => data),
...defaultOptions,
enabled: Boolean(itemId),
}),
};
};
2 changes: 2 additions & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import configureItemHooks from './item';
import configureMemberHooks from './member';
import configureItemTagHooks from './itemTag';
import configureItemFlagHooks from './itemFlag';
import configureChatHooks from './chat';
import { QueryClientConfig } from '../types';

export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => ({
...configureItemHooks(queryClient, queryConfig),
...configureMemberHooks(queryConfig),
...configureItemTagHooks(queryConfig),
...configureItemFlagHooks(queryConfig),
...configureChatHooks(queryConfig),
});
12 changes: 12 additions & 0 deletions src/mutations/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { QueryClient } from 'react-query';
import * as Api from '../api'
import { MUTATION_KEYS } from '../config/keys';
import { QueryClientConfig } from '../types';

const { POST_ITEM_CHAT_MESSAGE } = MUTATION_KEYS;

export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
queryClient.setMutationDefaults(POST_ITEM_CHAT_MESSAGE, {
mutationFn: (chatMsg) => Api.postItemChatMessage(chatMsg, queryConfig),
});
};
2 changes: 2 additions & 0 deletions src/mutations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import memberMutations from './member';
import tagsMutations from './itemTag';
import flagsMutations from './itemFlag';
import itemMembershipMutations from './membership';
import chatMutations from './chat';
import { QueryClientConfig } from '../types';

const configureMutations = (
Expand All @@ -15,6 +16,7 @@ const configureMutations = (
memberMutations(queryClient, queryConfig);
tagsMutations(queryClient, queryConfig);
flagsMutations(queryClient, queryConfig);
chatMutations(queryClient, queryConfig);
};

export default configureMutations;
17 changes: 17 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,20 @@ export enum PERMISSION_LEVELS {
WRITE = 'write',
ADMIN = 'admin',
}

export type PartialChatMessage = {
chatId: string;
body: string;
};

export type ChatMessage = {
chatId: string;
creator: string;
createdAt: string;
body: string;
};

export interface Chat {
id: string;
messages: Array<ChatMessage>;
}
27 changes: 27 additions & 0 deletions src/ws/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* TODO: use types from graasp-websockets
*/

/** Namespace for notifications realm */
export const REALM_NOTIF = 'notif';

/** Client actions */
export const CLIENT_ACTION_SUBSCRIBE = 'subscribe';
export const CLIENT_ACTION_UNSUBSCRIBE = 'unsubscribe';
export const CLIENT_ACTION_SUBSCRIBE_ONLY = 'subscribeOnly';
export const CLIENT_ACTION_DISCONNECT = 'disconnect';

/** Server message types */
export const SERVER_TYPE_RESPONSE = 'response';
export const SERVER_TYPE_UPDATE = 'update';
export const SERVER_TYPE_INFO = 'info';

/** Server response status */
export const RESPONSE_STATUS_SUCCESS = 'success';
export const RESPONSE_STATUS_ERROR = 'error';

/** Error names */
export const ERROR_ACCESS_DENIED = 'ACCESS_DENIED';
export const ERROR_BAD_REQUEST = 'BAD_REQUEST';
export const ERROR_NOT_FOUND = 'NOT_FOUND';
export const ERROR_SERVER_ERROR = 'SERVER_ERROR';
134 changes: 0 additions & 134 deletions src/ws/hooks.ts

This file was deleted.

54 changes: 54 additions & 0 deletions src/ws/hooks/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { QueryClient } from 'react-query';
import { buildItemChatKey } from '../../config/keys';
import { Chat, ChatMessage, UUID } from '../../types';
import { Channel, GraaspWebsocketClient } from '../ws-client';

// todo: use graasp-types?
interface ChatEvent {
kind: string;
op: string;
message: ChatMessage;
}

export default (
websocketClient: GraaspWebsocketClient,
queryClient: QueryClient,
) => ({
/**
* React hook to subscribe to the updates of the given chat ID
* @param chatId The ID of the chat of which to observves updates
*/
useItemChatUpdates: (chatId: UUID) => {
if (!chatId) {
return;
}

const channel: Channel = { name: chatId, topic: 'chat/item' };

const handler = (event: ChatEvent) => {
if (event.kind === 'item') {
const chatKey = buildItemChatKey(chatId);
const current: Chat | undefined = queryClient.getQueryData(chatKey);

if (current) {
switch (event.op) {
case 'publish': {
const msg = event.message;
const newChat = Object.assign({}, current);
newChat.messages = [...current.messages];
newChat.messages.push(msg);
queryClient.setQueryData(chatKey, newChat);
break;
}
}
}
}
};

websocketClient.subscribe(channel, handler);

return function cleanup() {
websocketClient.unsubscribe(channel, handler);
};
},
});
16 changes: 16 additions & 0 deletions src/ws/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { QueryClient } from 'react-query';
import { GraaspWebsocketClient } from '../ws-client';
import configureChatHooks from './chat';
import configureItemHooks from './item';
import configureMembershipHooks from './membership';

export default (
websocketClient: GraaspWebsocketClient,
queryClient: QueryClient,
) => {
return {
...configureItemHooks(websocketClient, queryClient),
...configureMembershipHooks(websocketClient, queryClient),
...configureChatHooks(websocketClient, queryClient),
};
};
Loading

0 comments on commit e836e7c

Please sign in to comment.