@peers-app/peers-ui 0.18.8 → 0.19.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,17 +2,20 @@ import {
2
2
  computePackageVersionHash,
3
3
  getEffectivePackagePrefs,
4
4
  getMe,
5
+ hasDeviceFollowOverride,
5
6
  type IDoc,
6
7
  type IPackage,
7
8
  type IPackageVersion,
8
9
  Packages,
9
10
  PackageVersions,
10
11
  packagePrefsVar,
12
+ rpcServerCalls,
11
13
  Users,
12
14
  updatePackagePrefs,
13
15
  } from "@peers-app/peers-sdk";
14
16
  import { useState } from "react";
15
17
  import { useObservable, useObservableState, usePromise } from "../../hooks";
18
+ import { activatePackageVersion, isGroupAdmin } from "./package-helpers";
16
19
 
17
20
  function formatDate(iso: string): string {
18
21
  try {
@@ -83,19 +86,7 @@ export const PackageVersionsList = (props: { pkg: IDoc<IPackage> }) => {
83
86
  async function activateVersion(pv: IPackageVersion) {
84
87
  setActivating(pv.packageVersionId);
85
88
  try {
86
- await updatePackagePrefs(pkg.packageId, {
87
- activePackageVersionId: pv.packageVersionId,
88
- });
89
-
90
- // For non-dev versions, also update the group default so other devices
91
- // can discover this as the recommended version.
92
- if (pv.versionTag !== "dev") {
93
- const current = await Packages().get(pkg.packageId);
94
- if (current) {
95
- current.activePackageVersionId = pv.packageVersionId;
96
- await Packages().signAndSave(current);
97
- }
98
- }
89
+ await activatePackageVersion(pkg.packageId, pv);
99
90
  await pkg.load();
100
91
  refreshKey(refreshKey() + 1);
101
92
  } catch (err: unknown) {
@@ -136,7 +127,25 @@ export const PackageVersionsList = (props: { pkg: IDoc<IPackage> }) => {
136
127
  pv.routesBundleFileHash,
137
128
  pv.uiBundleFileHash,
138
129
  );
139
- const updated = { ...pv, versionTag: newTag, packageVersionHash: pvHash, history };
130
+ const updated: IPackageVersion = {
131
+ ...pv,
132
+ versionTag: newTag,
133
+ packageVersionHash: pvHash,
134
+ history,
135
+ };
136
+
137
+ // Auto-sign with package author key if available (server-side, key never exposed)
138
+ const authorSig = await rpcServerCalls.signPackageAuthorVersion({
139
+ packageId: updated.packageId,
140
+ packageVersionId: updated.packageVersionId,
141
+ version: updated.version,
142
+ versionTag: newTag,
143
+ packageBundleFileHash: updated.packageBundleFileHash,
144
+ routesBundleFileHash: updated.routesBundleFileHash,
145
+ uiBundleFileHash: updated.uiBundleFileHash,
146
+ });
147
+ updated.packageAuthorSignature = authorSig;
148
+
140
149
  await PackageVersions().signAndSave(updated, { saveAsSnapshot: true });
141
150
  refreshKey(refreshKey() + 1);
142
151
  } catch (err: unknown) {
@@ -164,7 +173,17 @@ export const PackageVersionsList = (props: { pkg: IDoc<IPackage> }) => {
164
173
  async function pinVersion() {
165
174
  setPinning(true);
166
175
  try {
167
- await updatePackagePrefs(pkg.packageId, { pinned: true });
176
+ const pinsGroup = !hasDeviceFollowOverride(devicePrefs) && (await isGroupAdmin());
177
+
178
+ if (pinsGroup) {
179
+ const current = await Packages().get(pkg.packageId);
180
+ if (current) {
181
+ current.versionFollowRange = "pinned";
182
+ await Packages().signAndSave(current);
183
+ }
184
+ } else {
185
+ await updatePackagePrefs(pkg.packageId, { pinned: true });
186
+ }
168
187
  await pkg.load();
169
188
  } catch (err: unknown) {
170
189
  alert(`Failed to pin version: ${err instanceof Error ? err.message : String(err)}`);
@@ -1,4 +1,5 @@
1
- import type { IAppNav, IPackage } from "@peers-app/peers-sdk";
1
+ import type { IAppNav } from "@peers-app/peers-sdk";
2
+ import type { IPackageWithNavs } from "../ui-router/routes-loader";
2
3
 
3
4
  export { accountApp } from "./account.app";
4
5
  export { assistantsApp } from "./assistants.app";
@@ -73,12 +74,13 @@ export const systemApps: IAppNav[] = [
73
74
  ];
74
75
 
75
76
  // Virtual system package for tabs integration
76
- export const systemPackage: IPackage = {
77
+ export const systemPackage: IPackageWithNavs = {
77
78
  packageId: "system-apps",
78
79
  name: "System Apps",
79
80
  description: "Core system functionality and management tools",
80
81
  appNavs: systemApps,
81
82
  createdBy: "system",
82
83
  disabled: false,
84
+ publishPublicKey: "",
83
85
  signature: "",
84
86
  };
@@ -2,14 +2,13 @@ import {
2
2
  groupDeviceVar,
3
3
  groupUserVar,
4
4
  type IAppNav,
5
- type IPackage,
6
5
  newid,
7
6
  type Observable,
8
7
  observable,
9
8
  } from "@peers-app/peers-sdk";
10
9
  import { _mainContentPath } from "../globals";
11
10
  import { systemPackage } from "../system-apps";
12
- import { allPackages } from "../ui-router/routes-loader";
11
+ import { allPackages, type IPackageWithNavs } from "../ui-router/routes-loader";
13
12
 
14
13
  export interface TabState {
15
14
  tabId: string;
@@ -158,7 +157,7 @@ export const handleMainPathChanged = (
158
157
  }
159
158
  };
160
159
 
161
- type AppInfo = { navItem: IAppNav; package: IPackage };
160
+ type AppInfo = { navItem: IAppNav; package: IPackageWithNavs };
162
161
 
163
162
  export function determineAppFromPath(path: string): AppInfo | undefined {
164
163
  const launcherInfo = {
@@ -186,7 +185,7 @@ export function determineAppFromPath(path: string): AppInfo | undefined {
186
185
  .flatMap((pkg) => {
187
186
  const navs = pkg.appNavs;
188
187
  if (!navs) return [];
189
- return navs.map((navItem) => ({ navItem, package: pkg as IPackage }));
188
+ return navs.map((navItem) => ({ navItem, package: pkg }));
190
189
  });
191
190
 
192
191
  // Find the most relevant app based on path
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  debounceByArgs,
3
+ type IAppNav,
3
4
  type IPackage,
4
5
  type IPeersPackageRoutes,
5
6
  type IPeersUIRoute,
@@ -11,7 +12,10 @@ import {
11
12
  } from "@peers-app/peers-sdk";
12
13
  import "../ui-defaults";
13
14
 
14
- export const allPackages = observable<IPackage[]>([]);
15
+ /** IPackage enriched with appNavs from the active PackageVersion. */
16
+ export type IPackageWithNavs = IPackage & { appNavs?: IAppNav[] };
17
+
18
+ export const allPackages = observable<IPackageWithNavs[]>([]);
15
19
  let allRoutesLoaded = false;
16
20
 
17
21
  const prefsSubscribed = new Set<string>();
@@ -41,10 +45,9 @@ function subscribeToPrefs(packageId: string) {
41
45
  }
42
46
 
43
47
  /**
44
- * Enrich an IPackage with appNavs from the device's active PV record.
45
- * Falls back to `pkg.appNavs` for backward compat with older data.
48
+ * Enrich an IPackage with appNavs from the device's active PackageVersion record.
46
49
  */
47
- async function enrichWithPvAppNavs(pkg: IPackage): Promise<IPackage> {
50
+ async function enrichWithPvAppNavs(pkg: IPackage): Promise<IPackageWithNavs> {
48
51
  let pvId = pkg.activePackageVersionId;
49
52
  try {
50
53
  const pvar = packagePrefsVar(pkg.packageId);
@@ -60,7 +63,7 @@ async function enrichWithPvAppNavs(pkg: IPackage): Promise<IPackage> {
60
63
  return { ...pkg, appNavs: pv.appNavs };
61
64
  }
62
65
  } catch {
63
- /* fall through to pkg.appNavs */
66
+ /* PV not found */
64
67
  }
65
68
  return pkg;
66
69
  }
@@ -115,7 +118,7 @@ type RoutesBundleWindow = Window & {
115
118
  };
116
119
 
117
120
  const routeLoadingPromises: Record<string, Promise<unknown>> = {};
118
- function loadRoutesBundle(pkg: IPackage, forceRefresh?: boolean): Promise<unknown> {
121
+ function loadRoutesBundle(pkg: IPackageWithNavs, forceRefresh?: boolean): Promise<unknown> {
119
122
  // Dynamically import the bundle
120
123
  let importPromise: Promise<unknown> = routeLoadingPromises[pkg.packageId];
121
124
  if (!importPromise || forceRefresh) {