@oxy.so/services 1.1.0 → 2.0.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 +1 -1
- package/lib/commonjs/ui/components/BenefitList.js +93 -0
- package/lib/commonjs/ui/components/BenefitList.js.map +1 -0
- package/lib/commonjs/ui/components/FollowTargetButton.js +1 -1
- package/lib/commonjs/ui/components/FollowTargetButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +95 -140
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +8 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +35 -25
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInRequestView.js +2 -3
- package/lib/commonjs/ui/components/authChooser/SignInRequestView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignUpView.js +12 -127
- package/lib/commonjs/ui/components/authChooser/SignUpView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/signInFailureMessage.js +40 -0
- package/lib/commonjs/ui/components/authChooser/signInFailureMessage.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/types.js +4 -6
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +1 -17
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +15 -2
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/passkeyFlow.js +2 -28
- package/lib/commonjs/ui/context/passkeyFlow.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js +3 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -1
- package/lib/commonjs/ui/hooks/useProfileEditing.js +14 -0
- package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js +10 -10
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +225 -9
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LearnMoreUsernamesScreen.js +6 -6
- package/lib/commonjs/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ManageAccountScreen.js +10 -0
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +5 -5
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js +13 -13
- package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js.map +1 -1
- package/lib/module/ui/components/BenefitList.js +89 -0
- package/lib/module/ui/components/BenefitList.js.map +1 -0
- package/lib/module/ui/components/FollowTargetButton.js +1 -1
- package/lib/module/ui/components/FollowTargetButton.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +97 -142
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js +8 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInEntryView.js +35 -25
- package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInRequestView.js +2 -3
- package/lib/module/ui/components/authChooser/SignInRequestView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignUpView.js +14 -129
- package/lib/module/ui/components/authChooser/SignUpView.js.map +1 -1
- package/lib/module/ui/components/authChooser/signInFailureMessage.js +36 -0
- package/lib/module/ui/components/authChooser/signInFailureMessage.js.map +1 -0
- package/lib/module/ui/components/authChooser/types.js +4 -6
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +2 -18
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +15 -2
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/passkeyFlow.js +2 -27
- package/lib/module/ui/context/passkeyFlow.js.map +1 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js +3 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -1
- package/lib/module/ui/hooks/useProfileEditing.js +14 -0
- package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js +6 -6
- package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +225 -9
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/LearnMoreUsernamesScreen.js +6 -6
- package/lib/module/ui/screens/LearnMoreUsernamesScreen.js.map +1 -1
- package/lib/module/ui/screens/ManageAccountScreen.js +12 -2
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js +1 -1
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustAboutScreen.js +7 -7
- package/lib/module/ui/screens/trust/TrustAboutScreen.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/BenefitList.d.ts +25 -0
- package/lib/typescript/commonjs/ui/components/BenefitList.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +3 -4
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +11 -12
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInRequestView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignUpView.d.ts +10 -13
- package/lib/typescript/commonjs/ui/components/authChooser/SignUpView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/signInFailureMessage.d.ts +16 -0
- package/lib/typescript/commonjs/ui/components/authChooser/signInFailureMessage.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +11 -13
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +3 -2
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +20 -10
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/passkeyFlow.d.ts +2 -21
- package/lib/typescript/commonjs/ui/context/passkeyFlow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +3 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts +1 -0
- package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/BenefitList.d.ts +25 -0
- package/lib/typescript/module/ui/components/BenefitList.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +3 -4
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +11 -12
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInRequestView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignUpView.d.ts +10 -13
- package/lib/typescript/module/ui/components/authChooser/SignUpView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/signInFailureMessage.d.ts +16 -0
- package/lib/typescript/module/ui/components/authChooser/signInFailureMessage.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +11 -13
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +3 -2
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +20 -10
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/passkeyFlow.d.ts +2 -21
- package/lib/typescript/module/ui/context/passkeyFlow.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +3 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts +1 -0
- package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +1 -1
- package/src/ui/components/BenefitList.tsx +111 -0
- package/src/ui/components/FollowTargetButton.tsx +1 -1
- package/src/ui/components/OxyAuthChooser.tsx +106 -156
- package/src/ui/components/authChooser/AccountsMenuView.tsx +7 -0
- package/src/ui/components/authChooser/SignInEntryView.tsx +41 -44
- package/src/ui/components/authChooser/SignInRequestView.tsx +1 -4
- package/src/ui/components/authChooser/SignUpView.tsx +16 -114
- package/src/ui/components/authChooser/signInFailureMessage.ts +39 -0
- package/src/ui/components/authChooser/types.ts +11 -13
- package/src/ui/context/OxyContext.tsx +2 -20
- package/src/ui/context/__tests__/passkeyFlow.test.ts +2 -65
- package/src/ui/context/hooks/useAuthOperations.ts +10 -5
- package/src/ui/context/oxyContextTypes.ts +16 -8
- package/src/ui/context/passkeyFlow.ts +2 -39
- package/src/ui/hooks/accountDialogSnapshot.ts +2 -0
- package/src/ui/hooks/useProfileEditing.ts +15 -0
- package/src/ui/screens/AccountVerificationScreen.tsx +6 -6
- package/src/ui/screens/EditProfileFieldScreen.tsx +202 -7
- package/src/ui/screens/LearnMoreUsernamesScreen.tsx +6 -6
- package/src/ui/screens/ManageAccountScreen.tsx +12 -0
- package/src/ui/screens/PremiumSubscriptionScreen.tsx +1 -1
- package/src/ui/screens/trust/TrustAboutScreen.tsx +7 -7
- package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js +0 -50
- package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js.map +0 -1
- package/lib/module/ui/components/authChooser/useUsernameAvailability.js +0 -46
- package/lib/module/ui/components/authChooser/useUsernameAvailability.js.map +0 -1
- package/lib/typescript/commonjs/ui/components/authChooser/useUsernameAvailability.d.ts +0 -16
- package/lib/typescript/commonjs/ui/components/authChooser/useUsernameAvailability.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/authChooser/useUsernameAvailability.d.ts +0 -16
- package/lib/typescript/module/ui/components/authChooser/useUsernameAvailability.d.ts.map +0 -1
- package/src/ui/components/authChooser/useUsernameAvailability.ts +0 -60
|
@@ -11,7 +11,7 @@ import { H4, Text } from '@oxy.so/bloom/typography';
|
|
|
11
11
|
import { Button } from '@oxy.so/bloom/button';
|
|
12
12
|
import { TextField, TextFieldInput } from '@oxy.so/bloom/text-field';
|
|
13
13
|
import { IconCircle } from '@oxy.so/bloom/icon-circle';
|
|
14
|
-
import { BenefitList, BenefitRow } from '
|
|
14
|
+
import { BenefitList, BenefitRow } from '../components/BenefitList';
|
|
15
15
|
import * as Icons from '@oxy.so/bloom/icons';
|
|
16
16
|
import { useI18n } from '../hooks/useI18n';
|
|
17
17
|
import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
|
|
@@ -82,7 +82,7 @@ const AccountVerificationScreen: React.FC<BaseScreenProps> = ({
|
|
|
82
82
|
<View className="px-screen-margin pb-space-32">
|
|
83
83
|
{/* Hero */}
|
|
84
84
|
<View className="items-center py-space-24 gap-space-12">
|
|
85
|
-
<IconCircle icon={Icons.
|
|
85
|
+
<IconCircle icon={Icons.RiVerifiedBadgeLine} />
|
|
86
86
|
<H4 className="text-headerBold font-headerBold text-text text-center">
|
|
87
87
|
{t('accountVerification.heroTitle') || 'Get a verified badge'}
|
|
88
88
|
</H4>
|
|
@@ -97,15 +97,15 @@ const AccountVerificationScreen: React.FC<BaseScreenProps> = ({
|
|
|
97
97
|
accessibilityLabel={t('accountVerification.sections.benefits') || 'What verification gives you'}
|
|
98
98
|
>
|
|
99
99
|
<BenefitRow
|
|
100
|
-
icon={<Icons.
|
|
100
|
+
icon={<Icons.RiShieldCheckLine size="sm" style={{ color: bloomTheme.colors.primary }} />}
|
|
101
101
|
label={t('accountVerification.benefits.authenticity') || 'Confirms your identity is authentic and trusted'}
|
|
102
102
|
/>
|
|
103
103
|
<BenefitRow
|
|
104
|
-
icon={<Icons.
|
|
104
|
+
icon={<Icons.RiVerifiedBadgeLine size="sm" style={{ color: bloomTheme.colors.primary }} />}
|
|
105
105
|
label={t('accountVerification.benefits.badge') || 'Displays a verified badge across the platform'}
|
|
106
106
|
/>
|
|
107
107
|
<BenefitRow
|
|
108
|
-
icon={<Icons.
|
|
108
|
+
icon={<Icons.RiSparklingLine size="sm" style={{ color: bloomTheme.colors.primary }} />}
|
|
109
109
|
label={t('accountVerification.benefits.credibility') || 'Builds credibility with people who follow you'}
|
|
110
110
|
/>
|
|
111
111
|
</BenefitList>
|
|
@@ -161,7 +161,7 @@ const AccountVerificationScreen: React.FC<BaseScreenProps> = ({
|
|
|
161
161
|
</Button>
|
|
162
162
|
|
|
163
163
|
<View className="flex-row items-start gap-space-8 mt-space-24">
|
|
164
|
-
<Icons.
|
|
164
|
+
<Icons.RiInformationLine
|
|
165
165
|
size="sm"
|
|
166
166
|
style={{ color: bloomTheme.colors.textTertiary }}
|
|
167
167
|
/>
|
|
@@ -12,6 +12,16 @@ import { useTheme } from '@oxy.so/bloom/theme';
|
|
|
12
12
|
import { Text } from '@oxy.so/bloom/typography';
|
|
13
13
|
import { Button } from '@oxy.so/bloom/button';
|
|
14
14
|
import { TextField, TextFieldInput } from '@oxy.so/bloom/text-field';
|
|
15
|
+
import {
|
|
16
|
+
Select,
|
|
17
|
+
SelectContent,
|
|
18
|
+
SelectIcon,
|
|
19
|
+
SelectItem,
|
|
20
|
+
SelectItemIndicator,
|
|
21
|
+
SelectItemText,
|
|
22
|
+
SelectTrigger,
|
|
23
|
+
SelectValue,
|
|
24
|
+
} from '@oxy.so/bloom/select';
|
|
15
25
|
import { normalizeTheme } from '@oxy.so/core';
|
|
16
26
|
import type { User } from '@oxy.so/core';
|
|
17
27
|
import { useI18n } from '../hooks/useI18n';
|
|
@@ -53,6 +63,67 @@ interface FieldConfig {
|
|
|
53
63
|
inputProps?: Partial<TextInputProps>;
|
|
54
64
|
}>;
|
|
55
65
|
isList?: boolean;
|
|
66
|
+
/** Renders `renderBirthdayPicker` instead of the plain-text `fields`. */
|
|
67
|
+
isDatePicker?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The earliest calendar year the birthday picker offers — matches the
|
|
72
|
+
* platform's own lower bound (`dateOfBirthSchema`, `@oxy.so/contracts`) so a
|
|
73
|
+
* value this picker can produce is never one the API would reject.
|
|
74
|
+
*/
|
|
75
|
+
const MIN_BIRTH_YEAR = 1900;
|
|
76
|
+
|
|
77
|
+
/** `MIN_BIRTH_YEAR` through the current year, most recent first. */
|
|
78
|
+
function birthYearOptions(): Array<{ value: string; label: string }> {
|
|
79
|
+
const currentYear = new Date().getUTCFullYear();
|
|
80
|
+
const years: Array<{ value: string; label: string }> = [];
|
|
81
|
+
for (let year = currentYear; year >= MIN_BIRTH_YEAR; year--) {
|
|
82
|
+
years.push({ value: String(year), label: String(year) });
|
|
83
|
+
}
|
|
84
|
+
return years;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Month names are not run through `t()` here: this SDK has no existing
|
|
89
|
+
* calendar-month translation catalogue to hook into, and inventing one is out
|
|
90
|
+
* of scope for wiring up the date picker itself. English month names are a
|
|
91
|
+
* known, deliberate limitation of this pass.
|
|
92
|
+
*/
|
|
93
|
+
const MONTH_OPTIONS: Array<{ value: string; label: string }> = [
|
|
94
|
+
{ value: '01', label: 'January' },
|
|
95
|
+
{ value: '02', label: 'February' },
|
|
96
|
+
{ value: '03', label: 'March' },
|
|
97
|
+
{ value: '04', label: 'April' },
|
|
98
|
+
{ value: '05', label: 'May' },
|
|
99
|
+
{ value: '06', label: 'June' },
|
|
100
|
+
{ value: '07', label: 'July' },
|
|
101
|
+
{ value: '08', label: 'August' },
|
|
102
|
+
{ value: '09', label: 'September' },
|
|
103
|
+
{ value: '10', label: 'October' },
|
|
104
|
+
{ value: '11', label: 'November' },
|
|
105
|
+
{ value: '12', label: 'December' },
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Real Gregorian days in `month` (1-12) of `year`, leap years included — the
|
|
110
|
+
* same rule `dateOfBirthSchema` (`@oxy.so/contracts`) applies server-side,
|
|
111
|
+
* written a second time here because a picker's OWN day list must bound
|
|
112
|
+
* itself before the value ever reaches it.
|
|
113
|
+
*/
|
|
114
|
+
function daysInMonth(year: number, month: number): number {
|
|
115
|
+
const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
116
|
+
const DAYS_PER_MONTH = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
117
|
+
return DAYS_PER_MONTH[month - 1];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Day options for the current year/month selection — 31 while either is unset. */
|
|
121
|
+
function dayOptions(year: string, month: string): Array<{ value: string; label: string }> {
|
|
122
|
+
const count = year && month ? daysInMonth(Number(year), Number(month)) : 31;
|
|
123
|
+
return Array.from({ length: count }, (_, index) => {
|
|
124
|
+
const day = String(index + 1).padStart(2, '0');
|
|
125
|
+
return { value: day, label: day };
|
|
126
|
+
});
|
|
56
127
|
}
|
|
57
128
|
|
|
58
129
|
interface EditProfileFieldScreenProps extends BaseScreenProps {
|
|
@@ -131,7 +202,13 @@ function buildInitialProfileState(
|
|
|
131
202
|
fieldValues.lastName = String(userData.lastName || userData.name?.last || '');
|
|
132
203
|
break;
|
|
133
204
|
case 'birthday':
|
|
134
|
-
|
|
205
|
+
// Seeded from `dateOfBirth` ONLY, never the legacy free-text
|
|
206
|
+
// `birthday` — a structured picker needs a real `YYYY-MM-DD`
|
|
207
|
+
// value to select against, and `birthday` has no guaranteed
|
|
208
|
+
// shape (see its own comment in `db/schema/users.ts`). An
|
|
209
|
+
// account whose `dateOfBirth` is not yet set opens the picker
|
|
210
|
+
// empty rather than seeded with a guess.
|
|
211
|
+
fieldValues.dateOfBirth = String(userData.dateOfBirth || '');
|
|
135
212
|
break;
|
|
136
213
|
case 'address':
|
|
137
214
|
fieldValues.address = String(userData.address || '');
|
|
@@ -322,17 +399,18 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
322
399
|
return {
|
|
323
400
|
title: t('editProfile.items.birthday.title') || 'Birthday',
|
|
324
401
|
subtitle: t('editProfile.items.birthday.subtitle') || 'Your date of birth',
|
|
402
|
+
// One entry, naming the field `handleSave`'s generic branch writes
|
|
403
|
+
// (`fieldConfig.fields[0]?.key`) — `renderBirthdayPicker` renders the
|
|
404
|
+
// actual day/month/year controls; this array is never mapped through
|
|
405
|
+
// `renderField` for this field type (see `isDatePicker` below).
|
|
325
406
|
fields: [
|
|
326
407
|
{
|
|
327
|
-
key: '
|
|
408
|
+
key: 'dateOfBirth',
|
|
328
409
|
label: t('editProfile.items.birthday.label') || 'Birthday',
|
|
329
410
|
placeholder: t('editProfile.items.birthday.placeholder') || 'YYYY-MM-DD',
|
|
330
|
-
inputProps: {
|
|
331
|
-
autoCapitalize: 'none',
|
|
332
|
-
autoCorrect: false,
|
|
333
|
-
},
|
|
334
411
|
},
|
|
335
412
|
],
|
|
413
|
+
isDatePicker: true,
|
|
336
414
|
};
|
|
337
415
|
case 'locations':
|
|
338
416
|
return {
|
|
@@ -364,6 +442,54 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
364
442
|
}
|
|
365
443
|
}, [fieldErrors]);
|
|
366
444
|
|
|
445
|
+
// The birthday picker's three pieces, seeded once from the same
|
|
446
|
+
// one-time-mount value `fieldValues.dateOfBirth` already carries (empty
|
|
447
|
+
// string parts when unset). Kept separate from `fieldValues` because a
|
|
448
|
+
// `Select` needs its OWN current value per control; each change handler
|
|
449
|
+
// below composes the three back into the single `dateOfBirth` field
|
|
450
|
+
// `handleSave` actually writes, in the event handler itself rather than
|
|
451
|
+
// an effect.
|
|
452
|
+
const [dobYear, setDobYear] = useState(() => (fieldValues.dateOfBirth || '').split('-')[0] || '');
|
|
453
|
+
const [dobMonth, setDobMonth] = useState(() => (fieldValues.dateOfBirth || '').split('-')[1] || '');
|
|
454
|
+
const [dobDay, setDobDay] = useState(() => (fieldValues.dateOfBirth || '').split('-')[2] || '');
|
|
455
|
+
|
|
456
|
+
// A partial selection (e.g. year and month but no day yet) writes ''`,
|
|
457
|
+
// which `updateField`/the API's `dateOfBirth` write path already treats
|
|
458
|
+
// as "clear" — the same behaviour a fully-erased text field would have
|
|
459
|
+
// had. There is deliberately no separate "incomplete date" error: the
|
|
460
|
+
// three controls are either all filled (a real date) or the field is
|
|
461
|
+
// unset, and both are valid states to save.
|
|
462
|
+
const commitDateOfBirth = useCallback((year: string, month: string, day: string) => {
|
|
463
|
+
handleFieldChange('dateOfBirth', year && month && day ? `${year}-${month}-${day}` : '');
|
|
464
|
+
}, [handleFieldChange]);
|
|
465
|
+
|
|
466
|
+
// Changing the year or month can strand a selected day past the new
|
|
467
|
+
// month's length (31 March -> April has none) — clamped down to the new
|
|
468
|
+
// last day rather than left pointing at a day `dayOptions` no longer
|
|
469
|
+
// lists, the same way a native date picker behaves.
|
|
470
|
+
const handleDobYearChange = useCallback((year: string) => {
|
|
471
|
+
const clampedDay = dobDay && dobMonth && Number(dobDay) > daysInMonth(Number(year), Number(dobMonth))
|
|
472
|
+
? String(daysInMonth(Number(year), Number(dobMonth))).padStart(2, '0')
|
|
473
|
+
: dobDay;
|
|
474
|
+
setDobYear(year);
|
|
475
|
+
setDobDay(clampedDay);
|
|
476
|
+
commitDateOfBirth(year, dobMonth, clampedDay);
|
|
477
|
+
}, [dobMonth, dobDay, commitDateOfBirth]);
|
|
478
|
+
|
|
479
|
+
const handleDobMonthChange = useCallback((month: string) => {
|
|
480
|
+
const clampedDay = dobDay && dobYear && Number(dobDay) > daysInMonth(Number(dobYear), Number(month))
|
|
481
|
+
? String(daysInMonth(Number(dobYear), Number(month))).padStart(2, '0')
|
|
482
|
+
: dobDay;
|
|
483
|
+
setDobMonth(month);
|
|
484
|
+
setDobDay(clampedDay);
|
|
485
|
+
commitDateOfBirth(dobYear, month, clampedDay);
|
|
486
|
+
}, [dobYear, dobDay, commitDateOfBirth]);
|
|
487
|
+
|
|
488
|
+
const handleDobDayChange = useCallback((day: string) => {
|
|
489
|
+
setDobDay(day);
|
|
490
|
+
commitDateOfBirth(dobYear, dobMonth, day);
|
|
491
|
+
}, [dobYear, dobMonth, commitDateOfBirth]);
|
|
492
|
+
|
|
367
493
|
// Validate all fields
|
|
368
494
|
const validateFields = useCallback((): boolean => {
|
|
369
495
|
const errors: Record<string, string | undefined> = {};
|
|
@@ -511,6 +637,71 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
511
637
|
);
|
|
512
638
|
};
|
|
513
639
|
|
|
640
|
+
// Render the birthday picker: three `Select`s (day, month, year) rather
|
|
641
|
+
// than a native date-picker dependency. Neither this app nor
|
|
642
|
+
// `@oxy.so/bloom` has one installed, and `Select` is already this
|
|
643
|
+
// design system's cross-platform choice control (a bottom sheet on
|
|
644
|
+
// native, an anchored dropdown on web — see its own docs) with no new
|
|
645
|
+
// dependency to add and no native linking to carry across platforms.
|
|
646
|
+
const renderBirthdayPicker = () => (
|
|
647
|
+
<View className="flex-row gap-space-8">
|
|
648
|
+
<View className="flex-1">
|
|
649
|
+
<Select value={dobMonth} onValueChange={handleDobMonthChange}>
|
|
650
|
+
<SelectTrigger label={t('editProfile.items.birthday.month') || 'Month'}>
|
|
651
|
+
<SelectValue placeholder={t('editProfile.items.birthday.month') || 'Month'} />
|
|
652
|
+
<SelectIcon />
|
|
653
|
+
</SelectTrigger>
|
|
654
|
+
<SelectContent
|
|
655
|
+
label={t('editProfile.items.birthday.month') || 'Month'}
|
|
656
|
+
items={MONTH_OPTIONS}
|
|
657
|
+
renderItem={(item) => (
|
|
658
|
+
<SelectItem value={item.value} label={item.label}>
|
|
659
|
+
<SelectItemIndicator />
|
|
660
|
+
<SelectItemText>{item.label}</SelectItemText>
|
|
661
|
+
</SelectItem>
|
|
662
|
+
)}
|
|
663
|
+
/>
|
|
664
|
+
</Select>
|
|
665
|
+
</View>
|
|
666
|
+
<View className="flex-1">
|
|
667
|
+
<Select value={dobDay} onValueChange={handleDobDayChange}>
|
|
668
|
+
<SelectTrigger label={t('editProfile.items.birthday.day') || 'Day'}>
|
|
669
|
+
<SelectValue placeholder={t('editProfile.items.birthday.day') || 'Day'} />
|
|
670
|
+
<SelectIcon />
|
|
671
|
+
</SelectTrigger>
|
|
672
|
+
<SelectContent
|
|
673
|
+
label={t('editProfile.items.birthday.day') || 'Day'}
|
|
674
|
+
items={dayOptions(dobYear, dobMonth)}
|
|
675
|
+
renderItem={(item) => (
|
|
676
|
+
<SelectItem value={item.value} label={item.label}>
|
|
677
|
+
<SelectItemIndicator />
|
|
678
|
+
<SelectItemText>{item.label}</SelectItemText>
|
|
679
|
+
</SelectItem>
|
|
680
|
+
)}
|
|
681
|
+
/>
|
|
682
|
+
</Select>
|
|
683
|
+
</View>
|
|
684
|
+
<View className="flex-1">
|
|
685
|
+
<Select value={dobYear} onValueChange={handleDobYearChange}>
|
|
686
|
+
<SelectTrigger label={t('editProfile.items.birthday.year') || 'Year'}>
|
|
687
|
+
<SelectValue placeholder={t('editProfile.items.birthday.year') || 'Year'} />
|
|
688
|
+
<SelectIcon />
|
|
689
|
+
</SelectTrigger>
|
|
690
|
+
<SelectContent
|
|
691
|
+
label={t('editProfile.items.birthday.year') || 'Year'}
|
|
692
|
+
items={birthYearOptions()}
|
|
693
|
+
renderItem={(item) => (
|
|
694
|
+
<SelectItem value={item.value} label={item.label}>
|
|
695
|
+
<SelectItemIndicator />
|
|
696
|
+
<SelectItemText>{item.label}</SelectItemText>
|
|
697
|
+
</SelectItem>
|
|
698
|
+
)}
|
|
699
|
+
/>
|
|
700
|
+
</Select>
|
|
701
|
+
</View>
|
|
702
|
+
</View>
|
|
703
|
+
);
|
|
704
|
+
|
|
514
705
|
// Render list content (locations or links)
|
|
515
706
|
const renderListContent = () => {
|
|
516
707
|
const addLabel = fieldType === 'locations'
|
|
@@ -590,7 +781,11 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
590
781
|
<View className="px-screen-margin pt-space-16 pb-space-32 gap-space-24">
|
|
591
782
|
{/* Form Content */}
|
|
592
783
|
<View className="gap-space-16 p-space-16 rounded-radius-20 bg-fill">
|
|
593
|
-
{fieldConfig.isList
|
|
784
|
+
{fieldConfig.isList
|
|
785
|
+
? renderListContent()
|
|
786
|
+
: fieldConfig.isDatePicker
|
|
787
|
+
? renderBirthdayPicker()
|
|
788
|
+
: fieldConfig.fields.map(renderField)}
|
|
594
789
|
</View>
|
|
595
790
|
</View>
|
|
596
791
|
);
|
|
@@ -28,31 +28,31 @@ const INFO_SECTIONS: InfoSection[] = [
|
|
|
28
28
|
id: 'what',
|
|
29
29
|
titleKey: 'learnMoreUsernames.sections.what.title',
|
|
30
30
|
contentKey: 'learnMoreUsernames.sections.what.content',
|
|
31
|
-
Icon: Icons.
|
|
31
|
+
Icon: Icons.RiAtLine,
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
id: 'rules',
|
|
35
35
|
titleKey: 'learnMoreUsernames.sections.rules.title',
|
|
36
36
|
contentKey: 'learnMoreUsernames.sections.rules.content',
|
|
37
|
-
Icon: Icons.
|
|
37
|
+
Icon: Icons.RiListUnordered,
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
id: 'unique',
|
|
41
41
|
titleKey: 'learnMoreUsernames.sections.unique.title',
|
|
42
42
|
contentKey: 'learnMoreUsernames.sections.unique.content',
|
|
43
|
-
Icon: Icons.
|
|
43
|
+
Icon: Icons.RiKey2Line,
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
id: 'change',
|
|
47
47
|
titleKey: 'learnMoreUsernames.sections.change.title',
|
|
48
48
|
contentKey: 'learnMoreUsernames.sections.change.content',
|
|
49
|
-
Icon: Icons.
|
|
49
|
+
Icon: Icons.RiResetRightLine,
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
id: 'tips',
|
|
53
53
|
titleKey: 'learnMoreUsernames.sections.tips.title',
|
|
54
54
|
contentKey: 'learnMoreUsernames.sections.tips.content',
|
|
55
|
-
Icon: Icons.
|
|
55
|
+
Icon: Icons.RiSparklingLine,
|
|
56
56
|
},
|
|
57
57
|
];
|
|
58
58
|
|
|
@@ -84,7 +84,7 @@ const LearnMoreUsernamesScreen: React.FC<BaseScreenProps> = ({
|
|
|
84
84
|
<>
|
|
85
85
|
<View className="px-screen-margin pb-space-32">
|
|
86
86
|
<View className="items-center py-space-24 gap-space-12">
|
|
87
|
-
<IconCircle icon={Icons.
|
|
87
|
+
<IconCircle icon={Icons.RiAtLine} />
|
|
88
88
|
<Text className="font-sans text-body text-text-secondary text-center">
|
|
89
89
|
{t('learnMoreUsernames.introText')}
|
|
90
90
|
</Text>
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
View,
|
|
5
5
|
StyleSheet,
|
|
6
6
|
ActivityIndicator,
|
|
7
|
+
Linking,
|
|
7
8
|
Platform,
|
|
8
9
|
} from 'react-native';
|
|
9
10
|
import Ionicons from '../icons/Ionicons';
|
|
@@ -13,6 +14,7 @@ import { useTheme } from '@oxy.so/bloom/theme';
|
|
|
13
14
|
import { Text } from '@oxy.so/bloom/typography';
|
|
14
15
|
import { SettingsListGroup, SettingsListItem } from '@oxy.so/bloom/settings-list';
|
|
15
16
|
import {
|
|
17
|
+
IDENTITY_WEB_ORIGIN,
|
|
16
18
|
getAccountDisplayName,
|
|
17
19
|
getAccountFallbackHandle,
|
|
18
20
|
getNormalizedUserHandle,
|
|
@@ -27,6 +29,7 @@ import { presentActionSheet } from '../components/surfaces/ActionSheetSurface';
|
|
|
27
29
|
import { useOxy } from '../context/OxyContext';
|
|
28
30
|
import { useI18n } from '../hooks/useI18n';
|
|
29
31
|
import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
|
|
32
|
+
import { isWebBrowser } from '../utils/isWebBrowser';
|
|
30
33
|
import { useCurrentUser } from '../hooks/queries/useAccountQueries';
|
|
31
34
|
import { useUserSubscription } from '../hooks/queries/usePaymentQueries';
|
|
32
35
|
import { useDeviceSessions } from '../hooks/queries/useServicesQueries';
|
|
@@ -326,6 +329,15 @@ const ManageAccountScreen: React.FC<BaseScreenProps> = ({
|
|
|
326
329
|
);
|
|
327
330
|
return;
|
|
328
331
|
}
|
|
332
|
+
// Web: deleting an account is signed with its identity key, which only
|
|
333
|
+
// the identity origin can unseal — the deletion happens there, never on
|
|
334
|
+
// this page.
|
|
335
|
+
if (isWebBrowser()) {
|
|
336
|
+
await Linking.openURL(`${IDENTITY_WEB_ORIGIN}/`).catch(() => {
|
|
337
|
+
toast.error(t('accountSwitcher.linkOpenFailed'));
|
|
338
|
+
});
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
329
341
|
const deleted = await presentDeleteAccount({
|
|
330
342
|
username: user.username || '',
|
|
331
343
|
onDelete: handleConfirmDelete,
|
|
@@ -10,7 +10,7 @@ import { Chip } from '@oxy.so/bloom/chip';
|
|
|
10
10
|
import { Badge } from '@oxy.so/bloom/badge';
|
|
11
11
|
import { Card, CardBody } from '@oxy.so/bloom/card';
|
|
12
12
|
import { H2, H4, H5, Text } from '@oxy.so/bloom/typography';
|
|
13
|
-
import { BenefitList, BenefitRow } from '
|
|
13
|
+
import { BenefitList, BenefitRow } from '../components/BenefitList';
|
|
14
14
|
import {
|
|
15
15
|
SegmentedControl,
|
|
16
16
|
SegmentedControlItem,
|
|
@@ -3,7 +3,7 @@ import { View } from 'react-native';
|
|
|
3
3
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
4
4
|
import { H4, Text } from '@oxy.so/bloom/typography';
|
|
5
5
|
import { IconCircle } from '@oxy.so/bloom/icon-circle';
|
|
6
|
-
import { BenefitList, BenefitRow } from '
|
|
6
|
+
import { BenefitList, BenefitRow } from '../../components/BenefitList';
|
|
7
7
|
import * as Icons from '@oxy.so/bloom/icons';
|
|
8
8
|
import type { BaseScreenProps } from '../../types/navigation';
|
|
9
9
|
import { useI18n } from '../../hooks/useI18n';
|
|
@@ -22,7 +22,7 @@ const TrustAboutScreen: React.FC<BaseScreenProps> = () => {
|
|
|
22
22
|
return (
|
|
23
23
|
<View className="px-screen-margin pt-space-16 pb-space-32">
|
|
24
24
|
<View className="items-center py-space-24 gap-space-12">
|
|
25
|
-
<IconCircle icon={Icons.
|
|
25
|
+
<IconCircle icon={Icons.RiShieldCheckLine} />
|
|
26
26
|
<Text className="font-sans text-body text-text-secondary text-center">
|
|
27
27
|
{t('trust.about.intro') || 'Oxy Trust is a recognition of your positive actions in the Oxy Ecosystem. Reputation cannot be sent or received directly, only earned by contributing to the community.'}
|
|
28
28
|
</Text>
|
|
@@ -36,23 +36,23 @@ const TrustAboutScreen: React.FC<BaseScreenProps> = () => {
|
|
|
36
36
|
accessibilityLabel={t('trust.about.how.title') || 'How to Earn Reputation'}
|
|
37
37
|
>
|
|
38
38
|
<BenefitRow
|
|
39
|
-
icon={<Icons.
|
|
39
|
+
icon={<Icons.RiAccountCircleLine size="sm" style={{ color: iconColor }} />}
|
|
40
40
|
label={t('trust.about.how.help') || 'Helping other users'}
|
|
41
41
|
/>
|
|
42
42
|
<BenefitRow
|
|
43
|
-
icon={<Icons.
|
|
43
|
+
icon={<Icons.RiFlagLine size="sm" style={{ color: iconColor }} />}
|
|
44
44
|
label={t('trust.about.how.report') || 'Reporting bugs'}
|
|
45
45
|
/>
|
|
46
46
|
<BenefitRow
|
|
47
|
-
icon={<Icons.
|
|
47
|
+
icon={<Icons.RiEditLine size="sm" style={{ color: iconColor }} />}
|
|
48
48
|
label={t('trust.about.how.contribute') || 'Contributing content'}
|
|
49
49
|
/>
|
|
50
50
|
<BenefitRow
|
|
51
|
-
icon={<Icons.
|
|
51
|
+
icon={<Icons.RiCalendarLine size="sm" style={{ color: iconColor }} />}
|
|
52
52
|
label={t('trust.about.how.participate') || 'Participating in events'}
|
|
53
53
|
/>
|
|
54
54
|
<BenefitRow
|
|
55
|
-
icon={<Icons.
|
|
55
|
+
icon={<Icons.RiSparklingLine size="sm" style={{ color: iconColor }} />}
|
|
56
56
|
label={t('trust.about.how.other') || 'Other positive actions'}
|
|
57
57
|
/>
|
|
58
58
|
</BenefitList>
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.useUsernameAvailability = useUsernameAvailability;
|
|
7
|
-
var _react = require("react");
|
|
8
|
-
var _toast = require("@oxy.so/bloom/toast");
|
|
9
|
-
/**
|
|
10
|
-
* Debounced username-availability check over the existing SDK method.
|
|
11
|
-
*
|
|
12
|
-
* One responsibility: turn keystrokes into an `idle | checking | available |
|
|
13
|
-
* taken` verdict without racing. A sequence guard drops superseded responses,
|
|
14
|
-
* and a NETWORK failure resets the (now stale) verdict and toasts — it never
|
|
15
|
-
* paints an inline error inside the dialog (owner mandate).
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/** Keystrokes settle for this long before a check is issued. */
|
|
19
|
-
const DEBOUNCE_MS = 400;
|
|
20
|
-
/** Shorter than this and there is nothing worth asking the server about. */
|
|
21
|
-
const MIN_USERNAME_LENGTH = 3;
|
|
22
|
-
function useUsernameAvailability(oxyServices, t) {
|
|
23
|
-
const [status, setStatus] = (0, _react.useState)('idle');
|
|
24
|
-
const timerRef = (0, _react.useRef)(null);
|
|
25
|
-
const requestSeqRef = (0, _react.useRef)(0);
|
|
26
|
-
const check = (0, _react.useCallback)(value => {
|
|
27
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
28
|
-
const seq = ++requestSeqRef.current;
|
|
29
|
-
if (!value || value.length < MIN_USERNAME_LENGTH) {
|
|
30
|
-
setStatus('idle');
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
setStatus('checking');
|
|
34
|
-
timerRef.current = setTimeout(() => {
|
|
35
|
-
void oxyServices.checkUsernameAvailability(value).then(result => {
|
|
36
|
-
if (seq !== requestSeqRef.current) return; // superseded by a newer check
|
|
37
|
-
setStatus(result.available ? 'available' : 'taken');
|
|
38
|
-
}).catch(() => {
|
|
39
|
-
if (seq !== requestSeqRef.current) return;
|
|
40
|
-
setStatus('idle');
|
|
41
|
-
_toast.toast.error(t('accounts.create.username.checkFailed'));
|
|
42
|
-
});
|
|
43
|
-
}, DEBOUNCE_MS);
|
|
44
|
-
}, [oxyServices, t]);
|
|
45
|
-
return {
|
|
46
|
-
status,
|
|
47
|
-
check
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=useUsernameAvailability.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_toast","DEBOUNCE_MS","MIN_USERNAME_LENGTH","useUsernameAvailability","oxyServices","t","status","setStatus","useState","timerRef","useRef","requestSeqRef","check","useCallback","value","current","clearTimeout","seq","length","setTimeout","checkUsernameAvailability","then","result","available","catch","toast","error"],"sourceRoot":"../../../../../src","sources":["ui/components/authChooser/useUsernameAvailability.ts"],"mappings":";;;;;;AASA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AAVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASA;AACA,MAAME,WAAW,GAAG,GAAG;AACvB;AACA,MAAMC,mBAAmB,GAAG,CAAC;AAEtB,SAASC,uBAAuBA,CACrCC,WAAwB,EACxBC,CAAY,EAIZ;EACA,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,GAAG,IAAAC,eAAQ,EAAiB,MAAM,CAAC;EAC5D,MAAMC,QAAQ,GAAG,IAAAC,aAAM,EAAuC,IAAI,CAAC;EACnE,MAAMC,aAAa,GAAG,IAAAD,aAAM,EAAC,CAAC,CAAC;EAE/B,MAAME,KAAK,GAAG,IAAAC,kBAAW,EACtBC,KAAa,IAAK;IACjB,IAAIL,QAAQ,CAACM,OAAO,EAAEC,YAAY,CAACP,QAAQ,CAACM,OAAO,CAAC;IACpD,MAAME,GAAG,GAAG,EAAEN,aAAa,CAACI,OAAO;IACnC,IAAI,CAACD,KAAK,IAAIA,KAAK,CAACI,MAAM,GAAGhB,mBAAmB,EAAE;MAChDK,SAAS,CAAC,MAAM,CAAC;MACjB;IACF;IACAA,SAAS,CAAC,UAAU,CAAC;IACrBE,QAAQ,CAACM,OAAO,GAAGI,UAAU,CAAC,MAAM;MAClC,KAAKf,WAAW,CACbgB,yBAAyB,CAACN,KAAK,CAAC,CAChCO,IAAI,CAAEC,MAAM,IAAK;QAChB,IAAIL,GAAG,KAAKN,aAAa,CAACI,OAAO,EAAE,OAAO,CAAC;QAC3CR,SAAS,CAACe,MAAM,CAACC,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;MACrD,CAAC,CAAC,CACDC,KAAK,CAAC,MAAM;QACX,IAAIP,GAAG,KAAKN,aAAa,CAACI,OAAO,EAAE;QACnCR,SAAS,CAAC,MAAM,CAAC;QACjBkB,YAAK,CAACC,KAAK,CAACrB,CAAC,CAAC,sCAAsC,CAAC,CAAC;MACxD,CAAC,CAAC;IACN,CAAC,EAAEJ,WAAW,CAAC;EACjB,CAAC,EACD,CAACG,WAAW,EAAEC,CAAC,CACjB,CAAC;EAED,OAAO;IAAEC,MAAM;IAAEM;EAAM,CAAC;AAC1B","ignoreList":[]}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Debounced username-availability check over the existing SDK method.
|
|
5
|
-
*
|
|
6
|
-
* One responsibility: turn keystrokes into an `idle | checking | available |
|
|
7
|
-
* taken` verdict without racing. A sequence guard drops superseded responses,
|
|
8
|
-
* and a NETWORK failure resets the (now stale) verdict and toasts — it never
|
|
9
|
-
* paints an inline error inside the dialog (owner mandate).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { useCallback, useRef, useState } from 'react';
|
|
13
|
-
import { toast } from '@oxy.so/bloom/toast';
|
|
14
|
-
/** Keystrokes settle for this long before a check is issued. */
|
|
15
|
-
const DEBOUNCE_MS = 400;
|
|
16
|
-
/** Shorter than this and there is nothing worth asking the server about. */
|
|
17
|
-
const MIN_USERNAME_LENGTH = 3;
|
|
18
|
-
export function useUsernameAvailability(oxyServices, t) {
|
|
19
|
-
const [status, setStatus] = useState('idle');
|
|
20
|
-
const timerRef = useRef(null);
|
|
21
|
-
const requestSeqRef = useRef(0);
|
|
22
|
-
const check = useCallback(value => {
|
|
23
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
24
|
-
const seq = ++requestSeqRef.current;
|
|
25
|
-
if (!value || value.length < MIN_USERNAME_LENGTH) {
|
|
26
|
-
setStatus('idle');
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
setStatus('checking');
|
|
30
|
-
timerRef.current = setTimeout(() => {
|
|
31
|
-
void oxyServices.checkUsernameAvailability(value).then(result => {
|
|
32
|
-
if (seq !== requestSeqRef.current) return; // superseded by a newer check
|
|
33
|
-
setStatus(result.available ? 'available' : 'taken');
|
|
34
|
-
}).catch(() => {
|
|
35
|
-
if (seq !== requestSeqRef.current) return;
|
|
36
|
-
setStatus('idle');
|
|
37
|
-
toast.error(t('accounts.create.username.checkFailed'));
|
|
38
|
-
});
|
|
39
|
-
}, DEBOUNCE_MS);
|
|
40
|
-
}, [oxyServices, t]);
|
|
41
|
-
return {
|
|
42
|
-
status,
|
|
43
|
-
check
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
//# sourceMappingURL=useUsernameAvailability.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["useCallback","useRef","useState","toast","DEBOUNCE_MS","MIN_USERNAME_LENGTH","useUsernameAvailability","oxyServices","t","status","setStatus","timerRef","requestSeqRef","check","value","current","clearTimeout","seq","length","setTimeout","checkUsernameAvailability","then","result","available","catch","error"],"sourceRoot":"../../../../../src","sources":["ui/components/authChooser/useUsernameAvailability.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,WAAW,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AACrD,SAASC,KAAK,QAAQ,qBAAqB;AAM3C;AACA,MAAMC,WAAW,GAAG,GAAG;AACvB;AACA,MAAMC,mBAAmB,GAAG,CAAC;AAE7B,OAAO,SAASC,uBAAuBA,CACrCC,WAAwB,EACxBC,CAAY,EAIZ;EACA,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,GAAGR,QAAQ,CAAiB,MAAM,CAAC;EAC5D,MAAMS,QAAQ,GAAGV,MAAM,CAAuC,IAAI,CAAC;EACnE,MAAMW,aAAa,GAAGX,MAAM,CAAC,CAAC,CAAC;EAE/B,MAAMY,KAAK,GAAGb,WAAW,CACtBc,KAAa,IAAK;IACjB,IAAIH,QAAQ,CAACI,OAAO,EAAEC,YAAY,CAACL,QAAQ,CAACI,OAAO,CAAC;IACpD,MAAME,GAAG,GAAG,EAAEL,aAAa,CAACG,OAAO;IACnC,IAAI,CAACD,KAAK,IAAIA,KAAK,CAACI,MAAM,GAAGb,mBAAmB,EAAE;MAChDK,SAAS,CAAC,MAAM,CAAC;MACjB;IACF;IACAA,SAAS,CAAC,UAAU,CAAC;IACrBC,QAAQ,CAACI,OAAO,GAAGI,UAAU,CAAC,MAAM;MAClC,KAAKZ,WAAW,CACba,yBAAyB,CAACN,KAAK,CAAC,CAChCO,IAAI,CAAEC,MAAM,IAAK;QAChB,IAAIL,GAAG,KAAKL,aAAa,CAACG,OAAO,EAAE,OAAO,CAAC;QAC3CL,SAAS,CAACY,MAAM,CAACC,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;MACrD,CAAC,CAAC,CACDC,KAAK,CAAC,MAAM;QACX,IAAIP,GAAG,KAAKL,aAAa,CAACG,OAAO,EAAE;QACnCL,SAAS,CAAC,MAAM,CAAC;QACjBP,KAAK,CAACsB,KAAK,CAACjB,CAAC,CAAC,sCAAsC,CAAC,CAAC;MACxD,CAAC,CAAC;IACN,CAAC,EAAEJ,WAAW,CAAC;EACjB,CAAC,EACD,CAACG,WAAW,EAAEC,CAAC,CACjB,CAAC;EAED,OAAO;IAAEC,MAAM;IAAEI;EAAM,CAAC;AAC1B","ignoreList":[]}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Debounced username-availability check over the existing SDK method.
|
|
3
|
-
*
|
|
4
|
-
* One responsibility: turn keystrokes into an `idle | checking | available |
|
|
5
|
-
* taken` verdict without racing. A sequence guard drops superseded responses,
|
|
6
|
-
* and a NETWORK failure resets the (now stale) verdict and toasts — it never
|
|
7
|
-
* paints an inline error inside the dialog (owner mandate).
|
|
8
|
-
*/
|
|
9
|
-
import type { OxyServices } from '@oxy.so/core';
|
|
10
|
-
import type { Translate } from './types';
|
|
11
|
-
export type UsernameStatus = 'idle' | 'checking' | 'available' | 'taken';
|
|
12
|
-
export declare function useUsernameAvailability(oxyServices: OxyServices, t: Translate): {
|
|
13
|
-
status: UsernameStatus;
|
|
14
|
-
check: (value: string) => void;
|
|
15
|
-
};
|
|
16
|
-
//# sourceMappingURL=useUsernameAvailability.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useUsernameAvailability.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/useUsernameAvailability.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;AAOzE,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,EACxB,CAAC,EAAE,SAAS,GACX;IACD,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC,CAgCA"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Debounced username-availability check over the existing SDK method.
|
|
3
|
-
*
|
|
4
|
-
* One responsibility: turn keystrokes into an `idle | checking | available |
|
|
5
|
-
* taken` verdict without racing. A sequence guard drops superseded responses,
|
|
6
|
-
* and a NETWORK failure resets the (now stale) verdict and toasts — it never
|
|
7
|
-
* paints an inline error inside the dialog (owner mandate).
|
|
8
|
-
*/
|
|
9
|
-
import type { OxyServices } from '@oxy.so/core';
|
|
10
|
-
import type { Translate } from './types.js';
|
|
11
|
-
export type UsernameStatus = 'idle' | 'checking' | 'available' | 'taken';
|
|
12
|
-
export declare function useUsernameAvailability(oxyServices: OxyServices, t: Translate): {
|
|
13
|
-
status: UsernameStatus;
|
|
14
|
-
check: (value: string) => void;
|
|
15
|
-
};
|
|
16
|
-
//# sourceMappingURL=useUsernameAvailability.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useUsernameAvailability.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/useUsernameAvailability.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAS,CAAC;AAEzC,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;AAOzE,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,EACxB,CAAC,EAAE,SAAS,GACX;IACD,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC,CAgCA"}
|