@tastic/profile 0.15.0 → 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 +10 -5
- package/dist/index.d.ts +10 -5
- package/dist/index.js +219 -115
- package/dist/index.mjs +212 -110
- package/package.json +5 -5
- package/src/IdentityChip.tsx +8 -2
- package/src/OptionPicker.tsx +1 -1
- package/src/PickerMenu.tsx +41 -10
- package/src/ProfileColorContext.tsx +30 -8
- package/src/ProfilePicker.tsx +10 -4
- package/src/ProfilesManager.tsx +2 -1
- package/src/SwatchLayer.tsx +28 -0
- package/src/index.ts +2 -2
- package/src/redux/resolveInitialProfiles.ts +3 -2
- package/src/redux/useSharedProfilesSync.ts +12 -6
- package/src/sharedProfileStore.ts +87 -11
package/dist/index.js
CHANGED
|
@@ -45,8 +45,10 @@ __export(index_exports, {
|
|
|
45
45
|
readSharedProfiles: () => readSharedProfiles,
|
|
46
46
|
resolveInitialProfiles: () => resolveInitialProfiles,
|
|
47
47
|
saveSharedProfiles: () => saveSharedProfiles,
|
|
48
|
+
subscribeSharedProfiles: () => subscribeSharedProfiles,
|
|
48
49
|
useProfileColorPreview: () => useProfileColorPreview,
|
|
49
50
|
useProfileDisplayColor: () => useProfileDisplayColor,
|
|
51
|
+
useProfileSwatchRenderer: () => useProfileSwatchRenderer,
|
|
50
52
|
useSharedProfilesSync: () => useSharedProfilesSync
|
|
51
53
|
});
|
|
52
54
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -54,23 +56,64 @@ module.exports = __toCommonJS(index_exports);
|
|
|
54
56
|
// src/PickerMenu.tsx
|
|
55
57
|
var import_auto_paper2 = require("@rific/auto-paper");
|
|
56
58
|
var import_feedback_press = require("@rific/feedback-press");
|
|
57
|
-
var
|
|
58
|
-
var
|
|
59
|
-
var
|
|
59
|
+
var import_chrome = require("@tastic/hud/chrome");
|
|
60
|
+
var import_react2 = require("react");
|
|
61
|
+
var import_react_native3 = require("react-native");
|
|
60
62
|
var import_react_native_paper2 = require("react-native-paper");
|
|
61
63
|
|
|
62
64
|
// src/IdentityChip.tsx
|
|
63
65
|
var import_auto_paper = require("@rific/auto-paper");
|
|
64
|
-
var
|
|
66
|
+
var import_react_native2 = require("react-native");
|
|
65
67
|
var import_react_native_paper = require("react-native-paper");
|
|
68
|
+
|
|
69
|
+
// src/ProfileColorContext.tsx
|
|
70
|
+
var import_react = require("react");
|
|
66
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");
|
|
67
106
|
var EMOJI_PATTERN = /\p{Extended_Pictographic}/u;
|
|
68
107
|
function IdentityChip({ color, tag, icon, size, filled = true }) {
|
|
108
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
69
109
|
const contrastColor = (0, import_auto_paper.getContrastColor)(color);
|
|
70
110
|
const tagFontSize = size * (tag && EMOJI_PATTERN.test(tag) ? 0.6 : tag && Array.from(tag).length >= 3 ? 0.32 : 0.4);
|
|
71
|
-
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
|
+
] });
|
|
72
115
|
}
|
|
73
|
-
var
|
|
116
|
+
var styles2 = import_react_native2.StyleSheet.create({
|
|
74
117
|
chip: {
|
|
75
118
|
alignItems: "center",
|
|
76
119
|
justifyContent: "center"
|
|
@@ -82,7 +125,7 @@ var styles = import_react_native.StyleSheet.create({
|
|
|
82
125
|
});
|
|
83
126
|
|
|
84
127
|
// src/PickerMenu.tsx
|
|
85
|
-
var
|
|
128
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
86
129
|
var TRIGGER_HEIGHT = 28;
|
|
87
130
|
var POPOVER_WIDTH = 220;
|
|
88
131
|
var ROW_HEIGHT = 36;
|
|
@@ -93,6 +136,8 @@ var CHIP_SIZE = 32;
|
|
|
93
136
|
var CHIP_TRIGGER_PADDING = 6;
|
|
94
137
|
var SWATCH_SIZE = 28;
|
|
95
138
|
var SWATCH_MIN_GAP = 6;
|
|
139
|
+
var SWATCH_RING_WIDTH = 2;
|
|
140
|
+
var COLOR_ROW_SWATCH_SIZE = 22;
|
|
96
141
|
var COLOR_ROW_CAPTIONED_HEIGHT = 48;
|
|
97
142
|
var COLOR_LABEL_LINE = 18;
|
|
98
143
|
var COLOR_CAPTION_LINE = 16;
|
|
@@ -118,14 +163,14 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
118
163
|
const menuBg = dark ? "#000000" : "#FFFFFF";
|
|
119
164
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
120
165
|
const open = host.openId === id;
|
|
121
|
-
const auto = (0,
|
|
166
|
+
const auto = (0, import_chrome.useAutoAlign)(open, contentSize.width, contentSize.height, rotation);
|
|
122
167
|
const placement = alignOverride ?? auto;
|
|
123
168
|
const { maxHeight, measured, triggerRef, verticalAlign } = placement;
|
|
124
169
|
const align = forcedAlign ?? placement.align;
|
|
125
170
|
const chipTrigger = !!chip;
|
|
126
|
-
const [triggerWidth, setTriggerWidth] = (0,
|
|
171
|
+
const [triggerWidth, setTriggerWidth] = (0, import_react2.useState)(0);
|
|
127
172
|
const handleAnchorLayout = chipTrigger ? (e) => setTriggerWidth(e.nativeEvent.layout.width) : void 0;
|
|
128
|
-
const [colorPage, setColorPage] = (0,
|
|
173
|
+
const [colorPage, setColorPage] = (0, import_react2.useState)(false);
|
|
129
174
|
const showColorPage = !!colorSection && colorPage;
|
|
130
175
|
const handleTriggerPress = () => {
|
|
131
176
|
setColorPage(false);
|
|
@@ -133,17 +178,17 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
133
178
|
};
|
|
134
179
|
const triggerLabel = label.toUpperCase();
|
|
135
180
|
const showFooter = !!footer && !showColorPage;
|
|
136
|
-
return /* @__PURE__ */ (0,
|
|
137
|
-
/* @__PURE__ */ (0,
|
|
138
|
-
chip && /* @__PURE__ */ (0,
|
|
139
|
-
/* @__PURE__ */ (0,
|
|
140
|
-
/* @__PURE__ */ (0,
|
|
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 }),
|
|
185
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "menu-down", size: 16, color: chipTrigger ? fg : color })
|
|
141
186
|
] }) }),
|
|
142
|
-
/* @__PURE__ */ (0,
|
|
143
|
-
|
|
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,
|
|
144
189
|
{
|
|
145
190
|
style: [
|
|
146
|
-
|
|
191
|
+
styles3.menu,
|
|
147
192
|
{
|
|
148
193
|
backgroundColor: menuBg,
|
|
149
194
|
borderColor: color,
|
|
@@ -152,12 +197,12 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
152
197
|
}
|
|
153
198
|
],
|
|
154
199
|
children: [
|
|
155
|
-
/* @__PURE__ */ (0,
|
|
156
|
-
colorSection && /* @__PURE__ */ (0,
|
|
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: [
|
|
201
|
+
colorSection && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ColorRow, { section: colorSection, fg, dividerColor: color, onPress: () => setColorPage(true) }),
|
|
157
202
|
children
|
|
158
203
|
] }) }, showColorPage ? "color" : "list"),
|
|
159
|
-
showFooter && /* @__PURE__ */ (0,
|
|
160
|
-
/* @__PURE__ */ (0,
|
|
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 }] }),
|
|
161
206
|
footer
|
|
162
207
|
] })
|
|
163
208
|
]
|
|
@@ -167,35 +212,37 @@ function PickerMenu({ id, host, color, dark, label, chip, colorSection, homeColo
|
|
|
167
212
|
}
|
|
168
213
|
function IconRow({ icon, label, selected, color, fg, disabled = false, onPress }) {
|
|
169
214
|
const textColor = selected ? (0, import_auto_paper2.getContrastColor)(color) : fg;
|
|
170
|
-
return /* @__PURE__ */ (0,
|
|
171
|
-
/* @__PURE__ */ (0,
|
|
172
|
-
/* @__PURE__ */ (0,
|
|
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 })
|
|
173
218
|
] }) });
|
|
174
219
|
}
|
|
175
220
|
function ColorRow({ section, fg, dividerColor, onPress }) {
|
|
176
221
|
const { value, previewValue, label = "Color", caption } = section;
|
|
222
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
177
223
|
const drawn = previewValue ? previewValue(value) : value;
|
|
178
|
-
return /* @__PURE__ */ (0,
|
|
179
|
-
/* @__PURE__ */ (0,
|
|
180
|
-
/* @__PURE__ */ (0,
|
|
181
|
-
/* @__PURE__ */ (0,
|
|
182
|
-
/* @__PURE__ */ (0,
|
|
183
|
-
caption ? /* @__PURE__ */ (0,
|
|
224
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
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
|
|
184
230
|
] }),
|
|
185
|
-
/* @__PURE__ */ (0,
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper2.Icon, { source: "chevron-right", size: 18, color: fg })
|
|
186
232
|
] }) }),
|
|
187
|
-
/* @__PURE__ */ (0,
|
|
233
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles3.divider, { backgroundColor: dividerColor }] })
|
|
188
234
|
] });
|
|
189
235
|
}
|
|
190
236
|
function ColorPage({ section, homeColor, fg, onBack }) {
|
|
191
237
|
const { value, onChange, previewValue, swatches = import_auto_paper2.defaultColors, label = "Color", takenValue, allowSwapTaken } = section;
|
|
238
|
+
const renderSwatch = useProfileSwatchRenderer();
|
|
192
239
|
const displayFor = previewValue ?? ((hex) => hex);
|
|
193
|
-
return /* @__PURE__ */ (0,
|
|
194
|
-
/* @__PURE__ */ (0,
|
|
195
|
-
/* @__PURE__ */ (0,
|
|
196
|
-
/* @__PURE__ */ (0,
|
|
240
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
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 })
|
|
197
244
|
] }) }),
|
|
198
|
-
/* @__PURE__ */ (0,
|
|
245
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: styles3.swatches, children: swatches.map((swatch) => {
|
|
199
246
|
const selected = swatch.value.toLowerCase() === value.toLowerCase();
|
|
200
247
|
const isHomeColor = !!homeColor && swatch.value.toLowerCase() === homeColor.toLowerCase();
|
|
201
248
|
const taken = !selected && !!takenValue && swatch.value.toLowerCase() === takenValue.toLowerCase();
|
|
@@ -203,11 +250,15 @@ function ColorPage({ section, homeColor, fg, onBack }) {
|
|
|
203
250
|
const swatchDisplay = displayFor(swatch.value);
|
|
204
251
|
const contrast = (0, import_auto_paper2.getContrastColor)(swatchDisplay);
|
|
205
252
|
const accessibilityLabel = swappable ? `${swatch.label}, swap` : taken ? `${swatch.label}, taken` : isHomeColor ? `${swatch.label}, profile color` : swatch.label;
|
|
206
|
-
|
|
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);
|
|
207
258
|
}) })
|
|
208
259
|
] });
|
|
209
260
|
}
|
|
210
|
-
var pickerMenuStyles =
|
|
261
|
+
var pickerMenuStyles = import_react_native3.StyleSheet.create({
|
|
211
262
|
row: {
|
|
212
263
|
borderRadius: 8,
|
|
213
264
|
height: ROW_HEIGHT
|
|
@@ -227,7 +278,7 @@ var pickerMenuStyles = import_react_native2.StyleSheet.create({
|
|
|
227
278
|
fontSize: 14
|
|
228
279
|
}
|
|
229
280
|
});
|
|
230
|
-
var
|
|
281
|
+
var styles3 = import_react_native3.StyleSheet.create({
|
|
231
282
|
anchor: {
|
|
232
283
|
position: "relative"
|
|
233
284
|
},
|
|
@@ -263,9 +314,9 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
263
314
|
},
|
|
264
315
|
// The color in effect, in the same 22pt slot as every row's chip.
|
|
265
316
|
colorRowSwatch: {
|
|
266
|
-
borderRadius:
|
|
267
|
-
height:
|
|
268
|
-
width:
|
|
317
|
+
borderRadius: COLOR_ROW_SWATCH_SIZE / 2,
|
|
318
|
+
height: COLOR_ROW_SWATCH_SIZE,
|
|
319
|
+
width: COLOR_ROW_SWATCH_SIZE
|
|
269
320
|
},
|
|
270
321
|
colorRowText: {
|
|
271
322
|
flex: 1
|
|
@@ -325,9 +376,19 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
325
376
|
justifyContent: "center",
|
|
326
377
|
width: SWATCH_SIZE
|
|
327
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
|
+
},
|
|
328
389
|
swatchSelected: {
|
|
329
390
|
borderColor: "#ffffff",
|
|
330
|
-
borderWidth:
|
|
391
|
+
borderWidth: SWATCH_RING_WIDTH
|
|
331
392
|
},
|
|
332
393
|
swatchTaken: {
|
|
333
394
|
opacity: 0.35
|
|
@@ -359,7 +420,7 @@ var styles2 = import_react_native2.StyleSheet.create({
|
|
|
359
420
|
});
|
|
360
421
|
|
|
361
422
|
// src/OptionPicker.tsx
|
|
362
|
-
var
|
|
423
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
363
424
|
function getOptionPickerContentSize(optionCount, colorSection) {
|
|
364
425
|
return pickerContentSize(optionCount, colorSection);
|
|
365
426
|
}
|
|
@@ -370,54 +431,40 @@ function OptionPicker({ id, host, options, value, onChange, color, dark, icon, r
|
|
|
370
431
|
onChange(option.value);
|
|
371
432
|
host.close();
|
|
372
433
|
};
|
|
373
|
-
return /* @__PURE__ */ (0,
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// src/ProfileColorContext.tsx
|
|
377
|
-
var import_react2 = require("react");
|
|
378
|
-
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
379
|
-
var ProfileColorContext = (0, import_react2.createContext)(void 0);
|
|
380
|
-
function ProfileColorProvider({ preview, children }) {
|
|
381
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ProfileColorContext.Provider, { value: preview, children });
|
|
382
|
-
}
|
|
383
|
-
function useProfileColorPreview() {
|
|
384
|
-
return (0, import_react2.useContext)(ProfileColorContext);
|
|
385
|
-
}
|
|
386
|
-
function useProfileDisplayColor(hex) {
|
|
387
|
-
const preview = useProfileColorPreview();
|
|
388
|
-
return preview ? preview(hex) : hex;
|
|
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)) });
|
|
389
435
|
}
|
|
390
436
|
|
|
391
437
|
// src/ProfileChip.tsx
|
|
392
|
-
var
|
|
438
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
393
439
|
function ProfileChip({ profile, size = 22, filled = true }) {
|
|
394
440
|
const color = useProfileDisplayColor(profile.color);
|
|
395
|
-
return /* @__PURE__ */ (0,
|
|
441
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IdentityChip, { color, tag: profile.tag, icon: "account", size, filled });
|
|
396
442
|
}
|
|
397
443
|
|
|
398
444
|
// src/ProfilePicker.tsx
|
|
399
445
|
var import_auto_paper3 = require("@rific/auto-paper");
|
|
400
446
|
var import_feedback_press2 = require("@rific/feedback-press");
|
|
401
447
|
var import_react3 = require("react");
|
|
402
|
-
var
|
|
448
|
+
var import_react_native4 = require("react-native");
|
|
403
449
|
var import_react_native_paper3 = require("react-native-paper");
|
|
404
|
-
var
|
|
450
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
405
451
|
function getProfilePickerContentSize(profileCount, hasManageRow = false, colorSection) {
|
|
406
452
|
return pickerContentSize(profileCount + 1 + (hasManageRow ? 1 : 0), colorSection);
|
|
407
453
|
}
|
|
408
|
-
function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, dark, align, alignOverride, rotation = 0, guestLabel, nullLabel = "Player", nullIcon = "account-off-outline", onSelect, onManage, colorSection, triggerStyle }) {
|
|
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 }) {
|
|
409
455
|
const id = `${idPrefix}-profile`;
|
|
410
456
|
const colorPreview = useProfileColorPreview();
|
|
411
457
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
412
458
|
const selectedProfile = profiles.find((p) => p.id === selectedId) ?? null;
|
|
413
459
|
const sortedProfiles = (0, import_react3.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
414
460
|
const contentSize = getProfilePickerContentSize(profiles.length, !!onManage, colorSection);
|
|
461
|
+
const isTaken = (profileId) => profileId === takenId || profileId !== selectedId && !!takenIds?.includes(profileId);
|
|
415
462
|
const handleSelectGuest = () => {
|
|
416
463
|
onSelect(null);
|
|
417
464
|
host.close();
|
|
418
465
|
};
|
|
419
466
|
const handleSelectExisting = (profile) => {
|
|
420
|
-
if (profile.id
|
|
467
|
+
if (isTaken(profile.id)) return;
|
|
421
468
|
onSelect(profile);
|
|
422
469
|
host.close();
|
|
423
470
|
};
|
|
@@ -425,19 +472,19 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, d
|
|
|
425
472
|
host.close();
|
|
426
473
|
onManage?.();
|
|
427
474
|
};
|
|
428
|
-
const manageFooter = onManage ? /* @__PURE__ */ (0,
|
|
429
|
-
/* @__PURE__ */ (0,
|
|
430
|
-
/* @__PURE__ */ (0,
|
|
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" })
|
|
431
478
|
] }) }) : void 0;
|
|
432
|
-
return /* @__PURE__ */ (0,
|
|
433
|
-
/* @__PURE__ */ (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 }),
|
|
434
481
|
sortedProfiles.map((profile) => {
|
|
435
|
-
const taken = profile.id
|
|
482
|
+
const taken = isTaken(profile.id);
|
|
436
483
|
const selected = selectedId === profile.id;
|
|
437
484
|
const rowColor = colorPreview ? colorPreview(profile.color) : profile.color;
|
|
438
|
-
return /* @__PURE__ */ (0,
|
|
439
|
-
/* @__PURE__ */ (0,
|
|
440
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
441
488
|
import_react_native_paper3.Text,
|
|
442
489
|
{
|
|
443
490
|
style: [
|
|
@@ -454,7 +501,7 @@ function ProfilePicker({ idPrefix, host, profiles, selectedId, takenId, color, d
|
|
|
454
501
|
})
|
|
455
502
|
] });
|
|
456
503
|
}
|
|
457
|
-
var
|
|
504
|
+
var styles4 = import_react_native4.StyleSheet.create({
|
|
458
505
|
manageLabel: {
|
|
459
506
|
fontSize: 13,
|
|
460
507
|
fontWeight: "700",
|
|
@@ -476,9 +523,9 @@ var import_auto_paper4 = require("@rific/auto-paper");
|
|
|
476
523
|
var import_feedback_press3 = require("@rific/feedback-press");
|
|
477
524
|
var import_focus_chain = require("@rific/focus-chain");
|
|
478
525
|
var import_toaster = require("@rific/toaster");
|
|
479
|
-
var
|
|
526
|
+
var import_chrome2 = require("@tastic/hud/chrome");
|
|
480
527
|
var import_react4 = require("react");
|
|
481
|
-
var
|
|
528
|
+
var import_react_native5 = require("react-native");
|
|
482
529
|
var import_react_native_paper4 = require("react-native-paper");
|
|
483
530
|
var import_react_native_safe_area_context = require("react-native-safe-area-context");
|
|
484
531
|
|
|
@@ -515,7 +562,7 @@ function isValidProfile(value) {
|
|
|
515
562
|
}
|
|
516
563
|
|
|
517
564
|
// src/ProfilesManager.tsx
|
|
518
|
-
var
|
|
565
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
519
566
|
var CHIP_SIZE2 = 40;
|
|
520
567
|
function deriveTag(name) {
|
|
521
568
|
const trimmed = name.trim();
|
|
@@ -527,7 +574,7 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
527
574
|
const { dark } = (0, import_auto_paper4.useAutoPaperTheme)();
|
|
528
575
|
const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
|
|
529
576
|
const { error: showErrorToast } = (0, import_toaster.useToast)();
|
|
530
|
-
const host = (0,
|
|
577
|
+
const host = (0, import_chrome2.usePopoverHost)();
|
|
531
578
|
const fg = fgOverride ?? (dark ? "#FFFFFF" : "#000000");
|
|
532
579
|
const fgMuted = fgMutedOverride ?? (dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)");
|
|
533
580
|
const [editingId, setEditingId] = (0, import_react4.useState)(null);
|
|
@@ -592,38 +639,38 @@ function ProfilesManager({ profiles, defaultColor, onCreate, onSave, onDelete, h
|
|
|
592
639
|
}, [confirmDeleteId, editingId, onDelete]);
|
|
593
640
|
const sortedProfiles = (0, import_react4.useMemo)(() => [...profiles].sort((a, b) => a.name.localeCompare(b.name)), [profiles]);
|
|
594
641
|
const rows = [...sortedProfiles.map((profile) => ({ kind: "profile", profile })), { kind: "new" }];
|
|
595
|
-
return /* @__PURE__ */ (0,
|
|
596
|
-
/* @__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: [
|
|
597
644
|
headerLeft,
|
|
598
|
-
/* @__PURE__ */ (0,
|
|
645
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper4.Text, { variant: titleVariant, style: [styles5.title, { color: fg }], children: "Profiles" })
|
|
599
646
|
] }),
|
|
600
|
-
/* @__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) => {
|
|
601
648
|
if (row.kind === "new") {
|
|
602
649
|
if (editingId !== "new") {
|
|
603
|
-
return /* @__PURE__ */ (0,
|
|
604
|
-
/* @__PURE__ */ (0,
|
|
605
|
-
/* @__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" })
|
|
606
653
|
] }) }, "new");
|
|
607
654
|
}
|
|
608
|
-
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");
|
|
609
656
|
}
|
|
610
657
|
const { profile } = row;
|
|
611
658
|
if (editingId === profile.id) {
|
|
612
|
-
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);
|
|
613
660
|
}
|
|
614
|
-
return /* @__PURE__ */ (0,
|
|
615
|
-
/* @__PURE__ */ (0,
|
|
616
|
-
/* @__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 })
|
|
617
664
|
] }) }, profile.id);
|
|
618
665
|
}) }) }),
|
|
619
|
-
/* @__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) })
|
|
620
667
|
] });
|
|
621
668
|
}
|
|
622
669
|
function AlignedColorPicker({ useAlign, ...props }) {
|
|
623
|
-
const { width: windowWidth } = (0,
|
|
624
|
-
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);
|
|
625
672
|
const alignOverride = useAlign(props.host.openId === props.id, width, height);
|
|
626
|
-
return /* @__PURE__ */ (0,
|
|
673
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_chrome2.InlineColorPicker, { ...props, alignOverride });
|
|
627
674
|
}
|
|
628
675
|
function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag, onTagChange, onSubmit, onDelete, autoFocus, fgMuted, host, dark, colorPreview, useColorPickerAlign }) {
|
|
629
676
|
const register = (0, import_focus_chain.useFocusChain)();
|
|
@@ -636,14 +683,14 @@ function EditRow({ draftName, onNameChange, draftColor, onColorChange, draftTag,
|
|
|
636
683
|
(0, import_react4.useEffect)(() => {
|
|
637
684
|
if (host.openId === "color") tagFocusRef.current();
|
|
638
685
|
}, [host.openId]);
|
|
639
|
-
return /* @__PURE__ */ (0,
|
|
640
|
-
useColorPickerAlign ? /* @__PURE__ */ (0,
|
|
641
|
-
/* @__PURE__ */ (0,
|
|
642
|
-
/* @__PURE__ */ (0,
|
|
643
|
-
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 }) })
|
|
644
691
|
] });
|
|
645
692
|
}
|
|
646
|
-
var
|
|
693
|
+
var styles5 = import_react_native5.StyleSheet.create({
|
|
647
694
|
content: {
|
|
648
695
|
gap: 4,
|
|
649
696
|
paddingHorizontal: 20
|
|
@@ -725,8 +772,8 @@ var styles4 = import_react_native4.StyleSheet.create({
|
|
|
725
772
|
var import_auto_paper5 = require("@rific/auto-paper");
|
|
726
773
|
var import_feedback_press4 = require("@rific/feedback-press");
|
|
727
774
|
var import_react5 = require("react");
|
|
728
|
-
var
|
|
729
|
-
var
|
|
775
|
+
var import_react_native6 = require("react-native");
|
|
776
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
730
777
|
function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, onBack, fg: fgOverride, fgMuted, bg: bgOverride, titleVariant, colorPreview, useColorPickerAlign }) {
|
|
731
778
|
const { dark } = (0, import_auto_paper5.useAutoPaperTheme)();
|
|
732
779
|
const managerRef = (0, import_react5.useRef)(null);
|
|
@@ -736,9 +783,9 @@ function ProfilesScreen({ profiles, defaultColor, onCreate, onSave, onDelete, on
|
|
|
736
783
|
managerRef.current?.commitPendingEdit();
|
|
737
784
|
onBack();
|
|
738
785
|
};
|
|
739
|
-
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" }) }) });
|
|
740
787
|
}
|
|
741
|
-
var
|
|
788
|
+
var styles6 = import_react_native6.StyleSheet.create({
|
|
742
789
|
container: {
|
|
743
790
|
flex: 1
|
|
744
791
|
}
|
|
@@ -849,6 +896,7 @@ function createProfileRecord(input) {
|
|
|
849
896
|
}
|
|
850
897
|
|
|
851
898
|
// src/sharedProfileStore.ts
|
|
899
|
+
var import_react_native7 = require("react-native");
|
|
852
900
|
var native = null;
|
|
853
901
|
try {
|
|
854
902
|
const { requireOptionalNativeModule } = require("expo-modules-core");
|
|
@@ -857,15 +905,42 @@ try {
|
|
|
857
905
|
native = null;
|
|
858
906
|
}
|
|
859
907
|
var PROFILES_KEY = "sharedProfiles";
|
|
860
|
-
var
|
|
861
|
-
|
|
862
|
-
if (
|
|
863
|
-
let json = null;
|
|
908
|
+
var webKey = (groupId, key) => `${groupId}:${key}`;
|
|
909
|
+
var createWebStore = () => {
|
|
910
|
+
if (import_react_native7.Platform.OS !== "web") return null;
|
|
864
911
|
try {
|
|
865
|
-
|
|
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
|
+
};
|
|
866
936
|
} catch {
|
|
867
937
|
return null;
|
|
868
938
|
}
|
|
939
|
+
};
|
|
940
|
+
var web = native ? null : createWebStore();
|
|
941
|
+
if (web) native = web.store;
|
|
942
|
+
var isSharedProfileStoreAvailable = native !== null;
|
|
943
|
+
var parseRoster = (json) => {
|
|
869
944
|
if (!json) return [];
|
|
870
945
|
try {
|
|
871
946
|
const parsed = JSON.parse(json);
|
|
@@ -875,6 +950,16 @@ async function readSharedProfiles(groupId) {
|
|
|
875
950
|
} catch {
|
|
876
951
|
return null;
|
|
877
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);
|
|
878
963
|
}
|
|
879
964
|
async function loadSharedProfiles(groupId) {
|
|
880
965
|
return await readSharedProfiles(groupId) ?? [];
|
|
@@ -886,6 +971,19 @@ async function saveSharedProfiles(groupId, profiles) {
|
|
|
886
971
|
} catch {
|
|
887
972
|
}
|
|
888
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
|
+
}
|
|
889
987
|
|
|
890
988
|
// src/redux/resolveInitialProfiles.ts
|
|
891
989
|
async function resolveInitialProfiles(groupId, localFallback) {
|
|
@@ -952,7 +1050,7 @@ function createSeatColorsSlice(namespace, defaultState, mountKey = "seatColors")
|
|
|
952
1050
|
|
|
953
1051
|
// src/redux/useSharedProfilesSync.ts
|
|
954
1052
|
var import_react6 = require("react");
|
|
955
|
-
var
|
|
1053
|
+
var import_react_native8 = require("react-native");
|
|
956
1054
|
function useSharedProfilesSync({ groupId, onRemoteChange }) {
|
|
957
1055
|
const pendingWriteRef = (0, import_react6.useRef)(false);
|
|
958
1056
|
const onRemoteChangeRef = (0, import_react6.useRef)(onRemoteChange);
|
|
@@ -962,15 +1060,19 @@ function useSharedProfilesSync({ groupId, onRemoteChange }) {
|
|
|
962
1060
|
(0, import_react6.useEffect)(() => {
|
|
963
1061
|
if (!isSharedProfileStoreAvailable) return;
|
|
964
1062
|
let cancelled = false;
|
|
965
|
-
const sub =
|
|
1063
|
+
const sub = import_react_native8.AppState.addEventListener("change", (next) => {
|
|
966
1064
|
if (next !== "active" || pendingWriteRef.current) return;
|
|
967
1065
|
readSharedProfiles(groupId).then((shared) => {
|
|
968
1066
|
if (!cancelled && shared !== null) onRemoteChangeRef.current(shared);
|
|
969
1067
|
});
|
|
970
1068
|
});
|
|
1069
|
+
const unsubscribe = subscribeSharedProfiles(groupId, (shared) => {
|
|
1070
|
+
if (!cancelled && !pendingWriteRef.current) onRemoteChangeRef.current(shared);
|
|
1071
|
+
});
|
|
971
1072
|
return () => {
|
|
972
1073
|
cancelled = true;
|
|
973
1074
|
sub.remove();
|
|
1075
|
+
unsubscribe();
|
|
974
1076
|
};
|
|
975
1077
|
}, [groupId]);
|
|
976
1078
|
const syncToShared = (0, import_react6.useCallback)(
|
|
@@ -1020,7 +1122,9 @@ function avoidSeatColorClash(p1Color, p2Color, defaults) {
|
|
|
1020
1122
|
readSharedProfiles,
|
|
1021
1123
|
resolveInitialProfiles,
|
|
1022
1124
|
saveSharedProfiles,
|
|
1125
|
+
subscribeSharedProfiles,
|
|
1023
1126
|
useProfileColorPreview,
|
|
1024
1127
|
useProfileDisplayColor,
|
|
1128
|
+
useProfileSwatchRenderer,
|
|
1025
1129
|
useSharedProfilesSync
|
|
1026
1130
|
});
|