Skip to content

Commit

Permalink
fix: conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
KaustubhKumar05 committed May 17, 2024
2 parents 5628068 + bee8761 commit c29d3a7
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class PollsManager {
}

this.updatePollResult(savedPoll, updatedPoll);
await this.updatePollResponses(savedPoll, false);
await this.updatePollResponses(savedPoll, true);

updatedPolls.push(savedPoll);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ export class WhiteboardInteractivityCenter implements HMSWhiteboardInteractivity
for (const whiteboard of whiteboards.values()) {
if (whiteboard.url) {
const response = await this.transport.signal.getWhiteboard({ id: whiteboard.id });
const localPeer = this.store.getLocalPeer();
const isOwner = localPeer?.customerUserId === response.owner;
const open = isOwner
? localPeer.role?.permissions.whiteboard?.includes('admin')
: response.permissions.length > 0;
const newWhiteboard: HMSWhiteboard = {
...whiteboard,
id: response.id,
Expand All @@ -86,7 +91,7 @@ export class WhiteboardInteractivityCenter implements HMSWhiteboardInteractivity
addr: response.addr,
owner: response.owner,
permissions: response.permissions,
open: response.permissions.length > 0,
open,
};

this.store.setWhiteboard(newWhiteboard);
Expand Down
1 change: 1 addition & 0 deletions packages/hms-whiteboard/src/Whiteboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export function Whiteboard({ onMount, endpoint, token, zoomToContent, transparen
onMount={handleMount}
components={{ ErrorFallback }}
hideUi={editor?.getInstanceState()?.isReadonly}
initialState={editor?.getInstanceState()?.isReadonly ? 'hand' : 'select'}
/>
);
}
56 changes: 24 additions & 32 deletions packages/hms-whiteboard/src/hooks/StoreClient.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport';
import { Value_Type } from '../grpc/sessionstore';
import { StoreClient } from '../grpc/sessionstore.client';
import { OPEN_WAIT_TIMEOUT } from '../utils';

interface OpenCallbacks<T> {
handleOpen: (values: T[]) => void;
handleChange: (key: string, value?: T) => void;
handleError: (error: Error) => void;
}

const WHITEBOARD_CLOSE_MESSAGE = 'client whiteboard abort';

export class SessionStore<T> {
private storeClient: StoreClient;
private abortController = new AbortController();

constructor(endpoint: string, token: string) {
const transport = new GrpcWebFetchTransport({
Expand All @@ -21,32 +25,30 @@ export class SessionStore<T> {
}

async open({ handleOpen, handleChange, handleError }: OpenCallbacks<T>) {
const call = this.storeClient.open({
changeId: '',
select: [],
});
/**
* on open, get key count to call handleOpen with the pre-existing values from the store
* retry if getKeysCount is called before open call is completed
*/
const keyCount = await this.retryForOpen(this.getKeysCount.bind(this));
const call = this.storeClient.open(
{
changeId: '',
select: [],
},
{ abort: this.abortController.signal },
);
const initialValues: T[] = [];
let initialised = false;

if (!keyCount) {
handleOpen([]);
}
// on open, wait to call handleOpen with the pre-existing values from the store
setTimeout(() => {
handleOpen(initialValues);
initialised = true;
}, OPEN_WAIT_TIMEOUT);

call.responses.onMessage(message => {
if (message.value) {
if (message.value?.data.oneofKind === 'str') {
const record = JSON.parse(message.value.data.str) as T;
if (initialValues.length === keyCount) {
if (initialised) {
handleChange(message.key, record);
} else {
initialValues.push(record);
if (initialValues.length === keyCount) {
handleOpen(initialValues);
}
}
}
} else {
Expand All @@ -55,12 +57,14 @@ export class SessionStore<T> {
});

call.responses.onError(error => {
handleError(error);
if (!error.message.includes('abort')) {
handleError(error);
}
});

call.status.then(status => {
console.log('SessionStoreClient open', status);
});
return () => {
this.abortController.abort(WHITEBOARD_CLOSE_MESSAGE);
};
}

set(key: string, value?: T) {
Expand Down Expand Up @@ -95,16 +99,4 @@ export class SessionStore<T> {
delete(key: string) {
return this.storeClient.delete({ key });
}

private async retryForOpen<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
try {
return await fn();
} catch (error) {
const shouldRetry = (error as Error).message.includes('peer not found') && retries > 0;
if (!shouldRetry) {
return Promise.reject(error);
}
return await this.retryForOpen(fn, retries - 1);
}
}
}
101 changes: 59 additions & 42 deletions packages/hms-whiteboard/src/hooks/useCollaboration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
HistoryEntry,
throttle,
TLAnyShapeUtilConstructor,
TLInstance,
TLINSTANCE_ID,
TLPage,
TLRecord,
TLStoreWithStatus,
Expand Down Expand Up @@ -61,37 +63,14 @@ export function useCollaboration({
}, []);

const sessionStore = useSessionStore({ token, endpoint, handleError });
const permissions = useSetEditorPermissions({ token, editor, zoomToContent, handleError });

useSetEditorPermissions({ token, editor, zoomToContent, handleError });

useEffect(() => {
if (!sessionStore) return;

setStoreWithStatus({ status: 'loading' });

const unsubs: (() => void)[] = [];

// 1.
// Connect store to yjs store and vis versa, for both the document and awareness

/* -------------------- Document -------------------- */

const handleChange = (key: string, value?: TLRecord) => {
// put / remove the records in the store
store.mergeRemoteChanges(() => {
if (!value) {
return store.remove([key as TLRecord['id']]);
}
if (key === CURRENT_PAGE_KEY) {
setCurrentPage(value as TLPage);
} else {
store.put([value]);
}
});
};
const handleOpen = useCallback(
(initialRecords: TLRecord[]) => {
if (!sessionStore) {
return;
}

const handleOpen = (initialRecords: TLRecord[]) => {
// 2.
// Initialize the tldraw store with the session store server records—or, if the session store
// is empty, initialize the session store server with the default tldraw store records.
const shouldUseServerRecords = FULL_SYNC_REQUIRED_RECORD_TYPES.every(
Expand All @@ -116,14 +95,49 @@ export function useCollaboration({
status: 'synced-remote',
connectionStatus: 'online',
});
};
},
[store, sessionStore],
);

const handleChange = useCallback(
(key: string, value?: TLRecord) => {
// put / remove the records in the store
store.mergeRemoteChanges(() => {
if (!value) {
return store.remove([key as TLRecord['id']]);
}
if (key === CURRENT_PAGE_KEY) {
setCurrentPage(value as TLPage);
} else {
transact(() => {
store.put([value]);
if (key === TLINSTANCE_ID) {
store.put([
{ ...value, canMoveCamera: !!zoomToContent, isReadonly: !permissions.includes('write') } as TLInstance,
]);
}
});
}
});
},
[store, permissions, zoomToContent],
);

useEffect(() => {
if (!sessionStore) return;

setStoreWithStatus({ status: 'loading' });

const unsubs: (() => void)[] = [];

// Open session and sync the session store changes to the store
sessionStore.open({
handleOpen,
handleChange,
handleError,
});
sessionStore
.open({
handleOpen,
handleChange,
handleError,
})
.then(unsub => unsubs.push(unsub));

// Sync store changes to the yjs doc
unsubs.push(
Expand Down Expand Up @@ -179,7 +193,7 @@ export function useCollaboration({
unsubs.forEach(fn => fn());
unsubs.length = 0;
};
}, [store, sessionStore, handleError]);
}, [store, sessionStore, handleChange, handleOpen, handleError]);

useEffect(() => {
if (!editor || !sessionStore) return;
Expand All @@ -194,12 +208,15 @@ export function useCollaboration({
if (!key.includes('instance')) {
return;
}
// full store sync
const instanceRecords = store
.allRecords()
.filter(record => FULL_SYNC_REQUIRED_RECORD_TYPES.includes(record.typeName));
for (const record of instanceRecords) {
sessionStore.set(record.id, record);
const newPage = editor?.getCurrentPage();

if (newPage?.id !== currentPage?.id) {
sessionStore.get(TLINSTANCE_ID).then(instance => {
if (instance) {
sessionStore?.set(instance.id, { ...instance, currentPageId: newPage?.id } as TLInstance);
}
});
setCurrentPage(newPage);
}
});
}, PAGES_DEBOUNCE_TIME),
Expand Down
5 changes: 2 additions & 3 deletions packages/hms-whiteboard/src/hooks/useSetEditorPermissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ export const useSetEditorPermissions = ({

const isReadonly = !permissions.includes('write');
editor?.updateInstanceState({ isReadonly });
if (isReadonly) {
editor?.setCurrentTool('hand');
}
}, [permissions, zoomToContent, editor]);

return permissions;
};
1 change: 1 addition & 0 deletions packages/hms-whiteboard/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export default function decodeJWT(token?: string) {
export const CURRENT_PAGE_KEY = 'currentPage';
export const SHAPES_THROTTLE_TIME = 11;
export const PAGES_DEBOUNCE_TIME = 200;
export const OPEN_WAIT_TIMEOUT = 1000;

0 comments on commit c29d3a7

Please sign in to comment.