@peers-app/peers-ui 0.27.4 → 0.27.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,5 +2,9 @@ interface IGroupDetailsModalProps {
2
2
  groupId: string;
3
3
  onClose: () => void;
4
4
  }
5
+ /**
6
+ * Network Viewer dialog for one personal or group context: its details and members, and the
7
+ * Sync now and Do full sync actions.
8
+ */
5
9
  export declare function GroupDetailsModal({ groupId, onClose }: IGroupDetailsModalProps): import("react/jsx-runtime").JSX.Element;
6
10
  export {};
@@ -7,26 +7,22 @@ const react_1 = require("react");
7
7
  const loading_indicator_1 = require("../../components/loading-indicator");
8
8
  const trust_level_badge_1 = require("../../components/trust-level-badge");
9
9
  const network_viewer_ipc_1 = require("./network-viewer-ipc");
10
+ /**
11
+ * Network Viewer dialog for one personal or group context: its details and members, and the
12
+ * Sync now and Do full sync actions.
13
+ */
10
14
  function GroupDetailsModal({ groupId, onClose }) {
11
15
  const [group, setGroup] = (0, react_1.useState)(null);
12
- const [syncInfo, setSyncInfo] = (0, react_1.useState)(null);
13
16
  const [loading, setLoading] = (0, react_1.useState)(true);
14
17
  const [notFound, setNotFound] = (0, react_1.useState)(false);
15
- const [downloading, setDownloading] = (0, react_1.useState)(false);
16
- const [downloadProgress, setDownloadProgress] = (0, react_1.useState)(null);
17
- const [downloadResult, setDownloadResult] = (0, react_1.useState)(null);
18
18
  const [fullSyncing, setFullSyncing] = (0, react_1.useState)(false);
19
19
  const [fullSyncResult, setFullSyncResult] = (0, react_1.useState)(null);
20
20
  const [syncingNow, setSyncingNow] = (0, react_1.useState)(false);
21
21
  const [syncNowResult, setSyncNowResult] = (0, react_1.useState)(null);
22
- const platformApi = (0, network_viewer_ipc_1.getNetworkViewerPlatformApi)();
23
22
  const loadData = (0, react_1.useCallback)(async () => {
24
23
  try {
25
24
  const diagnostics = await (0, network_viewer_ipc_1.getNetworkDiagnosticsConsumer)();
26
- const [{ group: groupDetails }, { groups }] = await Promise.all([
27
- diagnostics.tools.getGroupDetails({ groupId }),
28
- diagnostics.tools.getGroupSyncStatus({ groupId }),
29
- ]);
25
+ const { group: groupDetails } = await diagnostics.tools.getGroupDetails({ groupId });
30
26
  if (!groupDetails) {
31
27
  setNotFound(true);
32
28
  }
@@ -34,9 +30,6 @@ function GroupDetailsModal({ groupId, onClose }) {
34
30
  setGroup(groupDetails);
35
31
  setNotFound(false);
36
32
  }
37
- if (groups.length > 0) {
38
- setSyncInfo(groups[0]);
39
- }
40
33
  }
41
34
  catch (error) {
42
35
  console.error("Error loading group details:", error);
@@ -46,37 +39,6 @@ function GroupDetailsModal({ groupId, onClose }) {
46
39
  setLoading(false);
47
40
  }
48
41
  }, [groupId]);
49
- const handleDownloadDatabase = async (deviceId) => {
50
- try {
51
- setDownloading(true);
52
- setDownloadProgress("Preparing download...");
53
- setDownloadResult(null);
54
- if (!platformApi?.downloadRemoteDatabase) {
55
- setDownloadResult({
56
- success: false,
57
- message: "Download API not available",
58
- });
59
- return;
60
- }
61
- setDownloadProgress("Downloading database...");
62
- const result = (await platformApi.downloadRemoteDatabase(groupId, deviceId));
63
- setDownloadResult(result);
64
- if (result.success) {
65
- setDownloadProgress(null);
66
- }
67
- }
68
- catch (error) {
69
- console.error("Error downloading database:", error);
70
- setDownloadResult({
71
- success: false,
72
- message: error instanceof Error ? error.message : "Download failed",
73
- });
74
- }
75
- finally {
76
- setDownloading(false);
77
- setDownloadProgress(null);
78
- }
79
- };
80
42
  const handleSyncNow = async () => {
81
43
  setSyncingNow(true);
82
44
  setSyncNowResult(null);
@@ -131,7 +93,5 @@ function GroupDetailsModal({ groupId, onClose }) {
131
93
  overflowY: "auto",
132
94
  paddingTop: "2rem",
133
95
  paddingBottom: "2rem",
134
- }, onClick: onClose, children: (0, jsx_runtime_1.jsxs)("div", { className: "card", style: { maxWidth: "900px", width: "90%", maxHeight: "90vh" }, onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("div", { className: "card-header d-flex justify-content-between align-items-center", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h5", { className: "mb-0", children: loading ? "Loading..." : group?.groupName || "Group Details" }), group && ((0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: (0, jsx_runtime_1.jsx)("code", { children: group.groupId }) }))] }), (0, jsx_runtime_1.jsx)("button", { className: "btn-close", onClick: onClose, "aria-label": "Close" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "card-body", style: { overflowY: "auto", maxHeight: "calc(90vh - 120px)" }, children: [loading && (0, jsx_runtime_1.jsx)(loading_indicator_1.LoadingIndicator, {}), !loading && (notFound || !group) && ((0, jsx_runtime_1.jsx)("div", { className: "alert alert-warning", children: "Group not found." })), !loading && group && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("h6", { className: "border-bottom pb-2 mb-3", children: "Group Information" }), (0, jsx_runtime_1.jsxs)("div", { className: "row", children: [(0, jsx_runtime_1.jsxs)("div", { className: "col-md-6 mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Group ID:" }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: group.groupId })] }), (0, jsx_runtime_1.jsxs)("div", { className: "col-md-6 mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Group Name:" }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("span", { children: group.groupName })] }), (0, jsx_runtime_1.jsxs)("div", { className: "col-md-6 mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Founder:" }), (0, jsx_runtime_1.jsx)("br", {}), group.founderUserName ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { children: group.founderUserName }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: group.founderUserId })] })) : ((0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: group.founderUserId }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "col-md-6 mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Total Members:" }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("span", { className: "h5", children: group.members.length })] })] })] }), group.members.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("h6", { className: "border-bottom pb-2 mb-3", children: ["Members (", group.members.length, ")"] }), (0, jsx_runtime_1.jsx)("div", { className: "table-responsive", children: (0, jsx_runtime_1.jsxs)("table", { className: "table table-sm table-hover", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "User" }), (0, jsx_runtime_1.jsx)("th", { children: "User ID" }), (0, jsx_runtime_1.jsx)("th", { children: "Role" }), (0, jsx_runtime_1.jsx)("th", { children: "Trust Level" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: group.members.map((member) => ((0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: member.userName || (0, jsx_runtime_1.jsx)("span", { className: "text-muted", children: "-" }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: member.userId }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("span", { className: "badge bg-primary", children: member.role }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)(trust_level_badge_1.TrustLevelBadge, { level: member.trustLevel || peers_sdk_1.TrustLevel.Unknown }) })] }, member.userId))) })] }) })] })), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("h6", { className: "border-bottom pb-2 mb-3", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-arrow-repeat me-2" }), "Synchronization"] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-muted small mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Sync now" }), " notifies connected peers that this context may have changes. ", (0, jsx_runtime_1.jsx)("strong", { children: "Do full sync" }), " resets change tracking for this context and rebuilds synchronization state; that may transfer substantial data and should be used only to repair suspected drift."] }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex flex-wrap gap-2", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-sm btn-outline-primary", onClick: () => void handleSyncNow(), disabled: syncingNow || fullSyncing, children: syncingNow ? "Sending sync notification..." : "Sync now" }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-sm btn-outline-warning", onClick: () => void handleFullSync(), disabled: fullSyncing || syncingNow, children: fullSyncing ? "Starting full sync..." : "Do full sync" })] }), syncNowResult && ((0, jsx_runtime_1.jsx)("div", { className: `alert mt-3 mb-0 ${syncNowResult.success ? "alert-success" : "alert-danger"}`, role: "status", children: syncNowResult.message })), fullSyncResult && ((0, jsx_runtime_1.jsx)("div", { className: `alert mt-3 mb-0 ${fullSyncResult.success ? "alert-success" : "alert-danger"}`, role: "status", children: fullSyncResult.message }))] }), platformApi?.downloadRemoteDatabase ? ((0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("h6", { className: "border-bottom pb-2 mb-3", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-download me-2" }), "Download Database (Fast Sync)"] }), (0, jsx_runtime_1.jsx)("p", { className: "text-muted small mb-3", children: "Download the entire database from a connected device. This is useful for fast initial sync on a new device. The database will be saved as a backup file." }), syncInfo && syncInfo.connectedDevices.length > 0 ? ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "form-label", children: "Select a connected device:" }), (0, jsx_runtime_1.jsx)("div", { className: "d-flex flex-wrap gap-2", children: syncInfo.connectedDevices.map((device) => ((0, jsx_runtime_1.jsx)("button", { className: "btn btn-outline-primary btn-sm", onClick: () => handleDownloadDatabase(device.deviceId), disabled: downloading, children: downloading ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "spinner-border spinner-border-sm me-1", role: "status", "aria-hidden": "true" }), "Downloading..."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-cloud-download me-1" }), device.deviceId.substring(0, 8), "...", (0, jsx_runtime_1.jsxs)("span", { className: "badge bg-secondary ms-1", children: [Math.round(device.latencyMs), "ms"] })] })) }, device.deviceId))) })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "alert alert-info mb-0", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-info-circle me-2" }), "No devices are currently connected to this group. Connect to a device first to download its database."] })), downloadProgress && ((0, jsx_runtime_1.jsx)("div", { className: "mt-3", children: (0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "spinner-border spinner-border-sm me-2", role: "status", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { children: downloadProgress })] }) })), downloadResult && ((0, jsx_runtime_1.jsx)("div", { className: `alert mt-3 mb-0 ${downloadResult.success ? "alert-success" : "alert-danger"}`, children: downloadResult.success ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-check-circle me-2" }), (0, jsx_runtime_1.jsx)("strong", { children: "Download complete!" }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsxs)("small", { children: ["File: ", (0, jsx_runtime_1.jsx)("code", { children: downloadResult.filePath }), (0, jsx_runtime_1.jsx)("br", {}), "Size:", " ", downloadResult.size
135
- ? `${(downloadResult.size / (1024 * 1024)).toFixed(2)} MB`
136
- : "Unknown"] })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-exclamation-circle me-2" }), (0, jsx_runtime_1.jsx)("strong", { children: "Download failed:" }), " ", downloadResult.message] })) }))] })) : ((0, jsx_runtime_1.jsx)("div", { className: "alert alert-info mb-4", children: "Raw database download is available only in the Electron desktop app." }))] }))] })] }) }));
96
+ }, onClick: onClose, children: (0, jsx_runtime_1.jsxs)("div", { className: "card", style: { maxWidth: "900px", width: "90%", maxHeight: "90vh" }, onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("div", { className: "card-header d-flex justify-content-between align-items-center", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h5", { className: "mb-0", children: loading ? "Loading..." : group?.groupName || "Group Details" }), group && ((0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: (0, jsx_runtime_1.jsx)("code", { children: group.groupId }) }))] }), (0, jsx_runtime_1.jsx)("button", { className: "btn-close", onClick: onClose, "aria-label": "Close" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "card-body", style: { overflowY: "auto", maxHeight: "calc(90vh - 120px)" }, children: [loading && (0, jsx_runtime_1.jsx)(loading_indicator_1.LoadingIndicator, {}), !loading && (notFound || !group) && ((0, jsx_runtime_1.jsx)("div", { className: "alert alert-warning", children: "Group not found." })), !loading && group && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("h6", { className: "border-bottom pb-2 mb-3", children: "Group Information" }), (0, jsx_runtime_1.jsxs)("div", { className: "row", children: [(0, jsx_runtime_1.jsxs)("div", { className: "col-md-6 mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Group ID:" }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: group.groupId })] }), (0, jsx_runtime_1.jsxs)("div", { className: "col-md-6 mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Group Name:" }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("span", { children: group.groupName })] }), (0, jsx_runtime_1.jsxs)("div", { className: "col-md-6 mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Founder:" }), (0, jsx_runtime_1.jsx)("br", {}), group.founderUserName ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { children: group.founderUserName }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: group.founderUserId })] })) : ((0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: group.founderUserId }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "col-md-6 mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Total Members:" }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("span", { className: "h5", children: group.members.length })] })] })] }), group.members.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("h6", { className: "border-bottom pb-2 mb-3", children: ["Members (", group.members.length, ")"] }), (0, jsx_runtime_1.jsx)("div", { className: "table-responsive", children: (0, jsx_runtime_1.jsxs)("table", { className: "table table-sm table-hover", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "User" }), (0, jsx_runtime_1.jsx)("th", { children: "User ID" }), (0, jsx_runtime_1.jsx)("th", { children: "Role" }), (0, jsx_runtime_1.jsx)("th", { children: "Trust Level" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: group.members.map((member) => ((0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: member.userName || (0, jsx_runtime_1.jsx)("span", { className: "text-muted", children: "-" }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: member.userId }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("span", { className: "badge bg-primary", children: member.role }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)(trust_level_badge_1.TrustLevelBadge, { level: member.trustLevel || peers_sdk_1.TrustLevel.Unknown }) })] }, member.userId))) })] }) })] })), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("h6", { className: "border-bottom pb-2 mb-3", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-arrow-repeat me-2" }), "Synchronization"] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-muted small mb-3", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Sync now" }), " notifies connected peers that this context may have changes. ", (0, jsx_runtime_1.jsx)("strong", { children: "Do full sync" }), " resets change tracking for this context and rebuilds synchronization state; that may transfer substantial data and should be used only to repair suspected drift."] }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex flex-wrap gap-2", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-sm btn-outline-primary", onClick: () => void handleSyncNow(), disabled: syncingNow || fullSyncing, children: syncingNow ? "Sending sync notification..." : "Sync now" }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-sm btn-outline-warning", onClick: () => void handleFullSync(), disabled: fullSyncing || syncingNow, children: fullSyncing ? "Starting full sync..." : "Do full sync" })] }), syncNowResult && ((0, jsx_runtime_1.jsx)("div", { className: `alert mt-3 mb-0 ${syncNowResult.success ? "alert-success" : "alert-danger"}`, role: "status", children: syncNowResult.message })), fullSyncResult && ((0, jsx_runtime_1.jsx)("div", { className: `alert mt-3 mb-0 ${fullSyncResult.success ? "alert-success" : "alert-danger"}`, role: "status", children: fullSyncResult.message }))] })] }))] })] }) }));
137
97
  }
@@ -1,7 +1,6 @@
1
1
  import { type INetworkControlConsumer, type INetworkDiagnosticsConsumer, type ISyncCheckConsumer } from "@peers-app/peers-sdk";
2
2
  /** Electron-only capabilities intentionally excluded from portable network contracts. */
3
3
  export type NetworkViewerPlatformApi = {
4
- downloadRemoteDatabase?: (groupId: string, deviceId: string) => Promise<unknown>;
5
4
  runDiagnostics: () => Promise<unknown>;
6
5
  };
7
6
  /** Return a typed diagnostics consumer bound to the current local host transport. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-ui",
3
- "version": "0.27.4",
3
+ "version": "0.27.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-ui.git"
@@ -36,7 +36,7 @@
36
36
  "lint:fix": "biome check --write ."
37
37
  },
38
38
  "peerDependencies": {
39
- "@peers-app/peers-sdk": "^0.27.4",
39
+ "@peers-app/peers-sdk": "^0.27.5",
40
40
  "bootstrap": "^5.3.3",
41
41
  "react": "^18.0.0",
42
42
  "react-dom": "^18.0.0"
@@ -47,7 +47,7 @@
47
47
  "@babel/preset-react": "^7.24.1",
48
48
  "@babel/preset-typescript": "^7.27.1",
49
49
  "@biomejs/biome": "2.3.11",
50
- "@peers-app/peers-sdk": "0.27.4",
50
+ "@peers-app/peers-sdk": "0.27.5",
51
51
  "@testing-library/dom": "^10.4.0",
52
52
  "@testing-library/jest-dom": "^6.6.3",
53
53
  "@testing-library/react": "^16.3.0",