@tastic/profile 0.16.2 → 0.18.0
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 +19 -10
- package/dist/index.d.mts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +117 -57
- package/dist/index.mjs +98 -38
- package/package.json +9 -8
- package/src/OptionPicker.tsx +26 -10
- package/src/PickerMenu.tsx +62 -22
- package/src/ProfileColorContext.tsx +2 -2
- package/src/ProfilePicker.tsx +2 -1
- package/src/ProfilesManager.tsx +77 -51
- package/src/sharedProfileStore.ts +35 -5
package/README.md
CHANGED
|
@@ -57,7 +57,10 @@ see its own doc comment for why that component is generic over your own `Profile
|
|
|
57
57
|
(`{ value, onChange, previewValue?, swatches?, label?, caption? }`) puts a Color row at the top of
|
|
58
58
|
the popover that opens a page of swatches (with a back row), and turns the trigger into one
|
|
59
59
|
button that leads with a chip in `color` with the selected profile's tag; `triggerStyle` styles
|
|
60
|
-
that button.
|
|
60
|
+
that button. A `flexShrink` in `triggerStyle` lets the button give way in a row too narrow for
|
|
61
|
+
it: its label is cut off with an ellipsis while the chip and chevron keep their size, and it never
|
|
62
|
+
gets narrower than those. With room it lays out as before. `OptionPicker` takes the same
|
|
63
|
+
`triggerStyle`. Also exports
|
|
61
64
|
`getProfilePickerContentSize(profileCount, hasManageRow?, colorSection?)` - the popover's
|
|
62
65
|
`{ width, height }`, for sizing your own placement hook.
|
|
63
66
|
- **`ProfilesManager`**: the roster screen itself: create/rename/recolor/retag/delete, editing
|
|
@@ -84,7 +87,10 @@ see its own doc comment for why that component is generic over your own `Profile
|
|
|
84
87
|
in that app's own local extension table yet - e.g. it was created on a different app in the
|
|
85
88
|
group). This split is deliberate, not a limitation to work around: it's this package's own
|
|
86
89
|
base-fields-only `Profile` boundary (see above), just applied to storage instead of just to the
|
|
87
|
-
TypeScript type.
|
|
90
|
+
TypeScript type. A read drops any stored entry that this version's `isValidProfile` rejects (a
|
|
91
|
+
profile saved by a newer app under a rule loosened since), and `saveSharedProfiles` writes those
|
|
92
|
+
entries back after your roster as they were, so an older app can't delete a newer app's profile
|
|
93
|
+
just by saving.
|
|
88
94
|
|
|
89
95
|
```ts
|
|
90
96
|
// Conceptual sketch of the pattern - see "Redux helpers for the shared roster" below for a
|
|
@@ -174,14 +180,16 @@ rect). Pass a placement **hook** as `useColorPickerAlign`:
|
|
|
174
180
|
<ProfilesScreen useColorPickerAlign={useFrameAutoAlign} /* ...the usual props */ />
|
|
175
181
|
```
|
|
176
182
|
|
|
177
|
-
It is called as `useColorPickerAlign(open, contentWidth, contentHeight)` - `open` is whether the swatch
|
|
178
|
-
popover is showing,
|
|
179
|
-
`getInlineColorPickerContentSize` for the default swatch list at the raw window width)
|
|
183
|
+
It is called as `useColorPickerAlign(open, contentWidth, contentHeight, column)` - `open` is whether the swatch
|
|
184
|
+
popover is showing, the size is what `InlineColorPicker` itself computes (`@tastic/hud`'s
|
|
185
|
+
`getInlineColorPickerContentSize` for the default swatch list at the raw window width), and `column` is a ref
|
|
186
|
+
to the roster's scroll view, which clips the popover at its edges: measure it to keep the popover inside a
|
|
187
|
+
roster narrower than the frame (a column capped in a wide window) - and must return a
|
|
180
188
|
`ProfilePickerAlignResult`, which is handed to `InlineColorPicker` as its `alignOverride` (its `triggerRef`
|
|
181
189
|
is the ref that gets measured). It is a hook, not a plain function: give it a stable identity for the life of
|
|
182
190
|
the mount (a module-level function), since switching it at runtime remounts the swatch. Omit it and behaviour is
|
|
183
|
-
identical to before
|
|
184
|
-
|
|
191
|
+
identical to before, and without a hook the popover is kept inside that column for you (`InlineColorPicker`'s
|
|
192
|
+
`bounds`, `@tastic/hud` 0.25.0+).
|
|
185
193
|
|
|
186
194
|
### Routed screens: `ProfilesScreen`
|
|
187
195
|
|
|
@@ -327,9 +335,10 @@ const renderSwatch: ProfileSwatchRenderer = (color, size, place) => (place === '
|
|
|
327
335
|
```
|
|
328
336
|
|
|
329
337
|
(`TriadPie` is your own component, e.g. three `react-native-svg` arcs.) Give it a stable identity
|
|
330
|
-
(module level, or `useCallback`).
|
|
331
|
-
|
|
332
|
-
|
|
338
|
+
(module level, or `useCallback`). `ProfilesManager`'s edit row hands it to `@tastic/hud`'s
|
|
339
|
+
`InlineColorPicker` too: its trigger (the profile's color with its tag on it) as a `'chip'`, its grid
|
|
340
|
+
as the `'colorPage'`. Rows that fill with a color (a selected picker row) stay flat.
|
|
341
|
+
`useProfileSwatchRenderer()` returns it (or `undefined`).
|
|
333
342
|
|
|
334
343
|
## Per-seat color persistence
|
|
335
344
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { RefObject, ReactNode, Ref } from 'react';
|
|
3
|
-
import { PopoverHost, PopoverRotation } from '@tastic/hud/chrome';
|
|
4
|
-
import {
|
|
3
|
+
import { ViewInstance, PopoverHost, PopoverRotation } from '@tastic/hud/chrome';
|
|
4
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
5
5
|
import { SeedColor } from '@rific/auto-paper';
|
|
6
6
|
|
|
7
7
|
interface ProfilePickerColorSection {
|
|
@@ -19,7 +19,7 @@ interface ProfilePickerAlignResult {
|
|
|
19
19
|
verticalAlign: 'below' | 'above';
|
|
20
20
|
maxHeight: number;
|
|
21
21
|
measured: boolean;
|
|
22
|
-
triggerRef: RefObject<
|
|
22
|
+
triggerRef: RefObject<ViewInstance | null>;
|
|
23
23
|
}
|
|
24
24
|
interface ProfilePickerContentSize {
|
|
25
25
|
width: number;
|
|
@@ -31,8 +31,9 @@ interface OptionPickerOption<T extends string> {
|
|
|
31
31
|
label: string;
|
|
32
32
|
icon?: string;
|
|
33
33
|
color?: string;
|
|
34
|
+
dividerBefore?: boolean;
|
|
34
35
|
}
|
|
35
|
-
declare function getOptionPickerContentSize(
|
|
36
|
+
declare function getOptionPickerContentSize(options: number | readonly OptionPickerOption<string>[], colorSection?: Pick<ProfilePickerColorSection, 'swatches' | 'caption'>): ProfilePickerContentSize;
|
|
36
37
|
interface OptionPickerProps<T extends string> {
|
|
37
38
|
id: string;
|
|
38
39
|
host: PopoverHost;
|
|
@@ -123,7 +124,7 @@ interface ProfilesManagerProps {
|
|
|
123
124
|
fgMuted?: string;
|
|
124
125
|
titleVariant?: MD3TextVariant$1;
|
|
125
126
|
colorPreview?: (hex: string) => string;
|
|
126
|
-
useColorPickerAlign?: (open: boolean, contentWidth: number, contentHeight: number) => ProfilePickerAlignResult;
|
|
127
|
+
useColorPickerAlign?: (open: boolean, contentWidth: number, contentHeight: number, column: RefObject<ViewInstance | null>) => ProfilePickerAlignResult;
|
|
127
128
|
}
|
|
128
129
|
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant, colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps): react.JSX.Element;
|
|
129
130
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { RefObject, ReactNode, Ref } from 'react';
|
|
3
|
-
import { PopoverHost, PopoverRotation } from '@tastic/hud/chrome';
|
|
4
|
-
import {
|
|
3
|
+
import { ViewInstance, PopoverHost, PopoverRotation } from '@tastic/hud/chrome';
|
|
4
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
5
5
|
import { SeedColor } from '@rific/auto-paper';
|
|
6
6
|
|
|
7
7
|
interface ProfilePickerColorSection {
|
|
@@ -19,7 +19,7 @@ interface ProfilePickerAlignResult {
|
|
|
19
19
|
verticalAlign: 'below' | 'above';
|
|
20
20
|
maxHeight: number;
|
|
21
21
|
measured: boolean;
|
|
22
|
-
triggerRef: RefObject<
|
|
22
|
+
triggerRef: RefObject<ViewInstance | null>;
|
|
23
23
|
}
|
|
24
24
|
interface ProfilePickerContentSize {
|
|
25
25
|
width: number;
|
|
@@ -31,8 +31,9 @@ interface OptionPickerOption<T extends string> {
|
|
|
31
31
|
label: string;
|
|
32
32
|
icon?: string;
|
|
33
33
|
color?: string;
|
|
34
|
+
dividerBefore?: boolean;
|
|
34
35
|
}
|
|
35
|
-
declare function getOptionPickerContentSize(
|
|
36
|
+
declare function getOptionPickerContentSize(options: number | readonly OptionPickerOption<string>[], colorSection?: Pick<ProfilePickerColorSection, 'swatches' | 'caption'>): ProfilePickerContentSize;
|
|
36
37
|
interface OptionPickerProps<T extends string> {
|
|
37
38
|
id: string;
|
|
38
39
|
host: PopoverHost;
|
|
@@ -123,7 +124,7 @@ interface ProfilesManagerProps {
|
|
|
123
124
|
fgMuted?: string;
|
|
124
125
|
titleVariant?: MD3TextVariant$1;
|
|
125
126
|
colorPreview?: (hex: string) => string;
|
|
126
|
-
useColorPickerAlign?: (open: boolean, contentWidth: number, contentHeight: number) => ProfilePickerAlignResult;
|
|
127
|
+
useColorPickerAlign?: (open: boolean, contentWidth: number, contentHeight: number, column: RefObject<ViewInstance | null>) => ProfilePickerAlignResult;
|
|
127
128
|
}
|
|
128
129
|
declare function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant, colorPreview: colorPreviewProp, useColorPickerAlign }: ProfilesManagerProps): react.JSX.Element;
|
|
129
130
|
|
package/dist/index.js
CHANGED
|
@@ -53,6 +53,9 @@ __export(index_exports, {
|
|
|
53
53
|
});
|
|
54
54
|
module.exports = __toCommonJS(index_exports);
|
|
55
55
|
|
|
56
|
+
// src/OptionPicker.tsx
|
|
57
|
+
var import_react3 = require("react");
|
|
58
|
+
|
|
56
59
|
// src/PickerMenu.tsx
|
|
57
60
|
var import_auto_paper2 = require("@rific/auto-paper");
|
|
58
61
|
var import_feedback_press = require("@rific/feedback-press");
|
|
@@ -138,6 +141,12 @@ var MENU_BORDER_WIDTH = 2;
|
|
|
138
141
|
var ROW_INSET = 12;
|
|
139
142
|
var CHIP_SIZE = 32;
|
|
140
143
|
var CHIP_TRIGGER_PADDING = 6;
|
|
144
|
+
var CHIP_TRIGGER_END_PADDING = CHIP_TRIGGER_PADDING + 8;
|
|
145
|
+
var CHIP_TRIGGER_GAP = 8;
|
|
146
|
+
var PLAIN_TRIGGER_GAP = 2;
|
|
147
|
+
var CHEVRON_SIZE = 16;
|
|
148
|
+
var CHIP_TRIGGER_FIXED_WIDTH = CHIP_TRIGGER_PADDING + CHIP_SIZE + 2 * CHIP_TRIGGER_GAP + CHEVRON_SIZE + CHIP_TRIGGER_END_PADDING;
|
|
149
|
+
var PLAIN_TRIGGER_FIXED_WIDTH = PLAIN_TRIGGER_GAP + CHEVRON_SIZE;
|
|
141
150
|
var SWATCH_SIZE = 28;
|
|
142
151
|
var SWATCH_MIN_GAP = 6;
|
|
143
152
|
var SWATCH_RING_WIDTH = 2;
|
|
@@ -145,7 +154,7 @@ var COLOR_ROW_SWATCH_SIZE = 22;
|
|
|
145
154
|
var COLOR_ROW_CAPTIONED_HEIGHT = 48;
|
|
146
155
|
var COLOR_LABEL_LINE = 18;
|
|
147
156
|
var COLOR_CAPTION_LINE = 16;
|
|
148
|
-
var
|
|
157
|
+
var DIVIDER_HEIGHT = 9;
|
|
149
158
|
var GRID_TOP = 4;
|
|
150
159
|
var GRID_BOTTOM = 8;
|
|
151
160
|
var GRID_WIDTH = POPOVER_WIDTH - 2 * MENU_BORDER_WIDTH - 2 * LIST_PADDING - 2 * ROW_INSET;
|
|
@@ -158,8 +167,8 @@ function colorPageHeight(swatches) {
|
|
|
158
167
|
const rows = Math.ceil(swatches.length / GRID_COLUMNS);
|
|
159
168
|
return ROW_HEIGHT + GRID_TOP + rows * SWATCH_SIZE + (rows - 1) * GRID_GAP + GRID_BOTTOM;
|
|
160
169
|
}
|
|
161
|
-
function pickerContentSize(rowCount, colorSection) {
|
|
162
|
-
const list = rowCount * ROW_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) +
|
|
170
|
+
function pickerContentSize(rowCount, colorSection, dividerCount = 0) {
|
|
171
|
+
const list = rowCount * ROW_HEIGHT + dividerCount * DIVIDER_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) + DIVIDER_HEIGHT : 0);
|
|
163
172
|
const content = colorSection ? Math.max(list, colorPageHeight(colorSection.swatches ?? import_auto_paper2.defaultColors)) : list;
|
|
164
173
|
return { width: POPOVER_WIDTH, height: LIST_PADDING * 2 + content };
|
|
165
174
|
}
|
|
@@ -182,11 +191,15 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
182
191
|
};
|
|
183
192
|
const triggerLabel = label.toUpperCase();
|
|
184
193
|
const showFooter = !!footer && !showColorPage;
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
const flatTriggerStyle = import_react_native3.StyleSheet.flatten(triggerStyle) ?? {};
|
|
195
|
+
const { flexShrink, ...pillStyle } = flatTriggerStyle;
|
|
196
|
+
const shrinks = typeof flexShrink === "number" && flexShrink > 0;
|
|
197
|
+
const anchorShrink = shrinks ? { flexShrink, minWidth: chipTrigger ? CHIP_TRIGGER_FIXED_WIDTH : PLAIN_TRIGGER_FIXED_WIDTH } : void 0;
|
|
198
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: [styles3.anchor, anchorShrink, open && styles3.anchorOpen], onLayout: handleAnchorLayout, children: [
|
|
199
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: handleTriggerPress, borderless: chipTrigger, style: [styles3.trigger, chipTrigger && styles3.chipTrigger, shrinks && styles3.triggerShrinks, shrinks ? pillStyle : triggerStyle], accessibilityRole: "button", accessibilityLabel: triggerLabel, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { ref: triggerRef, collapsable: false, style: chipTrigger ? styles3.chipTriggerInner : styles3.triggerInner, children: [
|
|
187
200
|
chip && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IdentityChip, { color, tag: chip.tag, icon: chip.icon, size: CHIP_SIZE }),
|
|
188
201
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles3.triggerLabel, { color: chipTrigger ? fg : color }], numberOfLines: 1, children: triggerLabel }),
|
|
189
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "menu-down", size:
|
|
202
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "menu-down", size: CHEVRON_SIZE, color: chipTrigger ? fg : color })
|
|
190
203
|
] }) }),
|
|
191
204
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_chrome.PopoverBody, { visible: open && measured, align, verticalAlign, caretColor: menuBg, caretBorderColor: color, caretBorderWidth: MENU_BORDER_WIDTH, triggerSize: chipTrigger ? triggerWidth : TRIGGER_HEIGHT, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
192
205
|
import_react_native3.View,
|
|
@@ -206,7 +219,7 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
206
219
|
children
|
|
207
220
|
] }) }, showColorPage ? "color" : "list"),
|
|
208
221
|
showFooter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: styles3.menuFooter, children: [
|
|
209
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MenuDivider, { color }),
|
|
210
223
|
footer
|
|
211
224
|
] })
|
|
212
225
|
]
|
|
@@ -235,9 +248,12 @@ function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
|
235
248
|
] }),
|
|
236
249
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "chevron-right", size: 18, color: fg })
|
|
237
250
|
] }) }),
|
|
238
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
251
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MenuDivider, { color: dividerColor })
|
|
239
252
|
] });
|
|
240
253
|
}
|
|
254
|
+
function MenuDivider({ color }) {
|
|
255
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.divider, { backgroundColor: color }] });
|
|
256
|
+
}
|
|
241
257
|
function ColorPage({ section, homeColor, fg, onBack }) {
|
|
242
258
|
const { value, onChange, previewValue, swatches = import_auto_paper2.defaultColors, label = "Color", takenValue, allowSwapTaken } = section;
|
|
243
259
|
const renderSwatch = useProfileSwatchRenderer();
|
|
@@ -304,10 +320,10 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
304
320
|
chipTriggerInner: {
|
|
305
321
|
alignItems: "center",
|
|
306
322
|
flexDirection: "row",
|
|
307
|
-
gap:
|
|
323
|
+
gap: CHIP_TRIGGER_GAP,
|
|
308
324
|
height: CHIP_SIZE + 2 * CHIP_TRIGGER_PADDING,
|
|
309
325
|
paddingLeft: CHIP_TRIGGER_PADDING,
|
|
310
|
-
paddingRight:
|
|
326
|
+
paddingRight: CHIP_TRIGGER_END_PADDING
|
|
311
327
|
},
|
|
312
328
|
colorRowCaption: {
|
|
313
329
|
fontSize: 12,
|
|
@@ -329,7 +345,8 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
329
345
|
flex: 1
|
|
330
346
|
},
|
|
331
347
|
// Separates the footer (a navigation action) and the Color row from the selectable rows - a
|
|
332
|
-
// different kind of action, not just another row in the same list
|
|
348
|
+
// different kind of action, not just another row in the same list - and an OptionPicker's groups
|
|
349
|
+
// of options from each other (its `dividerBefore`). Tinted with the seat's own color
|
|
333
350
|
// (passed in as backgroundColor) rather than a flat gray, matching the border/caret's own
|
|
334
351
|
// accent-tinted look.
|
|
335
352
|
divider: {
|
|
@@ -416,20 +433,33 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
416
433
|
triggerInner: {
|
|
417
434
|
alignItems: "center",
|
|
418
435
|
flexDirection: "row",
|
|
419
|
-
gap:
|
|
436
|
+
gap: PLAIN_TRIGGER_GAP,
|
|
420
437
|
height: TRIGGER_HEIGHT
|
|
421
438
|
},
|
|
439
|
+
// Lets the label, alone in its row, give way to a trigger narrower than its name (see `shrinks`).
|
|
440
|
+
// With room it keeps its natural width, so this changes nothing there.
|
|
422
441
|
triggerLabel: {
|
|
442
|
+
flexShrink: 1,
|
|
423
443
|
fontSize: 13,
|
|
424
444
|
fontWeight: "700",
|
|
425
|
-
letterSpacing: 1
|
|
445
|
+
letterSpacing: 1,
|
|
446
|
+
minWidth: 0
|
|
447
|
+
},
|
|
448
|
+
// A shrinking trigger (see `shrinks`): no wider than its anchor. Its row is then laid out at that
|
|
449
|
+
// width, which is what cuts the label off.
|
|
450
|
+
triggerShrinks: {
|
|
451
|
+
maxWidth: "100%"
|
|
426
452
|
}
|
|
427
453
|
});
|
|
428
454
|
|
|
429
455
|
// src/OptionPicker.tsx
|
|
430
456
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
431
|
-
function
|
|
432
|
-
return
|
|
457
|
+
function hasDividerBefore(option, index) {
|
|
458
|
+
return index > 0 && !!option.dividerBefore;
|
|
459
|
+
}
|
|
460
|
+
function getOptionPickerContentSize(options, colorSection) {
|
|
461
|
+
if (typeof options === "number") return pickerContentSize(options, colorSection);
|
|
462
|
+
return pickerContentSize(options.length, colorSection, options.filter(hasDividerBefore).length);
|
|
433
463
|
}
|
|
434
464
|
function OptionPicker({ id, host, options, value, onChange, color, dark, icon, rowIcon = icon, colorSection, align, alignOverride, rotation = 0, triggerStyle }) {
|
|
435
465
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
@@ -438,7 +468,10 @@ function OptionPicker({ id, host, options, value, onChange, color, dark, icon, r
|
|
|
438
468
|
onChange(option.value);
|
|
439
469
|
host.close();
|
|
440
470
|
};
|
|
441
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PickerMenu, { id, host, color, dark, label: selected?.label ?? "", chip: { icon }, colorSection, contentSize: getOptionPickerContentSize(options
|
|
471
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PickerMenu, { id, host, color, dark, label: selected?.label ?? "", chip: { icon }, colorSection, contentSize: getOptionPickerContentSize(options, colorSection), align, alignOverride, rotation, triggerStyle, children: options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react3.Fragment, { children: [
|
|
472
|
+
hasDividerBefore(option, index) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MenuDivider, { color }),
|
|
473
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconRow, { icon: option.icon ?? rowIcon, iconColor: option.color, label: option.label, selected: option.value === value, color, fg, onPress: () => handleSelect(option) })
|
|
474
|
+
] }, option.value)) });
|
|
442
475
|
}
|
|
443
476
|
|
|
444
477
|
// src/ProfileChip.tsx
|
|
@@ -451,7 +484,7 @@ function ProfileChip({ profile, size = 22, filled = true }) {
|
|
|
451
484
|
// src/ProfilePicker.tsx
|
|
452
485
|
var import_auto_paper3 = require("@rific/auto-paper");
|
|
453
486
|
var import_feedback_press2 = require("@rific/feedback-press");
|
|
454
|
-
var
|
|
487
|
+
var import_react4 = require("react");
|
|
455
488
|
var import_react_native4 = require("react-native");
|
|
456
489
|
var import_react_native_paper3 = require("react-native-paper");
|
|
457
490
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
@@ -463,7 +496,7 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, takenIds
|
|
|
463
496
|
const colorPreview = useProfileColorPreview();
|
|
464
497
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
465
498
|
const selectedProfile = profiles.find((p) => p.id === selectedId) ?? null;
|
|
466
|
-
const sortedProfiles = (0,
|
|
499
|
+
const sortedProfiles = (0, import_react4.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
467
500
|
const contentSize = getProfilePickerContentSize(profiles.length, !!onManage, colorSection);
|
|
468
501
|
const isTaken = (profileId) => profileId === takenId || profileId !== selectedId && !!takenIds?.includes(profileId);
|
|
469
502
|
const handleSelectGuest = () => {
|
|
@@ -531,7 +564,7 @@ var import_feedback_press3 = require("@rific/feedback-press");
|
|
|
531
564
|
var import_focus_chain = require("@rific/focus-chain");
|
|
532
565
|
var import_toaster = require("@rific/toaster");
|
|
533
566
|
var import_chrome2 = require("@tastic/hud/chrome");
|
|
534
|
-
var
|
|
567
|
+
var import_react5 = require("react");
|
|
535
568
|
var import_react_native5 = require("react-native");
|
|
536
569
|
var import_react_native_paper4 = require("react-native-paper");
|
|
537
570
|
var import_react_native_safe_area_context = require("react-native-safe-area-context");
|
|
@@ -571,6 +604,7 @@ function isValidProfile(value) {
|
|
|
571
604
|
// src/ProfilesManager.tsx
|
|
572
605
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
573
606
|
var CHIP_SIZE2 = 40;
|
|
607
|
+
var textInputRef = (ref) => ref;
|
|
574
608
|
function deriveTag(name) {
|
|
575
609
|
const trimmed = name.trim();
|
|
576
610
|
return trimmed ? trimmed[0].toUpperCase() : "";
|
|
@@ -581,16 +615,18 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
581
615
|
const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
|
|
582
616
|
const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
|
|
583
617
|
const { error: showErrorToast } = (0, import_toaster.useToast)();
|
|
618
|
+
const columnRef = (0, import_react5.useRef)(null);
|
|
619
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
584
620
|
const host = (0, import_chrome2.usePopoverHost)();
|
|
585
621
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
586
622
|
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
587
|
-
const [editingId, setEditingId] = (0,
|
|
588
|
-
const [draftName, setDraftName] = (0,
|
|
589
|
-
const [draftColor, setDraftColor] = (0,
|
|
590
|
-
const [draftTag, setDraftTag] = (0,
|
|
591
|
-
const [tagManuallySet, setTagManuallySet] = (0,
|
|
592
|
-
const [confirmDeleteId, setConfirmDeleteId] = (0,
|
|
593
|
-
const commitEdit = (0,
|
|
623
|
+
const [editingId, setEditingId] = (0, import_react5.useState)(null);
|
|
624
|
+
const [draftName, setDraftName] = (0, import_react5.useState)("");
|
|
625
|
+
const [draftColor, setDraftColor] = (0, import_react5.useState)(defaultColor);
|
|
626
|
+
const [draftTag, setDraftTag] = (0, import_react5.useState)("");
|
|
627
|
+
const [tagManuallySet, setTagManuallySet] = (0, import_react5.useState)(false);
|
|
628
|
+
const [confirmDeleteId, setConfirmDeleteId] = (0, import_react5.useState)(null);
|
|
629
|
+
const commitEdit = (0, import_react5.useCallback)(() => {
|
|
594
630
|
const name = draftName.trim();
|
|
595
631
|
if (editingId && name) {
|
|
596
632
|
const tagValid = isValidTag(draftTag);
|
|
@@ -601,13 +637,13 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
601
637
|
}
|
|
602
638
|
setEditingId(null);
|
|
603
639
|
}, [editingId, draftName, draftColor, draftTag, onCreate, onSave, showErrorToast]);
|
|
604
|
-
const commitEditRef = (0,
|
|
605
|
-
(0,
|
|
640
|
+
const commitEditRef = (0, import_react5.useRef)(commitEdit);
|
|
641
|
+
(0, import_react5.useEffect)(() => {
|
|
606
642
|
commitEditRef.current = commitEdit;
|
|
607
643
|
});
|
|
608
|
-
(0,
|
|
609
|
-
(0,
|
|
610
|
-
const startEdit = (0,
|
|
644
|
+
(0, import_react5.useEffect)(() => () => commitEditRef.current(), []);
|
|
645
|
+
(0, import_react5.useImperativeHandle)(ref, () => ({ commitPendingEdit: () => commitEditRef.current() }), []);
|
|
646
|
+
const startEdit = (0, import_react5.useCallback)(
|
|
611
647
|
(target) => {
|
|
612
648
|
commitEdit();
|
|
613
649
|
if (target === "new") {
|
|
@@ -625,33 +661,33 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
625
661
|
},
|
|
626
662
|
[commitEdit, defaultColor]
|
|
627
663
|
);
|
|
628
|
-
const handleNameChange = (0,
|
|
664
|
+
const handleNameChange = (0, import_react5.useCallback)(
|
|
629
665
|
(name) => {
|
|
630
666
|
setDraftName(name);
|
|
631
667
|
if (!tagManuallySet) setDraftTag(deriveTag(name));
|
|
632
668
|
},
|
|
633
669
|
[tagManuallySet]
|
|
634
670
|
);
|
|
635
|
-
const handleTagChange = (0,
|
|
671
|
+
const handleTagChange = (0, import_react5.useCallback)((tag) => {
|
|
636
672
|
setDraftTag((previous) => clampTag(previous, tag));
|
|
637
673
|
setTagManuallySet(true);
|
|
638
674
|
}, []);
|
|
639
675
|
const confirmDeleteProfile = profiles.find((p) => p.id === confirmDeleteId) ?? null;
|
|
640
|
-
const handleConfirmDelete = (0,
|
|
676
|
+
const handleConfirmDelete = (0, import_react5.useCallback)(() => {
|
|
641
677
|
if (confirmDeleteId) {
|
|
642
678
|
onDelete(confirmDeleteId);
|
|
643
679
|
if (editingId === confirmDeleteId) setEditingId(null);
|
|
644
680
|
}
|
|
645
681
|
setConfirmDeleteId(null);
|
|
646
682
|
}, [confirmDeleteId, editingId, onDelete]);
|
|
647
|
-
const sortedProfiles = (0,
|
|
683
|
+
const sortedProfiles = (0, import_react5.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
648
684
|
const rows = [...sortedProfiles.map((profile) => ({ kind: "profile", profile })), { kind: "new" }];
|
|
649
685
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
650
686
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: [styles5.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
651
687
|
headerLeft,
|
|
652
688
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Text, { variant: titleVariant, style: [styles5.title, { color: fg }], children: "Profiles" })
|
|
653
689
|
] }),
|
|
654
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.KeyboardAvoidingView, { behavior: import_react_native5.Platform.OS === "ios" ? "padding" : void 0, style: styles5.keyboardAvoiding, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper4.FadingScrollView, { style: styles5.scrollView, contentContainerStyle: [styles5.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: rows.map((row) => {
|
|
690
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.KeyboardAvoidingView, { behavior: import_react_native5.Platform.OS === "ios" ? "padding" : void 0, style: styles5.keyboardAvoiding, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.View, { ref: columnRef, collapsable: false, style: styles5.column, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper4.FadingScrollView, { style: styles5.scrollView, contentContainerStyle: [styles5.content, { paddingBottom: 32 + insets.bottom }], showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: rows.map((row) => {
|
|
655
691
|
if (row.kind === "new") {
|
|
656
692
|
if (editingId !== "new") {
|
|
657
693
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press3.TouchableRipple, { onPress: () => startEdit("new"), style: styles5.row, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: styles5.rowInner, children: [
|
|
@@ -659,45 +695,50 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
659
695
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Text, { variant: "bodyLarge", style: { color: fg }, children: "New Profile" })
|
|
660
696
|
] }) }, "new");
|
|
661
697
|
}
|
|
662
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, autoFocus: true, fgMuted, host, dark, colorPreview, useColorPickerAlign }, "new");
|
|
698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, autoFocus: true, fgMuted, host, dark, colorPreview, useColorPickerAlign, column: columnRef, renderSwatch }, "new");
|
|
663
699
|
}
|
|
664
700
|
const { profile } = row;
|
|
665
701
|
if (editingId === profile.id) {
|
|
666
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, onDelete: () => setConfirmDeleteId(profile.id), fgMuted, host, dark, colorPreview, useColorPickerAlign }, profile.id);
|
|
702
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EditRow, { draftName, onNameChange: handleNameChange, draftColor, onColorChange: setDraftColor, draftTag, onTagChange: handleTagChange, onSubmit: commitEdit, onDelete: () => setConfirmDeleteId(profile.id), fgMuted, host, dark, colorPreview, useColorPickerAlign, column: columnRef, renderSwatch }, profile.id);
|
|
667
703
|
}
|
|
668
704
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press3.TouchableRipple, { onPress: () => startEdit(profile), style: styles5.row, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: styles5.rowInner, children: [
|
|
669
705
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ProfileChip, { profile, size: CHIP_SIZE2 }),
|
|
670
706
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Text, { variant: "bodyLarge", style: [styles5.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
|
|
671
707
|
] }) }, profile.id);
|
|
672
|
-
}) }) }),
|
|
708
|
+
}) }) }) }),
|
|
673
709
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_chrome2.ConfirmDialog, { visible: confirmDeleteProfile !== null, title: "Delete Profile?", message: confirmDeleteProfile ? `This permanently deletes ${confirmDeleteProfile.name}\u2019s profile. This cannot be undone.` : "", confirmLabel: "Delete", cancelLabel: "Cancel", icon: "alert-outline", destructive: true, onConfirm: handleConfirmDelete, onCancel: () => setConfirmDeleteId(null) })
|
|
674
710
|
] });
|
|
675
711
|
}
|
|
676
|
-
function AlignedColorPicker({ useAlign, ...props }) {
|
|
712
|
+
function AlignedColorPicker({ useAlign, column, ...props }) {
|
|
677
713
|
const { width: windowWidth } = (0, import_react_native5.useWindowDimensions)();
|
|
678
714
|
const { width, height } = (0, import_chrome2.getInlineColorPickerContentSize)(import_auto_paper4.defaultColors.length, windowWidth);
|
|
679
|
-
const alignOverride = useAlign(props.host.openId === props.id, width, height);
|
|
715
|
+
const alignOverride = useAlign(props.host.openId === props.id, width, height, column);
|
|
680
716
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_chrome2.InlineColorPicker, { ...props, alignOverride });
|
|
681
717
|
}
|
|
682
|
-
function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, onSubmit, onDelete, autoFocus, fgMuted, host, dark, colorPreview, useColorPickerAlign }) {
|
|
718
|
+
function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, onSubmit, onDelete, autoFocus, fgMuted, host, dark, colorPreview, useColorPickerAlign, column, renderSwatch }) {
|
|
719
|
+
const pickerSwatch = (0, import_react5.useMemo)(() => renderSwatch ? (color, size, place) => renderSwatch(color, size, place === "trigger" ? "chip" : "colorPage") : void 0, [renderSwatch]);
|
|
683
720
|
const register = (0, import_focus_chain.useFocusChain)();
|
|
684
721
|
const tag = register();
|
|
685
722
|
const name = register();
|
|
686
|
-
const tagFocusRef = (0,
|
|
687
|
-
(0,
|
|
723
|
+
const tagFocusRef = (0, import_react5.useRef)(tag.props.focus);
|
|
724
|
+
(0, import_react5.useEffect)(() => {
|
|
688
725
|
tagFocusRef.current = tag.props.focus;
|
|
689
726
|
});
|
|
690
|
-
(0,
|
|
727
|
+
(0, import_react5.useEffect)(() => {
|
|
691
728
|
if (host.openId === "color") tagFocusRef.current();
|
|
692
729
|
}, [host.openId]);
|
|
693
730
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: styles5.editRow, children: [
|
|
694
|
-
useColorPickerAlign ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AlignedColorPicker, { useAlign: useColorPickerAlign, id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_chrome2.InlineColorPicker, { id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark }),
|
|
695
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.TextInput, { ref: tag.ref, ...tag.props, value: draftTag, onChangeText: onTagChange, accessibilityLabel: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, returnKeyType: "next", style: styles5.hiddenTagInput, pointerEvents: "none" }),
|
|
696
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.TextInput, { ref: name.ref, ...name.props, onSubmitEditing: onSubmit, mode: "outlined", dense: true, value: draftName, onChangeText: onNameChange, maxLength: MAX_PROFILE_NAME_LENGTH, placeholder: "Name", autoFocus, returnKeyType: "done", style: styles5.nameInput }),
|
|
731
|
+
useColorPickerAlign ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AlignedColorPicker, { useAlign: useColorPickerAlign, column, id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark, renderSwatch: pickerSwatch }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_chrome2.InlineColorPicker, { id: "color", host, value: draftColor, onChange: onColorChange, previewValue: colorPreview, tag: draftTag, dark, renderSwatch: pickerSwatch, bounds: column }),
|
|
732
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.TextInput, { ref: textInputRef(tag.ref), ...tag.props, value: draftTag, onChangeText: onTagChange, accessibilityLabel: "Tag", autoCapitalize: "characters", selectTextOnFocus: true, returnKeyType: "next", style: styles5.hiddenTagInput, pointerEvents: "none" }),
|
|
733
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.TextInput, { ref: textInputRef(name.ref), ...name.props, onSubmitEditing: onSubmit, mode: "outlined", dense: true, value: draftName, onChangeText: onNameChange, maxLength: MAX_PROFILE_NAME_LENGTH, placeholder: "Name", autoFocus, returnKeyType: "done", style: styles5.nameInput }),
|
|
697
734
|
onDelete && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_feedback_press3.TouchableRipple, { onPress: onDelete, style: styles5.iconButton, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Icon, { source: "trash-can-outline", size: 18, color: fgMuted }) })
|
|
698
735
|
] });
|
|
699
736
|
}
|
|
700
737
|
var styles5 = import_react_native5.StyleSheet.create({
|
|
738
|
+
// The roster's column, around the scroll view (see columnRef).
|
|
739
|
+
column: {
|
|
740
|
+
flex: 1
|
|
741
|
+
},
|
|
701
742
|
content: {
|
|
702
743
|
gap: 4,
|
|
703
744
|
paddingHorizontal: 20
|
|
@@ -778,12 +819,12 @@ var styles5 = import_react_native5.StyleSheet.create({
|
|
|
778
819
|
// src/ProfilesScreen.tsx
|
|
779
820
|
var import_auto_paper5 = require("@rific/auto-paper");
|
|
780
821
|
var import_feedback_press4 = require("@rific/feedback-press");
|
|
781
|
-
var
|
|
822
|
+
var import_react6 = require("react");
|
|
782
823
|
var import_react_native6 = require("react-native");
|
|
783
824
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
784
825
|
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
|
|
785
826
|
const { dark } = (0, import_auto_paper5.useAutoPaperTheme)();
|
|
786
|
-
const managerRef = (0,
|
|
827
|
+
const managerRef = (0, import_react6.useRef)(null);
|
|
787
828
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
788
829
|
const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
|
|
789
830
|
const handleBack = () => {
|
|
@@ -971,10 +1012,29 @@ async function readSharedProfiles(groupId) {
|
|
|
971
1012
|
async function loadSharedProfiles(groupId) {
|
|
972
1013
|
return await readSharedProfiles(groupId) ?? [];
|
|
973
1014
|
}
|
|
1015
|
+
var hasId = (entry) => typeof entry === "object" && entry !== null && typeof entry.id === "string" && entry.id !== "";
|
|
1016
|
+
async function unreadableEntries(groupId, profiles) {
|
|
1017
|
+
let json;
|
|
1018
|
+
try {
|
|
1019
|
+
json = web ? web.storage.getItem(webKey(groupId, PROFILES_KEY)) : await native?.getSharedJSON(groupId, PROFILES_KEY);
|
|
1020
|
+
} catch {
|
|
1021
|
+
return [];
|
|
1022
|
+
}
|
|
1023
|
+
if (!json) return [];
|
|
1024
|
+
try {
|
|
1025
|
+
const parsed = JSON.parse(json);
|
|
1026
|
+
if (!Array.isArray(parsed)) return [];
|
|
1027
|
+
const saved = new Set(profiles.map((profile) => profile.id));
|
|
1028
|
+
return parsed.filter((entry) => !isValidProfile(entry) && hasId(entry) && !saved.has(entry.id));
|
|
1029
|
+
} catch {
|
|
1030
|
+
return [];
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
974
1033
|
async function saveSharedProfiles(groupId, profiles) {
|
|
975
1034
|
if (!native) return;
|
|
976
1035
|
try {
|
|
977
|
-
await
|
|
1036
|
+
const kept = await unreadableEntries(groupId, profiles);
|
|
1037
|
+
await native.setSharedJSON(groupId, PROFILES_KEY, JSON.stringify([...profiles, ...kept]));
|
|
978
1038
|
} catch {
|
|
979
1039
|
}
|
|
980
1040
|
}
|
|
@@ -1056,15 +1116,15 @@ function createSeatColorsSlice(namespace, defaultState, mountKey = "seatColors")
|
|
|
1056
1116
|
}
|
|
1057
1117
|
|
|
1058
1118
|
// src/redux/useSharedProfilesSync.ts
|
|
1059
|
-
var
|
|
1119
|
+
var import_react7 = require("react");
|
|
1060
1120
|
var import_react_native8 = require("react-native");
|
|
1061
1121
|
function useSharedProfilesSync({ groupId, onRemoteChange }) {
|
|
1062
|
-
const pendingWriteRef = (0,
|
|
1063
|
-
const onRemoteChangeRef = (0,
|
|
1064
|
-
(0,
|
|
1122
|
+
const pendingWriteRef = (0, import_react7.useRef)(false);
|
|
1123
|
+
const onRemoteChangeRef = (0, import_react7.useRef)(onRemoteChange);
|
|
1124
|
+
(0, import_react7.useEffect)(() => {
|
|
1065
1125
|
onRemoteChangeRef.current = onRemoteChange;
|
|
1066
1126
|
}, [onRemoteChange]);
|
|
1067
|
-
(0,
|
|
1127
|
+
(0, import_react7.useEffect)(() => {
|
|
1068
1128
|
if (!isSharedProfileStoreAvailable) return;
|
|
1069
1129
|
let cancelled = false;
|
|
1070
1130
|
const sub = import_react_native8.AppState.addEventListener("change", (next) => {
|
|
@@ -1082,7 +1142,7 @@ function useSharedProfilesSync({ groupId, onRemoteChange }) {
|
|
|
1082
1142
|
unsubscribe();
|
|
1083
1143
|
};
|
|
1084
1144
|
}, [groupId]);
|
|
1085
|
-
const syncToShared = (0,
|
|
1145
|
+
const syncToShared = (0, import_react7.useCallback)(
|
|
1086
1146
|
(next) => {
|
|
1087
1147
|
if (!isSharedProfileStoreAvailable) return;
|
|
1088
1148
|
pendingWriteRef.current = true;
|