@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
|
@@ -1 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import { type GroupMemberRole, type IGroup } from "@peers-app/peers-sdk";
|
|
2
|
+
/** Search, summarize, and create groups owned by the signed-in user. */
|
|
3
|
+
export declare function GroupList(props?: {
|
|
4
|
+
creating?: boolean;
|
|
5
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
/** Data shown for one group in the Identity list. */
|
|
7
|
+
export interface IGroupSummary {
|
|
8
|
+
/** Signed group record. */
|
|
9
|
+
group: IGroup;
|
|
10
|
+
/** Number of active memberships. */
|
|
11
|
+
memberCount: number;
|
|
12
|
+
/** Signed-in user's role in this group. */
|
|
13
|
+
myRole?: GroupMemberRole;
|
|
14
|
+
}
|
|
15
|
+
/** Compact, fully clickable group summary row. */
|
|
16
|
+
export declare function GroupSummaryRow({ summary }: {
|
|
17
|
+
summary: IGroupSummary;
|
|
18
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,77 +1,96 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GroupList = GroupList;
|
|
4
|
+
exports.GroupSummaryRow = GroupSummaryRow;
|
|
4
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
6
|
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
6
7
|
const react_1 = require("react");
|
|
7
|
-
const input_1 = require("../../components/input");
|
|
8
|
-
const lazy_list_1 = require("../../components/lazy-list");
|
|
9
8
|
const loading_indicator_1 = require("../../components/loading-indicator");
|
|
10
|
-
const markdown_with_mentions_1 = require("../../components/markdown-with-mentions");
|
|
11
9
|
const globals_1 = require("../../globals");
|
|
12
10
|
const hooks_1 = require("../../hooks");
|
|
13
|
-
const
|
|
14
|
-
|
|
11
|
+
const identity_routes_1 = require("../identity/identity-routes");
|
|
12
|
+
const activity_view_1 = require("../invites/activity-view");
|
|
13
|
+
/** Search, summarize, and create groups owned by the signed-in user. */
|
|
14
|
+
function GroupList(props = {}) {
|
|
15
15
|
const userContext = (0, hooks_1.usePromise)(peers_sdk_1.getUserContext);
|
|
16
|
-
const [
|
|
17
|
-
const [
|
|
18
|
-
const [
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
16
|
+
const [creating, setCreating] = (0, react_1.useState)(props.creating ?? false);
|
|
17
|
+
const [draftName, setDraftName] = (0, react_1.useState)("");
|
|
18
|
+
const [searchText, setSearchText] = (0, react_1.useState)("");
|
|
19
|
+
const [summaries, setSummaries] = (0, react_1.useState)();
|
|
20
|
+
const [error, setError] = (0, react_1.useState)("");
|
|
21
|
+
const [creatingBusy, setCreatingBusy] = (0, react_1.useState)(false);
|
|
22
|
+
const [groupRevision, setGroupRevision] = (0, react_1.useState)(0);
|
|
23
|
+
(0, react_1.useEffect)(() => {
|
|
24
|
+
if (!userContext) {
|
|
23
25
|
return;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
}
|
|
27
|
+
let disposed = false;
|
|
28
|
+
const subscriptions = [];
|
|
29
|
+
const refresh = async () => {
|
|
30
|
+
try {
|
|
31
|
+
const next = await loadGroupSummaries(userContext);
|
|
32
|
+
if (!disposed) {
|
|
33
|
+
setSummaries(next);
|
|
34
|
+
setError("");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (!disposed) {
|
|
39
|
+
setError(err.message || "Could not load groups.");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
27
42
|
};
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
const table = (0, peers_sdk_1.Groups)(userContext.userDataContext);
|
|
44
|
+
subscriptions.push(table.dataChanged.subscribe(() => setGroupRevision((value) => value + 1)));
|
|
45
|
+
void table.list({ disabled: { $ne: true } }).then((groups) => {
|
|
46
|
+
if (disposed) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const group of groups) {
|
|
50
|
+
subscriptions.push((0, peers_sdk_1.GroupMembers)(userContext.getDataContext(group.groupId)).dataChanged.subscribe(() => void refresh()));
|
|
51
|
+
}
|
|
52
|
+
void refresh();
|
|
31
53
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
return () => {
|
|
55
|
+
disposed = true;
|
|
56
|
+
for (const subscription of subscriptions) {
|
|
57
|
+
subscription.unsubscribe();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}, [groupRevision, userContext]);
|
|
35
61
|
(0, react_1.useEffect)(() => {
|
|
36
|
-
|
|
37
|
-
|
|
62
|
+
setCreating(props.creating ?? false);
|
|
63
|
+
}, [props.creating]);
|
|
64
|
+
const filtered = (0, react_1.useMemo)(() => {
|
|
65
|
+
const query = searchText.trim().toLocaleLowerCase();
|
|
66
|
+
if (!query) {
|
|
67
|
+
return summaries ?? [];
|
|
38
68
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
69
|
+
return (summaries ?? []).filter(({ group }) => group.name.toLocaleLowerCase().includes(query) ||
|
|
70
|
+
group.description.toLocaleLowerCase().includes(query));
|
|
71
|
+
}, [searchText, summaries]);
|
|
72
|
+
function startCreate() {
|
|
73
|
+
setDraftName(searchText.trim());
|
|
74
|
+
(0, globals_1.mainContentPath)(identity_routes_1.IDENTITY_GROUP_NEW_PATH);
|
|
43
75
|
}
|
|
44
|
-
async function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const cursor = cursorObs() || (await newCursor());
|
|
49
|
-
if (!cursor)
|
|
50
|
-
return [];
|
|
51
|
-
for await (const nextRecord of cursor) {
|
|
52
|
-
if (existing.some((e) => e.groupId === nextRecord.groupId))
|
|
53
|
-
continue;
|
|
54
|
-
if (moreMatches.some((e) => e.groupId === nextRecord.groupId))
|
|
55
|
-
continue;
|
|
56
|
-
moreMatches.push(nextRecord);
|
|
57
|
-
if (searchText.length && moreMatches.length > 5)
|
|
58
|
-
break;
|
|
59
|
-
if (moreMatches.length >= 10)
|
|
60
|
-
break;
|
|
76
|
+
async function confirmCreate() {
|
|
77
|
+
const name = draftName.trim();
|
|
78
|
+
if (!name) {
|
|
79
|
+
return;
|
|
61
80
|
}
|
|
62
|
-
|
|
63
|
-
|
|
81
|
+
setCreatingBusy(true);
|
|
82
|
+
setError("");
|
|
83
|
+
try {
|
|
84
|
+
setDraftName("");
|
|
85
|
+
setSearchText("");
|
|
86
|
+
await newRecord(name);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
setError(err.message || "Could not create the group.");
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
setCreatingBusy(false);
|
|
64
93
|
}
|
|
65
|
-
return moreMatches;
|
|
66
|
-
}
|
|
67
|
-
async function searchSubmit(evt) {
|
|
68
|
-
if (evt.key !== "Enter")
|
|
69
|
-
return;
|
|
70
|
-
const name = searchText.trim();
|
|
71
|
-
if (!name)
|
|
72
|
-
return;
|
|
73
|
-
searchTextObs("");
|
|
74
|
-
await newRecord(name);
|
|
75
94
|
}
|
|
76
95
|
async function newRecord(name) {
|
|
77
96
|
const userContext = await (0, peers_sdk_1.getUserContext)();
|
|
@@ -106,86 +125,38 @@ function GroupList() {
|
|
|
106
125
|
await peers_sdk_1.rpcServerCalls.seedBundledPeersCore(groupId).catch((err) => {
|
|
107
126
|
console.error("Error auto-installing peers-core for group:", err);
|
|
108
127
|
});
|
|
109
|
-
(0, globals_1.mainContentPath)(
|
|
128
|
+
(0, globals_1.mainContentPath)(`${identity_routes_1.IDENTITY_GROUPS_PATH}/${group.groupId}`);
|
|
110
129
|
return group;
|
|
111
130
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "container-xxl p-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-3", children: [(0, jsx_runtime_1.jsxs)("h4", { className: "mb-1", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi-people-fill me-2" }), "Groups"] }), (0, jsx_runtime_1.jsx)("div", { className: "small text-muted", children: "Shared spaces, membership, and access" })] }), creating ? ((0, jsx_runtime_1.jsx)("div", { className: "card mb-3", children: (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsx)("h6", { className: "card-title", children: "Create a group" }), (0, jsx_runtime_1.jsxs)("form", { className: "input-group", onSubmit: (event) => {
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
void confirmCreate();
|
|
134
|
+
}, children: [(0, jsx_runtime_1.jsx)("input", { id: "new-group-name", className: "form-control", value: draftName, onChange: (event) => setDraftName(event.target.value), placeholder: "Group name", "aria-label": "Group name" }), (0, jsx_runtime_1.jsx)("button", { type: "submit", className: "btn btn-primary", disabled: !draftName.trim() || creatingBusy, children: creatingBusy ? "Creating…" : "Create" }), (0, jsx_runtime_1.jsx)("a", { href: `#${identity_routes_1.IDENTITY_GROUPS_PATH}`, className: "btn btn-outline-secondary", children: "Cancel" })] })] }) })) : null, (0, jsx_runtime_1.jsx)(activity_view_1.PendingWorkLink, { type: "group" }), (0, jsx_runtime_1.jsxs)("div", { className: "position-relative my-3", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-search position-absolute text-muted", style: { left: 12, top: "50%", transform: "translateY(-50%)" } }), (0, jsx_runtime_1.jsx)("input", { type: "search", className: "form-control ps-5", value: searchText, onChange: (event) => setSearchText(event.target.value), placeholder: "Search groups", "aria-label": "Search groups" })] }), error ? (0, jsx_runtime_1.jsx)("div", { className: "alert alert-danger", children: error }) : null, !summaries && !error ? (0, jsx_runtime_1.jsx)(loading_indicator_1.LoadingIndicator, {}) : null, summaries && filtered.length === 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "border rounded p-4 text-center", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-people fs-2 text-muted" }), (0, jsx_runtime_1.jsx)("h6", { className: "mt-2", children: searchText ? "No matching groups" : "No groups yet" }), (0, jsx_runtime_1.jsx)("p", { className: "small text-muted", children: searchText
|
|
135
|
+
? "Try a different name or description."
|
|
136
|
+
: "Create a shared space or use an invite to join one." }), !searchText ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-primary btn-sm", onClick: startCreate, children: "Create your first group" })) : null] })) : null, filtered.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "list-group", children: filtered.map((summary) => ((0, jsx_runtime_1.jsx)(GroupSummaryRow, { summary: summary }, summary.group.groupId))) })) : null] }));
|
|
137
|
+
}
|
|
138
|
+
async function loadGroupSummaries(userContext) {
|
|
139
|
+
const groups = await (0, peers_sdk_1.Groups)(userContext.userDataContext).list({ disabled: { $ne: true } });
|
|
140
|
+
const summaries = await Promise.all(groups.map(async (group) => {
|
|
141
|
+
const members = await (0, peers_sdk_1.GroupMembers)(userContext.getDataContext(group.groupId)).list({
|
|
142
|
+
deleted: { $ne: true },
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
group,
|
|
146
|
+
memberCount: members.length,
|
|
147
|
+
myRole: members.find((member) => member.userId === userContext.userId)?.role,
|
|
148
|
+
};
|
|
149
|
+
}));
|
|
150
|
+
return summaries.sort((left, right) => left.group.name.localeCompare(right.group.name));
|
|
151
|
+
}
|
|
152
|
+
/** Compact, fully clickable group summary row. */
|
|
153
|
+
function GroupSummaryRow({ summary }) {
|
|
154
|
+
const { group } = summary;
|
|
155
|
+
return ((0, jsx_runtime_1.jsxs)("a", { href: `#${identity_routes_1.IDENTITY_GROUPS_PATH}/${group.groupId}`, className: "list-group-item list-group-item-action d-flex align-items-center gap-3 py-3", children: [(0, jsx_runtime_1.jsx)("span", { className: "rounded-3 bg-body-secondary d-inline-flex align-items-center justify-content-center flex-shrink-0", style: { width: 44, height: 44 }, "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("i", { className: group.iconClassName || "bi bi-people-fill" }) }), (0, jsx_runtime_1.jsxs)("span", { className: "flex-grow-1 overflow-hidden", children: [(0, jsx_runtime_1.jsx)("span", { className: "fw-semibold d-block text-truncate", children: group.name || "Unnamed group" }), (0, jsx_runtime_1.jsx)("span", { className: "small text-muted d-block text-truncate", children: plainDescription(group.description) || "No description yet" })] }), (0, jsx_runtime_1.jsxs)("span", { className: "d-flex align-items-center gap-2 flex-wrap justify-content-end", children: [(0, jsx_runtime_1.jsxs)("span", { className: "badge text-bg-light border", children: [summary.memberCount, " member", summary.memberCount === 1 ? "" : "s"] }), summary.myRole !== undefined ? ((0, jsx_runtime_1.jsx)("span", { className: "badge text-bg-secondary", children: (0, peers_sdk_1.getRoleLabel)(summary.myRole) })) : null] }), (0, jsx_runtime_1.jsx)("i", { className: "bi bi-chevron-right text-muted", "aria-hidden": "true" })] }));
|
|
156
|
+
}
|
|
157
|
+
function plainDescription(markdown) {
|
|
158
|
+
return markdown
|
|
159
|
+
.replace(/[#_*`>[\]()!-]/g, " ")
|
|
160
|
+
.replace(/\s+/g, " ")
|
|
161
|
+
.trim();
|
|
119
162
|
}
|
|
120
|
-
const ImportGroupButton = () => {
|
|
121
|
-
const [isImporting, setIsImporting] = (0, react_1.useState)(false);
|
|
122
|
-
const [showImportDialog, setShowImportDialog] = (0, react_1.useState)(false);
|
|
123
|
-
const [importText, setImportText] = (0, react_1.useState)("");
|
|
124
|
-
const [importResult, setImportResult] = (0, react_1.useState)(null);
|
|
125
|
-
const handleImport = async () => {
|
|
126
|
-
if (!importText.trim()) {
|
|
127
|
-
setImportResult({
|
|
128
|
-
success: false,
|
|
129
|
-
message: "Please paste the group share JSON data",
|
|
130
|
-
});
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
setIsImporting(true);
|
|
134
|
-
setImportResult(null);
|
|
135
|
-
try {
|
|
136
|
-
const groupId = await peers_sdk_1.rpcServerCalls.importGroupShare(importText.trim());
|
|
137
|
-
setImportResult({
|
|
138
|
-
success: true,
|
|
139
|
-
message: `Group imported successfully!`,
|
|
140
|
-
groupId,
|
|
141
|
-
});
|
|
142
|
-
setImportText("");
|
|
143
|
-
// Navigate to the imported group after a brief delay
|
|
144
|
-
setTimeout(() => {
|
|
145
|
-
(0, globals_1.mainContentPath)(`groups/${groupId}`);
|
|
146
|
-
setShowImportDialog(false);
|
|
147
|
-
setImportResult(null);
|
|
148
|
-
}, 2000);
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
151
|
-
setImportResult({
|
|
152
|
-
success: false,
|
|
153
|
-
message: `Import failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
finally {
|
|
157
|
-
setIsImporting(false);
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
const handlePasteFromClipboard = async () => {
|
|
161
|
-
try {
|
|
162
|
-
if (navigator.clipboard?.readText) {
|
|
163
|
-
const clipboardText = await navigator.clipboard.readText();
|
|
164
|
-
setImportText(clipboardText);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch (_error) {
|
|
168
|
-
console.log("Failed to read from clipboard - user can paste manually");
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { className: "btn btn-outline-secondary dropdown-toggle", type: "button", "data-bs-toggle": "dropdown", "aria-expanded": "false", children: (0, jsx_runtime_1.jsx)("i", { className: "bi-plus-lg" }) }), (0, jsx_runtime_1.jsx)("ul", { className: "dropdown-menu dropdown-menu-end", children: (0, jsx_runtime_1.jsx)("li", { children: (0, jsx_runtime_1.jsxs)("button", { className: "dropdown-item", onClick: () => setShowImportDialog(true), children: [(0, jsx_runtime_1.jsx)("i", { className: "bi-download me-2" }), "Import Shared Group"] }) }) }), showImportDialog && ((0, jsx_runtime_1.jsx)("div", { className: "position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center", style: { backgroundColor: "rgba(0,0,0,0.5)", zIndex: 1050 }, children: (0, jsx_runtime_1.jsxs)("div", { className: "card", style: { maxWidth: "600px", width: "90%" }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "card-header d-flex justify-content-between align-items-center", children: [(0, jsx_runtime_1.jsxs)("h6", { className: "mb-0", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi-download me-2" }), "Import Shared Group"] }), (0, jsx_runtime_1.jsx)("button", { className: "btn-close", onClick: () => {
|
|
172
|
-
setShowImportDialog(false);
|
|
173
|
-
setImportText("");
|
|
174
|
-
setImportResult(null);
|
|
175
|
-
}, "aria-label": "Close" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsx)("p", { className: "mb-3", children: "Paste the group share JSON data below to import a shared group with its admin members." }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-3", children: [(0, jsx_runtime_1.jsx)("label", { className: "form-label", children: "Group Share JSON:" }), (0, jsx_runtime_1.jsx)("div", { className: "d-flex gap-2 mb-2", children: (0, jsx_runtime_1.jsxs)("button", { className: "btn btn-secondary btn-sm", onClick: handlePasteFromClipboard, title: "Paste from clipboard", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi-clipboard me-1" }), "Paste from Clipboard"] }) }), (0, jsx_runtime_1.jsx)("textarea", { className: "form-control font-monospace", rows: 8, value: importText, onChange: (e) => setImportText(e.target.value), placeholder: "Paste the group share JSON here...", style: { fontSize: "0.9em" } })] }), importResult && ((0, jsx_runtime_1.jsxs)("div", { className: `alert alert-${importResult.success ? "success" : "danger"} mb-3`, children: [(0, jsx_runtime_1.jsx)("i", { className: `bi-${importResult.success ? "check-circle" : "exclamation-triangle"} me-2` }), importResult.message, importResult.success && importResult.groupId && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2", children: (0, jsx_runtime_1.jsx)("small", { children: "Redirecting to group in 2 seconds..." }) }))] })), (0, jsx_runtime_1.jsxs)("div", { className: "text-end", children: [(0, jsx_runtime_1.jsx)("button", { className: "btn btn-secondary me-2", onClick: () => {
|
|
176
|
-
setShowImportDialog(false);
|
|
177
|
-
setImportText("");
|
|
178
|
-
setImportResult(null);
|
|
179
|
-
}, disabled: isImporting, children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { className: "btn btn-primary", onClick: handleImport, disabled: isImporting || !importText.trim(), children: isImporting ? ((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" }), "Importing..."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("i", { className: "bi-download me-1" }), "Import Group"] })) })] })] })] }) }))] }));
|
|
180
|
-
};
|
|
181
|
-
(0, ui_loader_1.registerInternalPeersUI)({
|
|
182
|
-
peersUIId: "00mf8qrs004w5u2wqk64d2yp6",
|
|
183
|
-
component: GroupList,
|
|
184
|
-
routes: [
|
|
185
|
-
{
|
|
186
|
-
isMatch: (_props, context) => context.path === "groups",
|
|
187
|
-
uiCategory: "screen",
|
|
188
|
-
priority: 2,
|
|
189
|
-
},
|
|
190
|
-
],
|
|
191
|
-
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
+
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
5
|
+
const react_1 = require("@testing-library/react");
|
|
6
|
+
const group_list_1 = require("./group-list");
|
|
7
|
+
jest.mock("../../globals", () => ({
|
|
8
|
+
mainContentPath: jest.fn(),
|
|
9
|
+
}));
|
|
10
|
+
jest.mock("../../hooks", () => ({
|
|
11
|
+
usePromise: jest.fn(),
|
|
12
|
+
}));
|
|
13
|
+
jest.mock("../invites/activity-view", () => ({
|
|
14
|
+
PendingWorkLink: () => null,
|
|
15
|
+
}));
|
|
16
|
+
const summary = {
|
|
17
|
+
group: {
|
|
18
|
+
groupId: "zyxwvutsrqponmlkjihgfedcb",
|
|
19
|
+
name: "Neighborhood",
|
|
20
|
+
description: "**Local** projects and shared plans",
|
|
21
|
+
founderUserId: "abcdefghijklmnopqrstuvwxy",
|
|
22
|
+
publicKey: "signing-key",
|
|
23
|
+
publicBoxKey: "box-key",
|
|
24
|
+
signature: "signature",
|
|
25
|
+
},
|
|
26
|
+
memberCount: 7,
|
|
27
|
+
myRole: peers_sdk_1.GroupMemberRole.Admin,
|
|
28
|
+
};
|
|
29
|
+
describe("GroupSummaryRow", () => {
|
|
30
|
+
it("shows description, member count, and local role in a clickable row", () => {
|
|
31
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(group_list_1.GroupSummaryRow, { summary: summary }));
|
|
32
|
+
expect(react_1.screen.getByText("Neighborhood")).toBeInTheDocument();
|
|
33
|
+
expect(react_1.screen.getByText("Local projects and shared plans")).toBeInTheDocument();
|
|
34
|
+
expect(react_1.screen.getByText("7 members")).toBeInTheDocument();
|
|
35
|
+
expect(react_1.screen.getByText("Admin")).toBeInTheDocument();
|
|
36
|
+
expect(react_1.screen.getByRole("link")).toHaveAttribute("href", `#identity/groups/${summary.group.groupId}`);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -3,5 +3,6 @@ interface GroupMembersUIProps {
|
|
|
3
3
|
groupId: string;
|
|
4
4
|
userContext: UserContext;
|
|
5
5
|
}
|
|
6
|
+
/** List group memberships and expose role-appropriate invite and member controls. */
|
|
6
7
|
export declare const GroupMembersUI: (props: GroupMembersUIProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
8
|
export {};
|
|
@@ -11,6 +11,7 @@ const loading_indicator_1 = require("../../components/loading-indicator");
|
|
|
11
11
|
const trust_level_badge_1 = require("../../components/trust-level-badge");
|
|
12
12
|
const hooks_1 = require("../../hooks");
|
|
13
13
|
const group_invite_panel_1 = require("./group-invite-panel");
|
|
14
|
+
/** List group memberships and expose role-appropriate invite and member controls. */
|
|
14
15
|
const GroupMembersUI = (props) => {
|
|
15
16
|
const { groupId, userContext } = props;
|
|
16
17
|
// Get the group's data context and tables
|
|
@@ -135,7 +136,7 @@ const GroupMembersUI = (props) => {
|
|
|
135
136
|
return (0, jsx_runtime_1.jsx)(loading_indicator_1.LoadingIndicator, {});
|
|
136
137
|
}
|
|
137
138
|
const { canEdit, userRole, currentUserId } = userPermissions;
|
|
138
|
-
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between align-items-center mb-3", children: [(0, jsx_runtime_1.jsx)("h6", { className: "mb-0", children: "Group Members" }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center gap-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: `alert alert-${userRole ? "primary" : "warning"} py-2 px-3 mb-0 d-flex align-items-center`, children: [(0, jsx_runtime_1.jsx)("i", { className: `bi ${userRole ? "bi-person-badge" : "bi-exclamation-triangle"} me-2` }), (0, jsx_runtime_1.jsxs)("strong", { children: ["Your role: ", (0, peers_sdk_1.getRoleLabel)(userRole)] })] }), founderData && founderData.founderUserId !== currentUserId && ((0, jsx_runtime_1.jsxs)("a", { href: `#
|
|
139
|
+
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between align-items-center mb-3", children: [(0, jsx_runtime_1.jsx)("h6", { className: "mb-0", children: "Group Members" }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center gap-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: `alert alert-${userRole ? "primary" : "warning"} py-2 px-3 mb-0 d-flex align-items-center`, children: [(0, jsx_runtime_1.jsx)("i", { className: `bi ${userRole ? "bi-person-badge" : "bi-exclamation-triangle"} me-2` }), (0, jsx_runtime_1.jsxs)("strong", { children: ["Your role: ", (0, peers_sdk_1.getRoleLabel)(userRole)] })] }), founderData && founderData.founderUserId !== currentUserId && ((0, jsx_runtime_1.jsxs)("a", { href: `#identity/people/${founderData.founderUserId}`, className: "alert alert-warning py-2 px-3 mb-0 d-flex align-items-center text-decoration-none", style: { cursor: "pointer" }, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-crown-fill me-2" }), (0, jsx_runtime_1.jsxs)("strong", { children: ["Founder: ", founderData.founderUser?.name || founderData.founderUserId] })] }))] })] }), canEdit && ((0, jsx_runtime_1.jsx)(group_invite_panel_1.GroupInvitePanel, { groupId: groupId, excludeUserIds: membersData.map((m) => m.member.userId), myRole: userRole })), !canEdit && userRole && ((0, jsx_runtime_1.jsxs)("div", { className: "alert alert-info", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-info-circle me-2" }), "You can view group members, but only admins and owners can manage membership."] })), membersData.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "alert alert-info", children: "No members in this group yet. Invite someone above." })) : ((0, jsx_runtime_1.jsx)("div", { className: "list-group", children: membersData.map(({ member, user }) => ((0, jsx_runtime_1.jsx)("div", { className: "list-group-item", children: (0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between align-items-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex-grow-1", children: (0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-person-fill me-2" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center", children: [(0, jsx_runtime_1.jsxs)("strong", { children: [user?.name || member.userId, member.userId === currentUserId && " (You)"] }), user?.source === "userDataContext" && ((0, jsx_runtime_1.jsxs)("span", { className: "badge bg-info ms-2", style: { fontSize: "0.7rem" }, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-person me-1" }), "Personal"] })), user?.source === "otherDataContexts" && ((0, jsx_runtime_1.jsxs)("span", { className: "badge bg-secondary ms-2", style: { fontSize: "0.7rem" }, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-people me-1" }), "Other Groups"] }))] }), user && user.userId !== user.name && ((0, jsx_runtime_1.jsx)("small", { className: "text-muted d-block", children: member.userId }))] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "me-2", children: (0, jsx_runtime_1.jsx)(trust_level_badge_1.TrustLevelBadge, { level: user?.trustLevel || peers_sdk_1.TrustLevel.Unknown }) }), (0, jsx_runtime_1.jsxs)("a", { href: `#identity/people/${member.userId}`, className: "btn btn-outline-primary btn-sm me-2", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi-eye me-1" }), "View"] }), canEdit ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("select", { className: "form-select form-select-sm me-2", style: { width: "120px" }, value: member.role, onChange: (e) => updateRole(member.groupMemberId, Number(e.target.value) || 0), children: [(0, jsx_runtime_1.jsx)("option", { value: peers_sdk_1.GroupMemberRole.None, children: "None" }), (0, jsx_runtime_1.jsx)("option", { value: peers_sdk_1.GroupMemberRole.Reader, children: "Reader" }), (0, jsx_runtime_1.jsx)("option", { value: peers_sdk_1.GroupMemberRole.Writer, children: "Writer" }), (0, jsx_runtime_1.jsx)("option", { value: peers_sdk_1.GroupMemberRole.Admin, children: "Admin" }), (0, jsx_runtime_1.jsx)("option", { value: peers_sdk_1.GroupMemberRole.Owner, children: "Owner" })] }), (0, jsx_runtime_1.jsx)("button", { className: "btn btn-outline-danger btn-sm", onClick: () => removeMember(member.groupMemberId), title: "Remove member", children: (0, jsx_runtime_1.jsx)("i", { className: "bi bi-trash" }) })] })) : (
|
|
139
140
|
/* Read-only role display for non-admins */
|
|
140
141
|
(0, jsx_runtime_1.jsx)("span", { className: "badge bg-secondary text-capitalize", children: (0, peers_sdk_1.getRoleLabel)(member.role) }))] })] }) }, member.groupMemberId))) })), (0, jsx_runtime_1.jsx)("div", { className: "mt-4", children: (0, jsx_runtime_1.jsxs)("small", { className: "text-muted", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Roles:" }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("strong", { children: "Reader:" }), " Can view group content", (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("strong", { children: "Writer:" }), " Can create and edit content", (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("strong", { children: "Admin:" }), " Can manage members and settings", (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("strong", { children: "Owner:" }), " Full control including group deletion"] }) })] }));
|
|
141
142
|
};
|
|
@@ -14,10 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
// Import all group screen components to ensure they register their routes
|
|
18
|
-
require("./group-list");
|
|
19
|
-
require("./group-details");
|
|
20
|
-
require("./group-members");
|
|
21
17
|
__exportStar(require("./group-details"), exports);
|
|
22
18
|
__exportStar(require("./group-list"), exports);
|
|
23
19
|
__exportStar(require("./group-members"), exports);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Identity routes and legacy aliases.
|
|
3
|
+
*
|
|
4
|
+
* Contacts, Groups, Devices, Account, and the old Invites paths all render
|
|
5
|
+
* inside the Identity app. Navigation rewrites those aliases before a tab is
|
|
6
|
+
* updated.
|
|
7
|
+
*/
|
|
8
|
+
/** Top-level Identity app path. Opening this lands on Activity. */
|
|
9
|
+
export declare const IDENTITY_APP_PATH = "identity";
|
|
10
|
+
/** Unified invite inbox. */
|
|
11
|
+
export declare const IDENTITY_ACTIVITY_PATH = "identity/activity";
|
|
12
|
+
/** Paste or scan an invite. */
|
|
13
|
+
export declare const IDENTITY_ACCEPT_PATH = "identity/activity/accept";
|
|
14
|
+
/** People (contacts) list. */
|
|
15
|
+
export declare const IDENTITY_PEOPLE_PATH = "identity/people";
|
|
16
|
+
/** Issue a one-off invitation to connect with a person. */
|
|
17
|
+
export declare const IDENTITY_PEOPLE_INVITE_PATH = "identity/people/invite";
|
|
18
|
+
/** Show the reusable profile invitation. */
|
|
19
|
+
export declare const IDENTITY_PEOPLE_SHARE_PATH = "identity/people/share";
|
|
20
|
+
/** Groups list. */
|
|
21
|
+
export declare const IDENTITY_GROUPS_PATH = "identity/groups";
|
|
22
|
+
/** Create a group. */
|
|
23
|
+
export declare const IDENTITY_GROUP_NEW_PATH = "identity/groups/new";
|
|
24
|
+
/** Devices list. */
|
|
25
|
+
export declare const IDENTITY_DEVICES_PATH = "identity/devices";
|
|
26
|
+
/** Source-device pairing flow. */
|
|
27
|
+
export declare const IDENTITY_PAIR_PATH = "identity/devices/pair";
|
|
28
|
+
/** Peers Services registration. */
|
|
29
|
+
export declare const IDENTITY_ACCOUNT_PATH = "identity/account";
|
|
30
|
+
/** Sidebar sections of the Identity app. */
|
|
31
|
+
export type IdentitySectionId = "activity" | "people" | "groups" | "devices" | "account";
|
|
32
|
+
/** Invite kinds that can filter Activity. */
|
|
33
|
+
export type IdentityInviteType = "contact" | "group" | "device";
|
|
34
|
+
/** Parsed Identity location, including detail and filter state. */
|
|
35
|
+
export interface IdentityLocation {
|
|
36
|
+
/** Active sidebar section. */
|
|
37
|
+
section: IdentitySectionId;
|
|
38
|
+
/** Canonical path without a query string. */
|
|
39
|
+
pathname: string;
|
|
40
|
+
/** Person id when a contact detail is open. */
|
|
41
|
+
personId?: string;
|
|
42
|
+
/** People action selected by a route. */
|
|
43
|
+
peopleAction?: "invite" | "share";
|
|
44
|
+
/** Group id when a group detail is open. */
|
|
45
|
+
groupId?: string;
|
|
46
|
+
/** True when the group creation panel is open. */
|
|
47
|
+
creatingGroup: boolean;
|
|
48
|
+
/** Device id when a device detail is open. */
|
|
49
|
+
deviceId?: string;
|
|
50
|
+
/** True on the pairing route. */
|
|
51
|
+
pairing: boolean;
|
|
52
|
+
/** True on the paste-or-scan route. */
|
|
53
|
+
accepting: boolean;
|
|
54
|
+
/** Activity list filter from the query string. */
|
|
55
|
+
activityFilter: {
|
|
56
|
+
type?: IdentityInviteType;
|
|
57
|
+
groupId?: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
interface ParsedRoute {
|
|
61
|
+
pathname: string;
|
|
62
|
+
query: URLSearchParams;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Split a hash path into its pathname and query.
|
|
66
|
+
*
|
|
67
|
+
* @param raw - Route with an optional leading `#` or `/` and an optional query.
|
|
68
|
+
*/
|
|
69
|
+
export declare function parseRoute(raw: string | null | undefined): ParsedRoute;
|
|
70
|
+
/**
|
|
71
|
+
* Rewrite legacy identity aliases to their canonical `identity/*` path.
|
|
72
|
+
*
|
|
73
|
+
* Unrelated routes are returned with only slash cleanup, so other apps keep
|
|
74
|
+
* their existing path casing. `devices?action=pair` becomes the pair route.
|
|
75
|
+
*
|
|
76
|
+
* @param raw - Requested route, possibly a legacy alias.
|
|
77
|
+
* @returns Canonical route, including any query that still applies.
|
|
78
|
+
*/
|
|
79
|
+
export declare function canonicalizeRoute(raw: string | null | undefined): string;
|
|
80
|
+
/**
|
|
81
|
+
* Whether a route belongs to the Identity app after alias rewrite.
|
|
82
|
+
*
|
|
83
|
+
* @param raw - Route to test.
|
|
84
|
+
*/
|
|
85
|
+
export declare function isIdentityPath(raw: string | null | undefined): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Sidebar section for an Identity route.
|
|
88
|
+
*
|
|
89
|
+
* @param raw - Identity route or legacy alias.
|
|
90
|
+
* @returns The section, or `undefined` when the route is not Identity.
|
|
91
|
+
*/
|
|
92
|
+
export declare function identitySectionFromPath(raw: string | null | undefined): IdentitySectionId | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* Detail, pairing, accept, and filter state for an Identity route.
|
|
95
|
+
*
|
|
96
|
+
* @param raw - Identity route or legacy alias.
|
|
97
|
+
* @returns Parsed location, or `undefined` when the route is not Identity.
|
|
98
|
+
*/
|
|
99
|
+
export declare function parseIdentityLocation(raw: string | null | undefined): IdentityLocation | undefined;
|
|
100
|
+
/**
|
|
101
|
+
* Activity route that shows one invite type, optionally one group.
|
|
102
|
+
*
|
|
103
|
+
* @param type - Invite type to show.
|
|
104
|
+
* @param groupId - Group to limit the list to.
|
|
105
|
+
*/
|
|
106
|
+
export declare function identityActivityFilterPath(type: IdentityInviteType, groupId?: string): string;
|
|
107
|
+
export {};
|