@@ -738,6 +738,81 @@ export async function updateCollab(
738738 return context ;
739739}
740740
741+ /**
742+ * Batch sync multiple collab documents to the server.
743+ * This is the same API that desktop uses before duplicating to ensure
744+ * the server has the latest state of all documents.
745+ *
746+ * @param workspaceId - The workspace ID
747+ * @param items - Array of collab items to sync, each containing objectId, collabType, stateVector, and docState
748+ * @returns The batch sync response containing results for each collab
749+ */
750+ export async function collabFullSyncBatch (
751+ workspaceId : string ,
752+ items : Array < {
753+ objectId : string ;
754+ collabType : Types ;
755+ stateVector : Uint8Array ;
756+ docState : Uint8Array ;
757+ } >
758+ ) : Promise < void > {
759+ const url = `/api/workspace/v1/${ workspaceId } /collab/full-sync/batch` ;
760+
761+ // Import the collab proto types
762+ const { collab } = await import ( '@/proto/messages' ) ;
763+
764+ // Build the protobuf request
765+ const request = collab . CollabBatchSyncRequest . create ( {
766+ items : items . map ( ( item ) => ( {
767+ objectId : item . objectId ,
768+ collabType : item . collabType ,
769+ compression : collab . PayloadCompressionType . COMPRESSION_NONE ,
770+ sv : item . stateVector ,
771+ docState : item . docState ,
772+ } ) ) ,
773+ responseCompression : collab . PayloadCompressionType . COMPRESSION_NONE ,
774+ } ) ;
775+
776+ // Encode the request to binary
777+ const encoded = collab . CollabBatchSyncRequest . encode ( request ) . finish ( ) ;
778+
779+ let deviceId = localStorage . getItem ( 'x-device-id' ) ;
780+
781+ if ( ! deviceId ) {
782+ deviceId = nanoid ( 8 ) ;
783+ localStorage . setItem ( 'x-device-id' , deviceId ) ;
784+ }
785+
786+ // Send the request with protobuf content type
787+ const response = await axiosInstance ?. post ( url , encoded , {
788+ headers : {
789+ 'Content-Type' : 'application/octet-stream' ,
790+ 'client-version' : 'web' ,
791+ 'device-id' : deviceId ,
792+ } ,
793+ responseType : 'arraybuffer' ,
794+ } ) ;
795+
796+ if ( ! response || response . status !== 200 ) {
797+ throw new Error ( `Failed to sync collabs: ${ response ?. status } ` ) ;
798+ }
799+
800+ // Decode and check the response for errors
801+ const responseData = new Uint8Array ( response . data ) ;
802+ const batchResponse = collab . CollabBatchSyncResponse . decode ( responseData ) ;
803+
804+ // Check for any errors in the results
805+ for ( const result of batchResponse . results ) {
806+ if ( result . error ) {
807+ Log . warn ( 'Collab sync error' , {
808+ objectId : result . objectId ,
809+ collabType : result . collabType ,
810+ error : result . error ,
811+ } ) ;
812+ }
813+ }
814+ }
815+
741816export async function getCollab ( workspaceId : string , objectId : string , collabType : Types ) {
742817 const url = `/api/workspace/v1/${ workspaceId } /collab/${ objectId } ` ;
743818
0 commit comments