Skip to content

Commit

Permalink
Merge pull request #874 from vizhub-core/561-Saving/Saved-Status
Browse files Browse the repository at this point in the history
561 saving/saved status
  • Loading branch information
curran authored Nov 27, 2024
2 parents 0e6be12 + 8d34275 commit ce2e716
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 10 deletions.
2 changes: 2 additions & 0 deletions src/client/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ function App() {
docPresence,
submitOperation,
connected,
pending,
} = useShareDB({
connection,
});
Expand Down Expand Up @@ -104,6 +105,7 @@ function App() {
typeScriptWorker={typeScriptWorker}
initialUsername={initialUsername}
connected={connected}
pending={pending}
liveKitRoomName={liveKitRoomName}
setLiveKitRoom={setLiveKitRoomName}
liveKitToken={liveKitToken}
Expand Down
50 changes: 50 additions & 0 deletions src/client/App/usePending.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
useState,
useEffect,
useCallback,
useRef,
} from 'react';
import { ShareDBDoc, VZCodeContent } from '../../types';

// import { runDelay } from '../constants';
// TODO understand what this part is for
const runDelay = 1000;

export const usePending = (
shareDBDoc: ShareDBDoc<VZCodeContent>,
) => {
const lastOpTimerRef = useRef<NodeJS.Timeout>();

// TODO rename this to isPending,
// and also rename connected to isConnected.
const [pending, setPending] = useState<boolean>(false);

const resetOnNoPending = useCallback(() => {
if (!shareDBDoc) return;

// This happens AFTER the changes have been
// acknowledged by the server.
shareDBDoc.whenNothingPending(() => {
setPending(false);
});
}, [shareDBDoc, setPending]);

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

// This happens when you type, but BEFORE
// the changes are sent to the server.
shareDBDoc.on('before op', () => {
setPending(true);

clearTimeout(lastOpTimerRef.current);

lastOpTimerRef.current = setTimeout(() => {
// should be triggered only once, cause upcoming ops unschedule previous resetOnNoPending
resetOnNoPending();
}, runDelay);
});
}, [shareDBDoc, resetOnNoPending]);

return pending;
};
41 changes: 40 additions & 1 deletion src/client/App/useShareDB.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useEffect, useState } from 'react';
import { useEffect, useState, useRef } from 'react';
import { randomId } from '../../randomId';
import { ShareDBDoc, VZCodeContent } from '../../types';
import { useSubmitOperation } from '../useSubmitOperation';
import { usePending } from './usePending';

// Set up the connection to ShareDB.
export const useShareDB = ({
Expand Down Expand Up @@ -30,6 +31,14 @@ export const useShareDB = ({

const [connected, setConnected] =
useState<boolean>(false);

// const [isSaving, setIsSaving] = useState<boolean>(false);
// const [isSaved, setIsSaved] = useState<boolean>(false);

// const debounceTimeoutRef = useRef<ReturnType<
// typeof setTimeout
// > | null>(null);

useEffect(() => {
// Listen for connection state changes
connection.on('connected', () => {
Expand Down Expand Up @@ -69,7 +78,28 @@ export const useShareDB = ({
// This is not required, because no part of the app outside
// of the CodeMirror integration uses those file contents.
// The Sidebar only needs to know file names, not contents.
// Start saving process when there’s an operation.
// setIsSaving(true);
// setIsSaved(false);

setContent(shareDBDoc.data);

// // Simulate a saving process and switch status to "saved"
// if (debounceTimeoutRef.current) {
// clearTimeout(debounceTimeoutRef.current);
// }

// // Set up a debounce timer to switch from "saving" to "saved" after 2 seconds of inactivity.
// debounceTimeoutRef.current = setTimeout(() => {
// setIsSaving(false);
// setIsSaved(true); // Only set isSaved to true after 2 seconds of inactivity

// const resetSavedTimeout = setTimeout(() => {
// setIsSaved(false);
// }, 1000); // 1 second delay after `isSaved` becomes true.

// return () => clearTimeout(resetSavedTimeout);
// }, 1800); // 1.8-second delay for detecting inactivity
});

// Set up presence.
Expand Down Expand Up @@ -101,11 +131,19 @@ export const useShareDB = ({
// TODO unsubscribe from presence
// TODO unsubscribe from doc
return () => {
// if (debounceTimeoutRef.current)
// clearTimeout(debounceTimeoutRef.current);
// shareDBDoc.destroy();
// docPresence.destroy();
};
}, []);

// True for "Saving..."
// False for "Saved"
const pending = usePending(shareDBDoc);

console.log('pending ', pending);

// A convenience function for mutating the ShareDB document
// based submitting OT ops generated by diffing the JSON.
const submitOperation =
Expand All @@ -118,5 +156,6 @@ export const useShareDB = ({
docPresence,
submitOperation,
connected,
pending,
};
};
4 changes: 4 additions & 0 deletions src/client/VZCodeContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export type VZCodeContextValue = {
codeEditorRef: React.RefObject<HTMLDivElement>;

connected: boolean;
pending: boolean;

hoveredItemId: ItemId | null;
setHoveredItemId: (itemId: ItemId | null) => void;
Expand Down Expand Up @@ -192,6 +193,7 @@ export const VZCodeProvider = ({
children,
codeError = null,
connected,
pending,
liveKitToken,
setLiveKitToken,
liveKitRoomName,
Expand All @@ -210,6 +212,7 @@ export const VZCodeProvider = ({
children: React.ReactNode;
codeError?: string | null;
connected: boolean;
pending: boolean;
liveKitToken: string | undefined;
setLiveKitToken: (state: string) => void;
liveKitRoomName: string | undefined;
Expand Down Expand Up @@ -497,6 +500,7 @@ export const VZCodeProvider = ({
codeEditorRef,

connected,
pending,

hoveredItemId,
setHoveredItemId,
Expand Down
45 changes: 41 additions & 4 deletions src/client/VZSidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
useContext,
useEffect,
useMemo,
useState,
useRef
} from 'react';
import {
FileId,
Expand Down Expand Up @@ -124,6 +126,7 @@ export const VZSidebar = ({
handleOpenCreateFileModal,
handleOpenCreateDirModal,
connected,
pending,
sidebarRef,
enableAutoFollow,
toggleAutoFollow,
Expand Down Expand Up @@ -214,7 +217,31 @@ export const VZSidebar = ({
}
}, [docPresence]);

// console.log(sidebarPresenceIndicators);
const [saved, setSaved] = useState(false);
const [isConnecting, setIsConnecting] = useState(true);
const previousPendingRef = useRef<boolean>(pending);

// Handle connection status transitions
useEffect(() => {
if (isConnecting && connected) {
setIsConnecting(false);
}
}, [connected, isConnecting]);

// Handle 'saved' state based on 'pending' transition
useEffect(() => {
// Check if 'pending' transitioned from true to false
if (previousPendingRef.current && !pending && connected && !isConnecting) {
setSaved(true);
const timer = setTimeout(() => {
setSaved(false);
}, 1500); // Reset after 2 seconds
return () => clearTimeout(timer);
}
// Update the ref with the current 'pending' state
previousPendingRef.current = pending;
}, [pending, connected, isConnecting]);


return (
<div
Expand Down Expand Up @@ -445,12 +472,22 @@ export const VZSidebar = ({
</div>
{enableConnectionStatus && (
<div className="connection-status">
{connected ? 'Connected' : 'Connection Lost'}
{isConnecting ? 'Connecting...' :
!connected ? 'Connection Lost' :
pending ? 'Saving...' :
saved ? 'Saved.' :
'Connected'}
<div className="connection">
<div
className={`connection-status-indicator ${
connected ? 'connected' : 'disconnected'
}`}
isConnecting
? 'pending'
: !connected
? 'disconnected'
: pending
? 'pending'
: 'connected'
}`}
/>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions src/client/VZSidebar/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@
background-color: var(--vh-color-success-01);
}

&.pending {
background-color: var(--vh-color-caution-01);
}

&.disconnected {
background-color: var(--vh-color-warning-01);
}
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ export type ShareDBDoc<T> = {
options?: any,
callback?: () => void,
) => void;
whenNothingPending: (callback: () => void) => void;
};

// A unique ID for an AI stream.
Expand Down
10 changes: 5 additions & 5 deletions test/sampleDirectories/kitchenSink/fft.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ export { fft, fftInPlace } from './fft/fft.js';
//------------------------------------------------
// Note: Some of this code is not optimized and is
// primarily designed as an educational and testing
// tool.
// To get high performace would require transforming
// tool.
// To get high perfo rmace would require transforming
// the recursive calls into a loop and then loop
// unrolling. All of this is best accomplished
// in C or assembly.
// unrolling. All of this is best accomplished
// in C or assembly.
//-------------------------------------------------

//-------------------------------------------------
// The following code assumes a complex number is
// an array: [real, imaginary]
Expand Down

0 comments on commit ce2e716

Please sign in to comment.