mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-25 02:23:22 +00:00
Clarify device review guidance and follow dialogue windows
This commit is contained in:
1 parent
76808878da
commit
39ef429d7b
7 files changed
+81
-40
No files matched your search
+1
-1
@@ -236,7 +236,7 @@ The active connection type. This is automatically projected to the legacy config
|
|||||||
|
|
||||||
### Joined devices
|
### Joined devices
|
||||||
|
|
||||||
Open **Joined devices** to read the device records on the active CouchDB or Object Storage connection. The same dialogue is available from the command palette and version Notices. **Refresh** reads the remote again. This device appears first, including when its record is absent from the remote. Opening or refreshing this dialogue does not create a missing CouchDB database.
|
Choose **Review devices** under **Joined devices** to read the device records on the active CouchDB or Object Storage connection. The same **Review devices** action is available from the command palette and version Notices. **Refresh** reads the remote again. This device appears first, including when its record is absent from the remote. Opening or refreshing this dialogue does not create a missing CouchDB database.
|
||||||
|
|
||||||
The list shows each device ID, recorded platform, plug-in version, app version, and last recorded connection time. Older clients may have incomplete information. These records do not establish whether a device is online. On a locked remote, a device outside the accepted list is shown as **Locked out**.
|
The list shows each device ID, recorded platform, plug-in version, app version, and last recorded connection time. Older clients may have incomplete information. These records do not establish whether a device is online. On a locked remote, a device outside the accepted list is shown as **Locked out**.
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ export const liveSyncProvisionalEnglishMessages = {
|
|||||||
"This remote does not currently have a record of joined devices.",
|
"This remote does not currently have a record of joined devices.",
|
||||||
"This list shows device information recorded by the remote. It does not show whether a device is online now.":
|
"This list shows device information recorded by the remote. It does not show whether a device is online now.":
|
||||||
"This list shows device information recorded by the remote. It does not show whether a device is online now.",
|
"This list shows device information recorded by the remote. It does not show whether a device is online now.",
|
||||||
"This device is locked out. Use the existing recovery process to join again.":
|
"This device has been locked out. Use the existing recovery process to join again.":
|
||||||
"This device is locked out. Use the existing recovery process to join again.",
|
"This device has been locked out. Use the existing recovery process to join again.",
|
||||||
"This device": "This device",
|
"This device": "This device",
|
||||||
"This remote has been cleaned. Use the existing recovery process to join devices again as needed.":
|
"This remote has been cleaned. Use the existing recovery process to join devices again as needed.":
|
||||||
"This remote has been cleaned. Use the existing recovery process to join devices again as needed.",
|
"This remote has been cleaned. Use the existing recovery process to join devices again as needed.",
|
||||||
@@ -42,10 +42,10 @@ export const liveSyncProvisionalEnglishMessages = {
|
|||||||
Refresh: "Refresh",
|
Refresh: "Refresh",
|
||||||
"Plug-in version": "Plug-in version",
|
"Plug-in version": "Plug-in version",
|
||||||
Platform: "Platform",
|
Platform: "Platform",
|
||||||
"Other joined devices last reported an older plug-in version: ${versions}.":
|
"An older plug-in version was detected in other device records: ${versions}. We recommend checking whether those devices need updating.":
|
||||||
"Other joined devices last reported an older plug-in version: ${versions}.",
|
"An older plug-in version was detected in other device records: ${versions}. We recommend checking whether those devices need updating.",
|
||||||
"Other joined devices last reported a newer plug-in version: ${versions}.":
|
"A newer plug-in version was detected in other device records: ${versions}. We recommend checking whether this device needs updating.":
|
||||||
"Other joined devices last reported a newer plug-in version: ${versions}.",
|
"A newer plug-in version was detected in other device records: ${versions}. We recommend checking whether this device needs updating.",
|
||||||
Open: "Open",
|
Open: "Open",
|
||||||
"Not recorded": "Not recorded",
|
"Not recorded": "Not recorded",
|
||||||
"Not accepted": "Not accepted",
|
"Not accepted": "Not accepted",
|
||||||
@@ -58,6 +58,7 @@ export const liveSyncProvisionalEnglishMessages = {
|
|||||||
"Joined devices are available after database startup with an active CouchDB or Object Storage connection.":
|
"Joined devices are available after database startup with an active CouchDB or Object Storage connection.":
|
||||||
"Joined devices are available after database startup with an active CouchDB or Object Storage connection.",
|
"Joined devices are available after database startup with an active CouchDB or Object Storage connection.",
|
||||||
"Joined devices": "Joined devices",
|
"Joined devices": "Joined devices",
|
||||||
|
"Review devices": "Review devices",
|
||||||
"Forgetting…": "Forgetting…",
|
"Forgetting…": "Forgetting…",
|
||||||
"Forgetting the record does not delete Vault files or revoke credentials.":
|
"Forgetting the record does not delete Vault files or revoke credentials.":
|
||||||
"Forgetting the record does not delete Vault files or revoke credentials.",
|
"Forgetting the record does not delete Vault files or revoke credentials.",
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export function paneRemoteConfig(
|
|||||||
.setName($msg("Joined devices"))
|
.setName($msg("Joined devices"))
|
||||||
.setDesc($msg("Review device records for the active CouchDB or Object Storage connection."))
|
.setDesc($msg("Review device records for the active CouchDB or Object Storage connection."))
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
button.setButtonText($msg("Open")).onClick(async () => {
|
button.setButtonText($msg("Review devices")).onClick(async () => {
|
||||||
const result = await this.services.UI.commands.invoke(OPEN_JOINED_DEVICES, undefined);
|
const result = await this.services.UI.commands.invoke(OPEN_JOINED_DEVICES, undefined);
|
||||||
if (result.status === "unavailable" || result.value === "unavailable") {
|
if (result.status === "unavailable" || result.value === "unavailable") {
|
||||||
this.services.API.addLog(
|
this.services.API.addLog(
|
||||||
|
|||||||
@@ -176,7 +176,7 @@
|
|||||||
{#if device.self && device.lockedOut}
|
{#if device.self && device.lockedOut}
|
||||||
<p class="state-message">
|
<p class="state-message">
|
||||||
{translate(
|
{translate(
|
||||||
"This device is locked out. Use the existing recovery process to join again."
|
"This device has been locked out. Use the existing recovery process to join again."
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -62,14 +62,20 @@ export function useJoinedDevicesFeature(host: JoinedDevicesHost, notices: Obsidi
|
|||||||
notices.setItem(noticeGroup, direction, {
|
notices.setItem(noticeGroup, direction, {
|
||||||
message:
|
message:
|
||||||
direction === "newer"
|
direction === "newer"
|
||||||
? $msg("Other joined devices last reported a newer plug-in version: ${versions}.", {
|
? $msg(
|
||||||
versions: versions.join(", "),
|
"A newer plug-in version was detected in other device records: ${versions}. We recommend checking whether this device needs updating.",
|
||||||
})
|
{
|
||||||
: $msg("Other joined devices last reported an older plug-in version: ${versions}.", {
|
versions: versions.join(", "),
|
||||||
versions: versions.join(", "),
|
}
|
||||||
}),
|
)
|
||||||
|
: $msg(
|
||||||
|
"An older plug-in version was detected in other device records: ${versions}. We recommend checking whether those devices need updating.",
|
||||||
|
{
|
||||||
|
versions: versions.join(", "),
|
||||||
|
}
|
||||||
|
),
|
||||||
action: {
|
action: {
|
||||||
label: $msg("Joined devices"),
|
label: $msg("Review devices"),
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
if (!disposed && identity === remoteIdentity(services.setting.currentSettings())) {
|
if (!disposed && identity === remoteIdentity(services.setting.currentSettings())) {
|
||||||
void services.UI.commands.invoke(OPEN_JOINED_DEVICES, undefined);
|
void services.UI.commands.invoke(OPEN_JOINED_DEVICES, undefined);
|
||||||
@@ -190,7 +196,7 @@ export function useJoinedDevicesFeature(host: JoinedDevicesHost, notices: Obsidi
|
|||||||
services.appLifecycle.onInitialise.addHandler(() => {
|
services.appLifecycle.onInitialise.addHandler(() => {
|
||||||
services.API.addCommand({
|
services.API.addCommand({
|
||||||
id: "joined-devices",
|
id: "joined-devices",
|
||||||
name: $msg("Joined devices"),
|
name: $msg("Review devices"),
|
||||||
icon: "monitor-smartphone",
|
icon: "monitor-smartphone",
|
||||||
checkCallback: (checking) => {
|
checkCallback: (checking) => {
|
||||||
if (!available()) return false;
|
if (!available()) return false;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { mkdir } from "node:fs/promises";
|
||||||
|
import { join } from "node:path";
|
||||||
import { DEVICE_ID_PREFERRED, MILESTONE_DOCID, NODEINFO_DOCID } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
import { DEVICE_ID_PREFERRED, MILESTONE_DOCID, NODEINFO_DOCID } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||||
import { evalObsidianJson } from "../runner/cli.ts";
|
import { evalObsidianJson } from "../runner/cli.ts";
|
||||||
import {
|
import {
|
||||||
@@ -27,12 +29,7 @@ import {
|
|||||||
} from "@vrtmrz/obsidian-test-session";
|
} from "@vrtmrz/obsidian-test-session";
|
||||||
import { iPhoneSafeArea, setObsidianMobileTestMode } from "../runner/mobileUi.ts";
|
import { iPhoneSafeArea, setObsidianMobileTestMode } from "../runner/mobileUi.ts";
|
||||||
import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts";
|
import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts";
|
||||||
import {
|
import { openLiveSyncSettings, waitForVisibleObsidianDialogue, withObsidianPage } from "../runner/ui.ts";
|
||||||
captureObsidianDialogue,
|
|
||||||
openLiveSyncSettings,
|
|
||||||
waitForVisibleObsidianDialogue,
|
|
||||||
withObsidianPage,
|
|
||||||
} from "../runner/ui.ts";
|
|
||||||
import { createTemporaryVault } from "../runner/vault.ts";
|
import { createTemporaryVault } from "../runner/vault.ts";
|
||||||
import type { Locator, Page } from "playwright";
|
import type { Locator, Page } from "playwright";
|
||||||
|
|
||||||
@@ -161,7 +158,9 @@ function createFixture(self: SelfDeviceInfo): DeviceFixture {
|
|||||||
async function openFromRemoteSettings(page: Page): Promise<Locator> {
|
async function openFromRemoteSettings(page: Page): Promise<Locator> {
|
||||||
const settings = await openLiveSyncSettings(page, uiTimeoutMs);
|
const settings = await openLiveSyncSettings(page, uiTimeoutMs);
|
||||||
const remoteConfiguration = await settings.openPage("Remote Configuration");
|
const remoteConfiguration = await settings.openPage("Remote Configuration");
|
||||||
await remoteConfiguration.getByRole("button", { name: "Open", exact: true }).click({ timeout: uiTimeoutMs });
|
await remoteConfiguration
|
||||||
|
.getByRole("button", { name: "Review devices", exact: true })
|
||||||
|
.click({ timeout: uiTimeoutMs });
|
||||||
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
||||||
await dialogue.getByText(targetId, { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
await dialogue.getByText(targetId, { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
return dialogue;
|
return dialogue;
|
||||||
@@ -185,7 +184,7 @@ async function assertInitialDialogue(dialogue: Locator, self: SelfDeviceInfo, pa
|
|||||||
await selfCard.getByText("Locked out", { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
await selfCard.getByText("Locked out", { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
await selfCard.getByText("Not accepted", { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
await selfCard.getByText("Not accepted", { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
await selfCard
|
await selfCard
|
||||||
.getByText("This device is locked out.", { exact: false })
|
.getByText("This device has been locked out.", { exact: false })
|
||||||
.waitFor({ state: "visible", timeout: uiTimeoutMs });
|
.waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
await assertDetail(selfCard, "Platform", self.platform);
|
await assertDetail(selfCard, "Platform", self.platform);
|
||||||
await assertDetail(selfCard, "Plug-in version", self.pluginVersion);
|
await assertDetail(selfCard, "Plug-in version", self.pluginVersion);
|
||||||
@@ -339,6 +338,27 @@ async function dismissNotices(page: Page): Promise<void> {
|
|||||||
assertEqual(await notices.count(), 0, "Notices still obscured the dialogue screenshot.");
|
assertEqual(await notices.count(), 0, "Notices still obscured the dialogue screenshot.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function captureJoinedDevices(
|
||||||
|
port: number,
|
||||||
|
filename: string,
|
||||||
|
assertReady: (page: Page) => Promise<Locator>
|
||||||
|
): Promise<void> {
|
||||||
|
const outputDirectory = process.env.E2E_OBSIDIAN_DIAGNOSTICS_DIR ?? "/tmp/obsidian-livesync-e2e";
|
||||||
|
await mkdir(outputDirectory, { recursive: true });
|
||||||
|
await withObsidianPage(port, async (page) => {
|
||||||
|
try {
|
||||||
|
const dialogue = await assertReady(page);
|
||||||
|
await dialogue.page().screenshot({ path: join(outputDirectory, filename), fullPage: true });
|
||||||
|
} catch (error) {
|
||||||
|
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", 500).catch(() => undefined);
|
||||||
|
const failurePath = join(outputDirectory, filename.replace(/\.png$/u, ".failure.png"));
|
||||||
|
await (dialogue?.page() ?? page).screenshot({ path: failurePath, fullPage: true }).catch(() => undefined);
|
||||||
|
console.error(`Joined devices UI failure screenshot: ${failurePath}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
const binary = requireObsidianBinary();
|
const binary = requireObsidianBinary();
|
||||||
const cli = discoverObsidianCli();
|
const cli = discoverObsidianCli();
|
||||||
@@ -381,24 +401,35 @@ async function main(): Promise<void> {
|
|||||||
assert(Boolean(preparedMilestone._rev), "Remote preparation did not create a revisioned milestone document.");
|
assert(Boolean(preparedMilestone._rev), "Remote preparation did not create a revisioned milestone document.");
|
||||||
await putCouchDbDocument(couchDb, dbName, { ...fixture.milestone, _rev: preparedMilestone._rev });
|
await putCouchDbDocument(couchDb, dbName, { ...fixture.milestone, _rev: preparedMilestone._rev });
|
||||||
|
|
||||||
await captureObsidianDialogue(session.remoteDebuggingPort, "joined-devices-notice.png", async (page) => {
|
await captureJoinedDevices(session.remoteDebuggingPort, "joined-devices-notice.png", async (page) => {
|
||||||
await page.setViewportSize({ width: 1280, height: 1100 });
|
|
||||||
const dialogue = await openFromRemoteSettings(page);
|
const dialogue = await openFromRemoteSettings(page);
|
||||||
await assertInitialDialogue(dialogue, self, page);
|
const dialoguePage = dialogue.page();
|
||||||
await page
|
await dialoguePage.setViewportSize({ width: 1280, height: 1100 });
|
||||||
|
await assertInitialDialogue(dialogue, self, dialoguePage);
|
||||||
|
await dialoguePage
|
||||||
.locator(".notice")
|
.locator(".notice")
|
||||||
.getByText("Other joined devices last reported a newer plug-in version: 999.0.0.", { exact: true })
|
.getByText(
|
||||||
|
"A newer plug-in version was detected in other device records: 999.0.0. We recommend checking whether this device needs updating.",
|
||||||
|
{ exact: true }
|
||||||
|
)
|
||||||
.waitFor({ state: "visible", timeout: uiTimeoutMs });
|
.waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
|
return dialogue;
|
||||||
});
|
});
|
||||||
await captureObsidianDialogue(session.remoteDebuggingPort, "joined-devices-desktop.png", async (page) => {
|
await captureJoinedDevices(session.remoteDebuggingPort, "joined-devices-desktop.png", async (page) => {
|
||||||
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
||||||
await dialogue.getByRole("button", { name: "Close", exact: true }).click({ timeout: uiTimeoutMs });
|
await dialogue.getByRole("button", { name: "Close", exact: true }).click({ timeout: uiTimeoutMs });
|
||||||
await page.locator(".notice").getByRole("button", { name: "Joined devices", exact: true }).first().click();
|
await dialogue
|
||||||
|
.page()
|
||||||
|
.locator(".notice")
|
||||||
|
.getByRole("button", { name: "Review devices", exact: true })
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
const reopened = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
const reopened = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
||||||
await reopened.getByText(targetId, { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
await reopened.getByText(targetId, { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
await dismissNotices(page);
|
await dismissNotices(reopened.page());
|
||||||
|
return reopened;
|
||||||
});
|
});
|
||||||
await captureObsidianDialogue(
|
await captureJoinedDevices(
|
||||||
session.remoteDebuggingPort,
|
session.remoteDebuggingPort,
|
||||||
"joined-devices-forget-confirmation.png",
|
"joined-devices-forget-confirmation.png",
|
||||||
async (page) => {
|
async (page) => {
|
||||||
@@ -406,12 +437,14 @@ async function main(): Promise<void> {
|
|||||||
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
||||||
await showForgetConfirmation(dialogue);
|
await showForgetConfirmation(dialogue);
|
||||||
await deviceCard(dialogue, targetId).locator(".forget-confirmation").scrollIntoViewIfNeeded();
|
await deviceCard(dialogue, targetId).locator(".forget-confirmation").scrollIntoViewIfNeeded();
|
||||||
|
return dialogue;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await captureObsidianDialogue(session.remoteDebuggingPort, "joined-devices-after-forget.png", async (page) => {
|
await captureJoinedDevices(session.remoteDebuggingPort, "joined-devices-after-forget.png", async (page) => {
|
||||||
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
||||||
await confirmForgetTarget(dialogue);
|
await confirmForgetTarget(dialogue);
|
||||||
await dialogue.locator(".modal-content").evaluate((element) => element.scrollTo(0, 0));
|
await dialogue.locator(".modal-content").evaluate((element) => element.scrollTo(0, 0));
|
||||||
|
return dialogue;
|
||||||
});
|
});
|
||||||
await withObsidianPage(session.remoteDebuggingPort, async (page) => {
|
await withObsidianPage(session.remoteDebuggingPort, async (page) => {
|
||||||
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
||||||
@@ -423,7 +456,7 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
await setObsidianMobileTestMode(session.remoteDebuggingPort, true, uiTimeoutMs);
|
await setObsidianMobileTestMode(session.remoteDebuggingPort, true, uiTimeoutMs);
|
||||||
await waitForLiveSyncCoreReady(cli.binary, session.cliEnv);
|
await waitForLiveSyncCoreReady(cli.binary, session.cliEnv);
|
||||||
await captureObsidianDialogue(session.remoteDebuggingPort, "joined-devices-mobile.png", async (page) => {
|
await captureJoinedDevices(session.remoteDebuggingPort, "joined-devices-mobile.png", async (page) => {
|
||||||
const opened = await page.evaluate((id) => {
|
const opened = await page.evaluate((id) => {
|
||||||
const app = (
|
const app = (
|
||||||
window as typeof window & {
|
window as typeof window & {
|
||||||
@@ -435,13 +468,14 @@ async function main(): Promise<void> {
|
|||||||
assert(opened, "The Joined devices command did not open the dialogue.");
|
assert(opened, "The Joined devices command did not open the dialogue.");
|
||||||
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
const dialogue = await waitForVisibleObsidianDialogue(page, "Joined devices", uiTimeoutMs);
|
||||||
await dialogue.getByText(retainedId, { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
await dialogue.getByText(retainedId, { exact: true }).waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
await assertScrollableMobileDialogue(page, dialogue);
|
await assertScrollableMobileDialogue(dialogue.page(), dialogue);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
await dialogue.locator(".device-card").count(),
|
await dialogue.locator(".device-card").count(),
|
||||||
2,
|
2,
|
||||||
"The refreshed mobile list did not retain both remaining devices."
|
"The refreshed mobile list did not retain both remaining devices."
|
||||||
);
|
);
|
||||||
await dismissNotices(page);
|
await dismissNotices(dialogue.page());
|
||||||
|
return dialogue;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Joined devices scenario passed against CouchDB database ${dbName}.`);
|
console.log(`Joined devices scenario passed against CouchDB database ${dbName}.`);
|
||||||
|
|||||||
+2
-2
@@ -27,9 +27,9 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi
|
|||||||
|
|
||||||
#### Added
|
#### Added
|
||||||
|
|
||||||
- We can review participating devices through **Joined devices** in Remote Configuration, the command palette, or a version Notice.
|
- We can choose **Review devices** in Remote Configuration, the command palette, or a version Notice to review participating devices.
|
||||||
- Our device appears first, with its lockout state. Other records show the platform, plug-in version, app version, and last recorded connection time when available.
|
- Our device appears first, with its lockout state. Other records show the platform, plug-in version, app version, and last recorded connection time when available.
|
||||||
- Advisory Notices help us find devices which last reported newer or older plug-in versions.
|
- Advisory Notices highlight newer or older plug-in versions detected in device records and help us check which devices may need updating.
|
||||||
- We can forget records belonging to lost or retired devices while preserving the remote lock state and other devices.
|
- We can forget records belonging to lost or retired devices while preserving the remote lock state and other devices.
|
||||||
- Forgetting a record does not delete Vault files or revoke credentials. A returning device can register again on an unlocked remote.
|
- Forgetting a record does not delete Vault files or revoke credentials. A returning device can register again on an unlocked remote.
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user