@dubsdotapp/expo 0.2.75 → 0.2.77

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/dist/index.js CHANGED
@@ -2183,6 +2183,12 @@ async function getDeviceInfo() {
2183
2183
  }
2184
2184
  }
2185
2185
 
2186
+ // src/utils/avatarUrl.ts
2187
+ function ensurePngAvatar(url) {
2188
+ if (!url) return void 0;
2189
+ return url.replace("/svg?", "/png?");
2190
+ }
2191
+
2186
2192
  // src/hooks/useAuth.ts
2187
2193
  function useAuth() {
2188
2194
  const sharedAuth = (0, import_react13.useContext)(AuthContext);
@@ -2193,6 +2199,10 @@ function useAuth() {
2193
2199
  const [token, setToken] = (0, import_react13.useState)(null);
2194
2200
  const [error, setError] = (0, import_react13.useState)(null);
2195
2201
  const pendingAuth = (0, import_react13.useRef)(null);
2202
+ const normalizeUser = (u) => ({
2203
+ ...u,
2204
+ avatar: ensurePngAvatar(u.avatar) ?? u.avatar
2205
+ });
2196
2206
  const reset = (0, import_react13.useCallback)(() => {
2197
2207
  setStatus("idle");
2198
2208
  setUser(null);
@@ -2226,7 +2236,7 @@ function useAuth() {
2226
2236
  setStatus("needsRegistration");
2227
2237
  return;
2228
2238
  }
2229
- setUser(result.user);
2239
+ setUser(normalizeUser(result.user));
2230
2240
  setToken(result.token);
2231
2241
  setStatus("authenticated");
2232
2242
  } catch (err) {
@@ -2261,7 +2271,7 @@ function useAuth() {
2261
2271
  });
2262
2272
  pendingAuth.current = null;
2263
2273
  const user2 = avatarUrl && !result.user.avatar ? { ...result.user, avatar: avatarUrl } : result.user;
2264
- setUser(user2);
2274
+ setUser(normalizeUser(user2));
2265
2275
  setToken(result.token);
2266
2276
  setStatus("authenticated");
2267
2277
  } catch (err) {
@@ -2284,7 +2294,7 @@ function useAuth() {
2284
2294
  try {
2285
2295
  client.setToken(savedToken);
2286
2296
  const me = await client.getMe();
2287
- setUser(me);
2297
+ setUser(normalizeUser(me));
2288
2298
  setToken(savedToken);
2289
2299
  setStatus("authenticated");
2290
2300
  return true;
@@ -2299,7 +2309,7 @@ function useAuth() {
2299
2309
  const refreshUser = (0, import_react13.useCallback)(async () => {
2300
2310
  try {
2301
2311
  const me = await client.getMe();
2302
- setUser(me);
2312
+ setUser(normalizeUser(me));
2303
2313
  } catch {
2304
2314
  }
2305
2315
  }, [client]);
@@ -2526,11 +2536,23 @@ var DICEBEAR_STYLES = [
2526
2536
  "big-smile",
2527
2537
  "thumbs"
2528
2538
  ];
2539
+ var BG_COLORS = [
2540
+ "1a1a2e",
2541
+ "f43f5e",
2542
+ "f97316",
2543
+ "eab308",
2544
+ "22c55e",
2545
+ "3b82f6",
2546
+ "8b5cf6",
2547
+ "ec4899",
2548
+ "06b6d4",
2549
+ "64748b"
2550
+ ];
2529
2551
  function generateSeed() {
2530
2552
  return Math.random().toString(36).slice(2, 10);
2531
2553
  }
2532
- function getAvatarUrl(style, seed, size = 256) {
2533
- return `https://api.dicebear.com/9.x/${style}/png?seed=${seed}&size=${size}`;
2554
+ function getAvatarUrl(style, seed, bg = "1a1a2e", size = 256) {
2555
+ return `https://api.dicebear.com/9.x/${style}/png?seed=${seed}&backgroundColor=${bg}&size=${size}`;
2534
2556
  }
2535
2557
  function AuthGate({
2536
2558
  children,
@@ -2710,6 +2732,7 @@ function DefaultRegistrationScreen({
2710
2732
  const [step, setStep] = (0, import_react17.useState)(0);
2711
2733
  const [avatarSeed, setAvatarSeed] = (0, import_react17.useState)(generateSeed);
2712
2734
  const [avatarStyle, setAvatarStyle] = (0, import_react17.useState)("adventurer");
2735
+ const [avatarBg, setAvatarBg] = (0, import_react17.useState)("1a1a2e");
2713
2736
  const [showStyles, setShowStyles] = (0, import_react17.useState)(false);
2714
2737
  const [username, setUsername] = (0, import_react17.useState)("");
2715
2738
  const [referralCode, setReferralCode] = (0, import_react17.useState)("");
@@ -2718,7 +2741,7 @@ function DefaultRegistrationScreen({
2718
2741
  const debounceRef = (0, import_react17.useRef)(null);
2719
2742
  const fadeAnim = (0, import_react17.useRef)(new import_react_native7.Animated.Value(1)).current;
2720
2743
  const slideAnim = (0, import_react17.useRef)(new import_react_native7.Animated.Value(0)).current;
2721
- const avatarUrl = getAvatarUrl(avatarStyle, avatarSeed);
2744
+ const avatarUrl = getAvatarUrl(avatarStyle, avatarSeed, avatarBg);
2722
2745
  (0, import_react17.useEffect)(() => {
2723
2746
  if (debounceRef.current) clearTimeout(debounceRef.current);
2724
2747
  const trimmed = username.trim();
@@ -2791,15 +2814,30 @@ function DefaultRegistrationScreen({
2791
2814
  }
2792
2815
  )
2793
2816
  ] }),
2794
- showStyles && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native7.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: s.styleScroll, children: DICEBEAR_STYLES.map((st) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2795
- import_react_native7.TouchableOpacity,
2796
- {
2797
- onPress: () => setAvatarStyle(st),
2798
- style: [s.styleThumbWrap, { borderColor: st === avatarStyle ? accent : t.border }],
2799
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native7.Image, { source: { uri: getAvatarUrl(st, avatarSeed, 80) }, style: s.styleThumb })
2800
- },
2801
- st
2802
- )) })
2817
+ showStyles && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
2818
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native7.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: s.styleScroll, children: DICEBEAR_STYLES.map((st) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2819
+ import_react_native7.TouchableOpacity,
2820
+ {
2821
+ onPress: () => setAvatarStyle(st),
2822
+ style: [s.styleThumbWrap, { borderColor: st === avatarStyle ? accent : t.border }],
2823
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native7.Image, { source: { uri: getAvatarUrl(st, avatarSeed, avatarBg, 80) }, style: s.styleThumb })
2824
+ },
2825
+ st
2826
+ )) }),
2827
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native7.Text, { style: [s.subtitle, { color: t.textMuted, marginTop: 8 }], children: "Background Color" }),
2828
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native7.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: s.styleScroll, children: BG_COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2829
+ import_react_native7.TouchableOpacity,
2830
+ {
2831
+ onPress: () => setAvatarBg(color),
2832
+ style: [
2833
+ s.colorSwatch,
2834
+ { backgroundColor: `#${color}` },
2835
+ color === avatarBg && { borderColor: accent, borderWidth: 2.5 }
2836
+ ]
2837
+ },
2838
+ color
2839
+ )) })
2840
+ ] })
2803
2841
  ] }),
2804
2842
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native7.View, { style: s.bottomRow, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2805
2843
  import_react_native7.TouchableOpacity,
@@ -3110,6 +3148,7 @@ var s = import_react_native7.StyleSheet.create({
3110
3148
  styleScroll: { paddingHorizontal: 24, marginTop: 4 },
3111
3149
  styleThumbWrap: { borderWidth: 2, borderRadius: 12, padding: 3, marginRight: 10 },
3112
3150
  styleThumb: { width: 52, height: 52, borderRadius: 10, backgroundColor: "#E5E5EA" },
3151
+ colorSwatch: { width: 32, height: 32, borderRadius: 16, borderWidth: 1.5, borderColor: "rgba(255,255,255,0.15)", marginRight: 10 },
3113
3152
  // Input
3114
3153
  inputGroup: { paddingHorizontal: 24, gap: 6 },
3115
3154
  inputLabel: { fontSize: 15, fontWeight: "600" },
@@ -3336,14 +3375,6 @@ function useAppConfig() {
3336
3375
  // src/ui/UserProfileCard.tsx
3337
3376
  var import_react19 = require("react");
3338
3377
  var import_react_native8 = require("react-native");
3339
-
3340
- // src/utils/avatarUrl.ts
3341
- function ensurePngAvatar(url) {
3342
- if (!url) return void 0;
3343
- return url.replace("/svg?", "/png?");
3344
- }
3345
-
3346
- // src/ui/UserProfileCard.tsx
3347
3378
  var import_jsx_runtime5 = require("react/jsx-runtime");
3348
3379
  function truncateAddress(address, chars = 4) {
3349
3380
  if (address.length <= chars * 2 + 3) return address;
@@ -3566,20 +3597,35 @@ var DICEBEAR_STYLES2 = [
3566
3597
  "big-smile",
3567
3598
  "thumbs"
3568
3599
  ];
3600
+ var BG_COLORS2 = [
3601
+ "1a1a2e",
3602
+ "f43f5e",
3603
+ "f97316",
3604
+ "eab308",
3605
+ "22c55e",
3606
+ "3b82f6",
3607
+ "8b5cf6",
3608
+ "ec4899",
3609
+ "06b6d4",
3610
+ "64748b"
3611
+ ];
3569
3612
  function generateSeed2() {
3570
3613
  return Math.random().toString(36).slice(2, 10);
3571
3614
  }
3572
- function getAvatarUrl2(style, seed, size = 256) {
3573
- return `https://api.dicebear.com/9.x/${style}/png?seed=${seed}&size=${size}`;
3615
+ function getAvatarUrl2(style, seed, bg = "1a1a2e", size = 256) {
3616
+ return `https://api.dicebear.com/9.x/${style}/png?seed=${seed}&backgroundColor=${bg}&size=${size}`;
3574
3617
  }
3575
3618
  function parseAvatarUrl(url) {
3576
- if (!url) return { style: "adventurer", seed: generateSeed2() };
3619
+ if (!url) return { style: "adventurer", seed: generateSeed2(), bg: "1a1a2e" };
3577
3620
  try {
3578
3621
  const match = url.match(/\/\d+\.x\/([^/]+)\/(?:png|svg)\?seed=([^&]+)/);
3579
- if (match) return { style: match[1], seed: match[2] };
3622
+ if (match) {
3623
+ const bgMatch = url.match(/backgroundColor=([^&]+)/);
3624
+ return { style: match[1], seed: match[2], bg: bgMatch?.[1] || "1a1a2e" };
3625
+ }
3580
3626
  } catch {
3581
3627
  }
3582
- return { style: "adventurer", seed: generateSeed2() };
3628
+ return { style: "adventurer", seed: generateSeed2(), bg: "1a1a2e" };
3583
3629
  }
3584
3630
  function truncateAddress3(address, chars = 4) {
3585
3631
  if (address.length <= chars * 2 + 3) return address;
@@ -3599,12 +3645,14 @@ function UserProfileSheet({
3599
3645
  const parsed = (0, import_react20.useMemo)(() => parseAvatarUrl(user.avatar), [user.avatar]);
3600
3646
  const [avatarStyle, setAvatarStyle] = (0, import_react20.useState)(parsed.style);
3601
3647
  const [avatarSeed, setAvatarSeed] = (0, import_react20.useState)(parsed.seed);
3648
+ const [bgColor, setBgColor] = (0, import_react20.useState)(parsed.bg);
3602
3649
  const [saving, setSaving] = (0, import_react20.useState)(false);
3603
3650
  const [error, setError] = (0, import_react20.useState)(null);
3604
3651
  (0, import_react20.useEffect)(() => {
3605
3652
  const p = parseAvatarUrl(user.avatar);
3606
3653
  setAvatarStyle(p.style);
3607
3654
  setAvatarSeed(p.seed);
3655
+ setBgColor(p.bg);
3608
3656
  }, [user.avatar]);
3609
3657
  (0, import_react20.useEffect)(() => {
3610
3658
  import_react_native10.Animated.timing(overlayOpacity, {
@@ -3616,28 +3664,8 @@ function UserProfileSheet({
3616
3664
  (0, import_react20.useEffect)(() => {
3617
3665
  if (visible) setError(null);
3618
3666
  }, [visible]);
3619
- const currentAvatarUrl = getAvatarUrl2(avatarStyle, avatarSeed);
3620
- const handleSelectStyle = (0, import_react20.useCallback)(
3621
- async (style) => {
3622
- const newUrl = getAvatarUrl2(style, avatarSeed);
3623
- setAvatarStyle(style);
3624
- setSaving(true);
3625
- setError(null);
3626
- try {
3627
- await client.updateProfile({ avatar: newUrl });
3628
- onAvatarUpdated?.(newUrl);
3629
- } catch (err) {
3630
- setError(err instanceof Error ? err.message : "Failed to update avatar");
3631
- } finally {
3632
- setSaving(false);
3633
- }
3634
- },
3635
- [avatarSeed, client, onAvatarUpdated]
3636
- );
3637
- const handleShuffle = (0, import_react20.useCallback)(async () => {
3638
- const newSeed = generateSeed2();
3639
- const newUrl = getAvatarUrl2(avatarStyle, newSeed);
3640
- setAvatarSeed(newSeed);
3667
+ const currentAvatarUrl = getAvatarUrl2(avatarStyle, avatarSeed, bgColor);
3668
+ const saveAvatar = (0, import_react20.useCallback)(async (newUrl) => {
3641
3669
  setSaving(true);
3642
3670
  setError(null);
3643
3671
  try {
@@ -3648,7 +3676,26 @@ function UserProfileSheet({
3648
3676
  } finally {
3649
3677
  setSaving(false);
3650
3678
  }
3651
- }, [avatarStyle, client, onAvatarUpdated]);
3679
+ }, [client, onAvatarUpdated]);
3680
+ const handleSelectStyle = (0, import_react20.useCallback)(
3681
+ (style) => {
3682
+ setAvatarStyle(style);
3683
+ saveAvatar(getAvatarUrl2(style, avatarSeed, bgColor));
3684
+ },
3685
+ [avatarSeed, bgColor, saveAvatar]
3686
+ );
3687
+ const handleShuffle = (0, import_react20.useCallback)(() => {
3688
+ const newSeed = generateSeed2();
3689
+ setAvatarSeed(newSeed);
3690
+ saveAvatar(getAvatarUrl2(avatarStyle, newSeed, bgColor));
3691
+ }, [avatarStyle, bgColor, saveAvatar]);
3692
+ const handleSelectBgColor = (0, import_react20.useCallback)(
3693
+ (color) => {
3694
+ setBgColor(color);
3695
+ saveAvatar(getAvatarUrl2(avatarStyle, avatarSeed, color));
3696
+ },
3697
+ [avatarStyle, avatarSeed, saveAvatar]
3698
+ );
3652
3699
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3653
3700
  import_react_native10.Modal,
3654
3701
  {
@@ -3729,7 +3776,7 @@ function UserProfileSheet({
3729
3776
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3730
3777
  import_react_native10.Image,
3731
3778
  {
3732
- source: { uri: getAvatarUrl2(style, avatarSeed, 80) },
3779
+ source: { uri: getAvatarUrl2(style, avatarSeed, bgColor, 80) },
3733
3780
  style: styles4.styleTileImage
3734
3781
  }
3735
3782
  )
@@ -3738,6 +3785,32 @@ function UserProfileSheet({
3738
3785
  );
3739
3786
  })
3740
3787
  }
3788
+ ),
3789
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native10.Text, { style: [styles4.sectionLabel, { color: t.textSecondary, marginTop: 4 }], children: "Background Color" }),
3790
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3791
+ import_react_native10.ScrollView,
3792
+ {
3793
+ horizontal: true,
3794
+ showsHorizontalScrollIndicator: false,
3795
+ contentContainerStyle: styles4.colorPickerContent,
3796
+ children: BG_COLORS2.map((color) => {
3797
+ const isSelected = color === bgColor;
3798
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3799
+ import_react_native10.TouchableOpacity,
3800
+ {
3801
+ onPress: () => handleSelectBgColor(color),
3802
+ activeOpacity: 0.7,
3803
+ disabled: saving,
3804
+ style: [
3805
+ styles4.colorSwatch,
3806
+ { backgroundColor: `#${color}` },
3807
+ isSelected && { borderColor: t.accent, borderWidth: 2.5 }
3808
+ ]
3809
+ },
3810
+ color
3811
+ );
3812
+ })
3813
+ }
3741
3814
  )
3742
3815
  ] }),
3743
3816
  error ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native10.View, { style: [styles4.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native10.Text, { style: [styles4.errorText, { color: t.errorText }], children: error }) }) : null,
@@ -3899,6 +3972,16 @@ var styles4 = import_react_native10.StyleSheet.create({
3899
3972
  stylePickerContent: {
3900
3973
  gap: 10
3901
3974
  },
3975
+ colorPickerContent: {
3976
+ gap: 8
3977
+ },
3978
+ colorSwatch: {
3979
+ width: 32,
3980
+ height: 32,
3981
+ borderRadius: 16,
3982
+ borderWidth: 1.5,
3983
+ borderColor: "rgba(255,255,255,0.15)"
3984
+ },
3902
3985
  styleTile: {
3903
3986
  width: 72,
3904
3987
  height: 72,