mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-13 19:11:15 +00:00
Refactor: More refactor P2P Replicator
This commit is contained in:
@@ -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[] {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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`;
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: b5f22eb8f3...f1195550d7
12
src/main.ts
12
src/main.ts
@@ -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!),
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user