mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-08-28 22:37:08 +00:00
refactor(i18n): route remaining Obsidian UI text through the message catalogue
Continues the source-key migration inside the application boundary introduced in 1.0.0, where LiveSync owns its catalogue and consumes Commonlib as a published package. - Replace hardcoded user-visible strings in the Obsidian UI (Setup Wizard dialogues, P2P panes, Customisation Sync panes, Global History, the JSON conflict pane and the remote-configuration menu) with `$msg` calls, keeping the English source string as the key. - Wire up strings whose translations already existed in the catalogue but were still rendered as literals, for example the whole Intro dialogue. - Add the new entries to `src/common/messagesYAML/en.yaml` and `es.yaml`, then regenerate `messagesJson/` and `combinedMessages.prod.ts` through the documented `i18n:bake` pipeline. - No Commonlib gitlink or catalogue is involved; every change is LiveSync-owned. Regenerating the catalogue also normalises three pre-existing entries each in `ko.json` and `zh.json`, where the committed JSON kept a trailing space before a newline that YAML cannot represent. Verified with tsc-check, tsc-check:apps, svelte-check and lint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1362,7 +1362,7 @@ export class ConfigSync extends LiveSyncCommands {
|
||||
async storeCustomizationFiles(path: FilePath, termOverRide?: string) {
|
||||
const term = termOverRide || this.services.setting.getDeviceAndVaultName();
|
||||
if (term == "") {
|
||||
this._log("We have to configure the device name", LOG_LEVEL_NOTICE);
|
||||
this._log($msg("We have to configure the device name"), LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
}
|
||||
if (this.useV2) {
|
||||
@@ -1552,7 +1552,7 @@ export class ConfigSync extends LiveSyncCommands {
|
||||
this._log("Scanning customizing files.", logLevel, "scan-all-config");
|
||||
const term = this.services.setting.getDeviceAndVaultName();
|
||||
if (term == "") {
|
||||
this._log("We have to configure the device name", LOG_LEVEL_NOTICE);
|
||||
this._log($msg("We have to configure the device name"), LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
}
|
||||
const filesAll = await this.scanInternalFiles();
|
||||
@@ -1729,7 +1729,11 @@ export class ConfigSync extends LiveSyncCommands {
|
||||
|
||||
if (mode == "CUSTOMIZE") {
|
||||
if (!this.services.setting.getDeviceAndVaultName()) {
|
||||
let name = await this.core.confirm.askString("Device name", "Please set this device name", `desktop`);
|
||||
let name = await this.core.confirm.askString(
|
||||
$msg("Device name"),
|
||||
$msg("Please set this device name"),
|
||||
`desktop`
|
||||
);
|
||||
if (!name) {
|
||||
if (Platform.isAndroidApp) {
|
||||
name = "android-app";
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import type ObsidianLiveSyncPlugin from "@/main";
|
||||
// import { askString } from "../../common/utils";
|
||||
import { Menu } from "@/deps.ts";
|
||||
import { $msg as translateMessage } from "@/common/translation";
|
||||
|
||||
export let list: IPluginDataExDisplay[] = [];
|
||||
export let thisTerm = "";
|
||||
@@ -61,25 +62,25 @@
|
||||
// NO OP. what's happened?
|
||||
freshness = "";
|
||||
} else if (local && !remote) {
|
||||
freshness = "Local only";
|
||||
freshness = translateMessage("Local only");
|
||||
} else if (remote && !local) {
|
||||
freshness = "Remote only";
|
||||
freshness = translateMessage("Remote only");
|
||||
canApply = true;
|
||||
} else {
|
||||
const dtDiff = (local?.mtime ?? 0) - (remote?.mtime ?? 0);
|
||||
const diff = timeDeltaToHumanReadable(Math.abs(dtDiff));
|
||||
if (dtDiff / 1000 < -10) {
|
||||
// freshness = "✓ Newer";
|
||||
freshness = `Newer (${diff})`;
|
||||
freshness = translateMessage("Newer (${diff})", { diff });
|
||||
canApply = true;
|
||||
contentCheck = true;
|
||||
} else if (dtDiff / 1000 > 10) {
|
||||
// freshness = "⚠ Older";
|
||||
freshness = `Older (${diff})`;
|
||||
freshness = translateMessage("Older (${diff})", { diff });
|
||||
canApply = true;
|
||||
contentCheck = true;
|
||||
} else {
|
||||
freshness = "Same";
|
||||
freshness = translateMessage("Same");
|
||||
canApply = false;
|
||||
contentCheck = true;
|
||||
}
|
||||
@@ -89,11 +90,17 @@
|
||||
if (local?.version || remote?.version) {
|
||||
const compare = `${localVersionStr}`.localeCompare(remoteVersionStr, undefined, { numeric: true });
|
||||
if (compare == 0) {
|
||||
version = "Same";
|
||||
version = translateMessage("Same");
|
||||
} else if (compare < 0) {
|
||||
version = `Lower (${localVersionStr} < ${remoteVersionStr})`;
|
||||
version = translateMessage("Lower (${local} < ${remote})", {
|
||||
local: localVersionStr,
|
||||
remote: remoteVersionStr,
|
||||
});
|
||||
} else if (compare > 0) {
|
||||
version = `Higher (${localVersionStr} > ${remoteVersionStr})`;
|
||||
version = translateMessage("Higher (${local} > ${remote})", {
|
||||
local: localVersionStr,
|
||||
remote: remoteVersionStr,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,19 +142,19 @@
|
||||
})
|
||||
.reduce((p, c) => p | (c as number), 0 as number);
|
||||
if (matchingStatus == 0b0000100) {
|
||||
equivalency = "Same";
|
||||
equivalency = translateMessage("Same");
|
||||
canApply = false;
|
||||
} else if (matchingStatus <= 0b0000100) {
|
||||
equivalency = "Same or local only";
|
||||
equivalency = translateMessage("Same or local only");
|
||||
canApply = false;
|
||||
} else if (matchingStatus == 0b0010000) {
|
||||
canApply = true;
|
||||
canCompare = true;
|
||||
equivalency = "Different";
|
||||
equivalency = translateMessage("Different");
|
||||
} else {
|
||||
canApply = true;
|
||||
canCompare = true;
|
||||
equivalency = "Mixed";
|
||||
equivalency = translateMessage("Mixed");
|
||||
}
|
||||
return { equivalency, canApply, canCompare };
|
||||
}
|
||||
@@ -244,7 +251,7 @@
|
||||
if (selected == "") {
|
||||
// NO OP.
|
||||
} else if (selected == thisTerm) {
|
||||
freshness = "This device";
|
||||
freshness = translateMessage("This device");
|
||||
canApply = false;
|
||||
} else {
|
||||
const local = list.find((e) => e.term == thisTerm);
|
||||
@@ -304,11 +311,11 @@
|
||||
if (!local) return;
|
||||
if (!selectedItem) return;
|
||||
const menu = new Menu();
|
||||
menu.addItem((item) => item.setTitle("Compare file").setIsLabel(true));
|
||||
menu.addItem((item) => item.setTitle(translateMessage("Compare file")).setIsLabel(true));
|
||||
menu.addSeparator();
|
||||
const files = unique(local.files.map((e) => e.filename).concat(selectedItem.files.map((e) => e.filename)));
|
||||
const convDate = (dt: PluginDataExFile | undefined) => {
|
||||
if (!dt) return "(Missing)";
|
||||
if (!dt) return translateMessage("(Missing)");
|
||||
const d = new Date(dt.mtime);
|
||||
return d.toLocaleString();
|
||||
};
|
||||
@@ -335,10 +342,14 @@
|
||||
Logger(`Could not find local item`, LOG_LEVEL_VERBOSE);
|
||||
return;
|
||||
}
|
||||
const duplicateTermName = await core.confirm.askString("Duplicate", "device name", "");
|
||||
const duplicateTermName = await core.confirm.askString(
|
||||
translateMessage("Duplicate"),
|
||||
translateMessage("device name"),
|
||||
""
|
||||
);
|
||||
if (duplicateTermName) {
|
||||
if (duplicateTermName.contains("/")) {
|
||||
Logger(`We can not use "/" to the device name`, LOG_LEVEL_NOTICE);
|
||||
Logger(translateMessage('We can not use "/" to the device name'), LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
}
|
||||
const key = `${plugin.core.services.API.getSystemConfigDir()}/${local.files[0].filename}`;
|
||||
@@ -391,7 +402,7 @@
|
||||
{/if}
|
||||
{:else}
|
||||
<span class="spacer"></span>
|
||||
<span class="message even">All the same or non-existent</span>
|
||||
<span class="message even">{translateMessage("All the same or non-existent")}</span>
|
||||
<!-- svelte-ignore a11y_consider_explicit_label -->
|
||||
<button disabled></button>
|
||||
<!-- svelte-ignore a11y_consider_explicit_label -->
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
import { HiddenFileSync } from "@/features/HiddenFileSync/CmdHiddenFileSync.ts";
|
||||
import { LOG_LEVEL_NOTICE, Logger } from "octagonal-wheels/common/logger";
|
||||
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore.ts";
|
||||
import { $msg as translateMessage } from "@/common/translation";
|
||||
export let plugin: ObsidianLiveSyncPlugin;
|
||||
export let core :LiveSyncBaseCore;
|
||||
// $: core = plugin.core;
|
||||
@@ -32,15 +33,17 @@
|
||||
|
||||
const addOn = core.getAddOn<ConfigSync>(ConfigSync.name)!;
|
||||
if (!addOn) {
|
||||
const msg =
|
||||
"AddOn Module (ConfigSync) has not been loaded. This is very unexpected situation. Please report this issue.";
|
||||
const msg = translateMessage(
|
||||
"AddOn Module (ConfigSync) has not been loaded. This is very unexpected situation. Please report this issue."
|
||||
);
|
||||
Logger(msg, LOG_LEVEL_NOTICE);
|
||||
throw new Error(msg);
|
||||
}
|
||||
const addOnHiddenFileSync = core.getAddOn<HiddenFileSync>(HiddenFileSync.name) as HiddenFileSync;
|
||||
if (!addOnHiddenFileSync) {
|
||||
const msg =
|
||||
"AddOn Module (HiddenFileSync) has not been loaded. This is very unexpected situation. Please report this issue.";
|
||||
const msg = translateMessage(
|
||||
"AddOn Module (HiddenFileSync) has not been loaded. This is very unexpected situation. Please report this issue."
|
||||
);
|
||||
Logger(msg, LOG_LEVEL_NOTICE);
|
||||
throw new Error(msg);
|
||||
}
|
||||
@@ -92,9 +95,9 @@
|
||||
}
|
||||
|
||||
const displays = {
|
||||
CONFIG: "Configuration",
|
||||
THEME: "Themes",
|
||||
SNIPPET: "Snippets",
|
||||
CONFIG: translateMessage("Configuration"),
|
||||
THEME: translateMessage("Themes"),
|
||||
SNIPPET: translateMessage("Snippets"),
|
||||
};
|
||||
async function scanAgain() {
|
||||
await addOn.scanAllConfigFiles(true);
|
||||
@@ -156,20 +159,20 @@
|
||||
}
|
||||
function askOverwriteModeForAutomatic(evt: MouseEvent, key: string) {
|
||||
const menu = new Menu();
|
||||
menu.addItem((item) => item.setTitle("Initial Action").setIsLabel(true));
|
||||
menu.addItem((item) => item.setTitle(translateMessage("Initial Action")).setIsLabel(true));
|
||||
menu.addSeparator();
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(`↑: Overwrite Remote`).onClick((e) => {
|
||||
item.setTitle(translateMessage("↑: Overwrite Remote")).onClick((e) => {
|
||||
applyAutomaticSync(key, "pushForce");
|
||||
});
|
||||
})
|
||||
.addItem((item) => {
|
||||
item.setTitle(`↓: Overwrite Local`).onClick((e) => {
|
||||
item.setTitle(translateMessage("↓: Overwrite Local")).onClick((e) => {
|
||||
applyAutomaticSync(key, "pullForce");
|
||||
});
|
||||
})
|
||||
.addItem((item) => {
|
||||
item.setTitle(`⇅: Use newer`).onClick((e) => {
|
||||
item.setTitle(translateMessage("⇅: Use newer")).onClick((e) => {
|
||||
applyAutomaticSync(key, "safe");
|
||||
});
|
||||
});
|
||||
@@ -201,10 +204,10 @@
|
||||
[MODE_SHINY]: ICON_EMOJI_FLAGGED,
|
||||
};
|
||||
const TITLES: { [key: number]: string } = {
|
||||
[MODE_SELECTIVE]: "Selective",
|
||||
[MODE_PAUSED]: "Ignore",
|
||||
[MODE_AUTOMATIC]: "Automatic",
|
||||
[MODE_SHINY]: "Flagged Selective",
|
||||
[MODE_SELECTIVE]: translateMessage("Selective"),
|
||||
[MODE_PAUSED]: translateMessage("Ignore"),
|
||||
[MODE_AUTOMATIC]: translateMessage("Automatic"),
|
||||
[MODE_SHINY]: translateMessage("Flagged Selective"),
|
||||
};
|
||||
const PREFIX_PLUGIN_ALL = "PLUGIN_ALL";
|
||||
const PREFIX_PLUGIN_DATA = "PLUGIN_DATA";
|
||||
@@ -329,28 +332,30 @@
|
||||
|
||||
<div class="buttonsWrap">
|
||||
<div class="buttons">
|
||||
<button on:click={() => scanAgain()}>Scan changes</button>
|
||||
<button on:click={() => replicate()}>Sync once</button>
|
||||
<button on:click={() => requestUpdate()}>Refresh</button>
|
||||
<button on:click={() => scanAgain()}>{translateMessage("Scan changes")}</button>
|
||||
<button on:click={() => replicate()}>{translateMessage("Sync once")}</button>
|
||||
<button on:click={() => requestUpdate()}>{translateMessage("Refresh")}</button>
|
||||
{#if isMaintenanceMode}
|
||||
<button on:click={() => requestReload()}>Reload</button>
|
||||
<button on:click={() => requestReload()}>{translateMessage("Reload")}</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button on:click={() => selectAllNewest(true)}>Select All Shiny</button>
|
||||
<button on:click={() => selectAllNewest(false)}>{ICON_EMOJI_FLAGGED} Select Flagged Shiny</button>
|
||||
<button on:click={() => resetSelectNewest()}>Deselect all</button>
|
||||
<button on:click={() => applyAll()} class="mod-cta">Apply All Selected</button>
|
||||
<button on:click={() => selectAllNewest(true)}>{translateMessage("Select All Shiny")}</button>
|
||||
<button on:click={() => selectAllNewest(false)}
|
||||
>{ICON_EMOJI_FLAGGED} {translateMessage("Select Flagged Shiny")}</button
|
||||
>
|
||||
<button on:click={() => resetSelectNewest()}>{translateMessage("Deselect all")}</button>
|
||||
<button on:click={() => applyAll()} class="mod-cta">{translateMessage("Apply All Selected")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loading">
|
||||
{#if loading || $pluginV2Progress !== 0}
|
||||
<span>Updating list...{$pluginV2Progress == 0 ? "" : ` (${$pluginV2Progress})`}</span>
|
||||
<span>{translateMessage("Updating list...")}{$pluginV2Progress == 0 ? "" : ` (${$pluginV2Progress})`}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="list">
|
||||
{#if list.length == 0}
|
||||
<div class="center">No Items.</div>
|
||||
<div class="center">{translateMessage("No Items.")}</div>
|
||||
{:else}
|
||||
{#each displayEntries as [key, label]}
|
||||
<div>
|
||||
@@ -382,7 +387,7 @@
|
||||
</div>
|
||||
{/each}
|
||||
<div>
|
||||
<h3>Plugins</h3>
|
||||
<h3>{translateMessage("Plugins")}</h3>
|
||||
{#each pluginEntries as [name, listX]}
|
||||
{@const bindKeyAll = `${PREFIX_PLUGIN_ALL}/${name}`}
|
||||
{@const modeAll = automaticListDisp.get(bindKeyAll) ?? MODE_SELECTIVE}
|
||||
@@ -464,7 +469,7 @@
|
||||
>
|
||||
{getIcon(modeEtc)}
|
||||
</button>
|
||||
<span class="name">Other files</span>
|
||||
<span class="name">{translateMessage("Other files")}</span>
|
||||
</div>
|
||||
<div class="body">
|
||||
{#if modeEtc == MODE_SELECTIVE || modeEtc == MODE_SHINY}
|
||||
@@ -492,9 +497,9 @@
|
||||
{#if isMaintenanceMode}
|
||||
<div class="buttons">
|
||||
<div>
|
||||
<h3>Maintenance Commands</h3>
|
||||
<h3>{translateMessage("Maintenance Commands")}</h3>
|
||||
<div class="maintenancerow">
|
||||
<label for="">Delete All of </label>
|
||||
<label for="">{translateMessage("Delete All of")} </label>
|
||||
<select bind:value={deleteTerm}>
|
||||
{#each allTerms as term}
|
||||
<option value={term}>{term}</option>
|
||||
@@ -513,10 +518,20 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="buttons">
|
||||
<label><span>Hide not applicable items</span><input type="checkbox" bind:checked={hideEven} /></label>
|
||||
<label
|
||||
><span>{translateMessage("Hide not applicable items")}</span><input
|
||||
type="checkbox"
|
||||
bind:checked={hideEven}
|
||||
/></label
|
||||
>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<label><span>Maintenance mode</span><input type="checkbox" bind:checked={isMaintenanceMode} /></label>
|
||||
<label
|
||||
><span>{translateMessage("Maintenance mode")}</span><input
|
||||
type="checkbox"
|
||||
bind:checked={isMaintenanceMode}
|
||||
/></label
|
||||
>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import type { FilePath, LoadedEntry } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
import { decodeBinary, readString } from "@vrtmrz/livesync-commonlib/compat/string_and_binary/convert";
|
||||
import { getDocData, isObjectDifferent, mergeObject } from "@vrtmrz/livesync-commonlib/compat/common/utils";
|
||||
import { $msg as translateMessage } from "@/common/translation";
|
||||
|
||||
interface Props {
|
||||
docs?: LoadedEntry[];
|
||||
@@ -115,7 +116,7 @@
|
||||
let newModes = [] as typeof modesSrc;
|
||||
|
||||
if (!hideLocal) {
|
||||
newModes.push(["", "Not now"]);
|
||||
newModes.push(["", translateMessage("Not now")]);
|
||||
newModes.push(["A", nameA || "A"]);
|
||||
}
|
||||
newModes.push(["B", nameB || "B"]);
|
||||
@@ -127,9 +128,9 @@
|
||||
|
||||
<h2>{filename}</h2>
|
||||
{#if !docA || !docB}
|
||||
<div class="message">Just for a minute, please!</div>
|
||||
<div class="message">{translateMessage("Just for a minute, please!")}</div>
|
||||
<div class="buttons">
|
||||
<button onclick={apply}>Dismiss</button>
|
||||
<button onclick={apply}>{translateMessage("Dismiss")}</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="options">
|
||||
@@ -152,7 +153,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
NO PREVIEW
|
||||
{translateMessage("NO PREVIEW")}
|
||||
{/if}
|
||||
|
||||
<div class="infos">
|
||||
|
||||
@@ -57,6 +57,7 @@ import { tryGetFilePath } from "@vrtmrz/livesync-commonlib/compat/common/utils.d
|
||||
import { configureHiddenFileSyncMode, type ConfigureHiddenFileSyncResult } from "./configureHiddenFileSyncMode.ts";
|
||||
import type { OptionalSyncFeatureMode } from "@/features/optionalSyncFeatures.ts";
|
||||
import { getObsidianCommunityPluginManager } from "@/common/obsidianCommunityPlugins.ts";
|
||||
import { $msg } from "@/common/translation";
|
||||
type SyncDirection = "push" | "pull" | "safe" | "pullForce" | "pushForce";
|
||||
|
||||
type HiddenFileInitialisationProgress = {
|
||||
@@ -1539,10 +1540,7 @@ Offline Changed files: ${files.length}`;
|
||||
this.core.databaseFileAccess.fetchEntryMeta(prefixedFileName, undefined, true),
|
||||
this.core.databaseFileAccess.getConflictedRevs(prefixedFileName),
|
||||
]);
|
||||
const liveRevisions = new Set([
|
||||
...(current && current._rev ? [current._rev] : []),
|
||||
...conflicts,
|
||||
]);
|
||||
const liveRevisions = new Set([...(current && current._rev ? [current._rev] : []), ...conflicts]);
|
||||
if (!selected || selected._rev !== revision || !liveRevisions.has(revision)) {
|
||||
this._log(
|
||||
`Could not use hidden-file revision ${revision} of ${stripAllPrefixes(prefixedFileName)}; the selected revision is no longer live`,
|
||||
@@ -1834,15 +1832,7 @@ Offline Changed files: ${files.length}`;
|
||||
force = false
|
||||
): Promise<boolean> {
|
||||
return Boolean(
|
||||
await this.extractInternalFileFromDatabase(
|
||||
storageFilePath,
|
||||
force,
|
||||
undefined,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
revision
|
||||
)
|
||||
await this.extractInternalFileFromDatabase(storageFilePath, force, undefined, true, false, true, revision)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1917,7 +1907,9 @@ Offline Changed files: ${files.length}`;
|
||||
private _allSuspendExtraSync(): Promise<boolean> {
|
||||
if (this.core.settings.syncInternalFiles) {
|
||||
this._log(
|
||||
"Hidden file synchronization have been temporarily disabled. Please enable them after the fetching, if you need them.",
|
||||
$msg(
|
||||
"Hidden file synchronization have been temporarily disabled. Please enable them after the fetching, if you need them."
|
||||
),
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
this.core.settings.syncInternalFiles = false;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import type { LiveSyncTrysteroReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/LiveSyncTrysteroReplicator";
|
||||
import { delay, fireAndForget } from "octagonal-wheels/promises";
|
||||
import P2PServerStatusCard from "./P2PServerStatusCard.svelte";
|
||||
import { $msg as translateMessage } from "@/common/translation";
|
||||
|
||||
interface Props {
|
||||
liveSyncReplicator: LiveSyncTrysteroReplicator;
|
||||
@@ -84,11 +85,11 @@
|
||||
}
|
||||
|
||||
function getAcceptanceStatus(peer: P2PServerInfo["knownAdvertisements"][number]) {
|
||||
if (peer.isTemporaryAccepted === true) return "ACCEPTED (in session)";
|
||||
if (peer.isAccepted === true) return "ACCEPTED";
|
||||
if (peer.isTemporaryAccepted === false) return "DENIED (in session)";
|
||||
if (peer.isAccepted === false) return "DENIED";
|
||||
return "NEW";
|
||||
if (peer.isTemporaryAccepted === true) return translateMessage("ACCEPTED (in session)");
|
||||
if (peer.isAccepted === true) return translateMessage("ACCEPTED");
|
||||
if (peer.isTemporaryAccepted === false) return translateMessage("DENIED (in session)");
|
||||
if (peer.isAccepted === false) return translateMessage("DENIED");
|
||||
return translateMessage("NEW");
|
||||
}
|
||||
|
||||
function getAcceptanceStatusClass(peer: P2PServerInfo["knownAdvertisements"][number]) {
|
||||
@@ -102,7 +103,7 @@
|
||||
<P2PServerStatusCard {getLiveSyncReplicator} showBroadcastToggle={false} />
|
||||
|
||||
<div class="peers-section">
|
||||
<h3>Available Peers</h3>
|
||||
<h3>{translateMessage("Available Peers")}</h3>
|
||||
{#if serverInfo && serverInfo.knownAdvertisements.length > 0}
|
||||
<div class="peers-list">
|
||||
{#each serverInfo.knownAdvertisements as peer (peer.peerId)}
|
||||
@@ -126,14 +127,18 @@
|
||||
disabled={syncingPeerId !== null}
|
||||
onclick={() => handleSync(peer.peerId)}
|
||||
>
|
||||
{syncingPeerId === peer.peerId ? "Syncing..." : "Sync"}
|
||||
{syncingPeerId === peer.peerId
|
||||
? translateMessage("Syncing...")
|
||||
: translateMessage("Sync")}
|
||||
</button>
|
||||
<button
|
||||
class="btn {rebuildMode ? 'btn-primary' : 'btn-secondary'}"
|
||||
disabled={syncingPeerId !== null}
|
||||
onclick={() => handleSyncThenClose(peer.peerId)}
|
||||
>
|
||||
{syncingPeerId === peer.peerId ? "Syncing..." : "Start Sync & Close"}
|
||||
{syncingPeerId === peer.peerId
|
||||
? translateMessage("Syncing...")
|
||||
: translateMessage("Start Sync & Close")}
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
@@ -141,7 +146,9 @@
|
||||
disabled={syncingPeerId !== null}
|
||||
onclick={() => handleSyncThenClose(peer.peerId)}
|
||||
>
|
||||
{syncingPeerId === peer.peerId ? "Syncing..." : "Sync"}
|
||||
{syncingPeerId === peer.peerId
|
||||
? translateMessage("Syncing...")
|
||||
: translateMessage("Sync")}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -149,16 +156,22 @@
|
||||
{/each}
|
||||
</div>
|
||||
{:else if serverInfo}
|
||||
<p class="no-peers">No devices available. Waiting for other devices to connect...</p>
|
||||
<p class="no-peers">
|
||||
{translateMessage("No devices available. Waiting for other devices to connect...")}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
{#if rebuildMode}
|
||||
<button class="btn btn-cancel" onclick={onClose} disabled={syncingPeerId !== null}>Skip and close</button>
|
||||
<button class="btn btn-cancel" onclick={onClose} disabled={syncingPeerId !== null}
|
||||
>{translateMessage("Skip and close")}</button
|
||||
>
|
||||
{:else}
|
||||
<button class="btn btn-cancel" onclick={onClose}>Close</button>
|
||||
<button class="btn btn-cancel" onclick={onCloseAndDisconnect}>Close & Disconnect</button>
|
||||
<button class="btn btn-cancel" onclick={onClose}>{translateMessage("Close")}</button>
|
||||
<button class="btn btn-cancel" onclick={onCloseAndDisconnect}
|
||||
>{translateMessage("Close & Disconnect")}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -263,7 +263,7 @@
|
||||
</script>
|
||||
|
||||
<article>
|
||||
<h1>Peer to Peer Replicator</h1>
|
||||
<h1>{_msg("Peer to Peer Replicator")}</h1>
|
||||
<details bind:open={isNoticeOpened}>
|
||||
<summary>{_msg("P2P.Note.Summary")}</summary>
|
||||
<p class="important">{_msg("P2P.Note.important_note")}</p>
|
||||
@@ -274,23 +274,23 @@
|
||||
<p>{paragraph}</p>
|
||||
{/each}
|
||||
</details>
|
||||
<h2>Connection Settings</h2>
|
||||
<h2>{_msg("Connection Settings")}</h2>
|
||||
{#if isObsidian}
|
||||
You can configure in the Obsidian Plugin Settings.
|
||||
{_msg("You can configure in the Obsidian Plugin Settings.")}
|
||||
{:else}
|
||||
<details bind:open={isSettingOpened}>
|
||||
<summary>{eRelay}</summary>
|
||||
<table class="settings">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th> Enable P2P Replicator </th>
|
||||
<th>{_msg("Enable P2P Replicator")}</th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isP2PEnabledModified }}>
|
||||
<input type="checkbox" bind:checked={eP2PEnabled} />
|
||||
</label>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<th> Relay settings </th>
|
||||
<th>{_msg("Relay settings")}</th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isRelayModified }}>
|
||||
<input
|
||||
@@ -299,12 +299,12 @@
|
||||
bind:value={eRelay}
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button onclick={() => useDefaultRelay()}> Use vrtmrz's relay </button>
|
||||
<button onclick={() => useDefaultRelay()}>{_msg("Use vrtmrz's relay")}</button>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Room ID </th>
|
||||
<th>{_msg("Room ID")}</th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isRoomIdModified }}>
|
||||
<input
|
||||
@@ -315,31 +315,32 @@
|
||||
spellcheck="false"
|
||||
autocorrect="off"
|
||||
/>
|
||||
<button onclick={() => chooseRandom()}> Use Random Number </button>
|
||||
<button onclick={() => chooseRandom()}>{_msg("Use Random Number")}</button>
|
||||
</label>
|
||||
<span>
|
||||
<small>
|
||||
This can isolate your connections between devices. Use the same Room ID for the same
|
||||
devices.</small
|
||||
<small
|
||||
>{_msg(
|
||||
"This can isolate your connections between devices. Use the same Room ID for the same devices."
|
||||
)}</small
|
||||
>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Password </th>
|
||||
<th>{_msg("Password")}</th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isPasswordModified }}>
|
||||
<input type="password" placeholder="password" bind:value={ePassword} />
|
||||
</label>
|
||||
<span>
|
||||
<small>
|
||||
This password is used to encrypt the connection. Use something long enough.
|
||||
{_msg("This password is used to encrypt the connection. Use something long enough.")}
|
||||
</small>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> This device name </th>
|
||||
<th>{_msg("This device name")}</th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isDeviceNameModified }}>
|
||||
<input
|
||||
@@ -351,14 +352,15 @@
|
||||
</label>
|
||||
<span>
|
||||
<small>
|
||||
Device name to identify the device. Please use shorter one for the stable peer
|
||||
detection, i.e., "iphone-16" or "macbook-2021".
|
||||
{_msg(
|
||||
'Device name to identify the device. Please use shorter one for the stable peer detection, i.e., "iphone-16" or "macbook-2021".'
|
||||
)}
|
||||
</small>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Auto Connect </th>
|
||||
<th>{_msg("Auto Connect")}</th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isAutoStartModified }}>
|
||||
<input type="checkbox" bind:checked={eAutoStart} />
|
||||
@@ -366,7 +368,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> Start change-broadcasting on Connect </th>
|
||||
<th>{_msg("Start change-broadcasting on Connect")}</th>
|
||||
<td>
|
||||
<label class={{ "is-dirty": isAutoBroadcastModified }}>
|
||||
<input type="checkbox" bind:checked={eAutoBroadcast} />
|
||||
@@ -383,39 +385,43 @@
|
||||
</tr> -->
|
||||
</tbody>
|
||||
</table>
|
||||
<button disabled={!isAnyModified} class="button mod-cta" onclick={saveAndApply}>Save and Apply</button>
|
||||
<button disabled={!isAnyModified} class="button" onclick={revert}>Revert changes</button>
|
||||
<button disabled={!isAnyModified} class="button mod-cta" onclick={saveAndApply}
|
||||
>{_msg("Save and Apply")}</button
|
||||
>
|
||||
<button disabled={!isAnyModified} class="button" onclick={revert}>{_msg("Revert changes")}</button>
|
||||
</details>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<h2>Signaling Server Connection</h2>
|
||||
<h2>{_msg("Signaling Server Connection")}</h2>
|
||||
<div>
|
||||
{#if !isConnected}
|
||||
<p>No Connection</p>
|
||||
<p>{_msg("No Connection")}</p>
|
||||
{:else}
|
||||
<p>Connected to Signaling Server (as Peer ID: {serverPeerId})</p>
|
||||
<p>{_msg("Connected to Signaling Server (as Peer ID: ${peerId})", { peerId: serverPeerId })}</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
{#if !isConnected}
|
||||
<button onclick={openServer}>Connect</button>
|
||||
<button onclick={openServer}>{_msg("Connect")}</button>
|
||||
{:else}
|
||||
<button onclick={closeServer}>Disconnect</button>
|
||||
<button onclick={closeServer}>{_msg("Disconnect")}</button>
|
||||
{#if replicatorInfo?.isBroadcasting !== undefined}
|
||||
{#if replicatorInfo?.isBroadcasting}
|
||||
<button onclick={stopBroadcasting}>Stop Broadcasting</button>
|
||||
<button onclick={stopBroadcasting}>{_msg("Stop Broadcasting")}</button>
|
||||
{:else}
|
||||
<button onclick={startBroadcasting}>Start Broadcasting</button>
|
||||
<button onclick={startBroadcasting}>{_msg("Start Broadcasting")}</button>
|
||||
{/if}
|
||||
{/if}
|
||||
<details>
|
||||
<summary>Broadcasting?</summary>
|
||||
<summary>{_msg("Broadcasting?")}</summary>
|
||||
<p>
|
||||
<small>
|
||||
If you want to use `LiveSync`, you should broadcast changes. All `watching` peers which
|
||||
detects this will start the replication for fetching. <br />
|
||||
However, This should not be enabled if you want to increase your secrecy more.
|
||||
{_msg(
|
||||
"If you want to use `LiveSync`, you should broadcast changes. All `watching` peers which detects this will start the replication for fetching."
|
||||
)}
|
||||
<br />
|
||||
{_msg("However, This should not be enabled if you want to increase your secrecy more.")}
|
||||
</small>
|
||||
</p>
|
||||
</details>
|
||||
@@ -424,13 +430,13 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Peers</h2>
|
||||
<h2>{_msg("Peers")}</h2>
|
||||
<table class="peers">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Command</th>
|
||||
<th>{_msg("Name")}</th>
|
||||
<th>{_msg("Action")}</th>
|
||||
<th>{_msg("Command")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -95,40 +95,40 @@
|
||||
</script>
|
||||
|
||||
<div class="server-status">
|
||||
<h3>Signalling Status</h3>
|
||||
<h3>{translateMessage("Signalling Status")}</h3>
|
||||
|
||||
<div class="status-item">
|
||||
<span>Connection:</span>
|
||||
<span>{translateMessage("Connection:")}</span>
|
||||
<span class="status-value {isConnected ? 'connected' : 'disconnected'}">
|
||||
{isConnected ? "🟢 Connected" : "🔴 Disconnected"}
|
||||
{isConnected ? translateMessage("🟢 Connected") : translateMessage("🔴 Disconnected")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="status-item status-action">
|
||||
{#if !isConnected}
|
||||
<button onclick={onOpenConnection}>Open connection</button>
|
||||
<button onclick={onOpenConnection}>{translateMessage("Open connection")}</button>
|
||||
{:else}
|
||||
<button onclick={onDisconnect}>Disconnect</button>
|
||||
<button onclick={onDisconnect}>{translateMessage("Disconnect")}</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if serverInfo}
|
||||
<div class="status-item">
|
||||
<span>Room ID suffix:</span>
|
||||
<span class="room-suffix-display" title={roomSuffix || "Not configured"}>
|
||||
<span>{translateMessage("Room ID suffix:")}</span>
|
||||
<span class="room-suffix-display" title={roomSuffix || translateMessage("Not configured")}>
|
||||
{roomSuffix || "-"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="status-item">
|
||||
<span>Peer ID:</span>
|
||||
<span>{translateMessage("Peer ID:")}</span>
|
||||
<span class="peer-id-display" title={serverInfo.serverPeerId}>
|
||||
{serverInfo.serverPeerId.slice(0, 12)}...
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="status-item">
|
||||
<span>Devices:</span>
|
||||
<span>{translateMessage("Devices:")}</span>
|
||||
<span>{serverInfo.knownAdvertisements.length}</span>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -146,7 +146,7 @@
|
||||
? translateMessage("Stop announcing changes")
|
||||
: translateMessage("Start announcing changes")}
|
||||
>
|
||||
{isBroadcasting ? '📡 On' : '📡 Off'}
|
||||
{isBroadcasting ? translateMessage("📡 On") : translateMessage("📡 Off")}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -154,39 +154,39 @@
|
||||
{#if core}
|
||||
<div class="status-item status-action diag-toggle-row">
|
||||
<label class="broadcast-label" for="diag-toggle">
|
||||
🕵️ Diag
|
||||
{translateMessage("🕵️ Diag")}
|
||||
</label>
|
||||
<button
|
||||
id="diag-toggle"
|
||||
class="broadcast-button {useDiagRTC ? 'is-on' : 'is-off'}"
|
||||
onclick={toggleDiagRTC}
|
||||
title={useDiagRTC
|
||||
? 'Diagnostic RTCPeerConnection is enabled'
|
||||
: 'Use Diagnostic RTCPeerConnection for statistics'}
|
||||
? translateMessage("Diagnostic RTCPeerConnection is enabled")
|
||||
: translateMessage("Use Diagnostic RTCPeerConnection for statistics")}
|
||||
>
|
||||
{useDiagRTC ? 'On' : 'Off'}
|
||||
{useDiagRTC ? translateMessage("On") : translateMessage("Off")}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if serverInfo}
|
||||
<div class="diag-section">
|
||||
<h4>Stats</h4>
|
||||
<h4>{translateMessage("Stats")}</h4>
|
||||
<div class="diag-grid">
|
||||
<div class="diag-item">
|
||||
<span>Incoming:</span>
|
||||
<span>{translateMessage("Incoming:")}</span>
|
||||
<span>{serverInfo.diag.totalNewConnections}</span>
|
||||
</div>
|
||||
<div class="diag-item">
|
||||
<span>Connected:</span>
|
||||
<span>{translateMessage("Connected:")}</span>
|
||||
<span>{serverInfo.diag.totalSuccessfulConnections}</span>
|
||||
</div>
|
||||
<div class="diag-item">
|
||||
<span>Failed:</span>
|
||||
<span>{translateMessage("Failed:")}</span>
|
||||
<span>{serverInfo.diag.totalFailedConnections}</span>
|
||||
</div>
|
||||
<div class="diag-item">
|
||||
<span>Closed:</span>
|
||||
<span>{translateMessage("Closed:")}</span>
|
||||
<span>{serverInfo.diag.totalClosedConnections}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -170,11 +170,11 @@
|
||||
});
|
||||
|
||||
function getAcceptanceStatus(peer: P2PServerInfo["knownAdvertisements"][number]) {
|
||||
if (peer.isTemporaryAccepted === true) return "ACCEPTED (in session)";
|
||||
if (peer.isAccepted === true) return "ACCEPTED";
|
||||
if (peer.isTemporaryAccepted === false) return "DENIED (in session)";
|
||||
if (peer.isAccepted === false) return "DENIED";
|
||||
return "NEW";
|
||||
if (peer.isTemporaryAccepted === true) return translateMessage("ACCEPTED (in session)");
|
||||
if (peer.isAccepted === true) return translateMessage("ACCEPTED");
|
||||
if (peer.isTemporaryAccepted === false) return translateMessage("DENIED (in session)");
|
||||
if (peer.isAccepted === false) return translateMessage("DENIED");
|
||||
return translateMessage("NEW");
|
||||
}
|
||||
|
||||
function getAcceptanceStatusClass(peer: P2PServerInfo["knownAdvertisements"][number]) {
|
||||
@@ -409,7 +409,7 @@
|
||||
|
||||
<div class="p2p-container">
|
||||
<div class="pane-header">
|
||||
<h2>P2P Status</h2>
|
||||
<h2>{translateMessage("P2P Status")}</h2>
|
||||
<div class="pane-header-actions">
|
||||
<div class="remote-picker-wrap">
|
||||
<select
|
||||
@@ -417,11 +417,11 @@
|
||||
value={selectedP2PRemoteConfigurationId}
|
||||
onchange={onP2PRemoteSelected}
|
||||
disabled={selectingP2PRemote}
|
||||
aria-label="Select active P2P remote"
|
||||
title="Select active P2P remote"
|
||||
aria-label={translateMessage("Select active P2P remote")}
|
||||
title={translateMessage("Select active P2P remote")}
|
||||
>
|
||||
{#if p2pRemoteOptions.length === 0}
|
||||
<option value="">Select P2P remote...</option>
|
||||
<option value="">{translateMessage("Select P2P remote...")}</option>
|
||||
{/if}
|
||||
{#each p2pRemoteOptions as option}
|
||||
<option value={option.id}>
|
||||
@@ -432,8 +432,8 @@
|
||||
<button
|
||||
class="icon-button"
|
||||
onclick={() => createAndSelectP2PRemote()}
|
||||
title="Create P2P remote"
|
||||
aria-label="Create P2P remote"
|
||||
title={translateMessage("Create P2P remote")}
|
||||
aria-label={translateMessage("Create P2P remote")}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
@@ -441,8 +441,8 @@
|
||||
<button
|
||||
class="icon-button"
|
||||
onclick={openConnectionSettings}
|
||||
title="Open P2P Setup..."
|
||||
aria-label="Open P2P Setup..."
|
||||
title={translateMessage("Open P2P Setup...")}
|
||||
aria-label={translateMessage("Open P2P Setup...")}
|
||||
>
|
||||
⚙
|
||||
</button>
|
||||
@@ -450,15 +450,17 @@
|
||||
</div>
|
||||
|
||||
{#if !canEditP2PSettings()}
|
||||
<p class="warning-line">Please select an active P2P remote configuration to change P2P sync targets.</p>
|
||||
<p class="warning-line">
|
||||
{translateMessage("Please select an active P2P remote configuration to change P2P sync targets.")}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<P2PServerStatusCard {getLiveSyncReplicator} {core} />
|
||||
|
||||
<div class="peers-section">
|
||||
<div class="peers-header">
|
||||
<h3>Detected Peers</h3>
|
||||
<button class="refresh" onclick={requestServerStatus}>Refresh</button>
|
||||
<h3>{translateMessage("Detected Peers")}</h3>
|
||||
<button class="refresh" onclick={requestServerStatus}>{translateMessage("Refresh")}</button>
|
||||
</div>
|
||||
|
||||
{#if serverInfo && serverInfo.knownAdvertisements.length > 0}
|
||||
@@ -470,7 +472,11 @@
|
||||
{peer.name} :
|
||||
<span class="peer-id-mini" title={peer.peerId}>({peer.peerId.slice(0, 8)})</span>
|
||||
{#if isCommunicating(peer.peerId)}
|
||||
<span class="comm-icon" title="Communicating" aria-label="Communicating">📡</span>
|
||||
<span
|
||||
class="comm-icon"
|
||||
title={translateMessage("Communicating")}
|
||||
aria-label={translateMessage("Communicating")}>📡</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="peer-meta">
|
||||
@@ -486,8 +492,12 @@
|
||||
<button
|
||||
class="emoji-button"
|
||||
disabled={replicatingPeerId !== null}
|
||||
title={replicatingPeerId === peer.peerId ? "Replicating..." : "Replicate now"}
|
||||
aria-label={replicatingPeerId === peer.peerId ? "Replicating" : "Replicate now"}
|
||||
title={replicatingPeerId === peer.peerId
|
||||
? translateMessage("Replicating...")
|
||||
: translateMessage("Replicate now")}
|
||||
aria-label={replicatingPeerId === peer.peerId
|
||||
? translateMessage("Replicating")
|
||||
: translateMessage("Replicate now")}
|
||||
onclick={() => startReplication(peer)}
|
||||
>
|
||||
{replicatingPeerId === peer.peerId ? "⏳" : "🔄"}
|
||||
@@ -497,7 +507,7 @@
|
||||
disabled={decidingPeerId !== null}
|
||||
onclick={() => revokeDecision(peer)}
|
||||
>
|
||||
Revoke
|
||||
{translateMessage("Revoke")}
|
||||
</button>
|
||||
<button
|
||||
class="emoji-button"
|
||||
@@ -534,11 +544,11 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="decision-row">
|
||||
<span class="decision-label">PERMANENT</span>
|
||||
<span class="decision-label">{translateMessage("PERMANENT")}</span>
|
||||
<button
|
||||
class="emoji-button"
|
||||
title="Allow permanently"
|
||||
aria-label="Allow permanently"
|
||||
title={translateMessage("Allow permanently")}
|
||||
aria-label={translateMessage("Allow permanently")}
|
||||
disabled={decidingPeerId !== null}
|
||||
onclick={() => makeDecision(peer, true, false)}
|
||||
>
|
||||
@@ -546,8 +556,8 @@
|
||||
</button>
|
||||
<button
|
||||
class="emoji-button mod-warning"
|
||||
title="Deny permanently"
|
||||
aria-label="Deny permanently"
|
||||
title={translateMessage("Deny permanently")}
|
||||
aria-label={translateMessage("Deny permanently")}
|
||||
disabled={decidingPeerId !== null}
|
||||
onclick={() => makeDecision(peer, false, false)}
|
||||
>
|
||||
@@ -555,11 +565,11 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="decision-row">
|
||||
<span class="decision-label">SESSION</span>
|
||||
<span class="decision-label">{translateMessage("SESSION")}</span>
|
||||
<button
|
||||
class="emoji-button"
|
||||
title="Allow in session"
|
||||
aria-label="Allow in session"
|
||||
title={translateMessage("Allow in session")}
|
||||
aria-label={translateMessage("Allow in session")}
|
||||
disabled={decidingPeerId !== null}
|
||||
onclick={() => makeDecision(peer, true, true)}
|
||||
>
|
||||
@@ -567,8 +577,8 @@
|
||||
</button>
|
||||
<button
|
||||
class="emoji-button mod-warning"
|
||||
title="Deny in session"
|
||||
aria-label="Deny in session"
|
||||
title={translateMessage("Deny in session")}
|
||||
aria-label={translateMessage("Deny in session")}
|
||||
disabled={decidingPeerId !== null}
|
||||
onclick={() => makeDecision(peer, false, true)}
|
||||
>
|
||||
@@ -582,7 +592,7 @@
|
||||
disabled={decidingPeerId !== null}
|
||||
onclick={() => revokeDecision(peer)}
|
||||
>
|
||||
Revoke
|
||||
{translateMessage("Revoke")}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -590,9 +600,11 @@
|
||||
{/each}
|
||||
</div>
|
||||
{:else if serverInfo}
|
||||
<p class="no-peers">No devices available. Waiting for other devices to connect...</p>
|
||||
<p class="no-peers">
|
||||
{translateMessage("No devices available. Waiting for other devices to connect...")}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="no-peers">Fetching status...</p>
|
||||
<p class="no-peers">{translateMessage("Fetching status...")}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { AcceptedStatus, type PeerStatus } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/P2PReplicatorPaneCommon";
|
||||
import type { P2PReplicatorHandle } from "./P2PReplicatorPaneHost";
|
||||
import { $msg as translateMessage } from "@/common/translation";
|
||||
|
||||
interface Props {
|
||||
peerStatus: PeerStatus;
|
||||
@@ -27,6 +28,11 @@
|
||||
peer.isSending ? ["SENDING"] : [],
|
||||
].flat()
|
||||
);
|
||||
const chipLabels: Record<string, string> = {
|
||||
WATCHING: translateMessage("WATCHING"),
|
||||
FETCHING: translateMessage("FETCHING"),
|
||||
SENDING: translateMessage("SENDING"),
|
||||
};
|
||||
let acceptedStatusChip = $derived.by(() =>
|
||||
select(
|
||||
peer.accepted.toString(),
|
||||
@@ -40,6 +46,13 @@
|
||||
""
|
||||
) ?? ""
|
||||
);
|
||||
const acceptedStatusLabels: Record<string, string> = {
|
||||
ACCEPTED: translateMessage("ACCEPTED"),
|
||||
"ACCEPTED (in session)": translateMessage("ACCEPTED (in session)"),
|
||||
"DENIED (in session)": translateMessage("DENIED (in session)"),
|
||||
DENIED: translateMessage("DENIED"),
|
||||
NEW: translateMessage("NEW"),
|
||||
};
|
||||
const classList = {
|
||||
["SENDING"]: "connected",
|
||||
["FETCHING"]: "connected",
|
||||
@@ -75,13 +88,13 @@
|
||||
const peerAttrLabels = $derived.by(() => {
|
||||
const attrs = [];
|
||||
if (peer.syncOnConnect) {
|
||||
attrs.push("✔ SYNC");
|
||||
attrs.push(translateMessage("✔ SYNC"));
|
||||
}
|
||||
if (peer.watchOnConnect) {
|
||||
attrs.push("✔ WATCH");
|
||||
attrs.push(translateMessage("✔ WATCH"));
|
||||
}
|
||||
if (peer.syncOnReplicationCommand) {
|
||||
attrs.push("✔ SELECT");
|
||||
attrs.push(translateMessage("✔ SELECT"));
|
||||
}
|
||||
return attrs;
|
||||
});
|
||||
@@ -113,12 +126,14 @@
|
||||
</div>
|
||||
<div class="status-chips">
|
||||
<div class="row">
|
||||
<span class="chip {select(acceptedStatusChip, classList)}">{acceptedStatusChip}</span>
|
||||
<span class="chip {select(acceptedStatusChip, classList)}"
|
||||
>{acceptedStatusLabels[acceptedStatusChip] ?? acceptedStatusChip}</span
|
||||
>
|
||||
</div>
|
||||
{#if isAccepted}
|
||||
<div class="row">
|
||||
{#each statusChips as chip}
|
||||
<span class="chip {select(chip, classList)}">{chip}</span>
|
||||
<span class="chip {select(chip, classList)}">{chipLabels[chip] ?? chip}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -134,15 +149,25 @@
|
||||
<div class="row">
|
||||
{#if isNew}
|
||||
{#if !isAccepted}
|
||||
<button class="button" onclick={() => makeDecision(true, true)}>Accept in session</button>
|
||||
<button class="button mod-cta" onclick={() => makeDecision(true, false)}>Accept</button>
|
||||
<button class="button" onclick={() => makeDecision(true, true)}
|
||||
>{translateMessage("Accept in session")}</button
|
||||
>
|
||||
<button class="button mod-cta" onclick={() => makeDecision(true, false)}
|
||||
>{translateMessage("Accept")}</button
|
||||
>
|
||||
{/if}
|
||||
{#if !isDenied}
|
||||
<button class="button" onclick={() => makeDecision(false, true)}>Deny in session</button>
|
||||
<button class="button mod-warning" onclick={() => makeDecision(false, false)}>Deny</button>
|
||||
<button class="button" onclick={() => makeDecision(false, true)}
|
||||
>{translateMessage("Deny in session")}</button
|
||||
>
|
||||
<button class="button mod-warning" onclick={() => makeDecision(false, false)}
|
||||
>{translateMessage("Deny")}</button
|
||||
>
|
||||
{/if}
|
||||
{:else}
|
||||
<button class="button mod-warning" onclick={() => revokeDecision()}>Revoke</button>
|
||||
<button class="button mod-warning" onclick={() => revokeDecision()}
|
||||
>{translateMessage("Revoke")}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -155,9 +180,9 @@
|
||||
<!-- <button class="button" onclick={replicateFrom} disabled={peer.isFetching}>📥</button>
|
||||
<button class="button" onclick={replicateTo} disabled={peer.isSending}>📤</button> -->
|
||||
{#if peer.isWatching}
|
||||
<button class="button" onclick={stopWatching}>Stop ⚡</button>
|
||||
<button class="button" onclick={stopWatching}>{translateMessage("Stop ⚡")}</button>
|
||||
{:else}
|
||||
<button class="button" onclick={startWatching} title="live">⚡</button>
|
||||
<button class="button" onclick={startWatching} title={translateMessage("live")}>⚡</button>
|
||||
{/if}
|
||||
{#if showPeerMenu}
|
||||
<button class="button" onclick={moreMenu}>...</button>
|
||||
|
||||
Reference in New Issue
Block a user