@peers-app/peers-ui 0.16.0 → 0.16.2
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/dist/components/router.js +4 -0
- package/dist/peers-services-registration.d.ts +5 -0
- package/dist/peers-services-registration.js +18 -0
- package/dist/screens/account/account-screen.d.ts +1 -0
- package/dist/screens/account/account-screen.js +53 -0
- package/dist/screens/setup-user.js +1 -1
- package/dist/screens/welcome-modal.js +12 -0
- package/dist/system-apps/account.app.d.ts +2 -0
- package/dist/system-apps/account.app.js +8 -0
- package/dist/system-apps/index.d.ts +1 -0
- package/dist/system-apps/index.js +5 -1
- package/package.json +3 -3
- package/src/components/router.tsx +4 -0
- package/src/peers-services-registration.ts +17 -0
- package/src/screens/account/account-screen.tsx +135 -0
- package/src/screens/setup-user.tsx +1 -1
- package/src/screens/welcome-modal.tsx +14 -0
- package/src/system-apps/account.app.ts +7 -0
- package/src/system-apps/index.ts +3 -0
|
@@ -37,6 +37,7 @@ 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");
|
|
40
41
|
const assistant_details_1 = require("../screens/assistants/assistant-details");
|
|
41
42
|
const assistant_list_1 = require("../screens/assistants/assistant-list");
|
|
42
43
|
const package_details_1 = require("../screens/packages/package-details");
|
|
@@ -87,6 +88,9 @@ function Router({ path: providedPath } = {}) {
|
|
|
87
88
|
if (path === "settings" || path === "profile") {
|
|
88
89
|
return (0, jsx_runtime_1.jsx)(settings_page_1.SettingsPage, {});
|
|
89
90
|
}
|
|
91
|
+
if (path === "account") {
|
|
92
|
+
return (0, jsx_runtime_1.jsx)(account_screen_1.AccountScreen, {});
|
|
93
|
+
}
|
|
90
94
|
if (path === "threads" || path === "") {
|
|
91
95
|
return (0, jsx_runtime_1.jsx)(channel_view_1.ChannelMessages, {});
|
|
92
96
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Set when the user has completed challenge-response registration (ISO timestamp). */
|
|
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;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.servicesTokenPvar = exports.registrationStatusPvar = void 0;
|
|
4
|
+
exports.persistPeersServicesRegistration = persistPeersServicesRegistration;
|
|
5
|
+
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
6
|
+
/** Set when the user has completed challenge-response registration (ISO timestamp). */
|
|
7
|
+
exports.registrationStatusPvar = (0, peers_sdk_1.userVar)("PEERS_SERVICES_REGISTERED", {
|
|
8
|
+
defaultValue: "",
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function AccountScreen(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccountScreen = AccountScreen;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const tooltip_1 = require("../../components/tooltip");
|
|
8
|
+
const hooks_1 = require("../../hooks");
|
|
9
|
+
const peers_services_registration_1 = require("../../peers-services-registration");
|
|
10
|
+
const INFO_TEXT = `### What does registering do?
|
|
11
|
+
|
|
12
|
+
Registering your account links your **public key** with the Peers cloud service. This enables cloud-powered features like:
|
|
13
|
+
|
|
14
|
+
- **AI Assistant** - use the built-in assistant powered by hosted LLM inference
|
|
15
|
+
- **Cloud relay** - improved connectivity between your devices
|
|
16
|
+
- **Future services** - new cloud features as they become available
|
|
17
|
+
|
|
18
|
+
### Is it safe?
|
|
19
|
+
|
|
20
|
+
Registration uses a **cryptographic challenge-response** to prove you own your secret key.
|
|
21
|
+
Your **secret key never leaves your device**. Only your public key is shared with the server.
|
|
22
|
+
|
|
23
|
+
### Do I have to register?
|
|
24
|
+
|
|
25
|
+
No. Peers works fully in local/peer-to-peer mode without registration. Cloud features simply won't be available until you register.`;
|
|
26
|
+
function AccountScreen() {
|
|
27
|
+
const [registrationStatus] = (0, hooks_1.useObservable)(peers_services_registration_1.registrationStatusPvar);
|
|
28
|
+
const [isRegistering, setIsRegistering] = (0, react_1.useState)(false);
|
|
29
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
30
|
+
const [success, setSuccess] = (0, react_1.useState)(null);
|
|
31
|
+
const isRegistered = !!registrationStatus;
|
|
32
|
+
const handleRegister = async () => {
|
|
33
|
+
setIsRegistering(true);
|
|
34
|
+
setError(null);
|
|
35
|
+
setSuccess(null);
|
|
36
|
+
try {
|
|
37
|
+
const token = await peers_sdk_1.rpcServerCalls.registerWithPeersServices();
|
|
38
|
+
(0, peers_services_registration_1.persistPeersServicesRegistration)(token);
|
|
39
|
+
setSuccess("Account registered successfully.");
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
setError(`Registration failed: ${String(err)}`);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
setIsRegistering(false);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "container-fluid p-3", style: { maxWidth: 600 }, children: [(0, jsx_runtime_1.jsxs)("h4", { className: "mb-3 d-flex align-items-center gap-2", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-person-circle" }), "Account"] }), error && ((0, jsx_runtime_1.jsxs)("div", { className: "alert alert-danger alert-dismissible", role: "alert", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-exclamation-triangle me-2" }), error, (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn-close", onClick: () => setError(null), "aria-label": "Close" })] })), success && ((0, jsx_runtime_1.jsxs)("div", { className: "alert alert-success alert-dismissible", role: "alert", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-check-circle me-2" }), success, (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn-close", onClick: () => setSuccess(null), "aria-label": "Close" })] })), (0, jsx_runtime_1.jsx)("div", { className: "card", children: (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsxs)("h5", { className: "card-title d-flex align-items-center gap-2", children: ["Peers Services Registration", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: INFO_TEXT })] }), isRegistered ? ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-check-circle-fill text-success", style: { fontSize: "1.2em" } }), (0, jsx_runtime_1.jsx)("span", { children: "Registered" })] }), (0, jsx_runtime_1.jsxs)("small", { className: "text-muted", children: ["Registered on", " ", new Date(registrationStatus).toLocaleDateString(undefined, {
|
|
49
|
+
year: "numeric",
|
|
50
|
+
month: "long",
|
|
51
|
+
day: "numeric",
|
|
52
|
+
})] })] })) : ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-muted mb-3", children: "Register your account to enable cloud-powered features like the AI assistant." }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-primary", onClick: handleRegister, disabled: isRegistering, children: isRegistering ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "spinner-border spinner-border-sm me-2" }), "Registering..."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-cloud-upload me-2" }), "Register Account"] })) })] }))] }) })] }));
|
|
53
|
+
}
|
|
@@ -31,7 +31,7 @@ const SetupUser = () => {
|
|
|
31
31
|
.catch((err) => {
|
|
32
32
|
console.error("Error auto-installing peers-core:", err);
|
|
33
33
|
});
|
|
34
|
-
// Success - reload the app
|
|
34
|
+
// Success - reload the app (peers-services registration runs after Welcome modal)
|
|
35
35
|
window.location.reload();
|
|
36
36
|
}
|
|
37
37
|
catch (err) {
|
|
@@ -5,6 +5,16 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
6
6
|
const react_1 = require("react");
|
|
7
7
|
const hooks_1 = require("../hooks");
|
|
8
|
+
const peers_services_registration_1 = require("../peers-services-registration");
|
|
9
|
+
async function tryRegisterPeersServicesAfterWelcome() {
|
|
10
|
+
try {
|
|
11
|
+
const token = await peers_sdk_1.rpcServerCalls.registerWithPeersServices();
|
|
12
|
+
(0, peers_services_registration_1.persistPeersServicesRegistration)(token);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
console.warn("Peers services registration after welcome modal:", err);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
8
18
|
function WelcomeModal({ colorMode, onClose }) {
|
|
9
19
|
const [displayName, setDisplayName] = (0, react_1.useState)("");
|
|
10
20
|
const [deviceName, setDeviceName] = (0, react_1.useState)("");
|
|
@@ -60,6 +70,7 @@ function WelcomeModal({ colorMode, onClose }) {
|
|
|
60
70
|
device.name = deviceName.trim();
|
|
61
71
|
await devicesTable.save(device);
|
|
62
72
|
}
|
|
73
|
+
await tryRegisterPeersServicesAfterWelcome();
|
|
63
74
|
// Mark the welcome modal as shown
|
|
64
75
|
(0, peers_sdk_1.hasShownWelcomeModal)(true);
|
|
65
76
|
onClose();
|
|
@@ -71,6 +82,7 @@ function WelcomeModal({ colorMode, onClose }) {
|
|
|
71
82
|
}
|
|
72
83
|
};
|
|
73
84
|
const handleSkip = async () => {
|
|
85
|
+
await tryRegisterPeersServicesAfterWelcome();
|
|
74
86
|
// Mark as shown even if skipped
|
|
75
87
|
(0, peers_sdk_1.hasShownWelcomeModal)(true);
|
|
76
88
|
onClose();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.systemPackage = exports.systemApps = exports.workflowsApp = exports.variablesApp = exports.typesApp = exports.toolsApp = exports.threadsApp = exports.settingsApp = exports.searchApp = exports.packagesApp = exports.networkViewerApp = exports.mobileSettingsApp = exports.joinGroupApp = exports.groupsApp = exports.dataExplorerApp = exports.contactsApp = exports.consoleLogsApp = exports.assistantsApp = void 0;
|
|
3
|
+
exports.systemPackage = exports.systemApps = exports.workflowsApp = exports.variablesApp = exports.typesApp = exports.toolsApp = exports.threadsApp = exports.settingsApp = exports.searchApp = exports.packagesApp = exports.networkViewerApp = exports.mobileSettingsApp = exports.joinGroupApp = exports.groupsApp = exports.dataExplorerApp = exports.contactsApp = exports.consoleLogsApp = exports.assistantsApp = exports.accountApp = void 0;
|
|
4
|
+
var account_app_1 = require("./account.app");
|
|
5
|
+
Object.defineProperty(exports, "accountApp", { enumerable: true, get: function () { return account_app_1.accountApp; } });
|
|
4
6
|
var assistants_app_1 = require("./assistants.app");
|
|
5
7
|
Object.defineProperty(exports, "assistantsApp", { enumerable: true, get: function () { return assistants_app_1.assistantsApp; } });
|
|
6
8
|
var console_logs_app_1 = require("./console-logs.app");
|
|
@@ -34,6 +36,7 @@ var variables_app_1 = require("./variables.app");
|
|
|
34
36
|
Object.defineProperty(exports, "variablesApp", { enumerable: true, get: function () { return variables_app_1.variablesApp; } });
|
|
35
37
|
var workflows_app_1 = require("./workflows.app");
|
|
36
38
|
Object.defineProperty(exports, "workflowsApp", { enumerable: true, get: function () { return workflows_app_1.workflowsApp; } });
|
|
39
|
+
const account_app_2 = require("./account.app");
|
|
37
40
|
const assistants_app_2 = require("./assistants.app");
|
|
38
41
|
const console_logs_app_2 = require("./console-logs.app");
|
|
39
42
|
const contacts_app_2 = require("./contacts.app");
|
|
@@ -75,6 +78,7 @@ exports.systemApps = [
|
|
|
75
78
|
groups_app_2.groupsApp,
|
|
76
79
|
join_group_app_2.joinGroupApp,
|
|
77
80
|
// User & Settings Apps
|
|
81
|
+
account_app_2.accountApp,
|
|
78
82
|
settings_app_2.settingsApp,
|
|
79
83
|
// Mobile Settings (only in React Native)
|
|
80
84
|
...(isReactNative() ? [mobile_settings_app_2.mobileSettingsApp] : []),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peers-app/peers-ui",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/peers-app/peers-ui.git"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"lint:fix": "biome check --write ."
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@peers-app/peers-sdk": "^0.16.
|
|
31
|
+
"@peers-app/peers-sdk": "^0.16.2",
|
|
32
32
|
"bootstrap": "^5.3.3",
|
|
33
33
|
"react": "^18.0.0",
|
|
34
34
|
"react-dom": "^18.0.0"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@babel/preset-env": "^7.24.5",
|
|
40
40
|
"@babel/preset-react": "^7.24.1",
|
|
41
41
|
"@babel/preset-typescript": "^7.27.1",
|
|
42
|
-
"@peers-app/peers-sdk": "0.16.
|
|
42
|
+
"@peers-app/peers-sdk": "0.16.2",
|
|
43
43
|
"@testing-library/dom": "^10.4.0",
|
|
44
44
|
"@testing-library/jest-dom": "^6.6.3",
|
|
45
45
|
"@testing-library/react": "^16.3.0",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as globals from "../globals";
|
|
2
2
|
import { useObservable } from "../hooks";
|
|
3
|
+
import { AccountScreen } from "../screens/account/account-screen";
|
|
3
4
|
import { AssistantDetails } from "../screens/assistants/assistant-details";
|
|
4
5
|
import { AssistantList } from "../screens/assistants/assistant-list";
|
|
5
6
|
import { PackageDetails } from "../screens/packages/package-details";
|
|
@@ -54,6 +55,9 @@ export function Router({ path: providedPath }: { path?: string } = {}) {
|
|
|
54
55
|
if (path === "settings" || path === "profile") {
|
|
55
56
|
return <SettingsPage />;
|
|
56
57
|
}
|
|
58
|
+
if (path === "account") {
|
|
59
|
+
return <AccountScreen />;
|
|
60
|
+
}
|
|
57
61
|
if (path === "threads" || path === "") {
|
|
58
62
|
return <ChannelMessages />;
|
|
59
63
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { userVar } from "@peers-app/peers-sdk";
|
|
2
|
+
|
|
3
|
+
/** Set when the user has completed challenge-response registration (ISO timestamp). */
|
|
4
|
+
export const registrationStatusPvar = userVar<string>("PEERS_SERVICES_REGISTERED", {
|
|
5
|
+
defaultValue: "",
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const servicesTokenPvar = userVar<string>("PEERS_SERVICES_TOKEN", {
|
|
9
|
+
isSecret: true,
|
|
10
|
+
defaultValue: "",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/** Store JWT and local "registered" flag after a successful `registerWithPeersServices` call. */
|
|
14
|
+
export function persistPeersServicesRegistration(token: string): void {
|
|
15
|
+
servicesTokenPvar(token);
|
|
16
|
+
registrationStatusPvar(new Date().toISOString());
|
|
17
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { rpcServerCalls } from "@peers-app/peers-sdk";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Tooltip } from "../../components/tooltip";
|
|
4
|
+
import { useObservable } from "../../hooks";
|
|
5
|
+
import {
|
|
6
|
+
persistPeersServicesRegistration,
|
|
7
|
+
registrationStatusPvar,
|
|
8
|
+
} from "../../peers-services-registration";
|
|
9
|
+
|
|
10
|
+
const INFO_TEXT = `### What does registering do?
|
|
11
|
+
|
|
12
|
+
Registering your account links your **public key** with the Peers cloud service. This enables cloud-powered features like:
|
|
13
|
+
|
|
14
|
+
- **AI Assistant** - use the built-in assistant powered by hosted LLM inference
|
|
15
|
+
- **Cloud relay** - improved connectivity between your devices
|
|
16
|
+
- **Future services** - new cloud features as they become available
|
|
17
|
+
|
|
18
|
+
### Is it safe?
|
|
19
|
+
|
|
20
|
+
Registration uses a **cryptographic challenge-response** to prove you own your secret key.
|
|
21
|
+
Your **secret key never leaves your device**. Only your public key is shared with the server.
|
|
22
|
+
|
|
23
|
+
### Do I have to register?
|
|
24
|
+
|
|
25
|
+
No. Peers works fully in local/peer-to-peer mode without registration. Cloud features simply won't be available until you register.`;
|
|
26
|
+
|
|
27
|
+
export function AccountScreen() {
|
|
28
|
+
const [registrationStatus] = useObservable(registrationStatusPvar);
|
|
29
|
+
const [isRegistering, setIsRegistering] = useState(false);
|
|
30
|
+
const [error, setError] = useState<string | null>(null);
|
|
31
|
+
const [success, setSuccess] = useState<string | null>(null);
|
|
32
|
+
|
|
33
|
+
const isRegistered = !!registrationStatus;
|
|
34
|
+
|
|
35
|
+
const handleRegister = async () => {
|
|
36
|
+
setIsRegistering(true);
|
|
37
|
+
setError(null);
|
|
38
|
+
setSuccess(null);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const token = await rpcServerCalls.registerWithPeersServices();
|
|
42
|
+
persistPeersServicesRegistration(token);
|
|
43
|
+
setSuccess("Account registered successfully.");
|
|
44
|
+
} catch (err) {
|
|
45
|
+
setError(`Registration failed: ${String(err)}`);
|
|
46
|
+
} finally {
|
|
47
|
+
setIsRegistering(false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="container-fluid p-3" style={{ maxWidth: 600 }}>
|
|
53
|
+
<h4 className="mb-3 d-flex align-items-center gap-2">
|
|
54
|
+
<i className="bi bi-person-circle" />
|
|
55
|
+
Account
|
|
56
|
+
</h4>
|
|
57
|
+
|
|
58
|
+
{error && (
|
|
59
|
+
<div className="alert alert-danger alert-dismissible" role="alert">
|
|
60
|
+
<i className="bi bi-exclamation-triangle me-2" />
|
|
61
|
+
{error}
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
className="btn-close"
|
|
65
|
+
onClick={() => setError(null)}
|
|
66
|
+
aria-label="Close"
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
{success && (
|
|
72
|
+
<div className="alert alert-success alert-dismissible" role="alert">
|
|
73
|
+
<i className="bi bi-check-circle me-2" />
|
|
74
|
+
{success}
|
|
75
|
+
<button
|
|
76
|
+
type="button"
|
|
77
|
+
className="btn-close"
|
|
78
|
+
onClick={() => setSuccess(null)}
|
|
79
|
+
aria-label="Close"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
<div className="card">
|
|
85
|
+
<div className="card-body">
|
|
86
|
+
<h5 className="card-title d-flex align-items-center gap-2">
|
|
87
|
+
Peers Services Registration
|
|
88
|
+
<Tooltip markdownContent={INFO_TEXT} />
|
|
89
|
+
</h5>
|
|
90
|
+
|
|
91
|
+
{isRegistered ? (
|
|
92
|
+
<div>
|
|
93
|
+
<div className="d-flex align-items-center gap-2 mb-2">
|
|
94
|
+
<i className="bi bi-check-circle-fill text-success" style={{ fontSize: "1.2em" }} />
|
|
95
|
+
<span>Registered</span>
|
|
96
|
+
</div>
|
|
97
|
+
<small className="text-muted">
|
|
98
|
+
Registered on{" "}
|
|
99
|
+
{new Date(registrationStatus as string).toLocaleDateString(undefined, {
|
|
100
|
+
year: "numeric",
|
|
101
|
+
month: "long",
|
|
102
|
+
day: "numeric",
|
|
103
|
+
})}
|
|
104
|
+
</small>
|
|
105
|
+
</div>
|
|
106
|
+
) : (
|
|
107
|
+
<div>
|
|
108
|
+
<p className="text-muted mb-3">
|
|
109
|
+
Register your account to enable cloud-powered features like the AI assistant.
|
|
110
|
+
</p>
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
113
|
+
className="btn btn-primary"
|
|
114
|
+
onClick={handleRegister}
|
|
115
|
+
disabled={isRegistering}
|
|
116
|
+
>
|
|
117
|
+
{isRegistering ? (
|
|
118
|
+
<>
|
|
119
|
+
<span className="spinner-border spinner-border-sm me-2" />
|
|
120
|
+
Registering...
|
|
121
|
+
</>
|
|
122
|
+
) : (
|
|
123
|
+
<>
|
|
124
|
+
<i className="bi bi-cloud-upload me-2" />
|
|
125
|
+
Register Account
|
|
126
|
+
</>
|
|
127
|
+
)}
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
@@ -46,7 +46,7 @@ export const SetupUser = () => {
|
|
|
46
46
|
console.error("Error auto-installing peers-core:", err);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
// Success - reload the app
|
|
49
|
+
// Success - reload the app (peers-services registration runs after Welcome modal)
|
|
50
50
|
window.location.reload();
|
|
51
51
|
} catch (err) {
|
|
52
52
|
setError(`Error creating new user: ${String(err).replaceAll("\n", " ")}`);
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
getUserContext,
|
|
5
5
|
hasShownWelcomeModal,
|
|
6
6
|
type IDevice,
|
|
7
|
+
rpcServerCalls,
|
|
7
8
|
TrustLevel,
|
|
8
9
|
thisDeviceId,
|
|
9
10
|
Users,
|
|
@@ -11,12 +12,22 @@ import {
|
|
|
11
12
|
import type React from "react";
|
|
12
13
|
import { useEffect, useState } from "react";
|
|
13
14
|
import { useObservable, usePromise } from "../hooks";
|
|
15
|
+
import { persistPeersServicesRegistration } from "../peers-services-registration";
|
|
14
16
|
|
|
15
17
|
interface WelcomeModalProps {
|
|
16
18
|
colorMode: string;
|
|
17
19
|
onClose: () => void;
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
async function tryRegisterPeersServicesAfterWelcome(): Promise<void> {
|
|
23
|
+
try {
|
|
24
|
+
const token = await rpcServerCalls.registerWithPeersServices();
|
|
25
|
+
persistPeersServicesRegistration(token);
|
|
26
|
+
} catch (err: unknown) {
|
|
27
|
+
console.warn("Peers services registration after welcome modal:", err);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
export function WelcomeModal({ colorMode, onClose }: WelcomeModalProps) {
|
|
21
32
|
const [displayName, setDisplayName] = useState("");
|
|
22
33
|
const [deviceName, setDeviceName] = useState("");
|
|
@@ -79,6 +90,8 @@ export function WelcomeModal({ colorMode, onClose }: WelcomeModalProps) {
|
|
|
79
90
|
await devicesTable.save(device);
|
|
80
91
|
}
|
|
81
92
|
|
|
93
|
+
await tryRegisterPeersServicesAfterWelcome();
|
|
94
|
+
|
|
82
95
|
// Mark the welcome modal as shown
|
|
83
96
|
hasShownWelcomeModal(true);
|
|
84
97
|
|
|
@@ -91,6 +104,7 @@ export function WelcomeModal({ colorMode, onClose }: WelcomeModalProps) {
|
|
|
91
104
|
};
|
|
92
105
|
|
|
93
106
|
const handleSkip = async () => {
|
|
107
|
+
await tryRegisterPeersServicesAfterWelcome();
|
|
94
108
|
// Mark as shown even if skipped
|
|
95
109
|
hasShownWelcomeModal(true);
|
|
96
110
|
onClose();
|
package/src/system-apps/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IAppNav, IPackage } from "@peers-app/peers-sdk";
|
|
2
2
|
|
|
3
|
+
export { accountApp } from "./account.app";
|
|
3
4
|
export { assistantsApp } from "./assistants.app";
|
|
4
5
|
export { consoleLogsApp } from "./console-logs.app";
|
|
5
6
|
export { contactsApp } from "./contacts.app";
|
|
@@ -18,6 +19,7 @@ export { typesApp } from "./types.app";
|
|
|
18
19
|
export { variablesApp } from "./variables.app";
|
|
19
20
|
export { workflowsApp } from "./workflows.app";
|
|
20
21
|
|
|
22
|
+
import { accountApp } from "./account.app";
|
|
21
23
|
import { assistantsApp } from "./assistants.app";
|
|
22
24
|
import { consoleLogsApp } from "./console-logs.app";
|
|
23
25
|
import { contactsApp } from "./contacts.app";
|
|
@@ -64,6 +66,7 @@ export const systemApps: IAppNav[] = [
|
|
|
64
66
|
joinGroupApp,
|
|
65
67
|
|
|
66
68
|
// User & Settings Apps
|
|
69
|
+
accountApp,
|
|
67
70
|
settingsApp,
|
|
68
71
|
// Mobile Settings (only in React Native)
|
|
69
72
|
...(isReactNative() ? [mobileSettingsApp] : []),
|