@aglyn/aglyn 1.0.0-beta.275 → 1.0.0-beta.282
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/package.json +11 -11
- package/src/lib/app-utils/account-preferences.d.ts +146 -11
- package/src/lib/app-utils/account-preferences.js +266 -18
- package/src/lib/app-utils/account-preferences.js.map +1 -1
- package/src/lib/app-utils/activity-presenter.js +2 -26
- package/src/lib/app-utils/activity-presenter.js.map +1 -1
- package/src/lib/app-utils/activity-type-labels.d.ts +18 -0
- package/src/lib/app-utils/activity-type-labels.js +46 -0
- package/src/lib/app-utils/activity-type-labels.js.map +1 -0
- package/src/lib/app-utils/artifact-provenance.d.ts +3 -1
- package/src/lib/app-utils/artifact-provenance.js.map +1 -1
- package/src/lib/app-utils/bundle-intent.d.ts +37 -0
- package/src/lib/app-utils/bundle-intent.js +47 -3
- package/src/lib/app-utils/bundle-intent.js.map +1 -1
- package/src/lib/app-utils/compose-reusable-components.d.ts +17 -1
- package/src/lib/app-utils/compose-reusable-components.js +80 -5
- package/src/lib/app-utils/compose-reusable-components.js.map +1 -1
- package/src/lib/app-utils/docs-help-sections.generated.js +13 -1
- package/src/lib/app-utils/docs-help-sections.generated.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +23 -11
- package/src/lib/app-utils/docs-help.generated.js +64 -7
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.js +360 -52
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/health-report.d.ts +62 -2
- package/src/lib/app-utils/health-report.js +78 -2
- package/src/lib/app-utils/health-report.js.map +1 -1
- package/src/lib/app-utils/native-nav.d.ts +39 -4
- package/src/lib/app-utils/native-nav.js +69 -3
- package/src/lib/app-utils/native-nav.js.map +1 -1
- package/src/lib/app-utils/native-themes.d.ts +28 -17
- package/src/lib/app-utils/native-themes.js +123 -45
- package/src/lib/app-utils/native-themes.js.map +1 -1
- package/src/lib/app-utils/operator-alerts.js +4 -2
- package/src/lib/app-utils/operator-alerts.js.map +1 -1
- package/src/lib/app-utils/org-permission-editor.d.ts +75 -0
- package/src/lib/app-utils/org-permission-editor.js +124 -0
- package/src/lib/app-utils/org-permission-editor.js.map +1 -0
- package/src/lib/app-utils/org-permissions.d.ts +36 -0
- package/src/lib/app-utils/org-permissions.js +78 -26
- package/src/lib/app-utils/org-permissions.js.map +1 -1
- package/src/lib/app-utils/org-role-grants.d.ts +59 -9
- package/src/lib/app-utils/org-role-grants.js +97 -10
- package/src/lib/app-utils/org-role-grants.js.map +1 -1
- package/src/lib/app-utils/plan-entitlements.d.ts +13 -0
- package/src/lib/app-utils/plan-entitlements.js +22 -0
- package/src/lib/app-utils/plan-entitlements.js.map +1 -1
- package/src/lib/app-utils/release-flags.d.ts +52 -1
- package/src/lib/app-utils/release-flags.js +68 -2
- package/src/lib/app-utils/release-flags.js.map +1 -1
- package/src/lib/app-utils/reusable-component-keys.d.ts +13 -0
- package/src/lib/app-utils/reusable-component-keys.js +12 -0
- package/src/lib/app-utils/reusable-component-keys.js.map +1 -1
- package/src/lib/app-utils/screen-analytics-aggregate.d.ts +34 -0
- package/src/lib/app-utils/screen-analytics-aggregate.js +37 -0
- package/src/lib/app-utils/screen-analytics-aggregate.js.map +1 -1
- package/src/lib/app-utils/site-care.d.ts +35 -0
- package/src/lib/app-utils/site-care.js +48 -0
- package/src/lib/app-utils/site-care.js.map +1 -1
- package/src/lib/app-utils/site-font-pairings.d.ts +59 -0
- package/src/lib/app-utils/site-font-pairings.js +514 -0
- package/src/lib/app-utils/site-font-pairings.js.map +1 -0
- package/src/lib/app-utils/site-starter.d.ts +136 -0
- package/src/lib/app-utils/site-starter.js +116 -0
- package/src/lib/app-utils/site-starter.js.map +1 -0
- package/src/lib/plugin-manager/feature-plugins.d.ts +32 -0
- package/src/lib/plugin-manager/feature-plugins.js +12 -0
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js +27 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/plugin-entitlements.d.ts +37 -0
- package/src/lib/plugin-manager/plugin-entitlements.js +84 -2
- package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -1
- package/src/lib/plugin-manager/plugin-permissions.d.ts +7 -0
- package/src/lib/plugin-manager/plugin-permissions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-product-catalog.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-product-catalog.js.map +1 -1
- package/src/lib/plugin-manager/plugin-site-starters.d.ts +73 -0
- package/src/lib/plugin-manager/plugin-site-starters.js +30 -0
- package/src/lib/plugin-manager/plugin-site-starters.js.map +1 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.d.ts +8 -1
- package/src/lib/plugin-manager/plugin-sitemap-sections.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/aglyn",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.282",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -37,16 +37,16 @@
|
|
|
37
37
|
"./package.json": "./package.json"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
41
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
42
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
43
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
44
|
-
"@aglyn/shared-util-first-touch": "1.0.0-beta.
|
|
45
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
46
|
-
"@aglyn/shared-util-logger": "1.0.0-beta.
|
|
47
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
48
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
49
|
-
"@aglyn/shared-util-vendor": "1.0.0-beta.
|
|
40
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.282",
|
|
41
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.282",
|
|
42
|
+
"@aglyn/shared-data-types": "1.0.0-beta.282",
|
|
43
|
+
"@aglyn/shared-util-email": "1.0.0-beta.282",
|
|
44
|
+
"@aglyn/shared-util-first-touch": "1.0.0-beta.282",
|
|
45
|
+
"@aglyn/shared-util-http": "1.0.0-beta.282",
|
|
46
|
+
"@aglyn/shared-util-logger": "1.0.0-beta.282",
|
|
47
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.282",
|
|
48
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.282",
|
|
49
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.282",
|
|
50
50
|
"@data-driven-forms/react-form-renderer": "^4.2.0",
|
|
51
51
|
"@msgpack/msgpack": "^3.1.3",
|
|
52
52
|
"@types/unist": "^3.0.3",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
* | `recents` | {@link RecentItem}s, most recently opened first | 30 |
|
|
30
30
|
* | `workspaceColors` | workspace id → `#RRGGBB` the person picked | 100 |
|
|
31
31
|
* | `appTheme` | {@link AppThemePrefs}, the apps' look, synced | — |
|
|
32
|
+
* | `tabBars` | {@link TabBarPins}, the phone's tab bar per site and per workspace | 4 areas each, 100 in all |
|
|
32
33
|
*
|
|
33
34
|
* The caps are held twice: here, by every writer, and by the Firestore rules,
|
|
34
35
|
* which refuse a write that grows a list past its cap. A list that is ALREADY
|
|
@@ -53,8 +54,6 @@ export declare const RECENT_ORGS_FIELD = "recentOrgs";
|
|
|
53
54
|
export declare const RECENT_ORGS_MAX = 8;
|
|
54
55
|
/** How many recent workspaces the rail shows below the pins. */
|
|
55
56
|
export declare const RAIL_RECENT_ORGS = 3;
|
|
56
|
-
/** An account with this many workspaces or fewer sees them all on the rail. */
|
|
57
|
-
export declare const RAIL_ALL_WORKSPACES_AT_MOST = 8;
|
|
58
57
|
/** `users/{uid}` field holding the person's starred screens and records. */
|
|
59
58
|
export declare const STARRED_FIELD = "starred";
|
|
60
59
|
/** The most items a person may star. */
|
|
@@ -125,6 +124,10 @@ export interface AppThemePrefs {
|
|
|
125
124
|
density: AppDensity;
|
|
126
125
|
/** `system` follows Dynamic Type or the font scale; the rest are the Mac and Windows sizes. */
|
|
127
126
|
textSize: AppTextSize;
|
|
127
|
+
/**
|
|
128
|
+
* `plain` keeps the sidebar on the theme's surface; `tinted` lays the THEME's accent over it at 5%, on a theme other
|
|
129
|
+
* than Aglyn only (Aglyn's sidebar is always neutral). A workspace's color never tints it: that is its rail tile's.
|
|
130
|
+
*/
|
|
128
131
|
sidebarStyle: AppSidebarStyle;
|
|
129
132
|
/** The app icon's id: `aglyn`, or one of the alternates the apps ship. */
|
|
130
133
|
appIcon: string;
|
|
@@ -242,21 +245,34 @@ export interface RailWorkspaces {
|
|
|
242
245
|
pinned: string[];
|
|
243
246
|
/** Below the divider: recently opened and not pinned, at most {@link RAIL_RECENT_ORGS}. */
|
|
244
247
|
recent: string[];
|
|
245
|
-
/**
|
|
248
|
+
/**
|
|
249
|
+
* Workspaces listed on the rail beyond the pins and the recents. Always
|
|
250
|
+
* empty: every account reaches the rest through the All tile
|
|
251
|
+
* (AGL-3996). The field stays so the apps that read it need no change.
|
|
252
|
+
*/
|
|
246
253
|
rest: string[];
|
|
247
|
-
/** Whether the last tile is "All workspaces", opening the searchable switcher. */
|
|
254
|
+
/** Whether the last tile is "All workspaces", opening the searchable switcher: whenever there is a workspace. */
|
|
248
255
|
allTile: boolean;
|
|
249
256
|
/** How many workspaces the person belongs to, shown under the All tile. */
|
|
250
257
|
total: number;
|
|
251
258
|
}
|
|
252
259
|
/**
|
|
253
|
-
* The rail for one account
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
260
|
+
* The rail for one account, the same for every account however few
|
|
261
|
+
* workspaces it has (Figma Rail 12:338, "Many workspaces"): up to five
|
|
262
|
+
* pinned, a divider, up to three recent under "Recent", a divider, then the
|
|
263
|
+
* "All workspaces" tile with the total, which opens the switcher where pins
|
|
264
|
+
* are edited. A small account that saw every tile in one unlabeled column
|
|
265
|
+
* had no way into the switcher and no Recent label or divider. `orgIds` is every membership, in the switcher's order;
|
|
266
|
+
* an id the person no longer belongs to is ignored.
|
|
258
267
|
*/
|
|
259
268
|
export declare function railWorkspaces(orgIds: readonly string[], pinned: readonly string[], recent: readonly string[]): RailWorkspaces;
|
|
269
|
+
/**
|
|
270
|
+
* The one letter a workspace tile without a logo shows (Figma Rail/Workspace
|
|
271
|
+
* tile, Type=Initial): the first letter or digit of its name, upper-cased;
|
|
272
|
+
* "?" for a name with neither. One letter, not two: the tile's color, picked
|
|
273
|
+
* per workspace, tells two workspaces with the same letter apart.
|
|
274
|
+
*/
|
|
275
|
+
export declare function workspaceInitial(name: string): string;
|
|
260
276
|
/** One item's identity: the same screen or record in the same place. */
|
|
261
277
|
export declare function accountItemKey(item: Pick<AccountItemRef, 'kind' | 'id' | 'orgId' | 'hostId'>): string;
|
|
262
278
|
/** The starred items as stored, each well formed, without repeats, never more than the cap. */
|
|
@@ -277,7 +293,25 @@ export declare function readRecents(profile: unknown): RecentItem[];
|
|
|
277
293
|
export declare function recordRecent(recents: readonly RecentItem[], item: AccountItemRef, nowMs: number): RecentItem[];
|
|
278
294
|
/** The colors the person picked, by workspace id, the valid ones only. */
|
|
279
295
|
export declare function readWorkspaceColors(profile: unknown): Record<string, string>;
|
|
280
|
-
|
|
296
|
+
/**
|
|
297
|
+
* The colors a workspace's tile takes when nothing else gives it one
|
|
298
|
+
* ({@link workspaceStableColor}): the console's blue for text on a light
|
|
299
|
+
* ground (primary.dark), deep tones of the brand palette's
|
|
300
|
+
* violet and pink, and a rose, orange, gold, green and teal beside them,
|
|
301
|
+
* each dark enough that the tile's white initial reads at 4.5:1 or more. The
|
|
302
|
+
* slate is left out: it is the tile of a workspace with no id at all.
|
|
303
|
+
*/
|
|
304
|
+
export declare const WORKSPACE_STABLE_COLORS: readonly string[];
|
|
305
|
+
/**
|
|
306
|
+
* The color a workspace with no picked, brand or logo color shows on its
|
|
307
|
+
* tile: one of {@link WORKSPACE_STABLE_COLORS}, chosen by its id, so the same
|
|
308
|
+
* workspace has the same color on every device and in every app, and two
|
|
309
|
+
* workspaces with the same initial usually differ. The id is hashed with
|
|
310
|
+
* 32-bit FNV-1a over its UTF-16 code units, which each platform computes the
|
|
311
|
+
* same way. Null for an empty id.
|
|
312
|
+
*/
|
|
313
|
+
export declare function workspaceStableColor(orgId: unknown): string | null;
|
|
314
|
+
export type WorkspaceColorSource = 'picked' | 'brand' | 'logo' | 'stable' | 'none';
|
|
281
315
|
export interface WorkspaceColor {
|
|
282
316
|
/** `#RRGGBB`, or null when nothing gives the workspace a color. */
|
|
283
317
|
color: string | null;
|
|
@@ -292,7 +326,108 @@ export interface WorkspaceColor {
|
|
|
292
326
|
* it while you are in the workspace unless the person turned "Use each
|
|
293
327
|
* workspace's brand" off, which stops the brand and logo colors tinting the
|
|
294
328
|
* app (a picked color still does: the person chose it).
|
|
329
|
+
*
|
|
330
|
+
* Given the workspace's id, a workspace with none of the three still gets a
|
|
331
|
+
* color: its stable one ({@link workspaceStableColor}). That one colors only
|
|
332
|
+
* its tile (the rail, the pickers, the menus, the sidebar's header), never
|
|
333
|
+
* the app.
|
|
295
334
|
*/
|
|
296
|
-
export declare function resolveWorkspaceColor(picked: unknown, brandColor: unknown, logoColor: unknown, useWorkspaceBrand: boolean): WorkspaceColor;
|
|
335
|
+
export declare function resolveWorkspaceColor(picked: unknown, brandColor: unknown, logoColor: unknown, useWorkspaceBrand: boolean, orgId?: unknown): WorkspaceColor;
|
|
297
336
|
/** The synced theme, every field filled: a missing or unknown value reads as the default. */
|
|
298
337
|
export declare function readAppTheme(profile: unknown): AppThemePrefs;
|
|
338
|
+
/** `users/{uid}` field holding the areas the person keeps in the phone's tab bar, per site and per workspace. */
|
|
339
|
+
export declare const TAB_BARS_FIELD = "tabBars";
|
|
340
|
+
/**
|
|
341
|
+
* The most areas one tab bar holds beside Home: iOS and
|
|
342
|
+
* Material take five destinations at most, and nothing ever overflows into a
|
|
343
|
+
* More tab. With four, Create leaves the bar for the top bar.
|
|
344
|
+
*/
|
|
345
|
+
export declare const TAB_BAR_AREAS_MAX = 4;
|
|
346
|
+
/** The most sites and workspaces the account keeps a tab bar for, the bound the Firestore rules hold. */
|
|
347
|
+
export declare const TAB_BARS_MAX = 100;
|
|
348
|
+
/** The tab bar's own slots beside the areas: Home, always first, and Create new. */
|
|
349
|
+
export declare const TAB_BAR_HOME = "home";
|
|
350
|
+
export declare const TAB_BAR_CREATE = "create";
|
|
351
|
+
/** One site's or workspace's tab bar. */
|
|
352
|
+
export interface TabBarPins {
|
|
353
|
+
/** `site:<hostId>` or `org:<orgId>` ({@link tabBarScope}). */
|
|
354
|
+
scope: string;
|
|
355
|
+
/** Nav manifest row ids, in the person's order, at most {@link TAB_BAR_AREAS_MAX}. */
|
|
356
|
+
areas: string[];
|
|
357
|
+
/**
|
|
358
|
+
* The person pinned, unpinned or reordered here by hand. Until then the
|
|
359
|
+
* first areas they open take the free spots and stay (the default the
|
|
360
|
+
* orchestrator settled on 2026-10-20: nothing guessed, nothing rotating);
|
|
361
|
+
* after it, only the person changes the bar.
|
|
362
|
+
*/
|
|
363
|
+
chosen: boolean;
|
|
364
|
+
}
|
|
365
|
+
export type TabPinOutcome = 'pinned' | 'alreadyPinned' | 'full';
|
|
366
|
+
export interface TabPinResult {
|
|
367
|
+
/** Every stored tab bar after the request: unchanged unless `outcome` is `pinned`. */
|
|
368
|
+
tabBars: TabBarPins[];
|
|
369
|
+
outcome: TabPinOutcome;
|
|
370
|
+
}
|
|
371
|
+
/** What a phone's tab bar shows, in order, and where Create new goes. */
|
|
372
|
+
export interface TabBarLayout {
|
|
373
|
+
/** {@link TAB_BAR_HOME}, the areas, and {@link TAB_BAR_CREATE} in the centre unless it moved to the top bar. */
|
|
374
|
+
slots: string[];
|
|
375
|
+
/** Four areas fill the bar, so Create new sits in the top bar beside the AI button. */
|
|
376
|
+
createInTopBar: boolean;
|
|
377
|
+
}
|
|
378
|
+
/** The key of one site's tab bar, or the whole workspace's. */
|
|
379
|
+
export declare function tabBarScope(orgId: string, hostId?: string | null): string;
|
|
380
|
+
/**
|
|
381
|
+
* Every tab bar the person keeps, each well formed, without repeats. Anything
|
|
382
|
+
* that is not a `{ scope, areas, chosen }` reads as no tab bar, so a stray
|
|
383
|
+
* value can never take the phone's shell down.
|
|
384
|
+
*/
|
|
385
|
+
export declare function readTabBars(profile: unknown): TabBarPins[];
|
|
386
|
+
/** One scope's areas, in the person's order. */
|
|
387
|
+
export declare function tabBarAreas(tabBars: readonly TabBarPins[], scope: string): string[];
|
|
388
|
+
/**
|
|
389
|
+
* Pins an area at the end of a scope's tab bar (long-press a tile, "Add to tab
|
|
390
|
+
* bar"), or says the bar is full: a full bar is never made room in quietly.
|
|
391
|
+
*/
|
|
392
|
+
export declare function pinTabArea(tabBars: readonly TabBarPins[], scope: string, area: string): TabPinResult;
|
|
393
|
+
/** A scope's tab bar without one area; the person chose, so opened areas no longer fill the spot. */
|
|
394
|
+
export declare function unpinTabArea(tabBars: readonly TabBarPins[], scope: string, area: string): TabBarPins[];
|
|
395
|
+
/** A scope's tab bar with one area moved left (`-1`) or right (`1`) a place; unchanged at an end. */
|
|
396
|
+
export declare function moveTabArea(tabBars: readonly TabBarPins[], scope: string, area: string, offset: number): TabBarPins[];
|
|
397
|
+
/** A scope's tab bar as the Tab bar sheet left it: these areas, in this order, at most four. */
|
|
398
|
+
export declare function setTabAreas(tabBars: readonly TabBarPins[], scope: string, areas: readonly string[]): TabBarPins[];
|
|
399
|
+
/**
|
|
400
|
+
* The default before anyone pins: an area the person opens takes a free spot
|
|
401
|
+
* and stays there, until the bar is full or the person changes it by hand.
|
|
402
|
+
* Unchanged once the scope is `chosen`, the area is in the bar already, or the
|
|
403
|
+
* bar is full.
|
|
404
|
+
*/
|
|
405
|
+
export declare function autoPinTabArea(tabBars: readonly TabBarPins[], scope: string, area: string): TabBarPins[];
|
|
406
|
+
/**
|
|
407
|
+
* The tab bar to draw: Home first, then the pinned areas the scope can open
|
|
408
|
+
* (`available`, the Home tiles' ids in this build), with Create new in the
|
|
409
|
+
* centre slot. Four areas fill the bar, and Create moves to the top bar.
|
|
410
|
+
*/
|
|
411
|
+
export declare function tabBarLayout(areas: readonly string[], available: readonly string[]): TabBarLayout;
|
|
412
|
+
/**
|
|
413
|
+
* `users/{uid}` field holding the person's own picture (AGL-1961). Lowercase
|
|
414
|
+
* `url`: the roster's `photoURL` spelling reads as nothing here.
|
|
415
|
+
*/
|
|
416
|
+
export declare const ACCOUNT_PHOTO_FIELD = "photoUrl";
|
|
417
|
+
/**
|
|
418
|
+
* The person's own picture, the same answer in the console and both apps
|
|
419
|
+
* (AGL-4010): `users/{uid}.photoUrl` first (the profile they edit, filled from
|
|
420
|
+
* the identity provider on every sign-in), else the sign-in record's
|
|
421
|
+
* `photoURL`, else null, so the caller draws initials. A blank value is never
|
|
422
|
+
* a picture, so a photo in either place wins over an empty one in the other.
|
|
423
|
+
* A library pick is stored as a `/api/media/cdn/...` path; resolving it to an
|
|
424
|
+
* address is the caller's (`mediaPreviewURL` on native).
|
|
425
|
+
*/
|
|
426
|
+
export declare function readAccountPhoto(profile: unknown, authPhotoUrl: unknown): string | null;
|
|
427
|
+
/**
|
|
428
|
+
* The person's own name, the same answer in the console and both apps
|
|
429
|
+
* (AGL-2486, AGL-4010): the profile's first and last name, else the sign-in
|
|
430
|
+
* record's display name (empty for an SSO account), else the email address,
|
|
431
|
+
* else empty.
|
|
432
|
+
*/
|
|
433
|
+
export declare function readAccountName(profile: unknown, authDisplayName: unknown, email: unknown): string;
|
|
@@ -29,6 +29,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
29
29
|
* | `recents` | {@link RecentItem}s, most recently opened first | 30 |
|
|
30
30
|
* | `workspaceColors` | workspace id → `#RRGGBB` the person picked | 100 |
|
|
31
31
|
* | `appTheme` | {@link AppThemePrefs}, the apps' look, synced | — |
|
|
32
|
+
* | `tabBars` | {@link TabBarPins}, the phone's tab bar per site and per workspace | 4 areas each, 100 in all |
|
|
32
33
|
*
|
|
33
34
|
* The caps are held twice: here, by every writer, and by the Firestore rules,
|
|
34
35
|
* which refuse a write that grows a list past its cap. A list that is ALREADY
|
|
@@ -47,7 +48,6 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
47
48
|
/** `users/{uid}` field holding the workspaces opened most recently, newest first. */ export const RECENT_ORGS_FIELD = 'recentOrgs';
|
|
48
49
|
/** How many recently opened workspaces are kept. */ export const RECENT_ORGS_MAX = 8;
|
|
49
50
|
/** How many recent workspaces the rail shows below the pins. */ export const RAIL_RECENT_ORGS = 3;
|
|
50
|
-
/** An account with this many workspaces or fewer sees them all on the rail. */ export const RAIL_ALL_WORKSPACES_AT_MOST = 8;
|
|
51
51
|
/** `users/{uid}` field holding the person's starred screens and records. */ export const STARRED_FIELD = 'starred';
|
|
52
52
|
/** The most items a person may star. */ export const STARRED_MAX = 50;
|
|
53
53
|
/** `users/{uid}` field holding the screens and records opened most recently. */ export const RECENTS_FIELD = 'recents';
|
|
@@ -73,7 +73,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
73
73
|
accent: 'theme',
|
|
74
74
|
density: 'comfortable',
|
|
75
75
|
textSize: 'system',
|
|
76
|
-
|
|
76
|
+
// Plain: a sidebar keeps the theme's neutral ground unless the person picks Tinted (AGL-3990).
|
|
77
|
+
sidebarStyle: 'plain',
|
|
77
78
|
appIcon: 'aglyn',
|
|
78
79
|
useWorkspaceBrand: true
|
|
79
80
|
};
|
|
@@ -299,33 +300,35 @@ function pinnedHostList(value) {
|
|
|
299
300
|
].slice(0, RECENT_ORGS_MAX);
|
|
300
301
|
}
|
|
301
302
|
/**
|
|
302
|
-
* The rail for one account
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
303
|
+
* The rail for one account, the same for every account however few
|
|
304
|
+
* workspaces it has (Figma Rail 12:338, "Many workspaces"): up to five
|
|
305
|
+
* pinned, a divider, up to three recent under "Recent", a divider, then the
|
|
306
|
+
* "All workspaces" tile with the total, which opens the switcher where pins
|
|
307
|
+
* are edited. A small account that saw every tile in one unlabeled column
|
|
308
|
+
* had no way into the switcher and no Recent label or divider. `orgIds` is every membership, in the switcher's order;
|
|
309
|
+
* an id the person no longer belongs to is ignored.
|
|
307
310
|
*/ export function railWorkspaces(orgIds, pinned, recent) {
|
|
308
311
|
const members = idList(orgIds);
|
|
309
312
|
const belongs = new Set(members);
|
|
310
313
|
const pins = effectivePinnedOrgs(pinned).filter((id)=>belongs.has(id));
|
|
311
314
|
const pinnedSet = new Set(pins);
|
|
312
|
-
if (members.length <= RAIL_ALL_WORKSPACES_AT_MOST) {
|
|
313
|
-
return {
|
|
314
|
-
pinned: pins,
|
|
315
|
-
recent: [],
|
|
316
|
-
rest: members.filter((id)=>!pinnedSet.has(id)),
|
|
317
|
-
allTile: false,
|
|
318
|
-
total: members.length
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
315
|
return {
|
|
322
316
|
pinned: pins,
|
|
323
317
|
recent: idList(recent).filter((id)=>belongs.has(id) && !pinnedSet.has(id)).slice(0, RAIL_RECENT_ORGS),
|
|
324
318
|
rest: [],
|
|
325
|
-
allTile:
|
|
319
|
+
allTile: members.length > 0,
|
|
326
320
|
total: members.length
|
|
327
321
|
};
|
|
328
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* The one letter a workspace tile without a logo shows (Figma Rail/Workspace
|
|
325
|
+
* tile, Type=Initial): the first letter or digit of its name, upper-cased;
|
|
326
|
+
* "?" for a name with neither. One letter, not two: the tile's color, picked
|
|
327
|
+
* per workspace, tells two workspaces with the same letter apart.
|
|
328
|
+
*/ export function workspaceInitial(name) {
|
|
329
|
+
const first = RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}]", "u").exec(name != null ? name : '');
|
|
330
|
+
return first ? first[0].toUpperCase() : '?';
|
|
331
|
+
}
|
|
329
332
|
// ── Starred and recent items ────────────────────────────────────────────────
|
|
330
333
|
/** One item's identity: the same screen or record in the same place. */ export function accountItemKey(item) {
|
|
331
334
|
var _item_hostId;
|
|
@@ -473,6 +476,40 @@ function isStarColor(value) {
|
|
|
473
476
|
}
|
|
474
477
|
return out;
|
|
475
478
|
}
|
|
479
|
+
/**
|
|
480
|
+
* The colors a workspace's tile takes when nothing else gives it one
|
|
481
|
+
* ({@link workspaceStableColor}): the console's blue for text on a light
|
|
482
|
+
* ground (primary.dark), deep tones of the brand palette's
|
|
483
|
+
* violet and pink, and a rose, orange, gold, green and teal beside them,
|
|
484
|
+
* each dark enough that the tile's white initial reads at 4.5:1 or more. The
|
|
485
|
+
* slate is left out: it is the tile of a workspace with no id at all.
|
|
486
|
+
*/ export const WORKSPACE_STABLE_COLORS = [
|
|
487
|
+
'#0077AD',
|
|
488
|
+
'#6A4FD8',
|
|
489
|
+
'#A62BBF',
|
|
490
|
+
'#C2185B',
|
|
491
|
+
'#B4501A',
|
|
492
|
+
'#8A6A00',
|
|
493
|
+
'#2E7D5B',
|
|
494
|
+
'#00796B'
|
|
495
|
+
];
|
|
496
|
+
/**
|
|
497
|
+
* The color a workspace with no picked, brand or logo color shows on its
|
|
498
|
+
* tile: one of {@link WORKSPACE_STABLE_COLORS}, chosen by its id, so the same
|
|
499
|
+
* workspace has the same color on every device and in every app, and two
|
|
500
|
+
* workspaces with the same initial usually differ. The id is hashed with
|
|
501
|
+
* 32-bit FNV-1a over its UTF-16 code units, which each platform computes the
|
|
502
|
+
* same way. Null for an empty id.
|
|
503
|
+
*/ export function workspaceStableColor(orgId) {
|
|
504
|
+
var _WORKSPACE_STABLE_COLORS_;
|
|
505
|
+
if (typeof orgId !== 'string' || orgId === '') return null;
|
|
506
|
+
let hash = 0x811c9dc5;
|
|
507
|
+
for(let i = 0; i < orgId.length; i++){
|
|
508
|
+
hash ^= orgId.charCodeAt(i);
|
|
509
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
510
|
+
}
|
|
511
|
+
return (_WORKSPACE_STABLE_COLORS_ = WORKSPACE_STABLE_COLORS[hash % WORKSPACE_STABLE_COLORS.length]) != null ? _WORKSPACE_STABLE_COLORS_ : null;
|
|
512
|
+
}
|
|
476
513
|
/**
|
|
477
514
|
* A workspace's color: the one the person picked, else the workspace's brand
|
|
478
515
|
* color (`brandingProfile.primaryColor`), else a color taken from its logo
|
|
@@ -480,7 +517,12 @@ function isStarColor(value) {
|
|
|
480
517
|
* it while you are in the workspace unless the person turned "Use each
|
|
481
518
|
* workspace's brand" off, which stops the brand and logo colors tinting the
|
|
482
519
|
* app (a picked color still does: the person chose it).
|
|
483
|
-
|
|
520
|
+
*
|
|
521
|
+
* Given the workspace's id, a workspace with none of the three still gets a
|
|
522
|
+
* color: its stable one ({@link workspaceStableColor}). That one colors only
|
|
523
|
+
* its tile (the rail, the pickers, the menus, the sidebar's header), never
|
|
524
|
+
* the app.
|
|
525
|
+
*/ export function resolveWorkspaceColor(picked, brandColor, logoColor, useWorkspaceBrand, orgId) {
|
|
484
526
|
if (isAccountColor(picked)) return {
|
|
485
527
|
color: picked,
|
|
486
528
|
source: 'picked',
|
|
@@ -500,6 +542,12 @@ function isStarColor(value) {
|
|
|
500
542
|
tintsApp: useWorkspaceBrand
|
|
501
543
|
};
|
|
502
544
|
}
|
|
545
|
+
const stable = workspaceStableColor(orgId);
|
|
546
|
+
if (stable) return {
|
|
547
|
+
color: stable,
|
|
548
|
+
source: 'stable',
|
|
549
|
+
tintsApp: false
|
|
550
|
+
};
|
|
503
551
|
return {
|
|
504
552
|
color: null,
|
|
505
553
|
source: 'none',
|
|
@@ -526,5 +574,205 @@ function oneOf(value, allowed, fallback) {
|
|
|
526
574
|
useWorkspaceBrand: typeof raw['useWorkspaceBrand'] === 'boolean' ? raw['useWorkspaceBrand'] : DEFAULT_APP_THEME.useWorkspaceBrand
|
|
527
575
|
};
|
|
528
576
|
}
|
|
577
|
+
// ── The phone's tab bar (AGL-4006) ──────────────────────────────────────────
|
|
578
|
+
/** `users/{uid}` field holding the areas the person keeps in the phone's tab bar, per site and per workspace. */ export const TAB_BARS_FIELD = 'tabBars';
|
|
579
|
+
/**
|
|
580
|
+
* The most areas one tab bar holds beside Home: iOS and
|
|
581
|
+
* Material take five destinations at most, and nothing ever overflows into a
|
|
582
|
+
* More tab. With four, Create leaves the bar for the top bar.
|
|
583
|
+
*/ export const TAB_BAR_AREAS_MAX = 4;
|
|
584
|
+
/** The most sites and workspaces the account keeps a tab bar for, the bound the Firestore rules hold. */ export const TAB_BARS_MAX = 100;
|
|
585
|
+
/** The tab bar's own slots beside the areas: Home, always first, and Create new. */ export const TAB_BAR_HOME = 'home';
|
|
586
|
+
export const TAB_BAR_CREATE = 'create';
|
|
587
|
+
/** The key of one site's tab bar, or the whole workspace's. */ export function tabBarScope(orgId, hostId) {
|
|
588
|
+
return hostId ? `site:${hostId}` : `org:${orgId}`;
|
|
589
|
+
}
|
|
590
|
+
function tabAreaList(value) {
|
|
591
|
+
return idList(value).filter((id)=>id !== TAB_BAR_HOME && id !== TAB_BAR_CREATE).slice(0, TAB_BAR_AREAS_MAX);
|
|
592
|
+
}
|
|
593
|
+
function tabBarList(value) {
|
|
594
|
+
if (!Array.isArray(value)) return [];
|
|
595
|
+
const seen = new Set();
|
|
596
|
+
const out = [];
|
|
597
|
+
for (const entry of value){
|
|
598
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
599
|
+
const raw = entry;
|
|
600
|
+
const scope = raw['scope'];
|
|
601
|
+
if (typeof scope !== 'string' || !/^(site|org):./.test(scope) || seen.has(scope)) continue;
|
|
602
|
+
seen.add(scope);
|
|
603
|
+
out.push({
|
|
604
|
+
scope,
|
|
605
|
+
areas: tabAreaList(raw['areas']),
|
|
606
|
+
chosen: raw['chosen'] === true
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
return out.slice(-TAB_BARS_MAX);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Every tab bar the person keeps, each well formed, without repeats. Anything
|
|
613
|
+
* that is not a `{ scope, areas, chosen }` reads as no tab bar, so a stray
|
|
614
|
+
* value can never take the phone's shell down.
|
|
615
|
+
*/ export function readTabBars(profile) {
|
|
616
|
+
return tabBarList(field(profile, TAB_BARS_FIELD));
|
|
617
|
+
}
|
|
618
|
+
/** One scope's areas, in the person's order. */ export function tabBarAreas(tabBars, scope) {
|
|
619
|
+
var _ref;
|
|
620
|
+
var _tabBarList_find;
|
|
621
|
+
return (_ref = (_tabBarList_find = tabBarList(tabBars).find((bar)=>bar.scope === scope)) == null ? void 0 : _tabBarList_find.areas) != null ? _ref : [];
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Every tab bar with one scope's replaced. A scope seen for the first time
|
|
625
|
+
* goes last; past {@link TAB_BARS_MAX} the oldest OTHER scopes give way.
|
|
626
|
+
*/ function withTabBar(tabBars, next) {
|
|
627
|
+
const current = tabBarList(tabBars);
|
|
628
|
+
const at = current.findIndex((bar)=>bar.scope === next.scope);
|
|
629
|
+
const out = at === -1 ? [
|
|
630
|
+
...current,
|
|
631
|
+
next
|
|
632
|
+
] : current.map((bar, index)=>index === at ? next : bar);
|
|
633
|
+
while(out.length > TAB_BARS_MAX){
|
|
634
|
+
const oldest = out.findIndex((bar)=>bar.scope !== next.scope);
|
|
635
|
+
if (oldest === -1) break;
|
|
636
|
+
out.splice(oldest, 1);
|
|
637
|
+
}
|
|
638
|
+
return out;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Pins an area at the end of a scope's tab bar (long-press a tile, "Add to tab
|
|
642
|
+
* bar"), or says the bar is full: a full bar is never made room in quietly.
|
|
643
|
+
*/ export function pinTabArea(tabBars, scope, area) {
|
|
644
|
+
const current = tabBarList(tabBars);
|
|
645
|
+
const areas = tabBarAreas(current, scope);
|
|
646
|
+
if (!area || area === TAB_BAR_HOME || area === TAB_BAR_CREATE || areas.includes(area)) {
|
|
647
|
+
return {
|
|
648
|
+
tabBars: current,
|
|
649
|
+
outcome: 'alreadyPinned'
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (areas.length >= TAB_BAR_AREAS_MAX) return {
|
|
653
|
+
tabBars: current,
|
|
654
|
+
outcome: 'full'
|
|
655
|
+
};
|
|
656
|
+
return {
|
|
657
|
+
tabBars: withTabBar(current, {
|
|
658
|
+
scope,
|
|
659
|
+
areas: [
|
|
660
|
+
...areas,
|
|
661
|
+
area
|
|
662
|
+
],
|
|
663
|
+
chosen: true
|
|
664
|
+
}),
|
|
665
|
+
outcome: 'pinned'
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
/** A scope's tab bar without one area; the person chose, so opened areas no longer fill the spot. */ export function unpinTabArea(tabBars, scope, area) {
|
|
669
|
+
const areas = tabBarAreas(tabBars, scope);
|
|
670
|
+
return withTabBar(tabBars, {
|
|
671
|
+
scope,
|
|
672
|
+
areas: areas.filter((id)=>id !== area),
|
|
673
|
+
chosen: true
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
/** A scope's tab bar with one area moved left (`-1`) or right (`1`) a place; unchanged at an end. */ export function moveTabArea(tabBars, scope, area, offset) {
|
|
677
|
+
const areas = tabBarAreas(tabBars, scope);
|
|
678
|
+
const from = areas.indexOf(area);
|
|
679
|
+
const to = from + (offset < 0 ? -1 : 1);
|
|
680
|
+
if (from === -1 || to < 0 || to >= areas.length) return tabBarList(tabBars);
|
|
681
|
+
const next = [
|
|
682
|
+
...areas
|
|
683
|
+
];
|
|
684
|
+
next[from] = next[to];
|
|
685
|
+
next[to] = area;
|
|
686
|
+
return withTabBar(tabBars, {
|
|
687
|
+
scope,
|
|
688
|
+
areas: next,
|
|
689
|
+
chosen: true
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
/** A scope's tab bar as the Tab bar sheet left it: these areas, in this order, at most four. */ export function setTabAreas(tabBars, scope, areas) {
|
|
693
|
+
return withTabBar(tabBars, {
|
|
694
|
+
scope,
|
|
695
|
+
areas: tabAreaList(areas),
|
|
696
|
+
chosen: true
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* The default before anyone pins: an area the person opens takes a free spot
|
|
701
|
+
* and stays there, until the bar is full or the person changes it by hand.
|
|
702
|
+
* Unchanged once the scope is `chosen`, the area is in the bar already, or the
|
|
703
|
+
* bar is full.
|
|
704
|
+
*/ export function autoPinTabArea(tabBars, scope, area) {
|
|
705
|
+
var _ref;
|
|
706
|
+
const current = tabBarList(tabBars);
|
|
707
|
+
const bar = current.find((entry)=>entry.scope === scope);
|
|
708
|
+
const areas = (_ref = bar == null ? void 0 : bar.areas) != null ? _ref : [];
|
|
709
|
+
if (!area || area === TAB_BAR_HOME || area === TAB_BAR_CREATE) return current;
|
|
710
|
+
if ((bar == null ? void 0 : bar.chosen) || areas.includes(area) || areas.length >= TAB_BAR_AREAS_MAX) return current;
|
|
711
|
+
return withTabBar(current, {
|
|
712
|
+
scope,
|
|
713
|
+
areas: [
|
|
714
|
+
...areas,
|
|
715
|
+
area
|
|
716
|
+
],
|
|
717
|
+
chosen: false
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* The tab bar to draw: Home first, then the pinned areas the scope can open
|
|
722
|
+
* (`available`, the Home tiles' ids in this build), with Create new in the
|
|
723
|
+
* centre slot. Four areas fill the bar, and Create moves to the top bar.
|
|
724
|
+
*/ export function tabBarLayout(areas, available) {
|
|
725
|
+
const open = new Set(available);
|
|
726
|
+
const shown = tabAreaList(areas).filter((id)=>open.has(id));
|
|
727
|
+
if (shown.length >= TAB_BAR_AREAS_MAX) {
|
|
728
|
+
return {
|
|
729
|
+
slots: [
|
|
730
|
+
TAB_BAR_HOME,
|
|
731
|
+
...shown
|
|
732
|
+
],
|
|
733
|
+
createInTopBar: true
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
const slots = [
|
|
737
|
+
TAB_BAR_HOME,
|
|
738
|
+
...shown
|
|
739
|
+
];
|
|
740
|
+
slots.splice(Math.floor((shown.length + 2) / 2), 0, TAB_BAR_CREATE);
|
|
741
|
+
return {
|
|
742
|
+
slots,
|
|
743
|
+
createInTopBar: false
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
// ── Who is signed in ────────────────────────────────────────────────────────
|
|
747
|
+
/**
|
|
748
|
+
* `users/{uid}` field holding the person's own picture (AGL-1961). Lowercase
|
|
749
|
+
* `url`: the roster's `photoURL` spelling reads as nothing here.
|
|
750
|
+
*/ export const ACCOUNT_PHOTO_FIELD = 'photoUrl';
|
|
751
|
+
function trimmed(value) {
|
|
752
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* The person's own picture, the same answer in the console and both apps
|
|
756
|
+
* (AGL-4010): `users/{uid}.photoUrl` first (the profile they edit, filled from
|
|
757
|
+
* the identity provider on every sign-in), else the sign-in record's
|
|
758
|
+
* `photoURL`, else null, so the caller draws initials. A blank value is never
|
|
759
|
+
* a picture, so a photo in either place wins over an empty one in the other.
|
|
760
|
+
* A library pick is stored as a `/api/media/cdn/...` path; resolving it to an
|
|
761
|
+
* address is the caller's (`mediaPreviewURL` on native).
|
|
762
|
+
*/ export function readAccountPhoto(profile, authPhotoUrl) {
|
|
763
|
+
return trimmed(field(profile, ACCOUNT_PHOTO_FIELD)) || trimmed(authPhotoUrl) || null;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* The person's own name, the same answer in the console and both apps
|
|
767
|
+
* (AGL-2486, AGL-4010): the profile's first and last name, else the sign-in
|
|
768
|
+
* record's display name (empty for an SSO account), else the email address,
|
|
769
|
+
* else empty.
|
|
770
|
+
*/ export function readAccountName(profile, authDisplayName, email) {
|
|
771
|
+
const fromProfile = [
|
|
772
|
+
field(profile, 'firstName'),
|
|
773
|
+
field(profile, 'lastName')
|
|
774
|
+
].map(trimmed).filter(Boolean).join(' ');
|
|
775
|
+
return fromProfile || trimmed(authDisplayName) || trimmed(email);
|
|
776
|
+
}
|
|
529
777
|
|
|
530
778
|
//# sourceMappingURL=account-preferences.js.map
|