@tastic/profile 0.14.1 → 0.16.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 +46 -11
- package/dist/index.d.mts +67 -30
- package/dist/index.d.ts +67 -30
- package/dist/index.js +429 -237
- package/dist/index.mjs +393 -206
- package/package.json +9 -9
- package/src/IdentityChip.tsx +8 -2
- package/src/OptionPicker.tsx +64 -0
- package/src/PickerMenu.tsx +512 -0
- package/src/ProfileColorContext.tsx +30 -8
- package/src/ProfilePicker.tsx +61 -426
- package/src/ProfilesManager.tsx +9 -6
- package/src/SwatchLayer.tsx +28 -0
- package/src/index.ts +4 -3
- package/src/redux/resolveInitialProfiles.ts +3 -2
- package/src/redux/seatColorsSlice.ts +79 -29
- package/src/redux/useSharedProfilesSync.ts +12 -6
- package/src/sharedProfileStore.ts +87 -11
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
MAX_PROFILE_NAME_LENGTH: () => MAX_PROFILE_NAME_LENGTH,
|
|
24
24
|
MAX_TAG_LENGTH: () => MAX_TAG_LENGTH,
|
|
25
|
+
OptionPicker: () => OptionPicker,
|
|
25
26
|
ProfileChip: () => ProfileChip,
|
|
26
27
|
ProfileColorProvider: () => ProfileColorProvider,
|
|
27
28
|
ProfilePicker: () => ProfilePicker,
|
|
@@ -32,34 +33,87 @@ __export(index_exports, {
|
|
|
32
33
|
createProfileRecord: () => createProfileRecord,
|
|
33
34
|
createProfileSelectionSlice: () => createProfileSelectionSlice,
|
|
34
35
|
createSeatColorsSlice: () => createSeatColorsSlice,
|
|
36
|
+
getOptionPickerContentSize: () => getOptionPickerContentSize,
|
|
35
37
|
getProfilePickerContentSize: () => getProfilePickerContentSize,
|
|
36
38
|
isSharedProfileStoreAvailable: () => isSharedProfileStoreAvailable,
|
|
37
39
|
isValidProfile: () => isValidProfile,
|
|
38
40
|
isValidTag: () => isValidTag,
|
|
39
41
|
loadSharedProfiles: () => loadSharedProfiles,
|
|
42
|
+
profileSeatColor: () => profileSeatColor,
|
|
40
43
|
profilesActions: () => profilesActions,
|
|
41
44
|
profilesReducer: () => profilesReducer,
|
|
42
45
|
readSharedProfiles: () => readSharedProfiles,
|
|
43
46
|
resolveInitialProfiles: () => resolveInitialProfiles,
|
|
44
47
|
saveSharedProfiles: () => saveSharedProfiles,
|
|
48
|
+
subscribeSharedProfiles: () => subscribeSharedProfiles,
|
|
45
49
|
useProfileColorPreview: () => useProfileColorPreview,
|
|
46
50
|
useProfileDisplayColor: () => useProfileDisplayColor,
|
|
51
|
+
useProfileSwatchRenderer: () => useProfileSwatchRenderer,
|
|
47
52
|
useSharedProfilesSync: () => useSharedProfilesSync
|
|
48
53
|
});
|
|
49
54
|
module.exports = __toCommonJS(index_exports);
|
|
50
55
|
|
|
56
|
+
// src/PickerMenu.tsx
|
|
57
|
+
var import_auto_paper2 = require("@rific/auto-paper");
|
|
58
|
+
var import_feedback_press = require("@rific/feedback-press");
|
|
59
|
+
var import_chrome = require("@tastic/hud/chrome");
|
|
60
|
+
var import_react2 = require("react");
|
|
61
|
+
var import_react_native3 = require("react-native");
|
|
62
|
+
var import_react_native_paper2 = require("react-native-paper");
|
|
63
|
+
|
|
51
64
|
// src/IdentityChip.tsx
|
|
52
65
|
var import_auto_paper = require("@rific/auto-paper");
|
|
53
|
-
var
|
|
66
|
+
var import_react_native2 = require("react-native");
|
|
54
67
|
var import_react_native_paper = require("react-native-paper");
|
|
68
|
+
|
|
69
|
+
// src/ProfileColorContext.tsx
|
|
70
|
+
var import_react = require("react");
|
|
55
71
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
72
|
+
var ProfileColorContext = (0, import_react.createContext)({});
|
|
73
|
+
function ProfileColorProvider({ preview, renderSwatch, children }) {
|
|
74
|
+
const value = (0, import_react.useMemo)(() => ({ preview, renderSwatch }), [preview, renderSwatch]);
|
|
75
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProfileColorContext.Provider, { value, children });
|
|
76
|
+
}
|
|
77
|
+
function useProfileColorPreview() {
|
|
78
|
+
return (0, import_react.useContext)(ProfileColorContext).preview;
|
|
79
|
+
}
|
|
80
|
+
function useProfileSwatchRenderer() {
|
|
81
|
+
return (0, import_react.useContext)(ProfileColorContext).renderSwatch;
|
|
82
|
+
}
|
|
83
|
+
function useProfileDisplayColor(hex) {
|
|
84
|
+
const preview = useProfileColorPreview();
|
|
85
|
+
return preview ? preview(hex) : hex;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/SwatchLayer.tsx
|
|
89
|
+
var import_react_native = require("react-native");
|
|
90
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
91
|
+
function SwatchLayer({ render, color, size }) {
|
|
92
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native.View, { style: [styles.layer, { borderRadius: size / 2, height: size, width: size }], children: render(color, size) });
|
|
93
|
+
}
|
|
94
|
+
var styles = import_react_native.StyleSheet.create({
|
|
95
|
+
layer: {
|
|
96
|
+
left: 0,
|
|
97
|
+
overflow: "hidden",
|
|
98
|
+
pointerEvents: "none",
|
|
99
|
+
position: "absolute",
|
|
100
|
+
top: 0
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// src/IdentityChip.tsx
|
|
105
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
56
106
|
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
57
107
|
function IdentityChip({ color, tag, icon, size, filled = true }) {
|
|
108
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
58
109
|
const contrastColor = (0, import_auto_paper.getContrastColor)(color);
|
|
59
110
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : tag && Array.from(tag).length >= 3 ? 0.32 : 0.4);
|
|
60
|
-
return /* @__PURE__ */ (0,
|
|
111
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: [styles2.chip, { borderRadius: size / 2, height: size, width: size }, filled && !renderSwatch && { backgroundColor: color }], children: [
|
|
112
|
+
filled && renderSwatch && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SwatchLayer, { render: renderSwatch, color, size }),
|
|
113
|
+
tag ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper.Text, { style: [styles2.tag, { color: contrastColor, fontSize: tagFontSize }], numberOfLines: 1, adjustsFontSizeToFit: true, children: tag }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper.Icon, { source: icon, size: size * 0.6, color: contrastColor })
|
|
114
|
+
] });
|
|
61
115
|
}
|
|
62
|
-
var
|
|
116
|
+
var styles2 = import_react_native2.StyleSheet.create({
|
|
63
117
|
chip: {
|
|
64
118
|
alignItems: "center",
|
|
65
119
|
justifyContent: "center"
|
|
@@ -70,35 +124,7 @@ var styles = import_react_native.StyleSheet.create({
|
|
|
70
124
|
}
|
|
71
125
|
});
|
|
72
126
|
|
|
73
|
-
// src/
|
|
74
|
-
var import_react = require("react");
|
|
75
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
76
|
-
var ProfileColorContext = (0, import_react.createContext)(void 0);
|
|
77
|
-
function ProfileColorProvider({ preview, children }) {
|
|
78
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ProfileColorContext.Provider, { value: preview, children });
|
|
79
|
-
}
|
|
80
|
-
function useProfileColorPreview() {
|
|
81
|
-
return (0, import_react.useContext)(ProfileColorContext);
|
|
82
|
-
}
|
|
83
|
-
function useProfileDisplayColor(hex) {
|
|
84
|
-
const preview = useProfileColorPreview();
|
|
85
|
-
return preview ? preview(hex) : hex;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// src/ProfileChip.tsx
|
|
89
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
90
|
-
function ProfileChip({ profile, size = 22, filled = true }) {
|
|
91
|
-
const color = useProfileDisplayColor(profile.color);
|
|
92
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IdentityChip, { color, tag: profile.tag, icon: "account", size, filled });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// src/ProfilePicker.tsx
|
|
96
|
-
var import_auto_paper2 = require("@rific/auto-paper");
|
|
97
|
-
var import_feedback_press = require("@rific/feedback-press");
|
|
98
|
-
var import_hud = require("@tastic/hud");
|
|
99
|
-
var import_react2 = require("react");
|
|
100
|
-
var import_react_native2 = require("react-native");
|
|
101
|
-
var import_react_native_paper2 = require("react-native-paper");
|
|
127
|
+
// src/PickerMenu.tsx
|
|
102
128
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
103
129
|
var TRIGGER_HEIGHT = 28;
|
|
104
130
|
var POPOVER_WIDTH = 220;
|
|
@@ -110,6 +136,8 @@ var CHIP_SIZE = 32;
|
|
|
110
136
|
var CHIP_TRIGGER_PADDING = 6;
|
|
111
137
|
var SWATCH_SIZE = 28;
|
|
112
138
|
var SWATCH_MIN_GAP = 6;
|
|
139
|
+
var SWATCH_RING_WIDTH = 2;
|
|
140
|
+
var COLOR_ROW_SWATCH_SIZE = 22;
|
|
113
141
|
var COLOR_ROW_CAPTIONED_HEIGHT = 48;
|
|
114
142
|
var COLOR_LABEL_LINE = 18;
|
|
115
143
|
var COLOR_CAPTION_LINE = 16;
|
|
@@ -126,59 +154,41 @@ function colorPageHeight(swatches) {
|
|
|
126
154
|
const rows = Math.ceil(swatches.length / GRID_COLUMNS);
|
|
127
155
|
return ROW_HEIGHT + GRID_TOP + rows * SWATCH_SIZE + (rows - 1) * GRID_GAP + GRID_BOTTOM;
|
|
128
156
|
}
|
|
129
|
-
function
|
|
130
|
-
const rowCount = profileCount + 1 + (hasManageRow ? 1 : 0);
|
|
157
|
+
function pickerContentSize(rowCount, colorSection) {
|
|
131
158
|
const list = rowCount * ROW_HEIGHT + (colorSection ? colorRowHeight(colorSection.caption) + COLOR_ROW_DIVIDER : 0);
|
|
132
159
|
const content = colorSection ? Math.max(list, colorPageHeight(colorSection.swatches ?? import_auto_paper2.defaultColors)) : list;
|
|
133
160
|
return { width: POPOVER_WIDTH, height: LIST_PADDING * 2 + content };
|
|
134
161
|
}
|
|
135
|
-
function
|
|
136
|
-
const id = `${idPrefix}-profile`;
|
|
137
|
-
const colorPreview = useProfileColorPreview();
|
|
162
|
+
function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColor, contentSize, align: forcedAlign, alignOverride, rotation, triggerStyle, footer, children }) {
|
|
138
163
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
139
164
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
140
165
|
const open = host.openId === id;
|
|
141
|
-
const
|
|
142
|
-
const sortedProfiles = (0, import_react2.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
143
|
-
const { width: contentWidth, height: contentHeight } = getProfilePickerContentSize(profiles.length, !!onManage, colorSection);
|
|
144
|
-
const auto = (0, import_hud.useAutoAlign)(open, contentWidth, contentHeight, rotation);
|
|
166
|
+
const auto = (0, import_chrome.useAutoAlign)(open, contentSize.width, contentSize.height, rotation);
|
|
145
167
|
const placement = alignOverride ?? auto;
|
|
146
168
|
const { maxHeight, measured, triggerRef, verticalAlign } = placement;
|
|
147
169
|
const align = forcedAlign ?? placement.align;
|
|
148
|
-
const
|
|
149
|
-
onSelect(null);
|
|
150
|
-
host.close();
|
|
151
|
-
};
|
|
152
|
-
const handleSelectExisting = (profile) => {
|
|
153
|
-
if (profile.id === takenId) return;
|
|
154
|
-
onSelect(profile);
|
|
155
|
-
host.close();
|
|
156
|
-
};
|
|
157
|
-
const handleManage = () => {
|
|
158
|
-
host.close();
|
|
159
|
-
onManage?.();
|
|
160
|
-
};
|
|
161
|
-
const chipTrigger = !!colorSection;
|
|
170
|
+
const chipTrigger = !!chip;
|
|
162
171
|
const [triggerWidth, setTriggerWidth] = (0, import_react2.useState)(0);
|
|
163
172
|
const handleAnchorLayout = chipTrigger ? (e) => setTriggerWidth(e.nativeEvent.layout.width) : void 0;
|
|
164
173
|
const [colorPage, setColorPage] = (0, import_react2.useState)(false);
|
|
174
|
+
const showColorPage = !!colorSection && colorPage;
|
|
165
175
|
const handleTriggerPress = () => {
|
|
166
176
|
setColorPage(false);
|
|
167
177
|
host.toggle(id);
|
|
168
178
|
};
|
|
169
|
-
const triggerLabel =
|
|
170
|
-
const
|
|
171
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
172
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: handleTriggerPress, borderless: chipTrigger, style: [
|
|
173
|
-
|
|
174
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [
|
|
179
|
+
const triggerLabel = label.toUpperCase();
|
|
180
|
+
const showFooter = !!footer && !showColorPage;
|
|
181
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: [styles3.anchor, open && styles3.anchorOpen], onLayout: handleAnchorLayout, children: [
|
|
182
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: handleTriggerPress, borderless: chipTrigger, style: [styles3.trigger, chipTrigger && styles3.chipTrigger, 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: [
|
|
183
|
+
chip && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IdentityChip, { color, tag: chip.tag, icon: chip.icon, size: CHIP_SIZE }),
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles3.triggerLabel, { color: chipTrigger ? fg : color }], numberOfLines: 1, children: triggerLabel }),
|
|
175
185
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "menu-down", size: 16, color: chipTrigger ? fg : color })
|
|
176
186
|
] }) }),
|
|
177
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
178
|
-
|
|
187
|
+
/* @__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)(
|
|
188
|
+
import_react_native3.View,
|
|
179
189
|
{
|
|
180
190
|
style: [
|
|
181
|
-
|
|
191
|
+
styles3.menu,
|
|
182
192
|
{
|
|
183
193
|
backgroundColor: menuBg,
|
|
184
194
|
borderColor: color,
|
|
@@ -187,78 +197,88 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, d
|
|
|
187
197
|
}
|
|
188
198
|
],
|
|
189
199
|
children: [
|
|
190
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_auto_paper2.FadingScrollView, { contentContainerStyle: [
|
|
200
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_auto_paper2.FadingScrollView, { contentContainerStyle: [styles3.menuContent, showFooter && styles3.menuContentAboveFooter], showsVerticalScrollIndicator: false, children: colorSection && colorPage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ColorPage, { section: colorSection, homeColor, fg, onBack: () => setColorPage(false) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
191
201
|
colorSection && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ColorRow, { section: colorSection, fg, dividerColor: color, onPress: () => setColorPage(true) }),
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const taken = profile.id === takenId;
|
|
198
|
-
const selected = selectedId === profile.id;
|
|
199
|
-
const rowColor = colorPreview ? colorPreview(profile.color) : profile.color;
|
|
200
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { disabled: taken, onPress: () => handleSelectExisting(profile), style: [styles2.row, selected && { backgroundColor: rowColor }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: [styles2.rowInner, taken && styles2.rowDisabled], children: [
|
|
201
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ProfileChip, { profile, filled: !selected }),
|
|
202
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
203
|
-
import_react_native_paper2.Text,
|
|
204
|
-
{
|
|
205
|
-
style: [
|
|
206
|
-
styles2.rowLabel,
|
|
207
|
-
{
|
|
208
|
-
color: selected ? (0, import_auto_paper2.getContrastColor)(rowColor) : fg
|
|
209
|
-
}
|
|
210
|
-
],
|
|
211
|
-
numberOfLines: 1,
|
|
212
|
-
children: profile.name
|
|
213
|
-
}
|
|
214
|
-
)
|
|
215
|
-
] }) }, profile.id);
|
|
216
|
-
})
|
|
217
|
-
] }) }, colorSection && colorPage ? "color" : "list"),
|
|
218
|
-
manageFooter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.menuFooter, children: [
|
|
219
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native2.View, { style: [styles2.divider, { backgroundColor: color }] }),
|
|
220
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: handleManage, style: styles2.row, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.manageRowInner, children: [
|
|
221
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "account-cog-outline", size: 16, color }),
|
|
222
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles2.manageLabel, { color: fg }], children: "Manage" })
|
|
223
|
-
] }) })
|
|
202
|
+
children
|
|
203
|
+
] }) }, showColorPage ? "color" : "list"),
|
|
204
|
+
showFooter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: styles3.menuFooter, children: [
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.divider, { backgroundColor: color }] }),
|
|
206
|
+
footer
|
|
224
207
|
] })
|
|
225
208
|
]
|
|
226
209
|
}
|
|
227
210
|
) })
|
|
228
211
|
] });
|
|
229
212
|
}
|
|
213
|
+
function IconRow({ icon, label, selected, color, fg, disabled = false, onPress }) {
|
|
214
|
+
const textColor = selected ? (0, import_auto_paper2.getContrastColor)(color) : fg;
|
|
215
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { disabled, onPress, style: [pickerMenuStyles.row, selected && { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: [pickerMenuStyles.rowInner, disabled && pickerMenuStyles.rowDisabled], children: [
|
|
216
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: styles3.iconSlot, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: icon, size: 13, color: textColor }) }),
|
|
217
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [pickerMenuStyles.rowLabel, { color: textColor }], numberOfLines: 1, children: label })
|
|
218
|
+
] }) });
|
|
219
|
+
}
|
|
230
220
|
function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
231
221
|
const { value, previewValue, label = "Color", caption } = section;
|
|
222
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
232
223
|
const drawn = previewValue ? previewValue(value) : value;
|
|
233
224
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
234
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress, style: [
|
|
235
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
236
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
237
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [
|
|
238
|
-
caption ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [
|
|
225
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress, style: [pickerMenuStyles.row, { height: colorRowHeight(caption) }], accessibilityRole: "button", accessibilityLabel: caption ? `${label}, ${caption}` : label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: pickerMenuStyles.rowInner, children: [
|
|
226
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.colorRowSwatch, !renderSwatch && { backgroundColor: drawn }], children: renderSwatch && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SwatchLayer, { render: renderSwatch, color: drawn, size: COLOR_ROW_SWATCH_SIZE }) }),
|
|
227
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: styles3.colorRowText, children: [
|
|
228
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles3.colorRowLabel, { color: fg }], numberOfLines: 1, children: label }),
|
|
229
|
+
caption ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles3.colorRowCaption, { color: fg }], numberOfLines: 1, children: caption }) : null
|
|
239
230
|
] }),
|
|
240
231
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "chevron-right", size: 18, color: fg })
|
|
241
232
|
] }) }),
|
|
242
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
233
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.divider, { backgroundColor: dividerColor }] })
|
|
243
234
|
] });
|
|
244
235
|
}
|
|
245
|
-
function ColorPage({ section,
|
|
246
|
-
const { value, onChange, previewValue, swatches = import_auto_paper2.defaultColors, label = "Color" } = section;
|
|
236
|
+
function ColorPage({ section, homeColor, fg, onBack }) {
|
|
237
|
+
const { value, onChange, previewValue, swatches = import_auto_paper2.defaultColors, label = "Color", takenValue, allowSwapTaken } = section;
|
|
238
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
247
239
|
const displayFor = previewValue ?? ((hex) => hex);
|
|
248
240
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
249
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: onBack, style:
|
|
250
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
251
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [
|
|
241
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { onPress: onBack, style: pickerMenuStyles.row, accessibilityRole: "button", accessibilityLabel: "Back", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: pickerMenuStyles.rowInner, children: [
|
|
242
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: styles3.iconSlot, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "chevron-left", size: 20, color: fg }) }),
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Text, { style: [styles3.colorRowLabel, { color: fg }], numberOfLines: 1, children: label })
|
|
252
244
|
] }) }),
|
|
253
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
245
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: styles3.swatches, children: swatches.map((swatch) => {
|
|
254
246
|
const selected = swatch.value.toLowerCase() === value.toLowerCase();
|
|
255
|
-
const
|
|
247
|
+
const isHomeColor = !!homeColor && swatch.value.toLowerCase() === homeColor.toLowerCase();
|
|
248
|
+
const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
|
|
249
|
+
const swappable = taken && !!allowSwapTaken;
|
|
256
250
|
const swatchDisplay = displayFor(swatch.value);
|
|
257
|
-
|
|
251
|
+
const contrast = (0, import_auto_paper2.getContrastColor)(swatchDisplay);
|
|
252
|
+
const accessibilityLabel = swappable ? `${swatch.label}, swap` : taken ? `${swatch.label}, taken` : isHomeColor ? `${swatch.label}, profile color` : swatch.label;
|
|
253
|
+
const mark = selected ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "check", size: 16, color: contrast }) : swappable ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "swap-horizontal", size: 16, color: contrast }) : taken ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "close", size: 16, color: contrast }) : isHomeColor ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.homeDot, { backgroundColor: contrast }] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, {});
|
|
254
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_feedback_press.TouchableRipple, { disabled: taken && !swappable, onPress: () => onChange(swatch.value), borderless: true, style: [styles3.swatch, !renderSwatch && { backgroundColor: swatchDisplay }, selected && styles3.swatchSelected, taken && !swappable && styles3.swatchTaken], accessibilityRole: "button", accessibilityLabel, accessibilityState: { selected, disabled: taken && !swappable }, children: renderSwatch ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native3.View, { style: styles3.swatchFace, children: [
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SwatchLayer, { render: renderSwatch, color: swatchDisplay, size: selected ? SWATCH_SIZE - 2 * SWATCH_RING_WIDTH : SWATCH_SIZE }),
|
|
256
|
+
mark
|
|
257
|
+
] }) : mark }, swatch.value);
|
|
258
258
|
}) })
|
|
259
259
|
] });
|
|
260
260
|
}
|
|
261
|
-
var
|
|
261
|
+
var pickerMenuStyles = import_react_native3.StyleSheet.create({
|
|
262
|
+
row: {
|
|
263
|
+
borderRadius: 8,
|
|
264
|
+
height: ROW_HEIGHT
|
|
265
|
+
},
|
|
266
|
+
rowDisabled: {
|
|
267
|
+
opacity: 0.35
|
|
268
|
+
},
|
|
269
|
+
rowInner: {
|
|
270
|
+
alignItems: "center",
|
|
271
|
+
flexDirection: "row",
|
|
272
|
+
flex: 1,
|
|
273
|
+
gap: 8,
|
|
274
|
+
paddingHorizontal: ROW_INSET
|
|
275
|
+
},
|
|
276
|
+
rowLabel: {
|
|
277
|
+
flex: 1,
|
|
278
|
+
fontSize: 14
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
var styles3 = import_react_native3.StyleSheet.create({
|
|
262
282
|
anchor: {
|
|
263
283
|
position: "relative"
|
|
264
284
|
},
|
|
@@ -294,45 +314,38 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
294
314
|
},
|
|
295
315
|
// The color in effect, in the same 22pt slot as every row's chip.
|
|
296
316
|
colorRowSwatch: {
|
|
297
|
-
borderRadius:
|
|
298
|
-
height:
|
|
299
|
-
width:
|
|
317
|
+
borderRadius: COLOR_ROW_SWATCH_SIZE / 2,
|
|
318
|
+
height: COLOR_ROW_SWATCH_SIZE,
|
|
319
|
+
width: COLOR_ROW_SWATCH_SIZE
|
|
300
320
|
},
|
|
301
321
|
colorRowText: {
|
|
302
322
|
flex: 1
|
|
303
323
|
},
|
|
304
|
-
// Separates
|
|
305
|
-
// action, not just another row in the same list. Tinted with the seat's own color
|
|
306
|
-
// backgroundColor) rather than a flat gray, matching the border/caret's own
|
|
324
|
+
// Separates the footer (a navigation action) and the Color row from the selectable rows - a
|
|
325
|
+
// different kind of action, not just another row in the same list. Tinted with the seat's own color
|
|
326
|
+
// (passed in as backgroundColor) rather than a flat gray, matching the border/caret's own
|
|
327
|
+
// accent-tinted look.
|
|
307
328
|
divider: {
|
|
308
329
|
height: 1,
|
|
309
330
|
marginVertical: 4,
|
|
310
331
|
opacity: 0.25
|
|
311
332
|
},
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
333
|
+
// The selected profile's own color (see ColorPage).
|
|
334
|
+
homeDot: {
|
|
335
|
+
borderRadius: 4,
|
|
336
|
+
height: 8,
|
|
337
|
+
width: 8
|
|
338
|
+
},
|
|
339
|
+
// Matches ProfileChip's own default size/shape exactly (see IconRow's own render-site comment) -
|
|
340
|
+
// no backgroundColor, unlike ProfileChip's own default: an icon row has no identity color of its
|
|
341
|
+
// own to fill it with, and the render site colors the icon directly instead.
|
|
342
|
+
iconSlot: {
|
|
316
343
|
alignItems: "center",
|
|
317
344
|
borderRadius: 11,
|
|
318
345
|
height: 22,
|
|
319
346
|
justifyContent: "center",
|
|
320
347
|
width: 22
|
|
321
348
|
},
|
|
322
|
-
manageLabel: {
|
|
323
|
-
fontSize: 13,
|
|
324
|
-
fontWeight: "700",
|
|
325
|
-
letterSpacing: 0.5
|
|
326
|
-
},
|
|
327
|
-
// Centered rather than left-aligned like a normal row - see this row's own render-site comment
|
|
328
|
-
// for why (a footer action, not another selectable identity).
|
|
329
|
-
manageRowInner: {
|
|
330
|
-
alignItems: "center",
|
|
331
|
-
flexDirection: "row",
|
|
332
|
-
gap: 6,
|
|
333
|
-
height: "100%",
|
|
334
|
-
justifyContent: "center"
|
|
335
|
-
},
|
|
336
349
|
menu: {
|
|
337
350
|
borderRadius: 12,
|
|
338
351
|
borderWidth: MENU_BORDER_WIDTH,
|
|
@@ -348,7 +361,7 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
348
361
|
menuContent: {
|
|
349
362
|
padding: LIST_PADDING
|
|
350
363
|
},
|
|
351
|
-
// With
|
|
364
|
+
// With a footer pinned below, its own padding takes the bottom edge (same total height as before).
|
|
352
365
|
menuContentAboveFooter: {
|
|
353
366
|
paddingBottom: 0
|
|
354
367
|
},
|
|
@@ -356,30 +369,6 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
356
369
|
paddingBottom: LIST_PADDING,
|
|
357
370
|
paddingHorizontal: LIST_PADDING
|
|
358
371
|
},
|
|
359
|
-
// The selected profile's own color (see ColorSection).
|
|
360
|
-
profileDot: {
|
|
361
|
-
borderRadius: 4,
|
|
362
|
-
height: 8,
|
|
363
|
-
width: 8
|
|
364
|
-
},
|
|
365
|
-
row: {
|
|
366
|
-
borderRadius: 8,
|
|
367
|
-
height: ROW_HEIGHT
|
|
368
|
-
},
|
|
369
|
-
rowDisabled: {
|
|
370
|
-
opacity: 0.35
|
|
371
|
-
},
|
|
372
|
-
rowInner: {
|
|
373
|
-
alignItems: "center",
|
|
374
|
-
flexDirection: "row",
|
|
375
|
-
flex: 1,
|
|
376
|
-
gap: 8,
|
|
377
|
-
paddingHorizontal: ROW_INSET
|
|
378
|
-
},
|
|
379
|
-
rowLabel: {
|
|
380
|
-
flex: 1,
|
|
381
|
-
fontSize: 14
|
|
382
|
-
},
|
|
383
372
|
swatch: {
|
|
384
373
|
alignItems: "center",
|
|
385
374
|
borderRadius: SWATCH_SIZE / 2,
|
|
@@ -387,9 +376,22 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
387
376
|
justifyContent: "center",
|
|
388
377
|
width: SWATCH_SIZE
|
|
389
378
|
},
|
|
379
|
+
// A drawn swatch (see ColorPage) and its mark: the whole circle inside the selected one's ring.
|
|
380
|
+
swatchFace: {
|
|
381
|
+
alignItems: "center",
|
|
382
|
+
bottom: 0,
|
|
383
|
+
justifyContent: "center",
|
|
384
|
+
left: 0,
|
|
385
|
+
position: "absolute",
|
|
386
|
+
right: 0,
|
|
387
|
+
top: 0
|
|
388
|
+
},
|
|
390
389
|
swatchSelected: {
|
|
391
390
|
borderColor: "#ffffff",
|
|
392
|
-
borderWidth:
|
|
391
|
+
borderWidth: SWATCH_RING_WIDTH
|
|
392
|
+
},
|
|
393
|
+
swatchTaken: {
|
|
394
|
+
opacity: 0.35
|
|
393
395
|
},
|
|
394
396
|
swatches: {
|
|
395
397
|
columnGap: GRID_GAP,
|
|
@@ -417,15 +419,114 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
417
419
|
}
|
|
418
420
|
});
|
|
419
421
|
|
|
420
|
-
// src/
|
|
422
|
+
// src/OptionPicker.tsx
|
|
423
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
424
|
+
function getOptionPickerContentSize(optionCount, colorSection) {
|
|
425
|
+
return pickerContentSize(optionCount, colorSection);
|
|
426
|
+
}
|
|
427
|
+
function OptionPicker({ id, host, options, value, onChange, color, dark, icon, rowIcon = icon, colorSection, align, alignOverride, rotation = 0, triggerStyle }) {
|
|
428
|
+
const fg = dark ? "#FFFFFF" : "#000000";
|
|
429
|
+
const selected = options.find((option) => option.value === value);
|
|
430
|
+
const handleSelect = (option) => {
|
|
431
|
+
onChange(option.value);
|
|
432
|
+
host.close();
|
|
433
|
+
};
|
|
434
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PickerMenu, { id, host, color, dark, label: selected?.label ?? "", chip: { icon }, colorSection, contentSize: getOptionPickerContentSize(options.length, colorSection), align, alignOverride, rotation, triggerStyle, children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconRow, { icon: option.icon ?? rowIcon, label: option.label, selected: option.value === value, color, fg, onPress: () => handleSelect(option) }, option.value)) });
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/ProfileChip.tsx
|
|
438
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
439
|
+
function ProfileChip({ profile, size = 22, filled = true }) {
|
|
440
|
+
const color = useProfileDisplayColor(profile.color);
|
|
441
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IdentityChip, { color, tag: profile.tag, icon: "account", size, filled });
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/ProfilePicker.tsx
|
|
421
445
|
var import_auto_paper3 = require("@rific/auto-paper");
|
|
422
446
|
var import_feedback_press2 = require("@rific/feedback-press");
|
|
423
|
-
var import_focus_chain = require("@rific/focus-chain");
|
|
424
|
-
var import_toaster = require("@rific/toaster");
|
|
425
|
-
var import_hud2 = require("@tastic/hud");
|
|
426
447
|
var import_react3 = require("react");
|
|
427
|
-
var
|
|
448
|
+
var import_react_native4 = require("react-native");
|
|
428
449
|
var import_react_native_paper3 = require("react-native-paper");
|
|
450
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
451
|
+
function getProfilePickerContentSize(profileCount, hasManageRow = false, colorSection) {
|
|
452
|
+
return pickerContentSize(profileCount + 1 + (hasManageRow ? 1 : 0), colorSection);
|
|
453
|
+
}
|
|
454
|
+
function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, takenIds, color, dark, align, alignOverride, rotation = 0, guestLabel, nullLabel = "Player", nullIcon = "account-off-outline", onSelect, onManage, colorSection, triggerStyle }) {
|
|
455
|
+
const id = `${idPrefix}-profile`;
|
|
456
|
+
const colorPreview = useProfileColorPreview();
|
|
457
|
+
const fg = dark ? "#FFFFFF" : "#000000";
|
|
458
|
+
const selectedProfile = profiles.find((p) => p.id === selectedId) ?? null;
|
|
459
|
+
const sortedProfiles = (0, import_react3.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
460
|
+
const contentSize = getProfilePickerContentSize(profiles.length, !!onManage, colorSection);
|
|
461
|
+
const isTaken = (profileId) => profileId === takenId || profileId !== selectedId && !!takenIds?.includes(profileId);
|
|
462
|
+
const handleSelectGuest = () => {
|
|
463
|
+
onSelect(null);
|
|
464
|
+
host.close();
|
|
465
|
+
};
|
|
466
|
+
const handleSelectExisting = (profile) => {
|
|
467
|
+
if (isTaken(profile.id)) return;
|
|
468
|
+
onSelect(profile);
|
|
469
|
+
host.close();
|
|
470
|
+
};
|
|
471
|
+
const handleManage = () => {
|
|
472
|
+
host.close();
|
|
473
|
+
onManage?.();
|
|
474
|
+
};
|
|
475
|
+
const manageFooter = onManage ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press2.TouchableRipple, { onPress: handleManage, style: pickerMenuStyles.row, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native4.View, { style: styles4.manageRowInner, children: [
|
|
476
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Icon, { source: "account-cog-outline", size: 16, color }),
|
|
477
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_paper3.Text, { style: [styles4.manageLabel, { color: fg }], children: "Manage" })
|
|
478
|
+
] }) }) : void 0;
|
|
479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(PickerMenu, { id, host, color, dark, label: selectedProfile?.name ?? guestLabel, chip: colorSection ? { tag: selectedProfile?.tag, icon: selectedProfile ? "account" : nullIcon } : void 0, colorSection, homeColor: selectedProfile?.color, contentSize, align, alignOverride, rotation, triggerStyle, footer: manageFooter, children: [
|
|
480
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconRow, { icon: nullIcon, label: nullLabel, selected: selectedId === null, color, fg, onPress: handleSelectGuest }),
|
|
481
|
+
sortedProfiles.map((profile) => {
|
|
482
|
+
const taken = isTaken(profile.id);
|
|
483
|
+
const selected = selectedId === profile.id;
|
|
484
|
+
const rowColor = colorPreview ? colorPreview(profile.color) : profile.color;
|
|
485
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_feedback_press2.TouchableRipple, { disabled: taken, onPress: () => handleSelectExisting(profile), style: [pickerMenuStyles.row, selected && { backgroundColor: rowColor }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native4.View, { style: [pickerMenuStyles.rowInner, taken && pickerMenuStyles.rowDisabled], children: [
|
|
486
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ProfileChip, { profile, filled: !selected }),
|
|
487
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
488
|
+
import_react_native_paper3.Text,
|
|
489
|
+
{
|
|
490
|
+
style: [
|
|
491
|
+
pickerMenuStyles.rowLabel,
|
|
492
|
+
{
|
|
493
|
+
color: selected ? (0, import_auto_paper3.getContrastColor)(rowColor) : fg
|
|
494
|
+
}
|
|
495
|
+
],
|
|
496
|
+
numberOfLines: 1,
|
|
497
|
+
children: profile.name
|
|
498
|
+
}
|
|
499
|
+
)
|
|
500
|
+
] }) }, profile.id);
|
|
501
|
+
})
|
|
502
|
+
] });
|
|
503
|
+
}
|
|
504
|
+
var styles4 = import_react_native4.StyleSheet.create({
|
|
505
|
+
manageLabel: {
|
|
506
|
+
fontSize: 13,
|
|
507
|
+
fontWeight: "700",
|
|
508
|
+
letterSpacing: 0.5
|
|
509
|
+
},
|
|
510
|
+
// Centered rather than left-aligned like a normal row - see the footer's own comment for why (a
|
|
511
|
+
// footer action, not another selectable identity).
|
|
512
|
+
manageRowInner: {
|
|
513
|
+
alignItems: "center",
|
|
514
|
+
flexDirection: "row",
|
|
515
|
+
gap: 6,
|
|
516
|
+
height: "100%",
|
|
517
|
+
justifyContent: "center"
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// src/ProfilesManager.tsx
|
|
522
|
+
var import_auto_paper4 = require("@rific/auto-paper");
|
|
523
|
+
var import_feedback_press3 = require("@rific/feedback-press");
|
|
524
|
+
var import_focus_chain = require("@rific/focus-chain");
|
|
525
|
+
var import_toaster = require("@rific/toaster");
|
|
526
|
+
var import_chrome2 = require("@tastic/hud/chrome");
|
|
527
|
+
var import_react4 = require("react");
|
|
528
|
+
var import_react_native5 = require("react-native");
|
|
529
|
+
var import_react_native_paper4 = require("react-native-paper");
|
|
429
530
|
var import_react_native_safe_area_context = require("react-native-safe-area-context");
|
|
430
531
|
|
|
431
532
|
// src/profilesValidation.ts
|
|
@@ -461,7 +562,7 @@ function isValidProfile(value) {
|
|
|
461
562
|
}
|
|
462
563
|
|
|
463
564
|
// src/ProfilesManager.tsx
|
|
464
|
-
var
|
|
565
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
465
566
|
var CHIP_SIZE2 = 40;
|
|
466
567
|
function deriveTag(name) {
|
|
467
568
|
const trimmed = name.trim();
|
|
@@ -470,19 +571,19 @@ function deriveTag(name) {
|
|
|
470
571
|
function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, headerLeft, ref, fg: fgOverride, fgMuted: fgMutedOverride, titleVariant = "headlineSmall", colorPreview: colorPreviewProp, useColorPickerAlign }) {
|
|
471
572
|
const contextColorPreview = useProfileColorPreview();
|
|
472
573
|
const colorPreview = colorPreviewProp ?? contextColorPreview;
|
|
473
|
-
const { dark } = (0,
|
|
574
|
+
const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
|
|
474
575
|
const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
|
|
475
576
|
const { error: showErrorToast } = (0, import_toaster.useToast)();
|
|
476
|
-
const host = (0,
|
|
577
|
+
const host = (0, import_chrome2.usePopoverHost)();
|
|
477
578
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
478
579
|
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
479
|
-
const [editingId, setEditingId] = (0,
|
|
480
|
-
const [draftName, setDraftName] = (0,
|
|
481
|
-
const [draftColor, setDraftColor] = (0,
|
|
482
|
-
const [draftTag, setDraftTag] = (0,
|
|
483
|
-
const [tagManuallySet, setTagManuallySet] = (0,
|
|
484
|
-
const [confirmDeleteId, setConfirmDeleteId] = (0,
|
|
485
|
-
const commitEdit = (0,
|
|
580
|
+
const [editingId, setEditingId] = (0, import_react4.useState)(null);
|
|
581
|
+
const [draftName, setDraftName] = (0, import_react4.useState)("");
|
|
582
|
+
const [draftColor, setDraftColor] = (0, import_react4.useState)(defaultColor);
|
|
583
|
+
const [draftTag, setDraftTag] = (0, import_react4.useState)("");
|
|
584
|
+
const [tagManuallySet, setTagManuallySet] = (0, import_react4.useState)(false);
|
|
585
|
+
const [confirmDeleteId, setConfirmDeleteId] = (0, import_react4.useState)(null);
|
|
586
|
+
const commitEdit = (0, import_react4.useCallback)(() => {
|
|
486
587
|
const name = draftName.trim();
|
|
487
588
|
if (editingId && name) {
|
|
488
589
|
const tagValid = isValidTag(draftTag);
|
|
@@ -493,13 +594,13 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
493
594
|
}
|
|
494
595
|
setEditingId(null);
|
|
495
596
|
}, [editingId, draftName, draftColor, draftTag, onCreate, onSave, showErrorToast]);
|
|
496
|
-
const commitEditRef = (0,
|
|
497
|
-
(0,
|
|
597
|
+
const commitEditRef = (0, import_react4.useRef)(commitEdit);
|
|
598
|
+
(0, import_react4.useEffect)(() => {
|
|
498
599
|
commitEditRef.current = commitEdit;
|
|
499
600
|
});
|
|
500
|
-
(0,
|
|
501
|
-
(0,
|
|
502
|
-
const startEdit = (0,
|
|
601
|
+
(0, import_react4.useEffect)(() => () => commitEditRef.current(), []);
|
|
602
|
+
(0, import_react4.useImperativeHandle)(ref, () => ({ commitPendingEdit: () => commitEditRef.current() }), []);
|
|
603
|
+
const startEdit = (0, import_react4.useCallback)(
|
|
503
604
|
(target) => {
|
|
504
605
|
commitEdit();
|
|
505
606
|
if (target === "new") {
|
|
@@ -517,79 +618,79 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
517
618
|
},
|
|
518
619
|
[commitEdit, defaultColor]
|
|
519
620
|
);
|
|
520
|
-
const handleNameChange = (0,
|
|
621
|
+
const handleNameChange = (0, import_react4.useCallback)(
|
|
521
622
|
(name) => {
|
|
522
623
|
setDraftName(name);
|
|
523
624
|
if (!tagManuallySet) setDraftTag(deriveTag(name));
|
|
524
625
|
},
|
|
525
626
|
[tagManuallySet]
|
|
526
627
|
);
|
|
527
|
-
const handleTagChange = (0,
|
|
628
|
+
const handleTagChange = (0, import_react4.useCallback)((tag) => {
|
|
528
629
|
setDraftTag((previous) => clampTag(previous, tag));
|
|
529
630
|
setTagManuallySet(true);
|
|
530
631
|
}, []);
|
|
531
632
|
const confirmDeleteProfile = profiles.find((p) => p.id === confirmDeleteId) ?? null;
|
|
532
|
-
const handleConfirmDelete = (0,
|
|
633
|
+
const handleConfirmDelete = (0, import_react4.useCallback)(() => {
|
|
533
634
|
if (confirmDeleteId) {
|
|
534
635
|
onDelete(confirmDeleteId);
|
|
535
636
|
if (editingId === confirmDeleteId) setEditingId(null);
|
|
536
637
|
}
|
|
537
638
|
setConfirmDeleteId(null);
|
|
538
639
|
}, [confirmDeleteId, editingId, onDelete]);
|
|
539
|
-
const sortedProfiles = (0,
|
|
640
|
+
const sortedProfiles = (0, import_react4.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
540
641
|
const rows = [...sortedProfiles.map((profile) => ({ kind: "profile", profile })), { kind: "new" }];
|
|
541
|
-
return /* @__PURE__ */ (0,
|
|
542
|
-
/* @__PURE__ */ (0,
|
|
642
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
643
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: [styles5.header, { paddingTop: 8 + insets.top, paddingLeft: 8 + insets.left }], children: [
|
|
543
644
|
headerLeft,
|
|
544
|
-
/* @__PURE__ */ (0,
|
|
645
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Text, { variant: titleVariant, style: [styles5.title, { color: fg }], children: "Profiles" })
|
|
545
646
|
] }),
|
|
546
|
-
/* @__PURE__ */ (0,
|
|
647
|
+
/* @__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) => {
|
|
547
648
|
if (row.kind === "new") {
|
|
548
649
|
if (editingId !== "new") {
|
|
549
|
-
return /* @__PURE__ */ (0,
|
|
550
|
-
/* @__PURE__ */ (0,
|
|
551
|
-
/* @__PURE__ */ (0,
|
|
650
|
+
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: [
|
|
651
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.View, { style: [styles5.rowChip, styles5.newChip, { borderColor: fg }], children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Icon, { source: "plus", size: 20, color: fg }) }),
|
|
652
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Text, { variant: "bodyLarge", style: { color: fg }, children: "New Profile" })
|
|
552
653
|
] }) }, "new");
|
|
553
654
|
}
|
|
554
|
-
return /* @__PURE__ */ (0,
|
|
655
|
+
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");
|
|
555
656
|
}
|
|
556
657
|
const { profile } = row;
|
|
557
658
|
if (editingId === profile.id) {
|
|
558
|
-
return /* @__PURE__ */ (0,
|
|
659
|
+
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);
|
|
559
660
|
}
|
|
560
|
-
return /* @__PURE__ */ (0,
|
|
561
|
-
/* @__PURE__ */ (0,
|
|
562
|
-
/* @__PURE__ */ (0,
|
|
661
|
+
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: [
|
|
662
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ProfileChip, { profile, size: CHIP_SIZE2 }),
|
|
663
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Text, { variant: "bodyLarge", style: [styles5.rowName, { color: fg }], numberOfLines: 1, children: profile.name })
|
|
563
664
|
] }) }, profile.id);
|
|
564
665
|
}) }) }),
|
|
565
|
-
/* @__PURE__ */ (0,
|
|
666
|
+
/* @__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) })
|
|
566
667
|
] });
|
|
567
668
|
}
|
|
568
669
|
function AlignedColorPicker({ useAlign, ...props }) {
|
|
569
|
-
const { width: windowWidth } = (0,
|
|
570
|
-
const { width, height } = (0,
|
|
670
|
+
const { width: windowWidth } = (0, import_react_native5.useWindowDimensions)();
|
|
671
|
+
const { width, height } = (0, import_chrome2.getInlineColorPickerContentSize)(import_auto_paper4.defaultColors.length, windowWidth);
|
|
571
672
|
const alignOverride = useAlign(props.host.openId === props.id, width, height);
|
|
572
|
-
return /* @__PURE__ */ (0,
|
|
673
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_chrome2.InlineColorPicker, { ...props, alignOverride });
|
|
573
674
|
}
|
|
574
675
|
function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, onSubmit, onDelete, autoFocus, fgMuted, host, dark, colorPreview, useColorPickerAlign }) {
|
|
575
676
|
const register = (0, import_focus_chain.useFocusChain)();
|
|
576
677
|
const tag = register();
|
|
577
678
|
const name = register();
|
|
578
|
-
const tagFocusRef = (0,
|
|
579
|
-
(0,
|
|
679
|
+
const tagFocusRef = (0, import_react4.useRef)(tag.props.focus);
|
|
680
|
+
(0, import_react4.useEffect)(() => {
|
|
580
681
|
tagFocusRef.current = tag.props.focus;
|
|
581
682
|
});
|
|
582
|
-
(0,
|
|
683
|
+
(0, import_react4.useEffect)(() => {
|
|
583
684
|
if (host.openId === "color") tagFocusRef.current();
|
|
584
685
|
}, [host.openId]);
|
|
585
|
-
return /* @__PURE__ */ (0,
|
|
586
|
-
useColorPickerAlign ? /* @__PURE__ */ (0,
|
|
587
|
-
/* @__PURE__ */ (0,
|
|
588
|
-
/* @__PURE__ */ (0,
|
|
589
|
-
onDelete && /* @__PURE__ */ (0,
|
|
686
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: styles5.editRow, children: [
|
|
687
|
+
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 }),
|
|
688
|
+
/* @__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" }),
|
|
689
|
+
/* @__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 }),
|
|
690
|
+
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 }) })
|
|
590
691
|
] });
|
|
591
692
|
}
|
|
592
|
-
var
|
|
693
|
+
var styles5 = import_react_native5.StyleSheet.create({
|
|
593
694
|
content: {
|
|
594
695
|
gap: 4,
|
|
595
696
|
paddingHorizontal: 20
|
|
@@ -668,23 +769,23 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
668
769
|
});
|
|
669
770
|
|
|
670
771
|
// src/ProfilesScreen.tsx
|
|
671
|
-
var
|
|
672
|
-
var
|
|
673
|
-
var
|
|
674
|
-
var
|
|
675
|
-
var
|
|
772
|
+
var import_auto_paper5 = require("@rific/auto-paper");
|
|
773
|
+
var import_feedback_press4 = require("@rific/feedback-press");
|
|
774
|
+
var import_react5 = require("react");
|
|
775
|
+
var import_react_native6 = require("react-native");
|
|
776
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
676
777
|
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
|
|
677
|
-
const { dark } = (0,
|
|
678
|
-
const managerRef = (0,
|
|
778
|
+
const { dark } = (0, import_auto_paper5.useAutoPaperTheme)();
|
|
779
|
+
const managerRef = (0, import_react5.useRef)(null);
|
|
679
780
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
680
781
|
const bg = bgOverride ?? (dark ? "#000000" : "#FFFFFF");
|
|
681
782
|
const handleBack = () => {
|
|
682
783
|
managerRef.current?.commitPendingEdit();
|
|
683
784
|
onBack();
|
|
684
785
|
};
|
|
685
|
-
return /* @__PURE__ */ (0,
|
|
786
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native6.View, { style: [styles6.container, { backgroundColor: bg }], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ProfilesManager, { ref: managerRef, profiles, defaultColor, onCreate, onSave, onDelete, fg, fgMuted, titleVariant, colorPreview, useColorPickerAlign, headerLeft: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_feedback_press4.IconButton, { icon: "arrow-left", iconColor: fg, size: 24, onPress: handleBack, accessibilityLabel: "Back" }) }) });
|
|
686
787
|
}
|
|
687
|
-
var
|
|
788
|
+
var styles6 = import_react_native6.StyleSheet.create({
|
|
688
789
|
container: {
|
|
689
790
|
flex: 1
|
|
690
791
|
}
|
|
@@ -795,6 +896,7 @@ function createProfileRecord(input) {
|
|
|
795
896
|
}
|
|
796
897
|
|
|
797
898
|
// src/sharedProfileStore.ts
|
|
899
|
+
var import_react_native7 = require("react-native");
|
|
798
900
|
var native = null;
|
|
799
901
|
try {
|
|
800
902
|
const { requireOptionalNativeModule } = require("expo-modules-core");
|
|
@@ -803,15 +905,42 @@ try {
|
|
|
803
905
|
native = null;
|
|
804
906
|
}
|
|
805
907
|
var PROFILES_KEY = "sharedProfiles";
|
|
806
|
-
var
|
|
807
|
-
|
|
808
|
-
if (
|
|
809
|
-
let json = null;
|
|
908
|
+
var webKey = (groupId, key) => `${groupId}:${key}`;
|
|
909
|
+
var createWebStore = () => {
|
|
910
|
+
if (import_react_native7.Platform.OS !== "web") return null;
|
|
810
911
|
try {
|
|
811
|
-
|
|
912
|
+
const storage = globalThis.localStorage;
|
|
913
|
+
if (!storage) return null;
|
|
914
|
+
const probe = webKey("@tastic/profile", "probe");
|
|
915
|
+
storage.setItem(probe, "1");
|
|
916
|
+
storage.removeItem(probe);
|
|
917
|
+
let lastWriteFailed = false;
|
|
918
|
+
return {
|
|
919
|
+
storage,
|
|
920
|
+
store: {
|
|
921
|
+
getSharedJSON: async (groupId, key) => {
|
|
922
|
+
if (lastWriteFailed) throw new Error("The last write to the shared roster failed");
|
|
923
|
+
return storage.getItem(webKey(groupId, key));
|
|
924
|
+
},
|
|
925
|
+
setSharedJSON: async (groupId, key, value) => {
|
|
926
|
+
try {
|
|
927
|
+
storage.setItem(webKey(groupId, key), value);
|
|
928
|
+
lastWriteFailed = false;
|
|
929
|
+
} catch (error) {
|
|
930
|
+
lastWriteFailed = true;
|
|
931
|
+
throw error;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
};
|
|
812
936
|
} catch {
|
|
813
937
|
return null;
|
|
814
938
|
}
|
|
939
|
+
};
|
|
940
|
+
var web = native ? null : createWebStore();
|
|
941
|
+
if (web) native = web.store;
|
|
942
|
+
var isSharedProfileStoreAvailable = native !== null;
|
|
943
|
+
var parseRoster = (json) => {
|
|
815
944
|
if (!json) return [];
|
|
816
945
|
try {
|
|
817
946
|
const parsed = JSON.parse(json);
|
|
@@ -821,6 +950,16 @@ async function readSharedProfiles(groupId) {
|
|
|
821
950
|
} catch {
|
|
822
951
|
return null;
|
|
823
952
|
}
|
|
953
|
+
};
|
|
954
|
+
async function readSharedProfiles(groupId) {
|
|
955
|
+
if (!native) return [];
|
|
956
|
+
let json = null;
|
|
957
|
+
try {
|
|
958
|
+
json = await native.getSharedJSON(groupId, PROFILES_KEY);
|
|
959
|
+
} catch {
|
|
960
|
+
return null;
|
|
961
|
+
}
|
|
962
|
+
return parseRoster(json);
|
|
824
963
|
}
|
|
825
964
|
async function loadSharedProfiles(groupId) {
|
|
826
965
|
return await readSharedProfiles(groupId) ?? [];
|
|
@@ -832,6 +971,19 @@ async function saveSharedProfiles(groupId, profiles) {
|
|
|
832
971
|
} catch {
|
|
833
972
|
}
|
|
834
973
|
}
|
|
974
|
+
function subscribeSharedProfiles(groupId, listener) {
|
|
975
|
+
if (!web || typeof window === "undefined" || typeof window.addEventListener !== "function") return () => {
|
|
976
|
+
};
|
|
977
|
+
const storageKey = webKey(groupId, PROFILES_KEY);
|
|
978
|
+
const storage = web.storage;
|
|
979
|
+
const onStorage = (event) => {
|
|
980
|
+
if (event.key !== storageKey || event.storageArea !== storage || !event.newValue) return;
|
|
981
|
+
const profiles = parseRoster(event.newValue);
|
|
982
|
+
if (profiles !== null) listener(profiles);
|
|
983
|
+
};
|
|
984
|
+
window.addEventListener("storage", onStorage);
|
|
985
|
+
return () => window.removeEventListener("storage", onStorage);
|
|
986
|
+
}
|
|
835
987
|
|
|
836
988
|
// src/redux/resolveInitialProfiles.ts
|
|
837
989
|
async function resolveInitialProfiles(groupId, localFallback) {
|
|
@@ -848,47 +1000,82 @@ async function resolveInitialProfiles(groupId, localFallback) {
|
|
|
848
1000
|
|
|
849
1001
|
// src/redux/seatColorsSlice.ts
|
|
850
1002
|
var REHYDRATE2 = "persist/REHYDRATE";
|
|
1003
|
+
function sameColor(a, b) {
|
|
1004
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
1005
|
+
}
|
|
1006
|
+
function profileSeatColor(profile, profileColors) {
|
|
1007
|
+
const pick = profileColors[profile.id];
|
|
1008
|
+
return pick && sameColor(pick.profileColor, profile.color) ? pick.color : profile.color;
|
|
1009
|
+
}
|
|
851
1010
|
function createSeatColorsSlice(namespace, defaultState, mountKey = "seatColors") {
|
|
852
1011
|
const setLastCpuColor = createActionCreator(`seatColors/${namespace}/setLastCpuColor`);
|
|
853
1012
|
const setLastGuestColor = createActionCreator(`seatColors/${namespace}/setLastGuestColor`);
|
|
854
|
-
const
|
|
1013
|
+
const setProfileColor = createActionCreator(`seatColors/${namespace}/setProfileColor`);
|
|
1014
|
+
function withoutPick(state, id) {
|
|
1015
|
+
if (!state.profileColors[id]) return state;
|
|
1016
|
+
const profileColors = { ...state.profileColors };
|
|
1017
|
+
delete profileColors[id];
|
|
1018
|
+
return { ...state, profileColors };
|
|
1019
|
+
}
|
|
855
1020
|
function reducer(state = defaultState, action) {
|
|
856
1021
|
if (action.type === REHYDRATE2) {
|
|
857
1022
|
const persisted = action.payload?.[mountKey];
|
|
858
|
-
|
|
1023
|
+
const next = { ...defaultState, ...state, ...persisted };
|
|
1024
|
+
delete next.profileOverride;
|
|
1025
|
+
return next;
|
|
859
1026
|
}
|
|
860
1027
|
if (setLastCpuColor.match(action)) return { ...state, lastCpuColor: action.payload };
|
|
861
1028
|
if (setLastGuestColor.match(action)) return { ...state, lastGuestColor: { ...state.lastGuestColor, [action.payload.seat]: action.payload.color } };
|
|
862
|
-
if (
|
|
1029
|
+
if (setProfileColor.match(action)) {
|
|
1030
|
+
const { profile, color } = action.payload;
|
|
1031
|
+
if (color === null || sameColor(color, profile.color)) return withoutPick(state, profile.id);
|
|
1032
|
+
return { ...state, profileColors: { ...state.profileColors, [profile.id]: { color, profileColor: profile.color } } };
|
|
1033
|
+
}
|
|
1034
|
+
if (profilesActions.remove.match(action)) return withoutPick(state, action.payload);
|
|
1035
|
+
if (profilesActions.update.match(action)) {
|
|
1036
|
+
const { id, patch } = action.payload;
|
|
1037
|
+
const pick = state.profileColors[id];
|
|
1038
|
+
return pick && patch.color !== void 0 && !sameColor(patch.color, pick.profileColor) ? withoutPick(state, id) : state;
|
|
1039
|
+
}
|
|
1040
|
+
if (profilesActions.setAll.match(action)) {
|
|
1041
|
+
return action.payload.reduce((next, profile) => {
|
|
1042
|
+
const pick = next.profileColors[profile.id];
|
|
1043
|
+
return pick && !sameColor(profile.color, pick.profileColor) ? withoutPick(next, profile.id) : next;
|
|
1044
|
+
}, state);
|
|
1045
|
+
}
|
|
863
1046
|
return state;
|
|
864
1047
|
}
|
|
865
|
-
return { actions: { setLastCpuColor, setLastGuestColor,
|
|
1048
|
+
return { actions: { setLastCpuColor, setLastGuestColor, setProfileColor }, reducer };
|
|
866
1049
|
}
|
|
867
1050
|
|
|
868
1051
|
// src/redux/useSharedProfilesSync.ts
|
|
869
|
-
var
|
|
870
|
-
var
|
|
1052
|
+
var import_react6 = require("react");
|
|
1053
|
+
var import_react_native8 = require("react-native");
|
|
871
1054
|
function useSharedProfilesSync({ groupId, onRemoteChange }) {
|
|
872
|
-
const pendingWriteRef = (0,
|
|
873
|
-
const onRemoteChangeRef = (0,
|
|
874
|
-
(0,
|
|
1055
|
+
const pendingWriteRef = (0, import_react6.useRef)(false);
|
|
1056
|
+
const onRemoteChangeRef = (0, import_react6.useRef)(onRemoteChange);
|
|
1057
|
+
(0, import_react6.useEffect)(() => {
|
|
875
1058
|
onRemoteChangeRef.current = onRemoteChange;
|
|
876
1059
|
}, [onRemoteChange]);
|
|
877
|
-
(0,
|
|
1060
|
+
(0, import_react6.useEffect)(() => {
|
|
878
1061
|
if (!isSharedProfileStoreAvailable) return;
|
|
879
1062
|
let cancelled = false;
|
|
880
|
-
const sub =
|
|
1063
|
+
const sub = import_react_native8.AppState.addEventListener("change", (next) => {
|
|
881
1064
|
if (next !== "active" || pendingWriteRef.current) return;
|
|
882
1065
|
readSharedProfiles(groupId).then((shared) => {
|
|
883
1066
|
if (!cancelled && shared !== null) onRemoteChangeRef.current(shared);
|
|
884
1067
|
});
|
|
885
1068
|
});
|
|
1069
|
+
const unsubscribe = subscribeSharedProfiles(groupId, (shared) => {
|
|
1070
|
+
if (!cancelled && !pendingWriteRef.current) onRemoteChangeRef.current(shared);
|
|
1071
|
+
});
|
|
886
1072
|
return () => {
|
|
887
1073
|
cancelled = true;
|
|
888
1074
|
sub.remove();
|
|
1075
|
+
unsubscribe();
|
|
889
1076
|
};
|
|
890
1077
|
}, [groupId]);
|
|
891
|
-
const syncToShared = (0,
|
|
1078
|
+
const syncToShared = (0, import_react6.useCallback)(
|
|
892
1079
|
(next) => {
|
|
893
1080
|
if (!isSharedProfileStoreAvailable) return;
|
|
894
1081
|
pendingWriteRef.current = true;
|
|
@@ -912,6 +1099,7 @@ function avoidSeatColorClash(p1Color, p2Color, defaults) {
|
|
|
912
1099
|
0 && (module.exports = {
|
|
913
1100
|
MAX_PROFILE_NAME_LENGTH,
|
|
914
1101
|
MAX_TAG_LENGTH,
|
|
1102
|
+
OptionPicker,
|
|
915
1103
|
ProfileChip,
|
|
916
1104
|
ProfileColorProvider,
|
|
917
1105
|
ProfilePicker,
|
|
@@ -922,17 +1110,21 @@ function avoidSeatColorClash(p1Color, p2Color, defaults) {
|
|
|
922
1110
|
createProfileRecord,
|
|
923
1111
|
createProfileSelectionSlice,
|
|
924
1112
|
createSeatColorsSlice,
|
|
1113
|
+
getOptionPickerContentSize,
|
|
925
1114
|
getProfilePickerContentSize,
|
|
926
1115
|
isSharedProfileStoreAvailable,
|
|
927
1116
|
isValidProfile,
|
|
928
1117
|
isValidTag,
|
|
929
1118
|
loadSharedProfiles,
|
|
1119
|
+
profileSeatColor,
|
|
930
1120
|
profilesActions,
|
|
931
1121
|
profilesReducer,
|
|
932
1122
|
readSharedProfiles,
|
|
933
1123
|
resolveInitialProfiles,
|
|
934
1124
|
saveSharedProfiles,
|
|
1125
|
+
subscribeSharedProfiles,
|
|
935
1126
|
useProfileColorPreview,
|
|
936
1127
|
useProfileDisplayColor,
|
|
1128
|
+
useProfileSwatchRenderer,
|
|
937
1129
|
useSharedProfilesSync
|
|
938
1130
|
});
|