Merge final group into main #3
@@ -1,5 +1,6 @@
|
|||||||
import * as Y from "yjs";
|
import * as Y from "yjs";
|
||||||
import { getDeadline } from "../state";
|
import { getDeadline } from "../state";
|
||||||
|
import { enforceAppendOnly } from "../yDocUtil";
|
||||||
|
|
||||||
const DEADLINE_DURATION_MS = 2 * 60 * 1000; // 2 minutes
|
const DEADLINE_DURATION_MS = 2 * 60 * 1000; // 2 minutes
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ export function DeadlineTimer(
|
|||||||
onClearDeadline();
|
onClearDeadline();
|
||||||
});
|
});
|
||||||
|
|
||||||
deadlineMap.observe(() => render());
|
deadlineMap.observe(enforceAppendOnly(deadlineMap,render));
|
||||||
render();
|
render();
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as Y from "yjs";
|
import * as Y from "yjs";
|
||||||
import type { OptionRecord } from "../state";
|
import type { OptionRecord } from "../state";
|
||||||
import { PollOption } from "./PollOption";
|
import { PollOption } from "./PollOption";
|
||||||
|
import { enforceAppendOnly } from "../yDocUtil";
|
||||||
|
|
||||||
export function PollList(
|
export function PollList(
|
||||||
yOptions: Y.Map<OptionRecord>,
|
yOptions: Y.Map<OptionRecord>,
|
||||||
@@ -119,8 +120,8 @@ export function PollList(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
yOptions.observeDeep(() => render());
|
yOptions.observe(enforceAppendOnly(yOptions,render));
|
||||||
yVotes.observe(() => render());
|
yVotes.observe(enforceAppendOnly(yVotes,render));
|
||||||
render();
|
render();
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
|
|||||||
33
src/yDocUtil.ts
Normal file
33
src/yDocUtil.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import * as Y from "yjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforces append-only logic on a Y.Map.
|
||||||
|
* Reverts any 'update' or 'delete' actions detected in the observer.
|
||||||
|
*/
|
||||||
|
export function enforceAppendOnly(yMap: Y.Map<any>,render: () => void) {
|
||||||
|
return (event: Y.YMapEvent<any>, transaction: Y.Transaction) => {
|
||||||
|
// Avoid infinite loops: check if this change was
|
||||||
|
// triggered by our own 'undo' logic.
|
||||||
|
if (transaction.origin === 'revert-logic') return;
|
||||||
|
|
||||||
|
event.keys.forEach((change, key) => {
|
||||||
|
const { action, oldValue } = change;
|
||||||
|
|
||||||
|
if (action === 'update' || action === 'delete') {
|
||||||
|
// Use the transaction to undo the illegal operation
|
||||||
|
yMap.doc?.transact(() => {
|
||||||
|
if (action === 'update' && oldValue !== undefined) {
|
||||||
|
// Revert to previous value
|
||||||
|
yMap.set(key, oldValue);
|
||||||
|
} else if (action === 'delete' && oldValue !== undefined) {
|
||||||
|
// Restore the deleted key
|
||||||
|
yMap.set(key, oldValue);
|
||||||
|
}
|
||||||
|
console.warn(`Illegal ${action} attempt on key: "${key}". Reverted.`);
|
||||||
|
}, 'revert-logic');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user