@peers-app/peers-ui 0.25.2 → 0.25.4
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.
- package/README.md +2 -1
- package/dist/components/markdown-editor/mention-node.d.ts +4 -0
- package/dist/components/markdown-editor/mention-node.js +4 -1
- package/dist/components/responsive-section-nav.d.ts +34 -0
- package/dist/components/responsive-section-nav.js +19 -0
- package/dist/components/responsive-section-nav.test.d.ts +1 -0
- package/dist/components/responsive-section-nav.test.js +45 -0
- package/dist/components/router.d.ts +2 -3
- package/dist/components/router.js +6 -9
- package/dist/globals-hash.test.d.ts +1 -0
- package/dist/globals-hash.test.js +16 -0
- package/dist/globals.d.ts +19 -0
- package/dist/globals.js +51 -12
- package/dist/hooks/use-host-contract.d.ts +18 -0
- package/dist/hooks/use-host-contract.js +53 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/peers-services-registration.d.ts +1 -5
- package/dist/peers-services-registration.js +6 -16
- package/dist/root-layout/root-layout-host.js +7 -4
- package/dist/root-layout/root-layout-host.test.js +3 -6
- package/dist/screens/account/account-screen.d.ts +1 -0
- package/dist/screens/account/account-screen.js +29 -53
- package/dist/screens/account/account-screen.test.d.ts +1 -0
- package/dist/screens/account/account-screen.test.js +47 -0
- package/dist/screens/contacts/contact-details.js +2 -23
- package/dist/screens/contacts/contact-list.d.ts +24 -1
- package/dist/screens/contacts/contact-list.js +128 -143
- package/dist/screens/contacts/contact-list.test.d.ts +1 -0
- package/dist/screens/contacts/contact-list.test.js +48 -0
- package/dist/screens/contacts/index.d.ts +0 -2
- package/dist/screens/contacts/index.js +0 -3
- package/dist/screens/device-pairing/device-pairing.js +17 -16
- package/dist/screens/device-pairing/device-pairing.test.js +4 -9
- package/dist/screens/device-pairing/identity-ipc.d.ts +11 -0
- package/dist/screens/device-pairing/identity-ipc.js +20 -0
- package/dist/screens/device-pairing/use-device-pairing-state.d.ts +2 -2
- package/dist/screens/device-pairing/use-device-pairing-state.js +7 -25
- package/dist/screens/devices/devices-list.d.ts +18 -0
- package/dist/screens/devices/devices-list.js +173 -0
- package/dist/screens/devices/devices-list.test.d.ts +1 -0
- package/dist/screens/devices/devices-list.test.js +53 -0
- package/dist/screens/devices/index.d.ts +1 -0
- package/dist/screens/devices/index.js +5 -0
- package/dist/screens/groups/group-details.d.ts +1 -0
- package/dist/screens/groups/group-details.js +4 -63
- package/dist/screens/groups/group-invite-panel.js +2 -2
- package/dist/screens/groups/group-list.d.ts +18 -1
- package/dist/screens/groups/group-list.js +107 -136
- package/dist/screens/groups/group-list.test.d.ts +1 -0
- package/dist/screens/groups/group-list.test.js +38 -0
- package/dist/screens/groups/group-members.d.ts +1 -0
- package/dist/screens/groups/group-members.js +2 -1
- package/dist/screens/groups/index.d.ts +0 -3
- package/dist/screens/groups/index.js +0 -4
- package/dist/screens/identity/identity-routes.d.ts +107 -0
- package/dist/screens/identity/identity-routes.js +245 -0
- package/dist/screens/identity/identity-routes.test.d.ts +1 -0
- package/dist/screens/identity/identity-routes.test.js +51 -0
- package/dist/screens/identity/identity-screen.d.ts +9 -0
- package/dist/screens/identity/identity-screen.js +143 -0
- package/dist/screens/identity/identity-screen.test.d.ts +1 -0
- package/dist/screens/identity/identity-screen.test.js +103 -0
- package/dist/screens/identity/index.d.ts +2 -0
- package/dist/screens/identity/index.js +6 -0
- package/dist/screens/identity/invite-contact-panel.d.ts +4 -0
- package/dist/screens/identity/invite-contact-panel.js +30 -0
- package/dist/screens/identity/profile-invite.d.ts +7 -0
- package/dist/screens/identity/profile-invite.js +62 -0
- package/dist/screens/invites/accept-invite.js +44 -2
- package/dist/screens/invites/accept-invite.test.d.ts +1 -0
- package/dist/screens/invites/accept-invite.test.js +103 -0
- package/dist/screens/invites/activity-view.d.ts +24 -0
- package/dist/screens/invites/activity-view.js +64 -0
- package/dist/screens/invites/activity-view.test.d.ts +1 -0
- package/dist/screens/invites/activity-view.test.js +108 -0
- package/dist/screens/invites/index.d.ts +1 -2
- package/dist/screens/invites/index.js +1 -3
- package/dist/screens/invites/invite-capture.d.ts +0 -2
- package/dist/screens/invites/invite-capture.js +1 -3
- package/dist/screens/invites/invite-classify.test.d.ts +1 -0
- package/dist/screens/invites/invite-classify.test.js +115 -0
- package/dist/screens/invites/invites-ipc.d.ts +1 -1
- package/dist/screens/invites/invites-ipc.js +5 -45
- package/dist/screens/invites/pending-invites-list.d.ts +35 -16
- package/dist/screens/invites/pending-invites-list.js +75 -30
- package/dist/screens/network-viewer/network-viewer-ipc.js +4 -59
- package/dist/screens/search/global-search.d.ts +1 -0
- package/dist/screens/search/global-search.js +2 -1
- package/dist/screens/settings/settings-page.js +25 -58
- package/dist/screens/setup-user.js +5 -5
- package/dist/screens/tools/tool-access-ipc.d.ts +2 -0
- package/dist/screens/tools/tool-access-ipc.js +8 -55
- package/dist/screens/welcome-modal.js +3 -2
- package/dist/system-apps/identity.app.d.ts +3 -0
- package/dist/system-apps/identity.app.js +10 -0
- package/dist/system-apps/index.d.ts +1 -4
- package/dist/system-apps/index.js +5 -17
- package/dist/tabs-layout/network-status-indicator.js +10 -3
- package/dist/tabs-layout/network-status-indicator.test.js +15 -0
- package/dist/tabs-layout/tabs-layout.js +3 -1
- package/dist/tabs-layout/tabs-state.d.ts +79 -1
- package/dist/tabs-layout/tabs-state.js +256 -79
- package/dist/tabs-layout/tabs-state.test.d.ts +1 -0
- package/dist/tabs-layout/tabs-state.test.js +110 -0
- package/package.json +3 -3
- package/src/components/markdown-editor/mention-node.ts +5 -1
- package/src/components/responsive-section-nav.test.tsx +88 -0
- package/src/components/responsive-section-nav.tsx +92 -0
- package/src/components/router.tsx +6 -9
- package/src/globals-hash.test.ts +19 -0
- package/src/globals.tsx +53 -13
- package/src/hooks/use-host-contract.ts +75 -0
- package/src/index.tsx +1 -0
- package/src/peers-services-registration.ts +6 -17
- package/src/root-layout/root-layout-host.test.tsx +4 -7
- package/src/root-layout/root-layout-host.tsx +9 -11
- package/src/screens/account/account-screen.test.tsx +61 -0
- package/src/screens/account/account-screen.tsx +116 -95
- package/src/screens/contacts/contact-details.tsx +4 -26
- package/src/screens/contacts/contact-list.test.tsx +59 -0
- package/src/screens/contacts/contact-list.tsx +246 -260
- package/src/screens/contacts/index.ts +0 -4
- package/src/screens/device-pairing/device-pairing.test.tsx +7 -9
- package/src/screens/device-pairing/device-pairing.tsx +19 -23
- package/src/screens/device-pairing/identity-ipc.ts +25 -0
- package/src/screens/device-pairing/use-device-pairing-state.ts +10 -33
- package/src/screens/devices/devices-list.test.tsx +68 -0
- package/src/screens/devices/devices-list.tsx +361 -0
- package/src/screens/devices/index.ts +1 -0
- package/src/screens/groups/group-details.tsx +8 -142
- package/src/screens/groups/group-invite-panel.tsx +2 -7
- package/src/screens/groups/group-list.test.tsx +44 -0
- package/src/screens/groups/group-list.tsx +227 -288
- package/src/screens/groups/group-members.tsx +3 -2
- package/src/screens/groups/index.ts +0 -5
- package/src/screens/identity/identity-routes.test.ts +70 -0
- package/src/screens/identity/identity-routes.ts +301 -0
- package/src/screens/identity/identity-screen.test.tsx +138 -0
- package/src/screens/identity/identity-screen.tsx +282 -0
- package/src/screens/identity/index.ts +2 -0
- package/src/screens/identity/invite-contact-panel.tsx +45 -0
- package/src/screens/identity/profile-invite.tsx +93 -0
- package/src/screens/invites/accept-invite.test.tsx +115 -0
- package/src/screens/invites/accept-invite.tsx +50 -3
- package/src/screens/invites/activity-view.test.tsx +127 -0
- package/src/screens/invites/activity-view.tsx +162 -0
- package/src/screens/invites/index.ts +1 -4
- package/src/screens/invites/invite-capture.ts +0 -3
- package/src/screens/invites/invite-classify.test.ts +129 -0
- package/src/screens/invites/invites-ipc.ts +6 -55
- package/src/screens/invites/pending-invites-list.tsx +94 -62
- package/src/screens/network-viewer/network-viewer-ipc.ts +8 -82
- package/src/screens/search/global-search.tsx +2 -1
- package/src/screens/settings/settings-page.tsx +53 -126
- package/src/screens/setup-user.tsx +5 -6
- package/src/screens/tools/tool-access-ipc.ts +13 -74
- package/src/screens/welcome-modal.tsx +3 -3
- package/src/system-apps/identity.app.ts +9 -0
- package/src/system-apps/index.ts +3 -12
- package/src/tabs-layout/network-status-indicator.test.tsx +22 -0
- package/src/tabs-layout/network-status-indicator.tsx +31 -0
- package/src/tabs-layout/tabs-layout.tsx +7 -1
- package/src/tabs-layout/tabs-state.test.ts +152 -0
- package/src/tabs-layout/tabs-state.ts +300 -83
- package/dist/screens/invites/invites-screen.d.ts +0 -9
- package/dist/screens/invites/invites-screen.js +0 -36
- package/dist/system-apps/account.app.d.ts +0 -2
- package/dist/system-apps/account.app.js +0 -8
- package/dist/system-apps/contacts.app.d.ts +0 -2
- package/dist/system-apps/contacts.app.js +0 -9
- package/dist/system-apps/groups.app.d.ts +0 -2
- package/dist/system-apps/groups.app.js +0 -9
- package/dist/system-apps/invites.app.d.ts +0 -2
- package/dist/system-apps/invites.app.js +0 -9
- package/src/screens/invites/invites-screen.tsx +0 -78
- package/src/system-apps/account.app.ts +0 -7
- package/src/system-apps/contacts.app.ts +0 -8
- package/src/system-apps/groups.app.ts +0 -8
- package/src/system-apps/invites.app.ts +0 -8
package/README.md
CHANGED
|
@@ -35,7 +35,8 @@ Hooks that bridge `@peers-app/peers-sdk` observables to React 18 with concurrent
|
|
|
35
35
|
- **`OperatorConsolePanel`** — The presentational console chrome (header toolbar + body) that fills its container; the host layout owns sizing, the resize divider, the collapsed strip, and placement. Toggle the console with Ctrl+` while on the Tabs + Console layout.
|
|
36
36
|
- **`SortableList`** — Drag-and-drop list powered by SortableJS
|
|
37
37
|
- **`InverseLazyList`** — Bottom-anchored lazy-loading list for chat-style UIs
|
|
38
|
-
- **`Tabs`** / **`ScreenTabBody`** —
|
|
38
|
+
- **`Tabs`** / **`ScreenTabBody`** — Horizontal tab strip and content container for settings and detail pages
|
|
39
|
+
- **`ResponsiveSectionNav`** — Section navigation inside one app. Wide screens get a left sidebar (`nav-pills`); narrow screens get a non-wrapping horizontal strip (`nav-tabs`). Its optional toolbar stays fixed while one constrained content pane owns vertical scrolling. The parent owns the route and passes the selected id. Identity uses it for persistent, route-backed quick actions.
|
|
39
40
|
|
|
40
41
|
### Communication
|
|
41
42
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { IMentionData } from "@peers-app/peers-sdk";
|
|
2
2
|
import { type DOMConversionMap, type DOMExportOutput, type EditorConfig, type LexicalNode, type NodeKey, type SerializedTextNode, type Spread, TextNode } from "lexical";
|
|
3
|
+
/** Serialized Lexical payload for a Peers mention node. */
|
|
3
4
|
export type SerializedMentionNode = Spread<{
|
|
4
5
|
data: IMentionData;
|
|
5
6
|
}, SerializedTextNode>;
|
|
7
|
+
/** Lexical text entity that renders and navigates a typed Peers mention. */
|
|
6
8
|
export declare class MentionNode extends TextNode {
|
|
7
9
|
readonly data: IMentionData;
|
|
8
10
|
static getType(): string;
|
|
@@ -17,5 +19,7 @@ export declare class MentionNode extends TextNode {
|
|
|
17
19
|
canInsertTextBefore(): boolean;
|
|
18
20
|
canInsertTextAfter(): boolean;
|
|
19
21
|
}
|
|
22
|
+
/** Create a segmented Lexical mention node from mention data. */
|
|
20
23
|
export declare function $createMentionNode(data: IMentionData): MentionNode;
|
|
24
|
+
/** Return whether a Lexical node is a {@link MentionNode}. */
|
|
21
25
|
export declare function $isMentionNode(node: LexicalNode | null | undefined): node is MentionNode;
|
|
@@ -25,6 +25,7 @@ function $convertMentionElement(domNode) {
|
|
|
25
25
|
}
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
|
+
/** Lexical text entity that renders and navigates a typed Peers mention. */
|
|
28
29
|
class MentionNode extends lexical_1.TextNode {
|
|
29
30
|
data;
|
|
30
31
|
static getType() {
|
|
@@ -81,7 +82,7 @@ class MentionNode extends lexical_1.TextNode {
|
|
|
81
82
|
}
|
|
82
83
|
else {
|
|
83
84
|
const id = this.data.id;
|
|
84
|
-
const href =
|
|
85
|
+
const href = kind === "user" ? `identity/people/${id}` : `${kind}s/${id}`;
|
|
85
86
|
(0, globals_1.mainContentPath)(href);
|
|
86
87
|
}
|
|
87
88
|
});
|
|
@@ -150,11 +151,13 @@ class MentionNode extends lexical_1.TextNode {
|
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
exports.MentionNode = MentionNode;
|
|
154
|
+
/** Create a segmented Lexical mention node from mention data. */
|
|
153
155
|
function $createMentionNode(data) {
|
|
154
156
|
const mentionNode = new MentionNode(data);
|
|
155
157
|
mentionNode.setMode("segmented").toggleDirectionless();
|
|
156
158
|
return (0, lexical_1.$applyNodeReplacement)(mentionNode);
|
|
157
159
|
}
|
|
160
|
+
/** Return whether a Lexical node is a {@link MentionNode}. */
|
|
158
161
|
function $isMentionNode(node) {
|
|
159
162
|
return node instanceof MentionNode;
|
|
160
163
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** One destination in {@link ResponsiveSectionNav}. */
|
|
3
|
+
export interface ISectionNavItem {
|
|
4
|
+
/** Stable id passed to {@link ResponsiveSectionNavProps.onSelect}. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Visible label. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Bootstrap icon class, without a leading markup wrapper. */
|
|
9
|
+
iconClassName?: string;
|
|
10
|
+
/** Optional count. Hidden when zero or omitted. */
|
|
11
|
+
badge?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Props for {@link ResponsiveSectionNav}. */
|
|
14
|
+
export interface ResponsiveSectionNavProps {
|
|
15
|
+
/** Accessible name for the section navigation. */
|
|
16
|
+
label: string;
|
|
17
|
+
/** Sections in display order. */
|
|
18
|
+
sections: ISectionNavItem[];
|
|
19
|
+
/** Id of the selected section. */
|
|
20
|
+
activeSectionId: string;
|
|
21
|
+
/** Called when a section button is activated. */
|
|
22
|
+
onSelect: (sectionId: string) => void;
|
|
23
|
+
/** Optional controls that remain visible above the scrolling section body. */
|
|
24
|
+
toolbar?: ReactNode;
|
|
25
|
+
/** Selected section body. */
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Section navigation that uses a left sidebar on a wide screen and a
|
|
30
|
+
* non-wrapping horizontal strip on a narrow one.
|
|
31
|
+
*
|
|
32
|
+
* The parent owns the route. This component only reports the selected id.
|
|
33
|
+
*/
|
|
34
|
+
export declare function ResponsiveSectionNav(props: ResponsiveSectionNavProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResponsiveSectionNav = ResponsiveSectionNav;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const globals_1 = require("../globals");
|
|
6
|
+
const hooks_1 = require("../hooks");
|
|
7
|
+
/**
|
|
8
|
+
* Section navigation that uses a left sidebar on a wide screen and a
|
|
9
|
+
* non-wrapping horizontal strip on a narrow one.
|
|
10
|
+
*
|
|
11
|
+
* The parent owns the route. This component only reports the selected id.
|
|
12
|
+
*/
|
|
13
|
+
function ResponsiveSectionNav(props) {
|
|
14
|
+
const [desktop] = (0, hooks_1.useObservable)(globals_1.isDesktop);
|
|
15
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: `d-flex h-100 overflow-hidden ${desktop ? "flex-row" : "flex-column"}`, style: { minHeight: 0, minWidth: 0 }, children: [(0, jsx_runtime_1.jsx)("nav", { "aria-label": props.label, className: desktop ? "border-end p-3 overflow-auto" : "border-bottom px-2 pt-2 pb-1", style: desktop ? { flex: "0 0 220px", width: 220 } : undefined, children: (0, jsx_runtime_1.jsx)("ul", { className: `nav ${desktop ? "nav-pills flex-column" : "nav-tabs flex-nowrap overflow-x-auto overflow-y-hidden"}`, children: props.sections.map((section) => {
|
|
16
|
+
const selected = section.id === props.activeSectionId;
|
|
17
|
+
return ((0, jsx_runtime_1.jsx)("li", { className: "nav-item", children: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: `nav-link d-flex align-items-center gap-2 w-100 text-start ${selected ? "active" : ""}`, "aria-current": selected ? "page" : undefined, onClick: () => props.onSelect(section.id), children: [section.iconClassName ? (0, jsx_runtime_1.jsx)("i", { className: section.iconClassName }) : null, (0, jsx_runtime_1.jsx)("span", { children: section.label }), section.badge ? ((0, jsx_runtime_1.jsx)("span", { className: `badge ${selected ? "bg-light text-primary" : "bg-primary"}`, children: section.badge })) : null] }) }, section.id));
|
|
18
|
+
}) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex flex-column flex-grow-1 overflow-hidden", style: { minHeight: 0, minWidth: 0 }, children: [props.toolbar ? (0, jsx_runtime_1.jsx)("div", { className: "flex-shrink-0", children: props.toolbar }) : null, (0, jsx_runtime_1.jsx)("div", { className: "flex-grow-1 overflow-auto", style: { minHeight: 0, minWidth: 0 }, "data-section-scroll-container": true, children: props.children })] })] }));
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("@testing-library/react");
|
|
8
|
+
const user_event_1 = __importDefault(require("@testing-library/user-event"));
|
|
9
|
+
const responsive_section_nav_1 = require("./responsive-section-nav");
|
|
10
|
+
jest.mock("../globals", () => {
|
|
11
|
+
const { observable: createObservable } = jest.requireActual("@peers-app/peers-sdk");
|
|
12
|
+
return { isDesktop: createObservable(true) };
|
|
13
|
+
});
|
|
14
|
+
const globals_1 = require("../globals");
|
|
15
|
+
const sections = [
|
|
16
|
+
{ id: "activity", label: "Activity", iconClassName: "bi-inbox", badge: 2 },
|
|
17
|
+
{ id: "people", label: "People", iconClassName: "bi-person" },
|
|
18
|
+
{ id: "groups", label: "Groups" },
|
|
19
|
+
{ id: "devices", label: "Devices" },
|
|
20
|
+
];
|
|
21
|
+
describe("ResponsiveSectionNav", () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
(0, globals_1.isDesktop)(true);
|
|
24
|
+
});
|
|
25
|
+
it("uses a vertical sidebar on desktop and marks the selected section", () => {
|
|
26
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(responsive_section_nav_1.ResponsiveSectionNav, { label: "Identity", sections: sections, activeSectionId: "people", onSelect: () => undefined, toolbar: (0, jsx_runtime_1.jsx)("div", { children: "Quick actions" }), children: (0, jsx_runtime_1.jsx)("div", { children: "People body" }) }));
|
|
27
|
+
expect(react_1.screen.getByRole("navigation", { name: "Identity" }).querySelector("ul")).toHaveClass("nav-pills", "flex-column");
|
|
28
|
+
expect(react_1.screen.getByRole("button", { name: /People/ })).toHaveAttribute("aria-current", "page");
|
|
29
|
+
expect(react_1.screen.getByText("People body")).toBeInTheDocument();
|
|
30
|
+
expect(react_1.screen.getByText("Quick actions")).toBeInTheDocument();
|
|
31
|
+
expect(react_1.screen.getByText("Quick actions").closest("[data-section-scroll-container]")).toBeNull();
|
|
32
|
+
expect(react_1.screen.getByText("People body").closest("[data-section-scroll-container]")).toHaveClass("overflow-auto");
|
|
33
|
+
});
|
|
34
|
+
it("uses a non-wrapping horizontal strip on a narrow screen", () => {
|
|
35
|
+
(0, globals_1.isDesktop)(false);
|
|
36
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(responsive_section_nav_1.ResponsiveSectionNav, { label: "Identity", sections: sections, activeSectionId: "activity", onSelect: () => undefined, children: (0, jsx_runtime_1.jsx)("div", { children: "Activity body" }) }));
|
|
37
|
+
expect(react_1.screen.getByRole("navigation", { name: "Identity" }).querySelector("ul")).toHaveClass("nav-tabs", "flex-nowrap", "overflow-x-auto", "overflow-y-hidden");
|
|
38
|
+
});
|
|
39
|
+
it("reports the section chosen from the keyboard-focusable button", async () => {
|
|
40
|
+
const onSelect = jest.fn();
|
|
41
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(responsive_section_nav_1.ResponsiveSectionNav, { label: "Identity", sections: sections, activeSectionId: "activity", onSelect: onSelect, children: (0, jsx_runtime_1.jsx)("div", { children: "Activity body" }) }));
|
|
42
|
+
await user_event_1.default.click(react_1.screen.getByRole("button", { name: /Groups/ }));
|
|
43
|
+
expect(onSelect).toHaveBeenCalledWith("groups");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import "../screens/console-logs/console-logs-list";
|
|
2
|
-
import "../screens/
|
|
3
|
-
import "../screens/contacts";
|
|
4
|
-
import "../screens/invites";
|
|
2
|
+
import "../screens/identity";
|
|
5
3
|
import "../screens/network-viewer";
|
|
6
4
|
import "../screens/data-explorer";
|
|
5
|
+
/** Render the built-in or registered UI that owns a canonicalized route. */
|
|
7
6
|
export declare function Router({ path: providedPath }?: {
|
|
8
7
|
path?: string;
|
|
9
8
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -37,9 +37,9 @@ exports.Router = Router;
|
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const globals = __importStar(require("../globals"));
|
|
39
39
|
const hooks_1 = require("../hooks");
|
|
40
|
-
const account_screen_1 = require("../screens/account/account-screen");
|
|
41
40
|
const assistant_details_1 = require("../screens/assistants/assistant-details");
|
|
42
41
|
const assistant_list_1 = require("../screens/assistants/assistant-list");
|
|
42
|
+
const identity_routes_1 = require("../screens/identity/identity-routes");
|
|
43
43
|
const package_details_1 = require("../screens/packages/package-details");
|
|
44
44
|
const package_list_1 = require("../screens/packages/package-list");
|
|
45
45
|
const package_new_local_1 = require("../screens/packages/package-new-local");
|
|
@@ -60,17 +60,17 @@ const ui_loader_1 = require("../ui-router/ui-loader");
|
|
|
60
60
|
const channel_view_1 = require("./messages/channel-view");
|
|
61
61
|
const thread_message_list_1 = require("./messages/thread-message-list");
|
|
62
62
|
require("../screens/console-logs/console-logs-list");
|
|
63
|
-
require("../screens/
|
|
64
|
-
require("../screens/contacts");
|
|
65
|
-
require("../screens/invites");
|
|
63
|
+
require("../screens/identity");
|
|
66
64
|
require("../screens/network-viewer");
|
|
67
65
|
require("../screens/data-explorer");
|
|
66
|
+
/** Render the built-in or registered UI that owns a canonicalized route. */
|
|
68
67
|
function Router({ path: providedPath } = {}) {
|
|
69
68
|
// Hooks must run unconditionally (same order every render)
|
|
70
69
|
const [globalPath] = (0, hooks_1.useObservable)(globals.mainContentPath);
|
|
71
70
|
try {
|
|
72
|
-
// Use provided path or fall back to global mainContentPath
|
|
73
|
-
|
|
71
|
+
// Use provided path or fall back to global mainContentPath.
|
|
72
|
+
// Legacy contacts, groups, devices, invites, and account paths render as Identity.
|
|
73
|
+
let path = (0, identity_routes_1.canonicalizeRoute)(providedPath ?? globalPath);
|
|
74
74
|
const originalPath = path;
|
|
75
75
|
path = path.toLowerCase().trim();
|
|
76
76
|
path = path.split("?")[0]; // remove query string
|
|
@@ -88,9 +88,6 @@ function Router({ path: providedPath } = {}) {
|
|
|
88
88
|
if (path === "settings" || path === "profile") {
|
|
89
89
|
return (0, jsx_runtime_1.jsx)(settings_page_1.SettingsPage, {});
|
|
90
90
|
}
|
|
91
|
-
if (path === "account") {
|
|
92
|
-
return (0, jsx_runtime_1.jsx)(account_screen_1.AccountScreen, {});
|
|
93
|
-
}
|
|
94
91
|
if (path === "threads" || path === "") {
|
|
95
92
|
return (0, jsx_runtime_1.jsx)(channel_view_1.ChannelMessages, {});
|
|
96
93
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
jest.mock("./tabs-layout/tabs-state", () => ({
|
|
4
|
+
handleMainPathChanged: (_oldPath, newPath, storage) => storage(newPath),
|
|
5
|
+
openNewTab: jest.fn(),
|
|
6
|
+
}));
|
|
7
|
+
const globals_1 = require("./globals");
|
|
8
|
+
describe("initial hash navigation", () => {
|
|
9
|
+
it("lets an incoming legacy deep link win and canonicalizes it", () => {
|
|
10
|
+
(0, globals_1.mainContentPath)("identity/activity");
|
|
11
|
+
window.history.replaceState(null, "", "/#contacts/abcdefghijklmnopqrstuvwxy?from=link");
|
|
12
|
+
(0, globals_1.initializeHashNavigation)();
|
|
13
|
+
expect((0, globals_1.mainContentPath)()).toBe("identity/people/abcdefghijklmnopqrstuvwxy?from=link");
|
|
14
|
+
expect(window.location.hash).toBe("#identity/people/abcdefghijklmnopqrstuvwxy?from=link");
|
|
15
|
+
});
|
|
16
|
+
});
|
package/dist/globals.d.ts
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import { type IMessage, type IUser } from "@peers-app/peers-sdk";
|
|
2
|
+
/** Counter incremented when installed package UI should reload. */
|
|
2
3
|
export declare const packageReloaded: import("@peers-app/peers-sdk").Observable<number>;
|
|
4
|
+
/** Persisted route for the active data context. Prefer {@link mainContentPath}. */
|
|
3
5
|
export declare const _mainContentPath: import("@peers-app/peers-sdk").PersistentVar<string>;
|
|
6
|
+
/** Canonical navigation observable backed by the active tab state. */
|
|
4
7
|
export declare const mainContentPath: import("@peers-app/peers-sdk").Observable<string>;
|
|
8
|
+
/** Read or update one query parameter on {@link mainContentPath}. */
|
|
5
9
|
export declare function queryParam(name: string, value?: string): string;
|
|
10
|
+
/** Open a message thread in its owning app tab. */
|
|
6
11
|
export declare function openThread(thread: string | IMessage): Promise<void>;
|
|
12
|
+
/** Resolve a message and open its root thread in a new tab. */
|
|
7
13
|
export declare function openThreadInTab(thread: string | IMessage): Promise<void>;
|
|
14
|
+
/** Signed profile for the active user after {@link loadGlobals}. */
|
|
8
15
|
export declare let me: IUser;
|
|
16
|
+
/** Current browser viewport width. */
|
|
9
17
|
export declare const windowWidth: import("@peers-app/peers-sdk").Observable<number>;
|
|
18
|
+
/** Current browser viewport height. */
|
|
10
19
|
export declare const windowHeight: import("@peers-app/peers-sdk").Observable<number>;
|
|
20
|
+
/** True when the viewport uses wide-screen navigation. */
|
|
11
21
|
export declare const isDesktop: import("@peers-app/peers-sdk").Observable<boolean>;
|
|
22
|
+
/** Enabled groups available to the active user. */
|
|
12
23
|
export declare const groups: import("@peers-app/peers-sdk").Observable<{
|
|
13
24
|
name: string;
|
|
14
25
|
description: string;
|
|
@@ -21,4 +32,12 @@ export declare const groups: import("@peers-app/peers-sdk").Observable<{
|
|
|
21
32
|
iconClassName?: string | undefined;
|
|
22
33
|
publicRole?: import("@peers-app/peers-sdk").GroupMemberRole | undefined;
|
|
23
34
|
}[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Start two-way hash synchronization after routes and persisted tabs are restored.
|
|
37
|
+
*
|
|
38
|
+
* A non-empty startup hash wins over persisted navigation so canonical and legacy
|
|
39
|
+
* deep links open the destination the user requested.
|
|
40
|
+
*/
|
|
41
|
+
export declare function initializeHashNavigation(): void;
|
|
42
|
+
/** Load global user state required before the root layout renders. */
|
|
24
43
|
export declare function loadGlobals(): Promise<boolean>;
|
package/dist/globals.js
CHANGED
|
@@ -4,12 +4,17 @@ exports.groups = exports.isDesktop = exports.windowHeight = exports.windowWidth
|
|
|
4
4
|
exports.queryParam = queryParam;
|
|
5
5
|
exports.openThread = openThread;
|
|
6
6
|
exports.openThreadInTab = openThreadInTab;
|
|
7
|
+
exports.initializeHashNavigation = initializeHashNavigation;
|
|
7
8
|
exports.loadGlobals = loadGlobals;
|
|
8
9
|
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
10
|
+
const identity_routes_1 = require("./screens/identity/identity-routes");
|
|
9
11
|
const tabs_state_1 = require("./tabs-layout/tabs-state");
|
|
10
12
|
const utils_1 = require("./utils");
|
|
13
|
+
/** Counter incremented when installed package UI should reload. */
|
|
11
14
|
exports.packageReloaded = (0, peers_sdk_1.observable)(0);
|
|
15
|
+
/** Persisted route for the active data context. Prefer {@link mainContentPath}. */
|
|
12
16
|
exports._mainContentPath = (0, peers_sdk_1.groupUserVar)("mainContentPath", { defaultValue: "" });
|
|
17
|
+
/** Canonical navigation observable backed by the active tab state. */
|
|
13
18
|
exports.mainContentPath = (0, peers_sdk_1.computed)({
|
|
14
19
|
read: () => {
|
|
15
20
|
return (0, exports._mainContentPath)();
|
|
@@ -41,6 +46,7 @@ const queryParams = (0, peers_sdk_1.computed)({
|
|
|
41
46
|
(0, exports.mainContentPath)(`${(0, exports.mainContentPath)().split("?")[0]}?${params.toString()}`);
|
|
42
47
|
},
|
|
43
48
|
});
|
|
49
|
+
/** Read or update one query parameter on {@link mainContentPath}. */
|
|
44
50
|
function queryParam(name, value) {
|
|
45
51
|
const params = queryParams();
|
|
46
52
|
if (value !== undefined) {
|
|
@@ -57,9 +63,11 @@ function queryParam(name, value) {
|
|
|
57
63
|
}
|
|
58
64
|
return params[name];
|
|
59
65
|
}
|
|
66
|
+
/** Open a message thread in its owning app tab. */
|
|
60
67
|
async function openThread(thread) {
|
|
61
68
|
openThreadInTab(thread);
|
|
62
69
|
}
|
|
70
|
+
/** Resolve a message and open its root thread in a new tab. */
|
|
63
71
|
async function openThreadInTab(thread) {
|
|
64
72
|
const threadId = typeof thread === "string" ? thread : thread.messageId;
|
|
65
73
|
const message = await (0, peers_sdk_1.Messages)().get(threadId);
|
|
@@ -89,20 +97,63 @@ peers_sdk_1.rpcClientCalls.openThread = async (threadId) => {
|
|
|
89
97
|
console.warn("Opening thread:", threadId);
|
|
90
98
|
openThread(threadId);
|
|
91
99
|
};
|
|
100
|
+
/** Signed profile for the active user after {@link loadGlobals}. */
|
|
92
101
|
exports.me = null;
|
|
102
|
+
/** Current browser viewport width. */
|
|
93
103
|
exports.windowWidth = (0, peers_sdk_1.observable)(window.innerWidth); //.extend({ rateLimit: 500 });
|
|
104
|
+
/** Current browser viewport height. */
|
|
94
105
|
exports.windowHeight = (0, peers_sdk_1.observable)(window.innerHeight); //.extend({ rateLimit: 500 });
|
|
95
106
|
window.addEventListener("resize", (_ev) => {
|
|
96
107
|
(0, exports.windowWidth)(window.innerWidth);
|
|
97
108
|
(0, exports.windowHeight)(window.innerHeight);
|
|
98
109
|
});
|
|
110
|
+
/** True when the viewport uses wide-screen navigation. */
|
|
99
111
|
exports.isDesktop = (0, peers_sdk_1.computed)(() => {
|
|
100
112
|
// return windowWidth() > 1200;
|
|
101
113
|
return (0, exports.windowWidth)() > 992;
|
|
102
114
|
// return windowWidth() > 768;
|
|
103
115
|
});
|
|
116
|
+
/** Enabled groups available to the active user. */
|
|
104
117
|
exports.groups = (0, peers_sdk_1.observable)([]);
|
|
105
118
|
let globalsLoaded = false;
|
|
119
|
+
let hashNavigationInitialized = false;
|
|
120
|
+
/**
|
|
121
|
+
* Start two-way hash synchronization after routes and persisted tabs are restored.
|
|
122
|
+
*
|
|
123
|
+
* A non-empty startup hash wins over persisted navigation so canonical and legacy
|
|
124
|
+
* deep links open the destination the user requested.
|
|
125
|
+
*/
|
|
126
|
+
function initializeHashNavigation() {
|
|
127
|
+
if (hashNavigationInitialized) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
hashNavigationInitialized = true;
|
|
131
|
+
const updateWindowHash = () => {
|
|
132
|
+
const currentPath = window.location.hash.substring(1);
|
|
133
|
+
const expectedPath = (0, exports.mainContentPath)() || "";
|
|
134
|
+
if (currentPath !== expectedPath) {
|
|
135
|
+
window.location.hash = expectedPath;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const navigateFromHash = () => {
|
|
139
|
+
const requestedPath = (0, identity_routes_1.canonicalizeRoute)(window.location.hash.substring(1));
|
|
140
|
+
if (requestedPath !== (0, exports.mainContentPath)()) {
|
|
141
|
+
(0, exports.mainContentPath)(requestedPath);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
updateWindowHash();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
exports.mainContentPath.subscribe(updateWindowHash);
|
|
148
|
+
window.addEventListener("hashchange", navigateFromHash);
|
|
149
|
+
if (window.location.hash.substring(1)) {
|
|
150
|
+
navigateFromHash();
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
updateWindowHash();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** Load global user state required before the root layout renders. */
|
|
106
157
|
async function loadGlobals() {
|
|
107
158
|
if (globalsLoaded)
|
|
108
159
|
return true;
|
|
@@ -119,18 +170,6 @@ async function loadGlobals() {
|
|
|
119
170
|
exports._mainContentPath.loadingPromise,
|
|
120
171
|
(0, peers_sdk_1.sleep)(100),
|
|
121
172
|
]);
|
|
122
|
-
function updateWindowHash() {
|
|
123
|
-
const currentPath = window.location.hash.substring(1);
|
|
124
|
-
const expectedPath = (0, exports.mainContentPath)() || "";
|
|
125
|
-
// console.log({ expectedPath, currentPath });
|
|
126
|
-
if (currentPath !== expectedPath) {
|
|
127
|
-
window.location.hash = expectedPath;
|
|
128
|
-
}
|
|
129
|
-
(0, exports.mainContentPath)(expectedPath);
|
|
130
|
-
}
|
|
131
|
-
exports.mainContentPath.subscribe(updateWindowHash);
|
|
132
|
-
updateWindowHash();
|
|
133
|
-
window.addEventListener("hashchange", () => (0, exports.mainContentPath)(window.location.hash.substring(1)));
|
|
134
173
|
return true;
|
|
135
174
|
}
|
|
136
175
|
if (typeof window !== "undefined") {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type IContractConsumerBase, type IHostContractConsumer } from "@peers-app/peers-sdk";
|
|
2
|
+
/** Live consumer for one host contract, plus the last failure to obtain it. */
|
|
3
|
+
export interface IUseHostContractResult<T extends IContractConsumerBase> {
|
|
4
|
+
/** Current consumer, or `undefined` while the host transport is down. */
|
|
5
|
+
consumer: T | undefined;
|
|
6
|
+
/** Set when `get` rejects, for example when the transport wait times out. */
|
|
7
|
+
error: Error | undefined;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a cached local-host contract consumer and keep it current across reconnects.
|
|
11
|
+
*
|
|
12
|
+
* The handle owns caching and disposal. This hook re-reads it when the host transport
|
|
13
|
+
* changes. Read `consumer.observables.*` with `useObservable`.
|
|
14
|
+
*
|
|
15
|
+
* @param accessor Handle from `hostContractConsumer`, stable for the lifetime of the screen module.
|
|
16
|
+
* @returns The current consumer and the last error.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useHostContract<T extends IContractConsumerBase>(accessor: IHostContractConsumer<T>): IUseHostContractResult<T>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useHostContract = useHostContract;
|
|
4
|
+
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
/**
|
|
7
|
+
* Resolve a cached local-host contract consumer and keep it current across reconnects.
|
|
8
|
+
*
|
|
9
|
+
* The handle owns caching and disposal. This hook re-reads it when the host transport
|
|
10
|
+
* changes. Read `consumer.observables.*` with `useObservable`.
|
|
11
|
+
*
|
|
12
|
+
* @param accessor Handle from `hostContractConsumer`, stable for the lifetime of the screen module.
|
|
13
|
+
* @returns The current consumer and the last error.
|
|
14
|
+
*/
|
|
15
|
+
function useHostContract(accessor) {
|
|
16
|
+
const [consumer, setConsumer] = (0, react_1.useState)();
|
|
17
|
+
const [error, setError] = (0, react_1.useState)();
|
|
18
|
+
(0, react_1.useEffect)(() => {
|
|
19
|
+
let cancelled = false;
|
|
20
|
+
const apply = (next) => {
|
|
21
|
+
if (cancelled) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
setConsumer(next);
|
|
25
|
+
setError(undefined);
|
|
26
|
+
};
|
|
27
|
+
const fail = (cause) => {
|
|
28
|
+
if (cancelled) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
32
|
+
};
|
|
33
|
+
const load = () => {
|
|
34
|
+
accessor.get().then(apply, fail);
|
|
35
|
+
};
|
|
36
|
+
load();
|
|
37
|
+
const unsubscribe = (0, peers_sdk_1.subscribeHostContractTransport)((transport) => {
|
|
38
|
+
if (cancelled) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!transport) {
|
|
42
|
+
setConsumer(undefined);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
load();
|
|
46
|
+
}, { emitCurrent: false });
|
|
47
|
+
return () => {
|
|
48
|
+
cancelled = true;
|
|
49
|
+
unsubscribe();
|
|
50
|
+
};
|
|
51
|
+
}, [accessor]);
|
|
52
|
+
return { consumer, error };
|
|
53
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type { IEditorEffects, IMarkdownEditorProps } from "./components/markdown
|
|
|
3
3
|
export { MarkdownEditor } from "./components/markdown-editor/editor";
|
|
4
4
|
export * from "./components/markdown-editor/editor-inline";
|
|
5
5
|
export * from "./components/markdown-with-mentions";
|
|
6
|
+
export * from "./components/responsive-section-nav";
|
|
6
7
|
export * from "./components/sortable-list";
|
|
7
8
|
export * from "./components/tabs";
|
|
8
9
|
export { Typeahead, type TypeaheadItem } from "./components/typeahead";
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var editor_1 = require("./components/markdown-editor/editor");
|
|
|
20
20
|
Object.defineProperty(exports, "MarkdownEditor", { enumerable: true, get: function () { return editor_1.MarkdownEditor; } });
|
|
21
21
|
__exportStar(require("./components/markdown-editor/editor-inline"), exports);
|
|
22
22
|
__exportStar(require("./components/markdown-with-mentions"), exports);
|
|
23
|
+
__exportStar(require("./components/responsive-section-nav"), exports);
|
|
23
24
|
__exportStar(require("./components/sortable-list"), exports);
|
|
24
25
|
__exportStar(require("./components/tabs"), exports);
|
|
25
26
|
var typeahead_1 = require("./components/typeahead");
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const registrationStatusPvar: import("@peers-app/peers-sdk").PersistentVar<string>;
|
|
3
|
-
export declare const servicesTokenPvar: import("@peers-app/peers-sdk").PersistentVar<string>;
|
|
4
|
-
/** Store JWT and local "registered" flag after a successful `registerWithPeersServices` call. */
|
|
5
|
-
export declare function persistPeersServicesRegistration(token: string): void;
|
|
1
|
+
export { persistPeersServicesRegistration, registrationStatusPvar, servicesTokenPvar, } from "@peers-app/peers-sdk";
|
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.servicesTokenPvar = exports.registrationStatusPvar = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
exports
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
exports.servicesTokenPvar = (0, peers_sdk_1.userVar)("PEERS_SERVICES_TOKEN", {
|
|
11
|
-
isSecret: true,
|
|
12
|
-
defaultValue: "",
|
|
13
|
-
});
|
|
14
|
-
/** Store JWT and local "registered" flag after a successful `registerWithPeersServices` call. */
|
|
15
|
-
function persistPeersServicesRegistration(token) {
|
|
16
|
-
(0, exports.servicesTokenPvar)(token);
|
|
17
|
-
(0, exports.registrationStatusPvar)(new Date().toISOString());
|
|
18
|
-
}
|
|
3
|
+
exports.servicesTokenPvar = exports.registrationStatusPvar = exports.persistPeersServicesRegistration = void 0;
|
|
4
|
+
// The pvar definitions live in peers-sdk so headless hosts and UIs share one set of names.
|
|
5
|
+
var peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
6
|
+
Object.defineProperty(exports, "persistPeersServicesRegistration", { enumerable: true, get: function () { return peers_sdk_1.persistPeersServicesRegistration; } });
|
|
7
|
+
Object.defineProperty(exports, "registrationStatusPvar", { enumerable: true, get: function () { return peers_sdk_1.registrationStatusPvar; } });
|
|
8
|
+
Object.defineProperty(exports, "servicesTokenPvar", { enumerable: true, get: function () { return peers_sdk_1.servicesTokenPvar; } });
|
|
@@ -20,6 +20,8 @@ const react_1 = require("react");
|
|
|
20
20
|
const command_palette_ui_1 = require("../command-palette/command-palette-ui");
|
|
21
21
|
const globals_1 = require("../globals");
|
|
22
22
|
const hooks_1 = require("../hooks");
|
|
23
|
+
const identity_ipc_1 = require("../screens/device-pairing/identity-ipc");
|
|
24
|
+
const identity_routes_1 = require("../screens/identity/identity-routes");
|
|
23
25
|
const invite_capture_1 = require("../screens/invites/invite-capture");
|
|
24
26
|
const color_mode_dropdown_1 = require("../screens/settings/color-mode-dropdown");
|
|
25
27
|
const setup_user_1 = require("../screens/setup-user");
|
|
@@ -41,8 +43,8 @@ function RootLayoutHost() {
|
|
|
41
43
|
});
|
|
42
44
|
const userIdRef = (0, react_1.useRef)(undefined);
|
|
43
45
|
const userId = (0, hooks_1.usePromise)(async () => {
|
|
44
|
-
const
|
|
45
|
-
return
|
|
46
|
+
const session = await (await (0, identity_ipc_1.identityTools)()).getSession();
|
|
47
|
+
return session.userId || null;
|
|
46
48
|
});
|
|
47
49
|
userIdRef.current = userId;
|
|
48
50
|
(0, react_1.useEffect)(() => {
|
|
@@ -54,7 +56,7 @@ function RootLayoutHost() {
|
|
|
54
56
|
const stopOpenUrl = (0, invite_capture_1.listenForOpenUrlInvites)();
|
|
55
57
|
const sub = invite_capture_1.inviteCaptured.subscribe(() => {
|
|
56
58
|
if (userIdRef.current && (0, invite_capture_1.peekPendingInvite)()) {
|
|
57
|
-
(0, globals_1.mainContentPath)(
|
|
59
|
+
(0, globals_1.mainContentPath)(identity_routes_1.IDENTITY_ACCEPT_PATH);
|
|
58
60
|
}
|
|
59
61
|
});
|
|
60
62
|
return () => {
|
|
@@ -92,8 +94,9 @@ function RootLayoutBootstrap(props) {
|
|
|
92
94
|
tabs_state_1.recentlyUsedApps.loadingPromise,
|
|
93
95
|
peers_sdk_1.hasShownWelcomeModal.loadingPromise,
|
|
94
96
|
]);
|
|
97
|
+
(0, globals_1.initializeHashNavigation)();
|
|
95
98
|
if ((0, invite_capture_1.peekPendingInvite)()) {
|
|
96
|
-
(0, globals_1.mainContentPath)(
|
|
99
|
+
(0, globals_1.mainContentPath)(identity_routes_1.IDENTITY_ACCEPT_PATH);
|
|
97
100
|
}
|
|
98
101
|
return true;
|
|
99
102
|
});
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
4
|
const react_1 = require("@testing-library/react");
|
|
5
5
|
const globals_1 = require("../globals");
|
|
6
|
+
const identity_routes_1 = require("../screens/identity/identity-routes");
|
|
6
7
|
const invite_capture_1 = require("../screens/invites/invite-capture");
|
|
7
8
|
const root_layout_host_1 = require("./root-layout-host");
|
|
8
9
|
const TOKEN = "eyJhbGciOiJub25lIn0-_abcdefghijklmnopqrstuvwxyz0123456789";
|
|
@@ -13,10 +14,6 @@ jest.mock("@peers-app/peers-sdk", () => {
|
|
|
13
14
|
hasShownWelcomeModal: Object.assign(jest.fn(() => true), {
|
|
14
15
|
loadingPromise: Promise.resolve(),
|
|
15
16
|
}),
|
|
16
|
-
rpcServerCalls: {
|
|
17
|
-
...actual.rpcServerCalls,
|
|
18
|
-
getUserId: jest.fn(async () => "local-user"),
|
|
19
|
-
},
|
|
20
17
|
setDefaultClientUserContext: jest.fn(),
|
|
21
18
|
};
|
|
22
19
|
});
|
|
@@ -31,7 +28,7 @@ jest.mock("../hooks", () => ({
|
|
|
31
28
|
useObservable: (observable) => [observable(), observable],
|
|
32
29
|
usePromise: (promiseOrFactory) => {
|
|
33
30
|
if (typeof promiseOrFactory === "function" &&
|
|
34
|
-
Function.prototype.toString.call(promiseOrFactory).includes("
|
|
31
|
+
Function.prototype.toString.call(promiseOrFactory).includes("getSession")) {
|
|
35
32
|
return "local-user";
|
|
36
33
|
}
|
|
37
34
|
return true;
|
|
@@ -76,7 +73,7 @@ describe("RootLayoutHost", () => {
|
|
|
76
73
|
(0, react_1.fireEvent)(window, new HashChangeEvent("hashchange"));
|
|
77
74
|
expect(window.location.hash).toBe("");
|
|
78
75
|
expect((0, invite_capture_1.peekPendingInvite)()).toBe(TOKEN);
|
|
79
|
-
expect(globals_1.mainContentPath).toHaveBeenCalledWith(
|
|
76
|
+
expect(globals_1.mainContentPath).toHaveBeenCalledWith(identity_routes_1.IDENTITY_ACCEPT_PATH);
|
|
80
77
|
expect(globals_1.mainContentPath).not.toHaveBeenCalledWith(expect.stringContaining(TOKEN));
|
|
81
78
|
});
|
|
82
79
|
it("ignores ordinary hash routes", () => {
|