Refactor: More refactor P2P Replicator

This commit is contained in:
vorotamoroz
2026-03-15 04:07:47 +09:00
parent 653cf8dfbe
commit 89bf0488c3
9 changed files with 41 additions and 238 deletions

View File

@@ -2,6 +2,7 @@ import type { LiveSyncBaseCore } from "../../../LiveSyncBaseCore";
import { P2P_DEFAULT_SETTINGS } from "@lib/common/types";
import type { ServiceContext } from "@lib/services/base/ServiceBase";
import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator";
import { addP2PEventHandlers } from "@lib/replication/trystero/P2PReplicatorCore";
type CLIP2PPeer = {
peerId: string;
@@ -34,7 +35,9 @@ function validateP2PSettings(core: LiveSyncBaseCore<ServiceContext, any>) {
function createReplicator(core: LiveSyncBaseCore<ServiceContext, any>): LiveSyncTrysteroReplicator {
validateP2PSettings(core);
return new LiveSyncTrysteroReplicator({ services: core.services });
const replicator = new LiveSyncTrysteroReplicator({ services: core.services });
addP2PEventHandlers(replicator);
return replicator;
}
function getSortedPeers(replicator: LiveSyncTrysteroReplicator): CLIP2PPeer[] {

View File

@@ -17,11 +17,7 @@ import {
type PeerStatus,
type PluginShim,
} from "@lib/replication/trystero/P2PReplicatorPaneCommon";
import {
P2PLogCollector,
type P2PReplicatorBase,
useP2PReplicator,
} from "@lib/replication/trystero/P2PReplicatorCore";
import { P2PLogCollector, type P2PReplicatorBase, useP2PReplicator } from "@lib/replication/trystero/P2PReplicatorCore";
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
import { reactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
import { EVENT_SETTING_SAVED } from "@lib/events/coreEvents";
@@ -34,9 +30,7 @@ import { Menu } from "@lib/services/implements/browser/Menu";
import { SimpleStoreIDBv2 } from "octagonal-wheels/databases/SimpleStoreIDBv2";
import type { BrowserAPIService } from "@/lib/src/services/implements/browser/BrowserAPIService";
import type { InjectableSettingService } from "@/lib/src/services/implements/injectable/InjectableSettingService";
import {
LiveSyncTrysteroReplicator,
} from "@lib/replication/trystero/LiveSyncTrysteroReplicator";
import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator";
function addToList(item: string, list: string) {
return unique(
@@ -82,7 +76,11 @@ export class P2PReplicatorShim implements P2PReplicatorBase {
p2pLogCollector!: P2PLogCollector;
private _initP2PReplicator() {
const { replicator, p2pLogCollector, storeP2PStatusLine: p2pStatusLine } = useP2PReplicator({ services: this.services } as any);
const {
replicator,
p2pLogCollector,
storeP2PStatusLine: p2pStatusLine,
} = useP2PReplicator({ services: this.services } as any);
this._liveSyncReplicator = replicator;
this.p2pLogCollector = p2pLogCollector;
p2pLogCollector.p2pReplicationLine.onChanged((line) => {

View File

@@ -16,9 +16,6 @@ export const EVENT_REQUEST_RELOAD_SETTING_TAB = "reload-setting-tab";
export const EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG = "request-open-plugin-sync-dialog";
export const EVENT_REQUEST_OPEN_P2P = "request-open-p2p";
export const EVENT_REQUEST_CLOSE_P2P = "request-close-p2p";
export const EVENT_REQUEST_RUN_DOCTOR = "request-run-doctor";
export const EVENT_REQUEST_RUN_FIX_INCOMPLETE = "request-run-fix-incomplete";
@@ -36,8 +33,6 @@ declare global {
[EVENT_REQUEST_OPEN_SETTING_WIZARD]: undefined;
[EVENT_REQUEST_RELOAD_SETTING_TAB]: undefined;
[EVENT_LEAF_ACTIVE_CHANGED]: undefined;
[EVENT_REQUEST_CLOSE_P2P]: undefined;
[EVENT_REQUEST_OPEN_P2P]: undefined;
[EVENT_REQUEST_OPEN_SETUP_URI]: undefined;
[EVENT_REQUEST_COPY_SETUP_URI]: undefined;
[EVENT_REQUEST_SHOW_SETUP_QR]: undefined;

View File

@@ -1,188 +0,0 @@
import { P2PReplicatorPaneView, VIEW_TYPE_P2P } from "./P2PReplicator/P2PReplicatorPaneView.ts";
import {
AutoAccepting,
LOG_LEVEL_NOTICE,
REMOTE_P2P,
type P2PSyncSetting,
type RemoteDBSettings,
} from "../../lib/src/common/types.ts";
import { LiveSyncCommands } from "../LiveSyncCommands.ts";
import { LiveSyncTrysteroReplicator } from "../../lib/src/replication/trystero/LiveSyncTrysteroReplicator.ts";
import { EVENT_REQUEST_OPEN_P2P, eventHub } from "../../common/events.ts";
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator.ts";
import { Logger } from "octagonal-wheels/common/logger";
import {
P2PLogCollector,
type P2PReplicatorBase,
useP2PReplicator,
} from "../../lib/src/replication/trystero/P2PReplicatorCore.ts";
import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
import type { Confirm } from "../../lib/src/interfaces/Confirm.ts";
import type ObsidianLiveSyncPlugin from "../../main.ts";
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
import type { LiveSyncCore } from "../../main.ts";
import type { EntryDoc } from "../../lib/src/common/types.ts";
export class P2PReplicator extends LiveSyncCommands implements P2PReplicatorBase {
storeP2PStatusLine!: ReactiveSource<string>;
p2pLogCollector!: P2PLogCollector;
private _liveSyncReplicator?: LiveSyncTrysteroReplicator;
get liveSyncReplicator() {
return this._liveSyncReplicator;
}
getSettings(): P2PSyncSetting {
return this.core.settings;
}
getDB() {
return this.core.localDatabase.localDatabase;
}
get confirm(): Confirm {
return this.core.confirm;
}
_simpleStore!: SimpleStore<any>;
simpleStore(): SimpleStore<any> {
return this._simpleStore;
}
constructor(plugin: ObsidianLiveSyncPlugin, core: LiveSyncCore) {
super(plugin, core);
this.afterConstructor();
}
async handleReplicatedDocuments(docs: EntryDoc[]): Promise<boolean> {
return await this.services.replication.parseSynchroniseResult(
docs as PouchDB.Core.ExistingDocument<EntryDoc>[]
);
}
_anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> {
const settings = { ...this.settings, ...settingOverride };
if (settings.remoteType == REMOTE_P2P) {
return Promise.resolve(new LiveSyncTrysteroReplicator({ services: this.services }));
}
return undefined!;
}
afterConstructor() {
return;
}
async open() {
await this._liveSyncReplicator?.open();
}
async close() {
await this._liveSyncReplicator?.close();
}
getConfig(key: string) {
return this.services.config.getSmallConfig(key);
}
setConfig(key: string, value: string) {
return this.services.config.setSmallConfig(key, value);
}
enableBroadcastCastings() {
return this._liveSyncReplicator?.enableBroadcastChanges();
}
disableBroadcastCastings() {
return this._liveSyncReplicator?.disableBroadcastChanges();
}
init() {
this._simpleStore = this.services.keyValueDB.openSimpleStore("p2p-sync");
return Promise.resolve(this);
}
onunload(): void {
void this.close();
}
onload(): void | Promise<void> {
eventHub.onEvent(EVENT_REQUEST_OPEN_P2P, () => {
void this.openPane();
});
}
private async _allSuspendExtraSync() {
this.plugin.core.settings.P2P_Enabled = false;
this.plugin.core.settings.P2P_AutoAccepting = AutoAccepting.NONE;
this.plugin.core.settings.P2P_AutoBroadcast = false;
this.plugin.core.settings.P2P_AutoStart = false;
this.plugin.core.settings.P2P_AutoSyncPeers = "";
this.plugin.core.settings.P2P_AutoWatchPeers = "";
return await Promise.resolve(true);
}
async openPane() {
await this.services.API.showWindow(VIEW_TYPE_P2P);
}
async _everyOnloadStart(): Promise<boolean> {
this.plugin.registerView(
VIEW_TYPE_P2P,
(leaf) => new P2PReplicatorPaneView(leaf, this.plugin.core, this.plugin)
);
this.plugin.addCommand({
id: "open-p2p-replicator",
name: "P2P Sync : Open P2P Replicator",
callback: async () => {
await this.openPane();
},
});
this.plugin.addCommand({
id: "p2p-establish-connection",
name: "P2P Sync : Connect to the Signalling Server",
checkCallback: (isChecking) => {
if (isChecking) {
return !(this._liveSyncReplicator?.server?.isServing ?? false);
}
void this.open();
},
});
this.plugin.addCommand({
id: "p2p-close-connection",
name: "P2P Sync : Disconnect from the Signalling Server",
checkCallback: (isChecking) => {
if (isChecking) {
return this._liveSyncReplicator?.server?.isServing ?? false;
}
Logger(`Closing P2P Connection`, LOG_LEVEL_NOTICE);
void this.close();
},
});
this.plugin.addCommand({
id: "replicate-now-by-p2p",
name: "Replicate now by P2P",
checkCallback: (isChecking) => {
if (isChecking) {
if (this.settings.remoteType == REMOTE_P2P) return false;
if (!this._liveSyncReplicator?.server?.isServing) return false;
return true;
}
void this._liveSyncReplicator?.replicateFromCommand(false);
},
});
this.plugin
.addRibbonIcon("waypoints", "P2P Replicator", async () => {
await this.openPane();
})
.addClass("livesync-ribbon-replicate-p2p");
return await Promise.resolve(true);
}
override onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
// Initialise useP2PReplicator — wires lifecycle, event handlers, and log collector
const { replicator, p2pLogCollector, storeP2PStatusLine } = useP2PReplicator({ services } as any);
this._liveSyncReplicator = replicator;
this.p2pLogCollector = p2pLogCollector;
this.storeP2PStatusLine = storeP2PStatusLine;
services.replicator.getNewReplicator.addHandler(this._anyNewReplicator.bind(this));
services.appLifecycle.onInitialise.addHandler(this._everyOnloadStart.bind(this));
services.setting.suspendExtraSync.addHandler(this._allSuspendExtraSync.bind(this));
}
}

View File

@@ -6,7 +6,7 @@
ConnectionStatus,
type PeerStatus,
} from "../../../lib/src/replication/trystero/P2PReplicatorPaneCommon";
import type { P2PReplicator } from "../CmdP2PReplicator";
import type { LiveSyncTrysteroReplicator } from "../../../lib/src/replication/trystero/LiveSyncTrysteroReplicator";
import PeerStatusRow from "../P2PReplicator/PeerStatusRow.svelte";
import { EVENT_LAYOUT_READY, eventHub } from "../../../common/events";
import {
@@ -22,7 +22,7 @@
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore";
interface Props {
cmdSync: P2PReplicator;
cmdSync: LiveSyncTrysteroReplicator;
core: LiveSyncBaseCore;
}
@@ -237,10 +237,10 @@
await cmdSync.close();
}
function startBroadcasting() {
void cmdSync.enableBroadcastCastings();
void cmdSync.enableBroadcastChanges();
}
function stopBroadcasting() {
void cmdSync.disableBroadcastCastings();
void cmdSync.disableBroadcastChanges();
}
const initialDialogStatusKey = `p2p-dialog-status`;

View File

@@ -1,19 +1,15 @@
import { Menu, WorkspaceLeaf } from "@/deps.ts";
import ReplicatorPaneComponent from "./P2PReplicatorPane.svelte";
import type ObsidianLiveSyncPlugin from "../../../main.ts";
import { mount } from "svelte";
import { SvelteItemView } from "../../../common/SvelteItemView.ts";
import { eventHub } from "../../../common/events.ts";
import { SvelteItemView } from "@/common/SvelteItemView.ts";
import { eventHub } from "@/common/events.ts";
import { unique } from "octagonal-wheels/collection";
import { LOG_LEVEL_NOTICE, REMOTE_P2P } from "../../../lib/src/common/types.ts";
import { Logger } from "../../../lib/src/common/logger.ts";
import { P2PReplicator } from "../CmdP2PReplicator.ts";
import {
EVENT_P2P_PEER_SHOW_EXTRA_MENU,
type PeerStatus,
} from "../../../lib/src/replication/trystero/P2PReplicatorPaneCommon.ts";
import { LOG_LEVEL_NOTICE, REMOTE_P2P } from "@lib/common/types.ts";
import { Logger } from "@lib/common/logger.ts";
import { EVENT_P2P_PEER_SHOW_EXTRA_MENU, type PeerStatus } from "@lib/replication/trystero/P2PReplicatorPaneCommon.ts";
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore.ts";
import type { UseP2PReplicatorResult } from "@lib/replication/trystero/P2PReplicatorCore.ts";
export const VIEW_TYPE_P2P = "p2p-replicator";
function addToList(item: string, list: string) {
@@ -35,8 +31,8 @@ function removeFromList(item: string, list: string) {
}
export class P2PReplicatorPaneView extends SvelteItemView {
// plugin: ObsidianLiveSyncPlugin;
core: LiveSyncBaseCore;
private _p2pResult: UseP2PReplicatorResult;
override icon = "waypoints";
title: string = "";
override navigation = false;
@@ -45,11 +41,7 @@ export class P2PReplicatorPaneView extends SvelteItemView {
return "waypoints";
}
get replicator() {
const r = this.core.getAddOn<P2PReplicator>(P2PReplicator.name);
if (!r || !r.liveSyncReplicator) {
throw new Error("Replicator not found");
}
return r.liveSyncReplicator;
return this._p2pResult.replicator;
}
async replicateFrom(peer: PeerStatus) {
await this.replicator.replicateFrom(peer.peerId);
@@ -131,10 +123,10 @@ And you can also drop the local database to rebuild from the remote device.`,
await this.core.services.setting.applyPartial(currentSetting, true);
}
m?: Menu;
constructor(leaf: WorkspaceLeaf, core: LiveSyncBaseCore, plugin: ObsidianLiveSyncPlugin) {
constructor(leaf: WorkspaceLeaf, core: LiveSyncBaseCore, p2pResult: UseP2PReplicatorResult) {
super(leaf);
// this.plugin = plugin;
this.core = core;
this._p2pResult = p2pResult;
eventHub.onEvent(EVENT_P2P_PEER_SHOW_EXTRA_MENU, ({ peer, event }) => {
if (this.m) {
this.m.hide();
@@ -192,14 +184,10 @@ And you can also drop the local database to rebuild from the remote device.`,
}
}
instantiateComponent(target: HTMLElement) {
const cmdSync = this.core.getAddOn<P2PReplicator>(P2PReplicator.name);
if (!cmdSync) {
throw new Error("Replicator not found");
}
return mount(ReplicatorPaneComponent, {
target: target,
props: {
cmdSync: cmdSync,
cmdSync: this._p2pResult.replicator,
core: this.core,
},
});

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { getContext } from "svelte";
import { AcceptedStatus, type PeerStatus } from "../../../lib/src/replication/trystero/P2PReplicatorPaneCommon";
import type { P2PReplicator } from "../CmdP2PReplicator";
import type { LiveSyncTrysteroReplicator } from "../../../lib/src/replication/trystero/LiveSyncTrysteroReplicator";
import { eventHub } from "../../../common/events";
import { EVENT_P2P_PEER_SHOW_EXTRA_MENU } from "../../../lib/src/replication/trystero/P2PReplicatorPaneCommon";
@@ -57,7 +57,7 @@
let isNew = $derived.by(() => peer.accepted === AcceptedStatus.UNKNOWN);
function makeDecision(isAccepted: boolean, isTemporary: boolean) {
cmdReplicator.liveSyncReplicator?.makeDecision({
replicator.makeDecision({
peerId: peer.peerId,
name: peer.name,
decision: isAccepted,
@@ -65,13 +65,12 @@
});
}
function revokeDecision() {
cmdReplicator.liveSyncReplicator?.revokeDecision({
replicator.revokeDecision({
peerId: peer.peerId,
name: peer.name,
});
}
const cmdReplicator = getContext<() => P2PReplicator>("getReplicator")();
const replicator = cmdReplicator.liveSyncReplicator;
const replicator = getContext<() => LiveSyncTrysteroReplicator>("getReplicator")();
const peerAttrLabels = $derived.by(() => {
const attrs = [];

Submodule src/lib updated: b5f22eb8f3...f1195550d7

View File

@@ -14,7 +14,8 @@ import { ModuleObsidianGlobalHistory } from "./modules/features/ModuleGlobalHist
import { ModuleIntegratedTest } from "./modules/extras/ModuleIntegratedTest.ts";
import { ModuleReplicateTest } from "./modules/extras/ModuleReplicateTest.ts";
import { LocalDatabaseMaintenance } from "./features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
import { P2PReplicator } from "./features/P2PSync/CmdP2PReplicator.ts";
import { P2PReplicatorPaneView, VIEW_TYPE_P2P } from "./features/P2PSync/P2PReplicator/P2PReplicatorPaneView.ts";
import { useP2PReplicator } from "@lib/replication/trystero/P2PReplicatorCore.ts";
import type { InjectableServiceHub } from "./lib/src/services/implements/injectable/InjectableServiceHub.ts";
import { ObsidianServiceHub } from "./modules/services/ObsidianServiceHub.ts";
import { ServiceRebuilder } from "@lib/serviceModules/Rebuilder.ts";
@@ -132,6 +133,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
const serviceHub = new ObsidianServiceHub(this);
// Capture useP2PReplicator result so it can be passed to the P2PReplicator addon
// TODO: Dependency fix: bit hacky
let p2pReplicatorResult: ReturnType<typeof useP2PReplicator> | undefined;
this.core = new LiveSyncBaseCore(
serviceHub,
(core, serviceHub) => {
@@ -161,7 +166,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
new ConfigSync(this, core),
new HiddenFileSync(this, core),
new LocalDatabaseMaintenance(this, core),
new P2PReplicator(this, core),
];
return addOns;
},
@@ -173,6 +177,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
useOfflineScanner(core);
useRedFlagFeatures(core);
useCheckRemoteSize(core);
p2pReplicatorResult = useP2PReplicator(core, [
VIEW_TYPE_P2P,
(leaf: any) => new P2PReplicatorPaneView(leaf, core, p2pReplicatorResult!),
]);
}
);
}