Skip to content

Commit

Permalink
[MS] Watch entries with bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
Max-7 committed Jul 3, 2024
1 parent f048907 commit 35f3cfc
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 23 deletions.
10 changes: 9 additions & 1 deletion client/src/parsec/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,15 @@ export async function statFolderChildren(
path: FsPath,
): Promise<Result<Array<EntryStat>, WorkspaceStatFolderChildrenError>> {
if (!needsMocks()) {
const result = await libparsec.workspaceStatFolderChildren(workspaceHandle, path);
const watchResult = await libparsec.workspaceWatchEntryOneshot(workspaceHandle, path);

let result;
if (!watchResult.ok) {
result = await libparsec.workspaceStatFolderChildren(workspaceHandle, path);
} else {
result = await libparsec.workspaceStatFolderChildrenById(workspaceHandle, watchResult.value);
}

if (!result.ok) {
return result;
}
Expand Down
24 changes: 12 additions & 12 deletions client/src/parsec/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ import { DateTime } from 'luxon';
export interface LoggedInDeviceInfo {
handle: ConnectionHandle;
device: AvailableDevice;
// Used to simulate update events, remove when we have real events
intervalId: any;
}

const loggedInDevices: Array<LoggedInDeviceInfo> = [];
Expand Down Expand Up @@ -159,6 +157,15 @@ export async function login(
case ClientEventTag.WorkspacesSelfListChanged:
eventDistributor.dispatchEvent(Events.WorkspaceUpdated);
break;
case ClientEventTag.WorkspaceWatchedEntryChanged:
eventDistributor.dispatchEvent(Events.EntryUpdated, undefined, 300);
break;
case ClientEventTag.WorkspaceOpsInboundSyncDone:
eventDistributor.dispatchEvent(Events.EntrySynced, { workspaceId: event.realmId, entryId: event.entryId });
break;
case ClientEventTag.WorkspaceOpsOutboundSyncDone:
eventDistributor.dispatchEvent(Events.EntrySynced, { workspaceId: event.realmId, entryId: event.entryId });
break;
default:
window.electronAPI.log('debug', `Unhandled event ${event.tag}`);
break;
Expand All @@ -177,19 +184,15 @@ export async function login(
const clientConfig = getClientConfig();
const result = await libparsec.clientStart(clientConfig, callback, accessStrategy);
if (result.ok) {
// Simulate an update event every 10s to force a refresh
const intervalId = setInterval(() => {
eventDistributor.dispatchEvent(Events.EntryUpdated);
}, 10000);
loggedInDevices.push({ handle: result.value, device: device, intervalId: intervalId });
loggedInDevices.push({ handle: result.value, device: device });
}
return result;
} else {
if (
accessStrategy.tag === DeviceAccessStrategyTag.Password &&
['P@ssw0rd.', 'AVeryL0ngP@ssw0rd'].includes((accessStrategy as DeviceAccessStrategyPassword).password)
) {
loggedInDevices.push({ handle: DEFAULT_HANDLE, device: device, intervalId: null });
loggedInDevices.push({ handle: DEFAULT_HANDLE, device: device });
return { ok: true, value: DEFAULT_HANDLE };
}
return {
Expand All @@ -212,10 +215,7 @@ export async function logout(handle?: ConnectionHandle | undefined | null): Prom
if (result.ok) {
const index = loggedInDevices.findIndex((info) => info.handle === handle);
if (index !== -1) {
const removed = loggedInDevices.splice(index, 1);
if (removed && removed.length > 0) {
clearInterval(removed[0].intervalId);
}
loggedInDevices.splice(index, 1);
}
}
return result;
Expand Down
49 changes: 42 additions & 7 deletions client/src/services/eventDistributor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

import { InvitationStatus, InvitationToken, WorkspaceID } from '@/parsec';
import { EntryID, InvitationStatus, InvitationToken, WorkspaceID } from '@/parsec';
import { v4 as uuid4 } from 'uuid';

export const EventDistributorKey = 'eventDistributor';
Expand All @@ -17,6 +17,7 @@ enum Events {
UpdateAvailability = 1 << 8,
WorkspaceUpdated = 1 << 9,
EntryUpdated = 1 << 10,
EntrySynced = 1 << 11,
}

interface WorkspaceCreatedData {
Expand All @@ -33,7 +34,12 @@ interface UpdateAvailabilityData {
version?: string;
}

type EventData = WorkspaceCreatedData | InvitationUpdatedData | UpdateAvailabilityData;
interface EntrySyncedData {
workspaceId: WorkspaceID;
entryId: EntryID;
}

type EventData = WorkspaceCreatedData | InvitationUpdatedData | UpdateAvailabilityData | EntrySyncedData;

interface Callback {
id: string;
Expand All @@ -43,16 +49,45 @@ interface Callback {

class EventDistributor {
private callbacks: Array<Callback>;
private timeouts: Map<Events, number>;

constructor() {
this.callbacks = [];
this.timeouts = new Map<number, Events>();
}

async dispatchEvent(event: Events, data?: EventData): Promise<void> {
for (const cb of this.callbacks) {
if (event & cb.events) {
await cb.funct(event, data);
async dispatchEvent(event: Events, data?: EventData, aggregateTime?: number): Promise<void> {
async function sendToAll(callbacks: Array<Callback>, event: Events, data?: EventData): Promise<void> {
for (const cb of callbacks) {
if (event & cb.events) {
await cb.funct(event, data);
}
}
}

// In some cases, events can occur very close to each other, leading to some heavy operations.
// We can aggregate those cases in order to distribute only one event if multiple occur in a short
// time lapse.
if (aggregateTime !== undefined) {
if (data) {
// Can't have data with an aggregateTime, we wouldn't know what data to use
console.warn('Cannot have an aggregate time with data, ignoring this event.');
return;
}
// Clear previous interval if any
if (this.timeouts.has(event)) {
const interval = this.timeouts.get(event);
this.timeouts.delete(event);
window.clearInterval(interval);
}
// Create a new timeout
const interval = window.setTimeout(async () => {
await sendToAll(this.callbacks, event, undefined);
}, aggregateTime);
// Add it to the list
this.timeouts.set(event, interval);
} else {
await sendToAll(this.callbacks, event, data);
}
}

Expand All @@ -67,4 +102,4 @@ class EventDistributor {
}
}

export { EventData, EventDistributor, Events, InvitationUpdatedData, UpdateAvailabilityData, WorkspaceCreatedData };
export { EntrySyncedData, EventData, EventDistributor, Events, InvitationUpdatedData, UpdateAvailabilityData, WorkspaceCreatedDat };
18 changes: 15 additions & 3 deletions client/src/views/files/FoldersPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ import FileDetailsModal from '@/views/files/FileDetailsModal.vue';
import { IonContent, IonPage, IonText, modalController, popoverController } from '@ionic/vue';
import { arrowRedo, copy, folderOpen, informationCircle, link, pencil, trashBin } from 'ionicons/icons';
import { Ref, computed, inject, onMounted, onUnmounted, ref } from 'vue';
import { EventData, EventDistributor, EventDistributorKey, Events } from '@/services/eventDistributor';
import { EntrySyncedData, EventData, EventDistributor, EventDistributorKey, Events } from '@/services/eventDistributor';
interface FoldersPageSavedData {
displayState?: DisplayState;
Expand Down Expand Up @@ -400,12 +400,24 @@ onMounted(async () => {
await defineShortcuts();
eventCbId = await eventDistributor.registerCallback(
Events.EntryUpdated | Events.WorkspaceUpdated,
async (event: Events, _data?: EventData) => {
Events.EntryUpdated | Events.WorkspaceUpdated | Events.EntrySynced,
async (event: Events, data?: EventData) => {
if (event === Events.EntryUpdated) {
await listFolder();
} else if (event === Events.WorkspaceUpdated && workspaceInfo.value) {
await updateWorkspaceInfo(workspaceInfo.value.id);
} else if (event === Events.EntrySynced) {
const syncedData = data as EntrySyncedData;
if (!workspaceInfo.value || workspaceInfo.value.id !== syncedData.workspaceId) {
return;
}
let entry: EntryModel | undefined = files.value.getEntries().find((e) => e.id === syncedData.entryId);
if (!entry) {
entry = folders.value.getEntries().find((e) => e.id === syncedData.entryId);
}
if (entry) {
entry.needSync = false;
}
}
},
);
Expand Down

0 comments on commit 35f3cfc

Please sign in to comment.