@peers-app/peers-ui 0.25.2 → 0.25.3

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.
Files changed (177) hide show
  1. package/README.md +2 -1
  2. package/dist/components/markdown-editor/mention-node.d.ts +4 -0
  3. package/dist/components/markdown-editor/mention-node.js +4 -1
  4. package/dist/components/responsive-section-nav.d.ts +34 -0
  5. package/dist/components/responsive-section-nav.js +19 -0
  6. package/dist/components/responsive-section-nav.test.d.ts +1 -0
  7. package/dist/components/responsive-section-nav.test.js +45 -0
  8. package/dist/components/router.d.ts +2 -3
  9. package/dist/components/router.js +6 -9
  10. package/dist/globals-hash.test.d.ts +1 -0
  11. package/dist/globals-hash.test.js +16 -0
  12. package/dist/globals.d.ts +19 -0
  13. package/dist/globals.js +51 -12
  14. package/dist/hooks/use-host-contract.d.ts +18 -0
  15. package/dist/hooks/use-host-contract.js +53 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/peers-services-registration.d.ts +1 -5
  19. package/dist/peers-services-registration.js +6 -16
  20. package/dist/root-layout/root-layout-host.js +7 -4
  21. package/dist/root-layout/root-layout-host.test.js +3 -6
  22. package/dist/screens/account/account-screen.d.ts +1 -0
  23. package/dist/screens/account/account-screen.js +29 -53
  24. package/dist/screens/account/account-screen.test.d.ts +1 -0
  25. package/dist/screens/account/account-screen.test.js +47 -0
  26. package/dist/screens/contacts/contact-details.js +2 -23
  27. package/dist/screens/contacts/contact-list.d.ts +24 -1
  28. package/dist/screens/contacts/contact-list.js +128 -143
  29. package/dist/screens/contacts/contact-list.test.d.ts +1 -0
  30. package/dist/screens/contacts/contact-list.test.js +48 -0
  31. package/dist/screens/contacts/index.d.ts +0 -2
  32. package/dist/screens/contacts/index.js +0 -3
  33. package/dist/screens/device-pairing/device-pairing.js +17 -16
  34. package/dist/screens/device-pairing/device-pairing.test.js +4 -9
  35. package/dist/screens/device-pairing/identity-ipc.d.ts +11 -0
  36. package/dist/screens/device-pairing/identity-ipc.js +20 -0
  37. package/dist/screens/device-pairing/use-device-pairing-state.d.ts +2 -2
  38. package/dist/screens/device-pairing/use-device-pairing-state.js +7 -25
  39. package/dist/screens/devices/devices-list.d.ts +18 -0
  40. package/dist/screens/devices/devices-list.js +173 -0
  41. package/dist/screens/devices/devices-list.test.d.ts +1 -0
  42. package/dist/screens/devices/devices-list.test.js +53 -0
  43. package/dist/screens/devices/index.d.ts +1 -0
  44. package/dist/screens/devices/index.js +5 -0
  45. package/dist/screens/groups/group-details.d.ts +1 -0
  46. package/dist/screens/groups/group-details.js +4 -63
  47. package/dist/screens/groups/group-invite-panel.js +2 -2
  48. package/dist/screens/groups/group-list.d.ts +18 -1
  49. package/dist/screens/groups/group-list.js +107 -136
  50. package/dist/screens/groups/group-list.test.d.ts +1 -0
  51. package/dist/screens/groups/group-list.test.js +38 -0
  52. package/dist/screens/groups/group-members.d.ts +1 -0
  53. package/dist/screens/groups/group-members.js +2 -1
  54. package/dist/screens/groups/index.d.ts +0 -3
  55. package/dist/screens/groups/index.js +0 -4
  56. package/dist/screens/identity/identity-routes.d.ts +107 -0
  57. package/dist/screens/identity/identity-routes.js +245 -0
  58. package/dist/screens/identity/identity-routes.test.d.ts +1 -0
  59. package/dist/screens/identity/identity-routes.test.js +51 -0
  60. package/dist/screens/identity/identity-screen.d.ts +9 -0
  61. package/dist/screens/identity/identity-screen.js +143 -0
  62. package/dist/screens/identity/identity-screen.test.d.ts +1 -0
  63. package/dist/screens/identity/identity-screen.test.js +103 -0
  64. package/dist/screens/identity/index.d.ts +2 -0
  65. package/dist/screens/identity/index.js +6 -0
  66. package/dist/screens/identity/invite-contact-panel.d.ts +4 -0
  67. package/dist/screens/identity/invite-contact-panel.js +30 -0
  68. package/dist/screens/identity/profile-invite.d.ts +7 -0
  69. package/dist/screens/identity/profile-invite.js +62 -0
  70. package/dist/screens/invites/accept-invite.js +1 -1
  71. package/dist/screens/invites/activity-view.d.ts +24 -0
  72. package/dist/screens/invites/activity-view.js +64 -0
  73. package/dist/screens/invites/activity-view.test.d.ts +1 -0
  74. package/dist/screens/invites/activity-view.test.js +108 -0
  75. package/dist/screens/invites/index.d.ts +1 -2
  76. package/dist/screens/invites/index.js +1 -3
  77. package/dist/screens/invites/invite-capture.d.ts +0 -2
  78. package/dist/screens/invites/invite-capture.js +1 -3
  79. package/dist/screens/invites/invite-classify.test.d.ts +1 -0
  80. package/dist/screens/invites/invite-classify.test.js +98 -0
  81. package/dist/screens/invites/invites-ipc.d.ts +1 -1
  82. package/dist/screens/invites/invites-ipc.js +5 -45
  83. package/dist/screens/invites/pending-invites-list.d.ts +35 -16
  84. package/dist/screens/invites/pending-invites-list.js +69 -30
  85. package/dist/screens/network-viewer/network-viewer-ipc.js +4 -59
  86. package/dist/screens/search/global-search.d.ts +1 -0
  87. package/dist/screens/search/global-search.js +2 -1
  88. package/dist/screens/settings/settings-page.js +25 -58
  89. package/dist/screens/setup-user.js +5 -5
  90. package/dist/screens/tools/tool-access-ipc.d.ts +2 -0
  91. package/dist/screens/tools/tool-access-ipc.js +8 -55
  92. package/dist/screens/welcome-modal.js +3 -2
  93. package/dist/system-apps/identity.app.d.ts +3 -0
  94. package/dist/system-apps/identity.app.js +10 -0
  95. package/dist/system-apps/index.d.ts +1 -4
  96. package/dist/system-apps/index.js +5 -17
  97. package/dist/tabs-layout/network-status-indicator.js +10 -3
  98. package/dist/tabs-layout/network-status-indicator.test.js +15 -0
  99. package/dist/tabs-layout/tabs-layout.js +3 -1
  100. package/dist/tabs-layout/tabs-state.d.ts +79 -1
  101. package/dist/tabs-layout/tabs-state.js +256 -79
  102. package/dist/tabs-layout/tabs-state.test.d.ts +1 -0
  103. package/dist/tabs-layout/tabs-state.test.js +110 -0
  104. package/package.json +3 -3
  105. package/src/components/markdown-editor/mention-node.ts +5 -1
  106. package/src/components/responsive-section-nav.test.tsx +88 -0
  107. package/src/components/responsive-section-nav.tsx +92 -0
  108. package/src/components/router.tsx +6 -9
  109. package/src/globals-hash.test.ts +19 -0
  110. package/src/globals.tsx +53 -13
  111. package/src/hooks/use-host-contract.ts +75 -0
  112. package/src/index.tsx +1 -0
  113. package/src/peers-services-registration.ts +6 -17
  114. package/src/root-layout/root-layout-host.test.tsx +4 -7
  115. package/src/root-layout/root-layout-host.tsx +9 -11
  116. package/src/screens/account/account-screen.test.tsx +61 -0
  117. package/src/screens/account/account-screen.tsx +116 -95
  118. package/src/screens/contacts/contact-details.tsx +4 -26
  119. package/src/screens/contacts/contact-list.test.tsx +59 -0
  120. package/src/screens/contacts/contact-list.tsx +246 -260
  121. package/src/screens/contacts/index.ts +0 -4
  122. package/src/screens/device-pairing/device-pairing.test.tsx +7 -9
  123. package/src/screens/device-pairing/device-pairing.tsx +19 -23
  124. package/src/screens/device-pairing/identity-ipc.ts +25 -0
  125. package/src/screens/device-pairing/use-device-pairing-state.ts +10 -33
  126. package/src/screens/devices/devices-list.test.tsx +68 -0
  127. package/src/screens/devices/devices-list.tsx +361 -0
  128. package/src/screens/devices/index.ts +1 -0
  129. package/src/screens/groups/group-details.tsx +8 -142
  130. package/src/screens/groups/group-invite-panel.tsx +2 -7
  131. package/src/screens/groups/group-list.test.tsx +44 -0
  132. package/src/screens/groups/group-list.tsx +227 -288
  133. package/src/screens/groups/group-members.tsx +3 -2
  134. package/src/screens/groups/index.ts +0 -5
  135. package/src/screens/identity/identity-routes.test.ts +70 -0
  136. package/src/screens/identity/identity-routes.ts +301 -0
  137. package/src/screens/identity/identity-screen.test.tsx +138 -0
  138. package/src/screens/identity/identity-screen.tsx +282 -0
  139. package/src/screens/identity/index.ts +2 -0
  140. package/src/screens/identity/invite-contact-panel.tsx +45 -0
  141. package/src/screens/identity/profile-invite.tsx +93 -0
  142. package/src/screens/invites/accept-invite.tsx +1 -1
  143. package/src/screens/invites/activity-view.test.tsx +127 -0
  144. package/src/screens/invites/activity-view.tsx +162 -0
  145. package/src/screens/invites/index.ts +1 -4
  146. package/src/screens/invites/invite-capture.ts +0 -3
  147. package/src/screens/invites/invite-classify.test.ts +110 -0
  148. package/src/screens/invites/invites-ipc.ts +6 -55
  149. package/src/screens/invites/pending-invites-list.tsx +85 -61
  150. package/src/screens/network-viewer/network-viewer-ipc.ts +8 -82
  151. package/src/screens/search/global-search.tsx +2 -1
  152. package/src/screens/settings/settings-page.tsx +53 -126
  153. package/src/screens/setup-user.tsx +5 -6
  154. package/src/screens/tools/tool-access-ipc.ts +13 -74
  155. package/src/screens/welcome-modal.tsx +3 -3
  156. package/src/system-apps/identity.app.ts +9 -0
  157. package/src/system-apps/index.ts +3 -12
  158. package/src/tabs-layout/network-status-indicator.test.tsx +22 -0
  159. package/src/tabs-layout/network-status-indicator.tsx +31 -0
  160. package/src/tabs-layout/tabs-layout.tsx +7 -1
  161. package/src/tabs-layout/tabs-state.test.ts +152 -0
  162. package/src/tabs-layout/tabs-state.ts +300 -83
  163. package/dist/screens/invites/invites-screen.d.ts +0 -9
  164. package/dist/screens/invites/invites-screen.js +0 -36
  165. package/dist/system-apps/account.app.d.ts +0 -2
  166. package/dist/system-apps/account.app.js +0 -8
  167. package/dist/system-apps/contacts.app.d.ts +0 -2
  168. package/dist/system-apps/contacts.app.js +0 -9
  169. package/dist/system-apps/groups.app.d.ts +0 -2
  170. package/dist/system-apps/groups.app.js +0 -9
  171. package/dist/system-apps/invites.app.d.ts +0 -2
  172. package/dist/system-apps/invites.app.js +0 -9
  173. package/src/screens/invites/invites-screen.tsx +0 -78
  174. package/src/system-apps/account.app.ts +0 -7
  175. package/src/system-apps/contacts.app.ts +0 -8
  176. package/src/system-apps/groups.app.ts +0 -8
  177. package/src/system-apps/invites.app.ts +0 -8
@@ -1,6 +1,7 @@
1
1
  import type { INetworkConnectionInfo, INetworkStatusSummary } from "@peers-app/peers-sdk";
2
2
  import moment from "moment";
3
3
  import { useEffect, useId, useRef, useState } from "react";
4
+ import { IDENTITY_ACCEPT_PATH, IDENTITY_ACCOUNT_PATH } from "../screens/identity/identity-routes";
4
5
  import { getNetworkDiagnosticsConsumer } from "../screens/network-viewer/network-viewer-ipc";
5
6
  import { goToTabPath } from "./tabs-state";
6
7
  import { type INetworkStatusViewState, useNetworkStatus } from "./use-network-status";
@@ -311,12 +312,16 @@ function NetworkStatusDetails({
311
312
  status,
312
313
  unavailable,
313
314
  connectionsLoadState,
315
+ onUseInvite,
316
+ onOpenIdentity,
314
317
  onOpenViewer,
315
318
  }: {
316
319
  detailsId: string;
317
320
  status?: INetworkStatusSummary;
318
321
  unavailable: boolean;
319
322
  connectionsLoadState: ConnectionsLoadState;
323
+ onUseInvite: () => void;
324
+ onOpenIdentity: () => void;
320
325
  onOpenViewer: () => void;
321
326
  }) {
322
327
  return (
@@ -328,6 +333,24 @@ function NetworkStatusDetails({
328
333
  style={{ top: "100%", width: "320px", zIndex: 1100 }}
329
334
  >
330
335
  <div className="card-body p-3">
336
+ <div className="d-flex gap-2 mb-3">
337
+ <button
338
+ type="button"
339
+ className="btn btn-sm btn-outline-primary flex-fill"
340
+ onClick={onUseInvite}
341
+ >
342
+ <i className="bi bi-qr-code me-1" aria-hidden="true" />
343
+ Use Invite
344
+ </button>
345
+ <button
346
+ type="button"
347
+ className="btn btn-sm btn-outline-primary flex-fill"
348
+ onClick={onOpenIdentity}
349
+ >
350
+ <i className="bi bi-person-badge me-1" aria-hidden="true" />
351
+ Identity
352
+ </button>
353
+ </div>
331
354
  <h6 className="card-title mb-2">Network status</h6>
332
355
  {unavailable || !status ? (
333
356
  <p className="small text-muted mb-3">
@@ -559,6 +582,14 @@ export function NetworkStatusIndicator({ compact = false }: { compact?: boolean
559
582
  status={viewState.status}
560
583
  unavailable={viewState.availability !== "ready"}
561
584
  connectionsLoadState={connectionsLoadState}
585
+ onUseInvite={() => {
586
+ setOpen(false);
587
+ goToTabPath(IDENTITY_ACCEPT_PATH);
588
+ }}
589
+ onOpenIdentity={() => {
590
+ setOpen(false);
591
+ goToTabPath(IDENTITY_ACCOUNT_PATH);
592
+ }}
562
593
  onOpenViewer={() => {
563
594
  setOpen(false);
564
595
  goToTabPath("network-viewer");
@@ -10,6 +10,7 @@ import { GroupSwitcher } from "../components/group-switcher";
10
10
  import { Router } from "../components/router";
11
11
  import { isDesktop } from "../globals";
12
12
  import { useObservable } from "../hooks";
13
+ import { isIdentityPath } from "../screens/identity/identity-routes";
13
14
  import { colorMode } from "../screens/settings/color-mode-dropdown";
14
15
  import { systemPackage } from "../system-apps";
15
16
  import { allPackages } from "../ui-router/routes-loader";
@@ -546,9 +547,14 @@ const TabContentRenderer = React.memo(
546
547
  // System apps use the main Router (which reads from mainContentPath)
547
548
  // Package apps use UIRouter with explicit path
548
549
  const isSystemApp = tab.packageId === "system-apps";
550
+ const identityApp = isSystemApp && isIdentityPath(tab.path);
549
551
 
550
552
  return (
551
- <div className={`h-100 overflow-auto ${isMobile ? "p-2" : "p-3"}`}>
553
+ <div
554
+ className={`h-100 ${identityApp ? "overflow-hidden" : "overflow-auto"} ${
555
+ identityApp ? "" : isMobile ? "p-2" : "p-3"
556
+ }`}
557
+ >
552
558
  {isSystemApp ? (
553
559
  <Router path={tab.path} />
554
560
  ) : (
@@ -0,0 +1,152 @@
1
+ jest.mock("../globals", () => {
2
+ const { observable } =
3
+ jest.requireActual<typeof import("@peers-app/peers-sdk")>("@peers-app/peers-sdk");
4
+ return { _mainContentPath: observable("") };
5
+ });
6
+
7
+ jest.mock("../ui-router/routes-loader", () => {
8
+ const { observable } =
9
+ jest.requireActual<typeof import("@peers-app/peers-sdk")>("@peers-app/peers-sdk");
10
+ return { allPackages: observable([]) };
11
+ });
12
+
13
+ jest.mock("@peers-app/peers-sdk", () => {
14
+ const actual = jest.requireActual<typeof import("@peers-app/peers-sdk")>("@peers-app/peers-sdk");
15
+ let nextId = 0;
16
+ const deviceVars = new Map<string, ReturnType<typeof actual.observable>>();
17
+ const userVars = new Map<string, ReturnType<typeof actual.observable>>();
18
+
19
+ function stored<T>(
20
+ map: Map<string, ReturnType<typeof actual.observable>>,
21
+ id: string,
22
+ initial: T,
23
+ ) {
24
+ const existing = map.get(id);
25
+ if (existing) {
26
+ return existing;
27
+ }
28
+ const value = actual.observable(initial);
29
+ Object.assign(value, { loadingPromise: Promise.resolve() });
30
+ map.set(id, value);
31
+ return value;
32
+ }
33
+
34
+ return {
35
+ ...actual,
36
+ newid: () => `tab${++nextId}`,
37
+ groupDeviceVar: (id: string, options?: { defaultValue?: unknown }) =>
38
+ stored(deviceVars, id, options?.defaultValue),
39
+ groupUserVar: (id: string, options?: { defaultValue?: unknown }) =>
40
+ stored(userVars, id, options?.defaultValue),
41
+ };
42
+ });
43
+
44
+ import { _mainContentPath } from "../globals";
45
+ import {
46
+ activeTabId,
47
+ activeTabs,
48
+ goToTabPath,
49
+ launcherApp,
50
+ migrateRecentPath,
51
+ navigateTo,
52
+ recentlyUsedApps,
53
+ restoreNavigationState,
54
+ type TabState,
55
+ } from "./tabs-state";
56
+
57
+ const PERSON_ID = "abcdefghijklmnopqrstuvwxy";
58
+
59
+ function tab(partial: Partial<TabState> & Pick<TabState, "tabId" | "path">): TabState {
60
+ return {
61
+ packageId: "system-apps",
62
+ title: partial.path,
63
+ iconClassName: "bi-circle",
64
+ ...partial,
65
+ };
66
+ }
67
+
68
+ describe("restoreNavigationState", () => {
69
+ it("merges legacy identity tabs into the active Identity tab", () => {
70
+ const restored = restoreNavigationState(
71
+ [
72
+ launcherApp,
73
+ tab({ tabId: "people", path: "contacts", title: "Contacts" }),
74
+ tab({ tabId: "groups", path: `groups/${PERSON_ID}`, title: "Groups" }),
75
+ tab({ tabId: "devices", path: "devices?action=pair", title: "Devices" }),
76
+ tab({ tabId: "account", path: "account", title: "Account" }),
77
+ ],
78
+ "groups",
79
+ );
80
+
81
+ expect(restored.tabs.map((entry) => entry.path)).toEqual(["", `identity/groups/${PERSON_ID}`]);
82
+ expect(restored.activeId).toBe("groups");
83
+ expect(restored.tabs[1]?.title).toBe("Identity");
84
+ expect(migrateRecentPath("contacts")).toBe("identity");
85
+ expect(migrateRecentPath("invites/accept")).toBe("identity");
86
+ expect(migrateRecentPath("account")).toBe("identity");
87
+ });
88
+ });
89
+
90
+ describe("navigateTo", () => {
91
+ beforeEach(async () => {
92
+ await activeTabs.loadingPromise;
93
+ activeTabs([launcherApp, tab({ tabId: "threads", path: "threads", title: "Threads" })]);
94
+ activeTabId("threads");
95
+ recentlyUsedApps([]);
96
+ _mainContentPath("threads");
97
+ });
98
+
99
+ it("updates the same app in place and preserves the query", () => {
100
+ navigateTo("threads/thread-1?draft=1");
101
+
102
+ expect(activeTabs()).toHaveLength(2);
103
+ expect(activeTabs().find((entry) => entry.tabId === "threads")?.path).toBe(
104
+ "threads/thread-1?draft=1",
105
+ );
106
+ expect(activeTabId()).toBe("threads");
107
+ expect(_mainContentPath()).toBe("threads/thread-1?draft=1");
108
+ });
109
+
110
+ it("opens another app without duplicating an existing tab", () => {
111
+ navigateTo(`contacts/${PERSON_ID}`);
112
+ const identityId = activeTabId();
113
+ activeTabId("threads");
114
+ navigateTo("identity/devices?foo=bar");
115
+
116
+ const identityTabs = activeTabs().filter((entry) => entry.path.startsWith("identity"));
117
+ expect(identityTabs).toHaveLength(1);
118
+ expect(identityTabs[0]?.path).toBe("identity/devices?foo=bar");
119
+ expect(activeTabId()).toBe(identityId);
120
+ expect(activeTabs().some((entry) => entry.tabId === "threads")).toBe(true);
121
+ });
122
+
123
+ it("opens the account alias inside the Identity tab", () => {
124
+ navigateTo("account");
125
+ const identityId = activeTabId();
126
+
127
+ expect(_mainContentPath()).toBe("identity/account");
128
+ navigateTo("identity/people");
129
+
130
+ expect(activeTabId()).toBe(identityId);
131
+ expect(activeTabs().filter((entry) => entry.path.startsWith("identity"))).toHaveLength(1);
132
+ expect(_mainContentPath()).toBe("identity/people");
133
+ });
134
+
135
+ it("rewrites invite acceptance onto the open Identity tab", () => {
136
+ navigateTo("identity/people");
137
+ const identityId = activeTabId();
138
+ navigateTo("invites/accept");
139
+
140
+ expect(activeTabId()).toBe(identityId);
141
+ expect(activeTabs().filter((entry) => entry.path.startsWith("identity"))).toHaveLength(1);
142
+ expect(_mainContentPath()).toBe("identity/activity/accept");
143
+ });
144
+
145
+ it("keeps the current section when the app root is opened again", () => {
146
+ navigateTo("identity/devices/pair");
147
+ goToTabPath("identity");
148
+
149
+ expect(_mainContentPath()).toBe("identity/devices/pair");
150
+ expect(recentlyUsedApps()).toEqual(["identity"]);
151
+ });
152
+ });