@entropy-softworks/ui 2026.7.31
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/CHANGELOG.md +96 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/lib/components/auth/LockOverlay.d.ts +22 -0
- package/lib/components/auth/LockOverlay.d.ts.map +1 -0
- package/lib/components/auth/LockOverlay.jsx +82 -0
- package/lib/components/auth/LockOverlay.jsx.map +1 -0
- package/lib/components/auth/PasswordStrengthMeter.d.ts +12 -0
- package/lib/components/auth/PasswordStrengthMeter.d.ts.map +1 -0
- package/lib/components/auth/PasswordStrengthMeter.jsx +81 -0
- package/lib/components/auth/PasswordStrengthMeter.jsx.map +1 -0
- package/lib/components/auth/ProtectedRoute.d.ts +24 -0
- package/lib/components/auth/ProtectedRoute.d.ts.map +1 -0
- package/lib/components/auth/ProtectedRoute.jsx +56 -0
- package/lib/components/auth/ProtectedRoute.jsx.map +1 -0
- package/lib/components/auth/PwnedPasswordWarning.d.ts +13 -0
- package/lib/components/auth/PwnedPasswordWarning.d.ts.map +1 -0
- package/lib/components/auth/PwnedPasswordWarning.jsx +29 -0
- package/lib/components/auth/PwnedPasswordWarning.jsx.map +1 -0
- package/lib/components/auth/index.d.ts +5 -0
- package/lib/components/auth/index.d.ts.map +1 -0
- package/lib/components/auth/index.js +5 -0
- package/lib/components/auth/index.js.map +1 -0
- package/lib/components/bottom-nav.d.ts +25 -0
- package/lib/components/bottom-nav.d.ts.map +1 -0
- package/lib/components/bottom-nav.jsx +51 -0
- package/lib/components/bottom-nav.jsx.map +1 -0
- package/lib/components/button.d.ts +16 -0
- package/lib/components/button.d.ts.map +1 -0
- package/lib/components/button.jsx +73 -0
- package/lib/components/button.jsx.map +1 -0
- package/lib/components/card.d.ts +22 -0
- package/lib/components/card.d.ts.map +1 -0
- package/lib/components/card.jsx +36 -0
- package/lib/components/card.jsx.map +1 -0
- package/lib/components/checkbox.d.ts +12 -0
- package/lib/components/checkbox.d.ts.map +1 -0
- package/lib/components/checkbox.jsx +16 -0
- package/lib/components/checkbox.jsx.map +1 -0
- package/lib/components/connection-settings.d.ts +41 -0
- package/lib/components/connection-settings.d.ts.map +1 -0
- package/lib/components/connection-settings.jsx +354 -0
- package/lib/components/connection-settings.jsx.map +1 -0
- package/lib/components/context-menu.d.ts +52 -0
- package/lib/components/context-menu.d.ts.map +1 -0
- package/lib/components/context-menu.jsx +24 -0
- package/lib/components/context-menu.jsx.map +1 -0
- package/lib/components/data-table.d.ts +59 -0
- package/lib/components/data-table.d.ts.map +1 -0
- package/lib/components/data-table.jsx +101 -0
- package/lib/components/data-table.jsx.map +1 -0
- package/lib/components/dialog.d.ts +58 -0
- package/lib/components/dialog.d.ts.map +1 -0
- package/lib/components/dialog.jsx +197 -0
- package/lib/components/dialog.jsx.map +1 -0
- package/lib/components/dropdown-menu.d.ts +55 -0
- package/lib/components/dropdown-menu.d.ts.map +1 -0
- package/lib/components/dropdown-menu.jsx +38 -0
- package/lib/components/dropdown-menu.jsx.map +1 -0
- package/lib/components/entropy-boot-splash.d.ts +24 -0
- package/lib/components/entropy-boot-splash.d.ts.map +1 -0
- package/lib/components/entropy-boot-splash.jsx +83 -0
- package/lib/components/entropy-boot-splash.jsx.map +1 -0
- package/lib/components/entropy-mark-animated.d.ts +20 -0
- package/lib/components/entropy-mark-animated.d.ts.map +1 -0
- package/lib/components/entropy-mark-animated.jsx +148 -0
- package/lib/components/entropy-mark-animated.jsx.map +1 -0
- package/lib/components/entropy-mark-geometry.d.ts +8 -0
- package/lib/components/entropy-mark-geometry.d.ts.map +1 -0
- package/lib/components/entropy-mark-geometry.js +10 -0
- package/lib/components/entropy-mark-geometry.js.map +1 -0
- package/lib/components/entropy-mark-sequential.d.ts +12 -0
- package/lib/components/entropy-mark-sequential.d.ts.map +1 -0
- package/lib/components/entropy-mark-sequential.jsx +53 -0
- package/lib/components/entropy-mark-sequential.jsx.map +1 -0
- package/lib/components/entropy-mark.d.ts +29 -0
- package/lib/components/entropy-mark.d.ts.map +1 -0
- package/lib/components/entropy-mark.jsx +66 -0
- package/lib/components/entropy-mark.jsx.map +1 -0
- package/lib/components/error-boundary.d.ts +31 -0
- package/lib/components/error-boundary.d.ts.map +1 -0
- package/lib/components/error-boundary.jsx +40 -0
- package/lib/components/error-boundary.jsx.map +1 -0
- package/lib/components/index.d.ts +41 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/index.js +43 -0
- package/lib/components/index.js.map +1 -0
- package/lib/components/input.d.ts +26 -0
- package/lib/components/input.d.ts.map +1 -0
- package/lib/components/input.jsx +95 -0
- package/lib/components/input.jsx.map +1 -0
- package/lib/components/item-icon.d.ts +32 -0
- package/lib/components/item-icon.d.ts.map +1 -0
- package/lib/components/item-icon.jsx +54 -0
- package/lib/components/item-icon.jsx.map +1 -0
- package/lib/components/label.d.ts +13 -0
- package/lib/components/label.d.ts.map +1 -0
- package/lib/components/label.jsx +9 -0
- package/lib/components/label.jsx.map +1 -0
- package/lib/components/legal-dialog.d.ts +18 -0
- package/lib/components/legal-dialog.d.ts.map +1 -0
- package/lib/components/legal-dialog.jsx +31 -0
- package/lib/components/legal-dialog.jsx.map +1 -0
- package/lib/components/license-gate.d.ts +17 -0
- package/lib/components/license-gate.d.ts.map +1 -0
- package/lib/components/license-gate.jsx +21 -0
- package/lib/components/license-gate.jsx.map +1 -0
- package/lib/components/logo.d.ts +15 -0
- package/lib/components/logo.d.ts.map +1 -0
- package/lib/components/logo.jsx +20 -0
- package/lib/components/logo.jsx.map +1 -0
- package/lib/components/onboarding-wizard.d.ts +89 -0
- package/lib/components/onboarding-wizard.d.ts.map +1 -0
- package/lib/components/onboarding-wizard.jsx +185 -0
- package/lib/components/onboarding-wizard.jsx.map +1 -0
- package/lib/components/pagination.d.ts +43 -0
- package/lib/components/pagination.d.ts.map +1 -0
- package/lib/components/pagination.jsx +72 -0
- package/lib/components/pagination.jsx.map +1 -0
- package/lib/components/password-generator.d.ts +15 -0
- package/lib/components/password-generator.d.ts.map +1 -0
- package/lib/components/password-generator.jsx +273 -0
- package/lib/components/password-generator.jsx.map +1 -0
- package/lib/components/pin-pad.d.ts +20 -0
- package/lib/components/pin-pad.d.ts.map +1 -0
- package/lib/components/pin-pad.jsx +93 -0
- package/lib/components/pin-pad.jsx.map +1 -0
- package/lib/components/popover.d.ts +27 -0
- package/lib/components/popover.d.ts.map +1 -0
- package/lib/components/popover.jsx +18 -0
- package/lib/components/popover.jsx.map +1 -0
- package/lib/components/profile/ProfileDialog.d.ts +25 -0
- package/lib/components/profile/ProfileDialog.d.ts.map +1 -0
- package/lib/components/profile/ProfileDialog.jsx +71 -0
- package/lib/components/profile/ProfileDialog.jsx.map +1 -0
- package/lib/components/profile/ProfileTab.d.ts +12 -0
- package/lib/components/profile/ProfileTab.d.ts.map +1 -0
- package/lib/components/profile/ProfileTab.jsx +112 -0
- package/lib/components/profile/ProfileTab.jsx.map +1 -0
- package/lib/components/profile/SecurityTab.d.ts +32 -0
- package/lib/components/profile/SecurityTab.d.ts.map +1 -0
- package/lib/components/profile/SecurityTab.jsx +145 -0
- package/lib/components/profile/SecurityTab.jsx.map +1 -0
- package/lib/components/profile/index.d.ts +4 -0
- package/lib/components/profile/index.d.ts.map +1 -0
- package/lib/components/profile/index.js +4 -0
- package/lib/components/profile/index.js.map +1 -0
- package/lib/components/segmented-toggle.d.ts +28 -0
- package/lib/components/segmented-toggle.d.ts.map +1 -0
- package/lib/components/segmented-toggle.jsx +64 -0
- package/lib/components/segmented-toggle.jsx.map +1 -0
- package/lib/components/separator.d.ts +13 -0
- package/lib/components/separator.d.ts.map +1 -0
- package/lib/components/separator.jsx +16 -0
- package/lib/components/separator.jsx.map +1 -0
- package/lib/components/server-settings.d.ts +27 -0
- package/lib/components/server-settings.d.ts.map +1 -0
- package/lib/components/server-settings.jsx +307 -0
- package/lib/components/server-settings.jsx.map +1 -0
- package/lib/components/settings/AccountSection.d.ts +26 -0
- package/lib/components/settings/AccountSection.d.ts.map +1 -0
- package/lib/components/settings/AccountSection.jsx +32 -0
- package/lib/components/settings/AccountSection.jsx.map +1 -0
- package/lib/components/settings/DangerZoneSection.d.ts +13 -0
- package/lib/components/settings/DangerZoneSection.d.ts.map +1 -0
- package/lib/components/settings/DangerZoneSection.jsx +16 -0
- package/lib/components/settings/DangerZoneSection.jsx.map +1 -0
- package/lib/components/settings/GeneralSection.d.ts +11 -0
- package/lib/components/settings/GeneralSection.d.ts.map +1 -0
- package/lib/components/settings/GeneralSection.jsx +12 -0
- package/lib/components/settings/GeneralSection.jsx.map +1 -0
- package/lib/components/settings/SupportSection.d.ts +11 -0
- package/lib/components/settings/SupportSection.d.ts.map +1 -0
- package/lib/components/settings/SupportSection.jsx +22 -0
- package/lib/components/settings/SupportSection.jsx.map +1 -0
- package/lib/components/settings/index.d.ts +5 -0
- package/lib/components/settings/index.d.ts.map +1 -0
- package/lib/components/settings/index.js +5 -0
- package/lib/components/settings/index.js.map +1 -0
- package/lib/components/settings-row.d.ts +28 -0
- package/lib/components/settings-row.d.ts.map +1 -0
- package/lib/components/settings-row.jsx +68 -0
- package/lib/components/settings-row.jsx.map +1 -0
- package/lib/components/skeleton.d.ts +8 -0
- package/lib/components/skeleton.d.ts.map +1 -0
- package/lib/components/skeleton.jsx +24 -0
- package/lib/components/skeleton.jsx.map +1 -0
- package/lib/components/social-connections.d.ts +15 -0
- package/lib/components/social-connections.d.ts.map +1 -0
- package/lib/components/social-connections.jsx +31 -0
- package/lib/components/social-connections.jsx.map +1 -0
- package/lib/components/stat-card.d.ts +29 -0
- package/lib/components/stat-card.d.ts.map +1 -0
- package/lib/components/stat-card.jsx +36 -0
- package/lib/components/stat-card.jsx.map +1 -0
- package/lib/components/tag-chip.d.ts +18 -0
- package/lib/components/tag-chip.d.ts.map +1 -0
- package/lib/components/tag-chip.jsx +54 -0
- package/lib/components/tag-chip.jsx.map +1 -0
- package/lib/components/tag-multi-select.d.ts +27 -0
- package/lib/components/tag-multi-select.d.ts.map +1 -0
- package/lib/components/tag-multi-select.jsx +65 -0
- package/lib/components/tag-multi-select.jsx.map +1 -0
- package/lib/components/text.d.ts +22 -0
- package/lib/components/text.d.ts.map +1 -0
- package/lib/components/text.jsx +43 -0
- package/lib/components/text.jsx.map +1 -0
- package/lib/components/toast/ToastPortal.d.ts +11 -0
- package/lib/components/toast/ToastPortal.d.ts.map +1 -0
- package/lib/components/toast/ToastPortal.jsx +15 -0
- package/lib/components/toast/ToastPortal.jsx.map +1 -0
- package/lib/components/toast/ToastPortal.web.d.ts +14 -0
- package/lib/components/toast/ToastPortal.web.d.ts.map +1 -0
- package/lib/components/toast/ToastPortal.web.jsx +39 -0
- package/lib/components/toast/ToastPortal.web.jsx.map +1 -0
- package/lib/components/toast/index.d.ts +3 -0
- package/lib/components/toast/index.d.ts.map +1 -0
- package/lib/components/toast/index.js +3 -0
- package/lib/components/toast/index.js.map +1 -0
- package/lib/components/toast/toast-config.d.ts +14 -0
- package/lib/components/toast/toast-config.d.ts.map +1 -0
- package/lib/components/toast/toast-config.jsx +24 -0
- package/lib/components/toast/toast-config.jsx.map +1 -0
- package/lib/components/tooltip.d.ts +30 -0
- package/lib/components/tooltip.d.ts.map +1 -0
- package/lib/components/tooltip.jsx +14 -0
- package/lib/components/tooltip.jsx.map +1 -0
- package/lib/components/totp-display.d.ts +23 -0
- package/lib/components/totp-display.d.ts.map +1 -0
- package/lib/components/totp-display.jsx +132 -0
- package/lib/components/totp-display.jsx.map +1 -0
- package/lib/components/user-avatar.d.ts +18 -0
- package/lib/components/user-avatar.d.ts.map +1 -0
- package/lib/components/user-avatar.jsx +25 -0
- package/lib/components/user-avatar.jsx.map +1 -0
- package/lib/context/AppConfigContext.d.ts +56 -0
- package/lib/context/AppConfigContext.d.ts.map +1 -0
- package/lib/context/AppConfigContext.jsx +14 -0
- package/lib/context/AppConfigContext.jsx.map +1 -0
- package/lib/context/NavigationContext.d.ts +42 -0
- package/lib/context/NavigationContext.d.ts.map +1 -0
- package/lib/context/NavigationContext.jsx +53 -0
- package/lib/context/NavigationContext.jsx.map +1 -0
- package/lib/context/NotificationContext.d.ts +32 -0
- package/lib/context/NotificationContext.d.ts.map +1 -0
- package/lib/context/NotificationContext.jsx +49 -0
- package/lib/context/NotificationContext.jsx.map +1 -0
- package/lib/context/ThemeContext.d.ts +12 -0
- package/lib/context/ThemeContext.d.ts.map +1 -0
- package/lib/context/ThemeContext.jsx +66 -0
- package/lib/context/ThemeContext.jsx.map +1 -0
- package/lib/context/index.d.ts +5 -0
- package/lib/context/index.d.ts.map +1 -0
- package/lib/context/index.js +5 -0
- package/lib/context/index.js.map +1 -0
- package/lib/flags.d.ts +9 -0
- package/lib/flags.d.ts.map +1 -0
- package/lib/flags.js +37 -0
- package/lib/flags.js.map +1 -0
- package/lib/forms/form-error.d.ts +12 -0
- package/lib/forms/form-error.d.ts.map +1 -0
- package/lib/forms/form-error.jsx +14 -0
- package/lib/forms/form-error.jsx.map +1 -0
- package/lib/forms/index.d.ts +3 -0
- package/lib/forms/index.d.ts.map +1 -0
- package/lib/forms/index.js +3 -0
- package/lib/forms/index.js.map +1 -0
- package/lib/forms/shake-field.d.ts +18 -0
- package/lib/forms/shake-field.d.ts.map +1 -0
- package/lib/forms/shake-field.jsx +30 -0
- package/lib/forms/shake-field.jsx.map +1 -0
- package/lib/hooks/index.d.ts +6 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +6 -0
- package/lib/hooks/index.js.map +1 -0
- package/lib/hooks/useAuth.d.ts +109 -0
- package/lib/hooks/useAuth.d.ts.map +1 -0
- package/lib/hooks/useAuth.jsx +404 -0
- package/lib/hooks/useAuth.jsx.map +1 -0
- package/lib/hooks/useAutoLock.d.ts +22 -0
- package/lib/hooks/useAutoLock.d.ts.map +1 -0
- package/lib/hooks/useAutoLock.js +85 -0
- package/lib/hooks/useAutoLock.js.map +1 -0
- package/lib/hooks/useDebouncedValue.d.ts +19 -0
- package/lib/hooks/useDebouncedValue.d.ts.map +1 -0
- package/lib/hooks/useDebouncedValue.js +29 -0
- package/lib/hooks/useDebouncedValue.js.map +1 -0
- package/lib/hooks/useShakeAnimation.d.ts +17 -0
- package/lib/hooks/useShakeAnimation.d.ts.map +1 -0
- package/lib/hooks/useShakeAnimation.js +22 -0
- package/lib/hooks/useShakeAnimation.js.map +1 -0
- package/lib/hooks/useUploadBlobUrl.d.ts +13 -0
- package/lib/hooks/useUploadBlobUrl.d.ts.map +1 -0
- package/lib/hooks/useUploadBlobUrl.js +60 -0
- package/lib/hooks/useUploadBlobUrl.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +47 -0
- package/lib/index.js.map +1 -0
- package/lib/screens/auth/AccountPickerScreen.d.ts +24 -0
- package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -0
- package/lib/screens/auth/AccountPickerScreen.jsx +47 -0
- package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -0
- package/lib/screens/auth/AuthScreen.d.ts +59 -0
- package/lib/screens/auth/AuthScreen.d.ts.map +1 -0
- package/lib/screens/auth/AuthScreen.jsx +594 -0
- package/lib/screens/auth/AuthScreen.jsx.map +1 -0
- package/lib/screens/auth/ConsentScreen.d.ts +27 -0
- package/lib/screens/auth/ConsentScreen.d.ts.map +1 -0
- package/lib/screens/auth/ConsentScreen.jsx +55 -0
- package/lib/screens/auth/ConsentScreen.jsx.map +1 -0
- package/lib/screens/auth/ErrorScreen.d.ts +20 -0
- package/lib/screens/auth/ErrorScreen.d.ts.map +1 -0
- package/lib/screens/auth/ErrorScreen.jsx +43 -0
- package/lib/screens/auth/ErrorScreen.jsx.map +1 -0
- package/lib/screens/auth/ExpiredLinkScreen.d.ts +19 -0
- package/lib/screens/auth/ExpiredLinkScreen.d.ts.map +1 -0
- package/lib/screens/auth/ExpiredLinkScreen.jsx +38 -0
- package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -0
- package/lib/screens/auth/ForgotPasswordScreen.d.ts +4 -0
- package/lib/screens/auth/ForgotPasswordScreen.d.ts.map +1 -0
- package/lib/screens/auth/ForgotPasswordScreen.jsx +123 -0
- package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -0
- package/lib/screens/auth/ImportDataScreen.d.ts +17 -0
- package/lib/screens/auth/ImportDataScreen.d.ts.map +1 -0
- package/lib/screens/auth/ImportDataScreen.jsx +73 -0
- package/lib/screens/auth/ImportDataScreen.jsx.map +1 -0
- package/lib/screens/auth/LoginScreen.d.ts +38 -0
- package/lib/screens/auth/LoginScreen.d.ts.map +1 -0
- package/lib/screens/auth/LoginScreen.jsx +326 -0
- package/lib/screens/auth/LoginScreen.jsx.map +1 -0
- package/lib/screens/auth/MfaChallengeScreen.d.ts +4 -0
- package/lib/screens/auth/MfaChallengeScreen.d.ts.map +1 -0
- package/lib/screens/auth/MfaChallengeScreen.jsx +139 -0
- package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -0
- package/lib/screens/auth/MfaEnrollScreen.d.ts +12 -0
- package/lib/screens/auth/MfaEnrollScreen.d.ts.map +1 -0
- package/lib/screens/auth/MfaEnrollScreen.jsx +267 -0
- package/lib/screens/auth/MfaEnrollScreen.jsx.map +1 -0
- package/lib/screens/auth/PasskeyEnrollScreen.d.ts +18 -0
- package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -0
- package/lib/screens/auth/PasskeyEnrollScreen.jsx +51 -0
- package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -0
- package/lib/screens/auth/PrivacyPolicyScreen.d.ts +19 -0
- package/lib/screens/auth/PrivacyPolicyScreen.d.ts.map +1 -0
- package/lib/screens/auth/PrivacyPolicyScreen.jsx +143 -0
- package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -0
- package/lib/screens/auth/RegisterScreen.d.ts +14 -0
- package/lib/screens/auth/RegisterScreen.d.ts.map +1 -0
- package/lib/screens/auth/RegisterScreen.jsx +296 -0
- package/lib/screens/auth/RegisterScreen.jsx.map +1 -0
- package/lib/screens/auth/ResetPasswordScreen.d.ts +12 -0
- package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -0
- package/lib/screens/auth/ResetPasswordScreen.jsx +276 -0
- package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -0
- package/lib/screens/auth/ServerConfigScreen.d.ts +18 -0
- package/lib/screens/auth/ServerConfigScreen.d.ts.map +1 -0
- package/lib/screens/auth/ServerConfigScreen.jsx +259 -0
- package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -0
- package/lib/screens/auth/TermsOfServiceScreen.d.ts +30 -0
- package/lib/screens/auth/TermsOfServiceScreen.d.ts.map +1 -0
- package/lib/screens/auth/TermsOfServiceScreen.jsx +130 -0
- package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -0
- package/lib/screens/auth/VerifyEmailScreen.d.ts +4 -0
- package/lib/screens/auth/VerifyEmailScreen.d.ts.map +1 -0
- package/lib/screens/auth/VerifyEmailScreen.jsx +249 -0
- package/lib/screens/auth/VerifyEmailScreen.jsx.map +1 -0
- package/lib/screens/auth/WelcomeScreen.d.ts +17 -0
- package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -0
- package/lib/screens/auth/WelcomeScreen.jsx +58 -0
- package/lib/screens/auth/WelcomeScreen.jsx.map +1 -0
- package/lib/screens/auth/index.d.ts +19 -0
- package/lib/screens/auth/index.d.ts.map +1 -0
- package/lib/screens/auth/index.js +19 -0
- package/lib/screens/auth/index.js.map +1 -0
- package/lib/screens/index.d.ts +2 -0
- package/lib/screens/index.d.ts.map +1 -0
- package/lib/screens/index.js +2 -0
- package/lib/screens/index.js.map +1 -0
- package/lib/services/api-config.d.ts +87 -0
- package/lib/services/api-config.d.ts.map +1 -0
- package/lib/services/api-config.js +202 -0
- package/lib/services/api-config.js.map +1 -0
- package/lib/services/auth.service.d.ts +140 -0
- package/lib/services/auth.service.d.ts.map +1 -0
- package/lib/services/auth.service.js +376 -0
- package/lib/services/auth.service.js.map +1 -0
- package/lib/services/favicon.d.ts +46 -0
- package/lib/services/favicon.d.ts.map +1 -0
- package/lib/services/favicon.js +84 -0
- package/lib/services/favicon.js.map +1 -0
- package/lib/services/hibp-cache.d.ts +23 -0
- package/lib/services/hibp-cache.d.ts.map +1 -0
- package/lib/services/hibp-cache.js +56 -0
- package/lib/services/hibp-cache.js.map +1 -0
- package/lib/services/hibp.d.ts +28 -0
- package/lib/services/hibp.d.ts.map +1 -0
- package/lib/services/hibp.js +92 -0
- package/lib/services/hibp.js.map +1 -0
- package/lib/services/index.d.ts +8 -0
- package/lib/services/index.d.ts.map +1 -0
- package/lib/services/index.js +7 -0
- package/lib/services/index.js.map +1 -0
- package/lib/services/passkey.d.ts +10 -0
- package/lib/services/passkey.d.ts.map +1 -0
- package/lib/services/passkey.js +192 -0
- package/lib/services/passkey.js.map +1 -0
- package/lib/utils/api-fetch.d.ts +11 -0
- package/lib/utils/api-fetch.d.ts.map +1 -0
- package/lib/utils/api-fetch.js +52 -0
- package/lib/utils/api-fetch.js.map +1 -0
- package/lib/utils/clipboard.d.ts +31 -0
- package/lib/utils/clipboard.d.ts.map +1 -0
- package/lib/utils/clipboard.js +66 -0
- package/lib/utils/clipboard.js.map +1 -0
- package/lib/utils/cn.d.ts +9 -0
- package/lib/utils/cn.d.ts.map +1 -0
- package/lib/utils/cn.js +12 -0
- package/lib/utils/cn.js.map +1 -0
- package/lib/utils/debounce.d.ts +13 -0
- package/lib/utils/debounce.d.ts.map +1 -0
- package/lib/utils/debounce.js +28 -0
- package/lib/utils/debounce.js.map +1 -0
- package/lib/utils/error.d.ts +9 -0
- package/lib/utils/error.d.ts.map +1 -0
- package/lib/utils/error.js +11 -0
- package/lib/utils/error.js.map +1 -0
- package/lib/utils/fetch.d.ts +17 -0
- package/lib/utils/fetch.d.ts.map +1 -0
- package/lib/utils/fetch.js +29 -0
- package/lib/utils/fetch.js.map +1 -0
- package/lib/utils/http-error.d.ts +41 -0
- package/lib/utils/http-error.d.ts.map +1 -0
- package/lib/utils/http-error.js +63 -0
- package/lib/utils/http-error.js.map +1 -0
- package/lib/utils/index.d.ts +11 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +11 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/palette.d.ts +46 -0
- package/lib/utils/palette.d.ts.map +1 -0
- package/lib/utils/palette.js +75 -0
- package/lib/utils/palette.js.map +1 -0
- package/lib/utils/password.d.ts +20 -0
- package/lib/utils/password.d.ts.map +1 -0
- package/lib/utils/password.js +51 -0
- package/lib/utils/password.js.map +1 -0
- package/lib/utils/settings-storage.d.ts +3 -0
- package/lib/utils/settings-storage.d.ts.map +1 -0
- package/lib/utils/settings-storage.js +62 -0
- package/lib/utils/settings-storage.js.map +1 -0
- package/lib/utils/validation.d.ts +29 -0
- package/lib/utils/validation.d.ts.map +1 -0
- package/lib/utils/validation.js +58 -0
- package/lib/utils/validation.js.map +1 -0
- package/package.json +204 -0
- package/src/components/__tests__/data-table.test.ts +94 -0
- package/src/components/__tests__/pagination.test.ts +73 -0
- package/src/components/__tests__/stat-card.test.ts +21 -0
- package/src/components/auth/LockOverlay.tsx +122 -0
- package/src/components/auth/PasswordStrengthMeter.tsx +96 -0
- package/src/components/auth/ProtectedRoute.tsx +91 -0
- package/src/components/auth/PwnedPasswordWarning.tsx +54 -0
- package/src/components/auth/index.ts +10 -0
- package/src/components/bottom-nav.tsx +99 -0
- package/src/components/button.tsx +144 -0
- package/src/components/card.tsx +82 -0
- package/src/components/checkbox.tsx +48 -0
- package/src/components/connection-settings.tsx +583 -0
- package/src/components/context-menu.tsx +95 -0
- package/src/components/data-table.tsx +223 -0
- package/src/components/dialog.tsx +376 -0
- package/src/components/dropdown-menu.tsx +120 -0
- package/src/components/entropy-boot-splash.tsx +114 -0
- package/src/components/entropy-mark-animated.tsx +260 -0
- package/src/components/entropy-mark-geometry.ts +11 -0
- package/src/components/entropy-mark-sequential.tsx +132 -0
- package/src/components/entropy-mark.tsx +135 -0
- package/src/components/error-boundary.tsx +70 -0
- package/src/components/index.ts +156 -0
- package/src/components/input.tsx +246 -0
- package/src/components/item-icon.tsx +111 -0
- package/src/components/label.tsx +25 -0
- package/src/components/legal-dialog.tsx +47 -0
- package/src/components/license-gate.tsx +28 -0
- package/src/components/logo.tsx +34 -0
- package/src/components/onboarding-wizard.tsx +353 -0
- package/src/components/pagination.tsx +154 -0
- package/src/components/password-generator.tsx +393 -0
- package/src/components/pin-pad.tsx +156 -0
- package/src/components/popover.tsx +38 -0
- package/src/components/profile/ProfileDialog.tsx +129 -0
- package/src/components/profile/ProfileTab.tsx +151 -0
- package/src/components/profile/SecurityTab.tsx +229 -0
- package/src/components/profile/index.ts +3 -0
- package/src/components/segmented-toggle.tsx +119 -0
- package/src/components/separator.tsx +41 -0
- package/src/components/server-settings.tsx +442 -0
- package/src/components/settings/AccountSection.tsx +76 -0
- package/src/components/settings/DangerZoneSection.tsx +37 -0
- package/src/components/settings/GeneralSection.tsx +39 -0
- package/src/components/settings/SupportSection.tsx +53 -0
- package/src/components/settings/index.ts +4 -0
- package/src/components/settings-row.tsx +126 -0
- package/src/components/skeleton.tsx +48 -0
- package/src/components/social-connections.tsx +55 -0
- package/src/components/stat-card.tsx +64 -0
- package/src/components/tag-chip.tsx +78 -0
- package/src/components/tag-multi-select.tsx +136 -0
- package/src/components/text.tsx +77 -0
- package/src/components/toast/ToastPortal.tsx +16 -0
- package/src/components/toast/ToastPortal.web.tsx +47 -0
- package/src/components/toast/index.ts +2 -0
- package/src/components/toast/toast-config.tsx +31 -0
- package/src/components/tooltip.tsx +30 -0
- package/src/components/totp-display.tsx +207 -0
- package/src/components/user-avatar.tsx +46 -0
- package/src/context/AppConfigContext.tsx +76 -0
- package/src/context/NavigationContext.tsx +75 -0
- package/src/context/NotificationContext.tsx +69 -0
- package/src/context/ThemeContext.tsx +83 -0
- package/src/context/__tests__/NavigationContext.test.tsx +53 -0
- package/src/context/index.ts +24 -0
- package/src/flags.ts +44 -0
- package/src/forms/form-error.tsx +20 -0
- package/src/forms/index.ts +2 -0
- package/src/forms/shake-field.tsx +52 -0
- package/src/hooks/__tests__/useUploadBlobUrl.test.tsx +77 -0
- package/src/hooks/index.ts +18 -0
- package/src/hooks/useAuth.tsx +567 -0
- package/src/hooks/useAutoLock.ts +110 -0
- package/src/hooks/useDebouncedValue.ts +32 -0
- package/src/hooks/useShakeAnimation.ts +42 -0
- package/src/hooks/useUploadBlobUrl.ts +75 -0
- package/src/index.ts +48 -0
- package/src/screens/auth/AccountPickerScreen.tsx +103 -0
- package/src/screens/auth/AuthScreen.tsx +941 -0
- package/src/screens/auth/ConsentScreen.tsx +118 -0
- package/src/screens/auth/ErrorScreen.tsx +96 -0
- package/src/screens/auth/ExpiredLinkScreen.tsx +87 -0
- package/src/screens/auth/ForgotPasswordScreen.tsx +182 -0
- package/src/screens/auth/ImportDataScreen.tsx +112 -0
- package/src/screens/auth/LoginScreen.tsx +484 -0
- package/src/screens/auth/MfaChallengeScreen.tsx +216 -0
- package/src/screens/auth/MfaEnrollScreen.tsx +388 -0
- package/src/screens/auth/PasskeyEnrollScreen.tsx +96 -0
- package/src/screens/auth/PrivacyPolicyScreen.tsx +249 -0
- package/src/screens/auth/RegisterScreen.tsx +430 -0
- package/src/screens/auth/ResetPasswordScreen.tsx +377 -0
- package/src/screens/auth/ServerConfigScreen.tsx +388 -0
- package/src/screens/auth/TermsOfServiceScreen.tsx +236 -0
- package/src/screens/auth/VerifyEmailScreen.tsx +310 -0
- package/src/screens/auth/WelcomeScreen.tsx +78 -0
- package/src/screens/auth/index.ts +36 -0
- package/src/screens/index.ts +1 -0
- package/src/services/__tests__/api-config.test.ts +67 -0
- package/src/services/__tests__/favicon.test.ts +88 -0
- package/src/services/__tests__/hibp-cache.test.ts +37 -0
- package/src/services/__tests__/hibp.test.ts +39 -0
- package/src/services/api-config.ts +260 -0
- package/src/services/auth.service.ts +498 -0
- package/src/services/favicon.ts +102 -0
- package/src/services/hibp-cache.ts +71 -0
- package/src/services/hibp.ts +120 -0
- package/src/services/index.ts +52 -0
- package/src/services/passkey.ts +213 -0
- package/src/types/external.d.ts +24 -0
- package/src/utils/__tests__/clipboard.test.ts +59 -0
- package/src/utils/__tests__/cn.test.ts +24 -0
- package/src/utils/__tests__/debounce.test.ts +42 -0
- package/src/utils/__tests__/error.test.ts +18 -0
- package/src/utils/__tests__/http-error.test.ts +33 -0
- package/src/utils/__tests__/palette.test.ts +22 -0
- package/src/utils/__tests__/password.test.ts +55 -0
- package/src/utils/__tests__/settings-storage.test.ts +15 -0
- package/src/utils/__tests__/validation.test.ts +52 -0
- package/src/utils/api-fetch.ts +61 -0
- package/src/utils/clipboard.ts +69 -0
- package/src/utils/cn.ts +12 -0
- package/src/utils/debounce.ts +33 -0
- package/src/utils/error.ts +10 -0
- package/src/utils/fetch.ts +33 -0
- package/src/utils/http-error.ts +81 -0
- package/src/utils/index.ts +23 -0
- package/src/utils/palette.ts +105 -0
- package/src/utils/password.ts +64 -0
- package/src/utils/settings-storage.ts +68 -0
- package/src/utils/validation.ts +71 -0
- package/styles/globals.css +262 -0
- package/tailwind-preset.js +163 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import * as TooltipPrimitive from "@rn-primitives/tooltip";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
import { Text } from "./text";
|
|
5
|
+
|
|
6
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
7
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
8
|
+
|
|
9
|
+
const TooltipContent = React.forwardRef<
|
|
10
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
11
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & { className?: string }
|
|
12
|
+
>(({ className, sideOffset = 6, children, ...props }, ref) => (
|
|
13
|
+
<TooltipPrimitive.Portal>
|
|
14
|
+
<TooltipPrimitive.Content
|
|
15
|
+
ref={ref}
|
|
16
|
+
sideOffset={sideOffset}
|
|
17
|
+
className={cn("z-50 rounded-md px-3 py-1.5", "bg-foreground dark:bg-white", className)}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
{typeof children === "string" ? (
|
|
21
|
+
<Text className="text-xs text-background dark:text-black">{children}</Text>
|
|
22
|
+
) : (
|
|
23
|
+
children
|
|
24
|
+
)}
|
|
25
|
+
</TooltipPrimitive.Content>
|
|
26
|
+
</TooltipPrimitive.Portal>
|
|
27
|
+
));
|
|
28
|
+
TooltipContent.displayName = "TooltipContent";
|
|
29
|
+
|
|
30
|
+
export { Tooltip, TooltipTrigger, TooltipContent };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Pressable, View } from "react-native";
|
|
3
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
import { useColorScheme } from "nativewind";
|
|
5
|
+
import { MutedText, Text } from "./text";
|
|
6
|
+
import { copyToClipboard } from "../utils/clipboard";
|
|
7
|
+
|
|
8
|
+
export interface TotpDisplayProps {
|
|
9
|
+
/** Base32-encoded TOTP secret. Empty string disables the display. */
|
|
10
|
+
secret: string;
|
|
11
|
+
/**
|
|
12
|
+
* Pure function that computes the current TOTP code from `secret`.
|
|
13
|
+
* Apps inject their own implementation so this widget stays decoupled
|
|
14
|
+
* from the crypto module — e.g. vault wires
|
|
15
|
+
* `@vault/shared/crypto/totp.computeTotpCode`.
|
|
16
|
+
*/
|
|
17
|
+
computeCode: (secret: string) => string;
|
|
18
|
+
/** TOTP step period in seconds. Defaults to 30. */
|
|
19
|
+
period?: number;
|
|
20
|
+
/** Optional callback fired after a successful clipboard write. */
|
|
21
|
+
onCopy?: (code: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PLACEHOLDER = "------";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* TOTP code display with a circular countdown ring. Recomputes the code
|
|
28
|
+
* on each 30s window boundary; ticks the remaining-seconds value once
|
|
29
|
+
* per second to drive the ring animation.
|
|
30
|
+
*/
|
|
31
|
+
export function TotpDisplay({
|
|
32
|
+
secret,
|
|
33
|
+
computeCode,
|
|
34
|
+
period = 30,
|
|
35
|
+
onCopy,
|
|
36
|
+
}: TotpDisplayProps): React.JSX.Element | null {
|
|
37
|
+
const { colorScheme } = useColorScheme();
|
|
38
|
+
const isDark = colorScheme === "dark";
|
|
39
|
+
|
|
40
|
+
const safeCompute = (s: string): string => {
|
|
41
|
+
if (!s) {
|
|
42
|
+
return PLACEHOLDER;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
return computeCode(s);
|
|
46
|
+
} catch {
|
|
47
|
+
return PLACEHOLDER;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const [code, setCode] = useState<string>(() => safeCompute(secret));
|
|
52
|
+
const [remaining, setRemaining] = useState<number>(() => {
|
|
53
|
+
const now = Math.floor(Date.now() / 1000);
|
|
54
|
+
return period - (now % period);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!secret) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let lastWindow = -1;
|
|
62
|
+
const tick = () => {
|
|
63
|
+
const now = Math.floor(Date.now() / 1000);
|
|
64
|
+
const window = Math.floor(now / period);
|
|
65
|
+
setRemaining(period - (now % period));
|
|
66
|
+
if (window !== lastWindow) {
|
|
67
|
+
lastWindow = window;
|
|
68
|
+
setCode(safeCompute(secret));
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
tick();
|
|
72
|
+
const id = setInterval(tick, 1000);
|
|
73
|
+
return () => clearInterval(id);
|
|
74
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
75
|
+
}, [secret, period]);
|
|
76
|
+
|
|
77
|
+
if (!secret) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const copy = async (): Promise<void> => {
|
|
82
|
+
if (!code || code === PLACEHOLDER) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Route through the shared helper so the 30s auto-clear applies.
|
|
86
|
+
// TOTP codes rotate every 30s anyway, but the clipboard residue
|
|
87
|
+
// still leaks the code to whatever the user pastes next (or to
|
|
88
|
+
// a shoulder-surfer hitting paste).
|
|
89
|
+
const ok = await copyToClipboard(code);
|
|
90
|
+
if (ok) {
|
|
91
|
+
onCopy?.(code);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const urgent = remaining <= 5;
|
|
96
|
+
const foreground = isDark ? "rgb(255, 255, 255)" : "rgb(0, 0, 0)";
|
|
97
|
+
const destructive = "rgb(239, 68, 68)";
|
|
98
|
+
const border = isDark ? "rgb(38, 38, 38)" : "rgb(229, 229, 229)";
|
|
99
|
+
const card = isDark ? "rgb(0, 0, 0)" : "rgb(255, 255, 255)";
|
|
100
|
+
const secondary = isDark ? "rgb(38, 38, 38)" : "rgb(245, 245, 245)";
|
|
101
|
+
const accent = urgent ? destructive : foreground;
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Pressable
|
|
105
|
+
onPress={copy}
|
|
106
|
+
accessibilityRole="button"
|
|
107
|
+
accessibilityLabel="Copy TOTP code"
|
|
108
|
+
accessibilityHint="Copies the current one-time code to the clipboard"
|
|
109
|
+
style={({ pressed }) => ({
|
|
110
|
+
flexDirection: "row",
|
|
111
|
+
alignItems: "center",
|
|
112
|
+
gap: 12,
|
|
113
|
+
padding: 12,
|
|
114
|
+
borderRadius: 8,
|
|
115
|
+
borderWidth: 1,
|
|
116
|
+
borderColor: border,
|
|
117
|
+
backgroundColor: pressed ? secondary : card,
|
|
118
|
+
})}
|
|
119
|
+
>
|
|
120
|
+
<CountdownRing remaining={remaining} total={period} color={accent} backgroundColor={border} />
|
|
121
|
+
<View style={{ flex: 1 }}>
|
|
122
|
+
<MutedText className="text-xs" style={{ marginBottom: 2 }}>
|
|
123
|
+
TOTP code
|
|
124
|
+
</MutedText>
|
|
125
|
+
<Text
|
|
126
|
+
style={{
|
|
127
|
+
color: accent,
|
|
128
|
+
fontSize: 22,
|
|
129
|
+
fontWeight: "600",
|
|
130
|
+
letterSpacing: 4,
|
|
131
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
{`${code.slice(0, 3)} ${code.slice(3)}`}
|
|
135
|
+
</Text>
|
|
136
|
+
</View>
|
|
137
|
+
<Ionicons
|
|
138
|
+
name="copy-outline"
|
|
139
|
+
size={16}
|
|
140
|
+
color={isDark ? "rgb(163, 163, 163)" : "rgb(115, 115, 115)"}
|
|
141
|
+
/>
|
|
142
|
+
</Pressable>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface RingProps {
|
|
147
|
+
remaining: number;
|
|
148
|
+
total: number;
|
|
149
|
+
color: string;
|
|
150
|
+
backgroundColor: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function CountdownRing({ remaining, total, color, backgroundColor }: RingProps): React.JSX.Element {
|
|
154
|
+
const size = 36;
|
|
155
|
+
const stroke = 3;
|
|
156
|
+
const radius = (size - stroke) / 2;
|
|
157
|
+
const circumference = 2 * Math.PI * radius;
|
|
158
|
+
const fraction = Math.max(0, Math.min(1, remaining / total));
|
|
159
|
+
const dashOffset = circumference * (1 - fraction);
|
|
160
|
+
|
|
161
|
+
// Cast raw SVG tags through React.createElement so RN-Web renders them
|
|
162
|
+
// as real <svg> / <circle> elements without depending on
|
|
163
|
+
// `react-native-svg`. Native targets fall back to the View at the
|
|
164
|
+
// outer level — apps that want native parity should overlay this with
|
|
165
|
+
// an SVG-backed implementation in their own component layer.
|
|
166
|
+
const Svg = "svg" as unknown as React.ComponentType<Record<string, unknown>>;
|
|
167
|
+
const Circle = "circle" as unknown as React.ComponentType<Record<string, unknown>>;
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<View style={{ width: size, height: size, alignItems: "center", justifyContent: "center" }}>
|
|
171
|
+
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
|
172
|
+
<Circle
|
|
173
|
+
cx={size / 2}
|
|
174
|
+
cy={size / 2}
|
|
175
|
+
r={radius}
|
|
176
|
+
fill="none"
|
|
177
|
+
stroke={backgroundColor}
|
|
178
|
+
strokeWidth={stroke}
|
|
179
|
+
/>
|
|
180
|
+
<Circle
|
|
181
|
+
cx={size / 2}
|
|
182
|
+
cy={size / 2}
|
|
183
|
+
r={radius}
|
|
184
|
+
fill="none"
|
|
185
|
+
stroke={color}
|
|
186
|
+
strokeWidth={stroke}
|
|
187
|
+
strokeLinecap="round"
|
|
188
|
+
strokeDasharray={circumference}
|
|
189
|
+
strokeDashoffset={dashOffset}
|
|
190
|
+
transform={`rotate(-90 ${size / 2} ${size / 2})`}
|
|
191
|
+
style={{ transition: "stroke-dashoffset 1s linear" }}
|
|
192
|
+
/>
|
|
193
|
+
</Svg>
|
|
194
|
+
<Text
|
|
195
|
+
style={{
|
|
196
|
+
position: "absolute",
|
|
197
|
+
color,
|
|
198
|
+
fontSize: 11,
|
|
199
|
+
fontWeight: "600",
|
|
200
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
201
|
+
}}
|
|
202
|
+
>
|
|
203
|
+
{remaining}
|
|
204
|
+
</Text>
|
|
205
|
+
</View>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { useColorScheme } from "nativewind";
|
|
4
|
+
import { cn } from "../utils/cn";
|
|
5
|
+
import { useAuth } from "../hooks/useAuth";
|
|
6
|
+
import { Text } from "./text";
|
|
7
|
+
|
|
8
|
+
export interface UserAvatarProps {
|
|
9
|
+
/** Square pixel size. Defaults to 36. */
|
|
10
|
+
size?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Override for the initial letter. By default the avatar pulls the
|
|
13
|
+
* first character of `display_name`, falling back to `username`, then
|
|
14
|
+
* `email`, then `?`.
|
|
15
|
+
*/
|
|
16
|
+
initial?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initial-letter circular avatar. Pulls the current user from
|
|
21
|
+
* `useAuth()` and renders a foreground-on-primary disc. Apps that want
|
|
22
|
+
* a static initial (e.g. an icon strip) pass `initial` explicitly.
|
|
23
|
+
*/
|
|
24
|
+
export const UserAvatar = React.memo(function UserAvatar({ size = 36, initial }: UserAvatarProps) {
|
|
25
|
+
const { user } = useAuth();
|
|
26
|
+
const { colorScheme } = useColorScheme();
|
|
27
|
+
const isDark = colorScheme === "dark";
|
|
28
|
+
const resolved =
|
|
29
|
+
initial ??
|
|
30
|
+
(user?.display_name || user?.username || user?.email || "?")[0]?.toUpperCase() ??
|
|
31
|
+
"?";
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<View
|
|
35
|
+
className={cn("items-center justify-center rounded-full", isDark ? "bg-white" : "bg-black")}
|
|
36
|
+
style={{ width: size, height: size }}
|
|
37
|
+
>
|
|
38
|
+
<Text
|
|
39
|
+
className="font-bold"
|
|
40
|
+
style={{ fontSize: size * 0.44, color: isDark ? "#000000" : "#FFFFFF" }}
|
|
41
|
+
>
|
|
42
|
+
{resolved}
|
|
43
|
+
</Text>
|
|
44
|
+
</View>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo } from "react";
|
|
2
|
+
import { type Href } from "expo-router";
|
|
3
|
+
import { type ImageStyle, type StyleProp, type ViewStyle } from "react-native";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Per-app route map consumed by the entropy-ui auth screens / hooks.
|
|
7
|
+
* Apps supply this so the shared screens can navigate without hard-coding
|
|
8
|
+
* vault- or bootstrap-specific paths.
|
|
9
|
+
*/
|
|
10
|
+
export interface AppRoutes {
|
|
11
|
+
welcome: Href;
|
|
12
|
+
login: Href;
|
|
13
|
+
register: Href;
|
|
14
|
+
forgotPassword: Href;
|
|
15
|
+
resetPassword: Href;
|
|
16
|
+
verifyEmail: Href;
|
|
17
|
+
mfaChallenge: Href;
|
|
18
|
+
mfaEnroll: Href;
|
|
19
|
+
termsOfService: Href;
|
|
20
|
+
privacyPolicy: Href;
|
|
21
|
+
serverConfig: Href;
|
|
22
|
+
importData: Href;
|
|
23
|
+
/** Where to land authenticated users with no pending onboarding step. */
|
|
24
|
+
home: Href;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type LogoComponent = React.ComponentType<{
|
|
28
|
+
width?: number;
|
|
29
|
+
height?: number;
|
|
30
|
+
style?: StyleProp<ViewStyle> | StyleProp<ImageStyle>;
|
|
31
|
+
}>;
|
|
32
|
+
|
|
33
|
+
export interface AppConfig {
|
|
34
|
+
/** User-facing brand name. Renders in headings, toasts, MFA issuer, etc. */
|
|
35
|
+
appName: string;
|
|
36
|
+
/** Brand logo as a React component (SVG / Image). */
|
|
37
|
+
Logo: LogoComponent;
|
|
38
|
+
/** Per-app route map. */
|
|
39
|
+
routes: AppRoutes;
|
|
40
|
+
/**
|
|
41
|
+
* Tagline rendered under the brand on the welcome screen. Defaults to
|
|
42
|
+
* a generic "An Entropy Softworks platform".
|
|
43
|
+
*/
|
|
44
|
+
tagline?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional terms-of-service markdown / plain text. Welcome screen and
|
|
47
|
+
* the dedicated terms screen render this. When omitted, the screens
|
|
48
|
+
* render a generic stub.
|
|
49
|
+
*/
|
|
50
|
+
termsText?: string;
|
|
51
|
+
/** Optional privacy-policy markdown / plain text. */
|
|
52
|
+
privacyText?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const AppConfigContext = createContext<AppConfig | null>(null);
|
|
56
|
+
|
|
57
|
+
export function AppConfigProvider({
|
|
58
|
+
config,
|
|
59
|
+
children,
|
|
60
|
+
}: {
|
|
61
|
+
config: AppConfig;
|
|
62
|
+
children: React.ReactNode;
|
|
63
|
+
}) {
|
|
64
|
+
const value = useMemo(() => config, [config]);
|
|
65
|
+
return <AppConfigContext.Provider value={value}>{children}</AppConfigContext.Provider>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function useAppConfig(): AppConfig {
|
|
69
|
+
const ctx = useContext(AppConfigContext);
|
|
70
|
+
if (!ctx) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"entropy-ui: useAppConfig must be used inside <AppConfigProvider config={...}>."
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return ctx;
|
|
76
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable navigation surface used by entropy-ui's auth screens.
|
|
3
|
+
*
|
|
4
|
+
* The shared screens used to call `useRouter()` from `expo-router`
|
|
5
|
+
* directly, which made them un-usable in non-Expo bundles (the browser-
|
|
6
|
+
* extension popup, future Electron / web targets without the Expo
|
|
7
|
+
* router runtime). Routing through this context lets every consumer
|
|
8
|
+
* supply its own router implementation — expo-router on mobile / web,
|
|
9
|
+
* a stack-reducer adapter inside the extension popup, etc.
|
|
10
|
+
*
|
|
11
|
+
* When no consumer is wrapped in `<NavigationProvider>`, `useNavigation`
|
|
12
|
+
* falls back to `expo-router`'s `useRouter()`, so the existing mobile
|
|
13
|
+
* client keeps working unchanged.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React, { createContext, useContext, useMemo } from "react";
|
|
17
|
+
import { type Href, useRouter } from "expo-router";
|
|
18
|
+
|
|
19
|
+
export interface RouteTarget {
|
|
20
|
+
pathname: Href | string;
|
|
21
|
+
params?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AppRouter {
|
|
25
|
+
push(target: Href | string | RouteTarget): void;
|
|
26
|
+
replace(target: Href | string | RouteTarget): void;
|
|
27
|
+
back(): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const NavigationContext = createContext<AppRouter | null>(null);
|
|
31
|
+
|
|
32
|
+
export interface NavigationProviderProps {
|
|
33
|
+
router: AppRouter;
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Override the default expo-router-backed navigation for everything
|
|
39
|
+
* rendered below this provider. Apps without expo-router supply this
|
|
40
|
+
* with their own stack-reducer adapter; everyone else can ignore it.
|
|
41
|
+
*/
|
|
42
|
+
export function NavigationProvider({ router, children }: NavigationProviderProps) {
|
|
43
|
+
const value = useMemo(() => router, [router]);
|
|
44
|
+
return <NavigationContext.Provider value={value}>{children}</NavigationContext.Provider>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Hook used by entropy-ui screens to navigate. Returns the override
|
|
49
|
+
* router supplied by `NavigationProvider` when present; otherwise
|
|
50
|
+
* delegates to `expo-router`'s `useRouter()`.
|
|
51
|
+
*/
|
|
52
|
+
export function useNavigation(): AppRouter {
|
|
53
|
+
const override = useContext(NavigationContext);
|
|
54
|
+
const expoRouter = useRouter();
|
|
55
|
+
// Memoize the expo-router adapter so consumers using the returned
|
|
56
|
+
// value as a `useEffect`/`useCallback` dep don't see a fresh
|
|
57
|
+
// reference every render. `useRouter()` itself returns a stable
|
|
58
|
+
// object across renders, so an empty-dep memo would also work, but
|
|
59
|
+
// depending on `expoRouter` is defensive against future changes.
|
|
60
|
+
const expoAdapter = useMemo<AppRouter>(
|
|
61
|
+
() => ({
|
|
62
|
+
push(target) {
|
|
63
|
+
expoRouter.push(target as Href);
|
|
64
|
+
},
|
|
65
|
+
replace(target) {
|
|
66
|
+
expoRouter.replace(target as Href);
|
|
67
|
+
},
|
|
68
|
+
back() {
|
|
69
|
+
expoRouter.back();
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
[expoRouter]
|
|
73
|
+
);
|
|
74
|
+
return override ?? expoAdapter;
|
|
75
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification context — provides a `push()` helper that surfaces
|
|
3
|
+
* toast-style notifications via `react-native-toast-message`.
|
|
4
|
+
*
|
|
5
|
+
* Mount `<NotificationProvider>` somewhere above the components that
|
|
6
|
+
* consume `useNotificationContext()`. The `<Toast />` component itself
|
|
7
|
+
* must be rendered separately in the app root (see
|
|
8
|
+
* `react-native-toast-message` docs).
|
|
9
|
+
*
|
|
10
|
+
* The vault notification provider also bridged a WebSocket service into
|
|
11
|
+
* this context to dispatch server-pushed toasts; that piece is
|
|
12
|
+
* app-specific and not included here. Consumers that need it can build
|
|
13
|
+
* a thin wrapper around this provider.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React, { createContext, useCallback, useContext, useMemo } from "react";
|
|
17
|
+
import Toast from "react-native-toast-message";
|
|
18
|
+
|
|
19
|
+
export type NotificationLevel = "success" | "error" | "warning" | "info";
|
|
20
|
+
|
|
21
|
+
export interface ToastOptions {
|
|
22
|
+
level: NotificationLevel;
|
|
23
|
+
title: string;
|
|
24
|
+
message?: string;
|
|
25
|
+
/** Milliseconds before auto-dismiss. Defaults to 4000. */
|
|
26
|
+
ttl_ms?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface NotificationContextValue {
|
|
30
|
+
push: (options: ToastOptions) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const NotificationContext = createContext<NotificationContextValue | null>(null);
|
|
34
|
+
|
|
35
|
+
const LEVEL_TO_TOAST_TYPE: Record<NotificationLevel, "success" | "error" | "info"> = {
|
|
36
|
+
success: "success",
|
|
37
|
+
error: "error",
|
|
38
|
+
// react-native-toast-message only ships success/error/info — warnings
|
|
39
|
+
// map onto error so they show with the warning-coloured icon.
|
|
40
|
+
warning: "error",
|
|
41
|
+
info: "info",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export function NotificationProvider({ children }: { children: React.ReactNode }) {
|
|
45
|
+
const push = useCallback((options: ToastOptions) => {
|
|
46
|
+
Toast.show({
|
|
47
|
+
type: LEVEL_TO_TOAST_TYPE[options.level],
|
|
48
|
+
text1: options.title,
|
|
49
|
+
text2: options.message,
|
|
50
|
+
visibilityTime: options.ttl_ms ?? 4000,
|
|
51
|
+
});
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
// Memoize the context value so an inline-object identity churn
|
|
55
|
+
// doesn't re-render every consumer on each NotificationProvider
|
|
56
|
+
// render. `push` is already `useCallback`-stable, so the memo
|
|
57
|
+
// effectively pins the value across the provider's lifetime.
|
|
58
|
+
const value = useMemo(() => ({ push }), [push]);
|
|
59
|
+
|
|
60
|
+
return <NotificationContext.Provider value={value}>{children}</NotificationContext.Provider>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function useNotificationContext(): NotificationContextValue {
|
|
64
|
+
const ctx = useContext(NotificationContext);
|
|
65
|
+
if (!ctx) {
|
|
66
|
+
throw new Error("useNotificationContext must be used inside <NotificationProvider>");
|
|
67
|
+
}
|
|
68
|
+
return ctx;
|
|
69
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useState, useMemo, useCallback } from "react";
|
|
2
|
+
import { colorScheme } from "nativewind";
|
|
3
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
4
|
+
import { getStoragePrefix } from "../services/api-config";
|
|
5
|
+
|
|
6
|
+
export type ThemePreference = "light" | "dark" | "system";
|
|
7
|
+
|
|
8
|
+
function themeKey(): string {
|
|
9
|
+
return `@${getStoragePrefix()}/theme-preference`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function applyTheme(isDark: boolean) {
|
|
13
|
+
colorScheme.set(isDark ? "dark" : "light");
|
|
14
|
+
if (typeof document !== "undefined") {
|
|
15
|
+
if (isDark) {
|
|
16
|
+
document.documentElement.classList.add("dark");
|
|
17
|
+
} else {
|
|
18
|
+
document.documentElement.classList.remove("dark");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface ThemeContextValue {
|
|
24
|
+
preference: ThemePreference;
|
|
25
|
+
setPreference: (pref: ThemePreference) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ThemeContext = createContext<ThemeContextValue>({
|
|
29
|
+
preference: "system",
|
|
30
|
+
setPreference: () => {},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
34
|
+
const [preference, setPreferenceState] = useState<ThemePreference>("system");
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
AsyncStorage.getItem(themeKey())
|
|
38
|
+
.then((stored) => {
|
|
39
|
+
if (stored === "light" || stored === "dark" || stored === "system") {
|
|
40
|
+
setPreferenceState(stored);
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
.catch(() => {});
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
const setPreference = useCallback((pref: ThemePreference) => {
|
|
47
|
+
AsyncStorage.setItem(themeKey(), pref).catch(() => {});
|
|
48
|
+
setPreferenceState(pref);
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (preference !== "system") {
|
|
53
|
+
applyTheme(preference === "dark");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
|
|
58
|
+
// Web system mode: react-native-css-interop's internal Appearance
|
|
59
|
+
// listener uses the removed `MediaQueryList.addListener` API
|
|
60
|
+
// (Chrome 121+) so it never fires. Drive it ourselves via
|
|
61
|
+
// `addEventListener` instead.
|
|
62
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
63
|
+
applyTheme(mq.matches);
|
|
64
|
+
const handler = (e: MediaQueryListEvent) => applyTheme(e.matches);
|
|
65
|
+
mq.addEventListener("change", handler);
|
|
66
|
+
return () => mq.removeEventListener("change", handler);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Native system mode: react-native-css-interop's Appearance listener
|
|
70
|
+
// already tracks OS changes. Calling colorScheme.set("system") would
|
|
71
|
+
// pass null to the Android native module which rejects non-nullable
|
|
72
|
+
// parameters and crashes — leave it alone.
|
|
73
|
+
return undefined;
|
|
74
|
+
}, [preference]);
|
|
75
|
+
|
|
76
|
+
const contextValue = useMemo(() => ({ preference, setPreference }), [preference, setPreference]);
|
|
77
|
+
|
|
78
|
+
return <ThemeContext.Provider value={contextValue}>{children}</ThemeContext.Provider>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function useTheme() {
|
|
82
|
+
return useContext(ThemeContext);
|
|
83
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { renderHook } from "@testing-library/react-native";
|
|
3
|
+
|
|
4
|
+
import { NavigationProvider, useNavigation, type AppRouter } from "../NavigationContext";
|
|
5
|
+
import * as expoRouterModule from "expo-router";
|
|
6
|
+
|
|
7
|
+
jest.mock("expo-router", () => {
|
|
8
|
+
const mockRouter = { push: jest.fn(), replace: jest.fn(), back: jest.fn() };
|
|
9
|
+
return {
|
|
10
|
+
__mockRouter: mockRouter,
|
|
11
|
+
useRouter: () => mockRouter,
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const expoRouter = (expoRouterModule as unknown as { __mockRouter: AppRouter }).__mockRouter;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
(expoRouter.push as jest.Mock).mockClear();
|
|
19
|
+
(expoRouter.replace as jest.Mock).mockClear();
|
|
20
|
+
(expoRouter.back as jest.Mock).mockClear();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("useNavigation", () => {
|
|
24
|
+
it("falls back to expo-router when no provider is present", () => {
|
|
25
|
+
const { result } = renderHook(() => useNavigation());
|
|
26
|
+
result.current.push("/welcome");
|
|
27
|
+
result.current.replace("/login");
|
|
28
|
+
result.current.back();
|
|
29
|
+
expect(expoRouter.push).toHaveBeenCalledWith("/welcome");
|
|
30
|
+
expect(expoRouter.replace).toHaveBeenCalledWith("/login");
|
|
31
|
+
expect(expoRouter.back).toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("uses the override router when wrapped in NavigationProvider", () => {
|
|
35
|
+
const custom: AppRouter = {
|
|
36
|
+
push: jest.fn(),
|
|
37
|
+
replace: jest.fn(),
|
|
38
|
+
back: jest.fn(),
|
|
39
|
+
};
|
|
40
|
+
const { result } = renderHook(() => useNavigation(), {
|
|
41
|
+
wrapper: ({ children }) => (
|
|
42
|
+
<NavigationProvider router={custom}>{children}</NavigationProvider>
|
|
43
|
+
),
|
|
44
|
+
});
|
|
45
|
+
result.current.push("/a");
|
|
46
|
+
result.current.replace("/b");
|
|
47
|
+
result.current.back();
|
|
48
|
+
expect(custom.push).toHaveBeenCalledWith("/a");
|
|
49
|
+
expect(custom.replace).toHaveBeenCalledWith("/b");
|
|
50
|
+
expect(custom.back).toHaveBeenCalled();
|
|
51
|
+
expect(expoRouter.push).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export {
|
|
2
|
+
AppConfigProvider,
|
|
3
|
+
useAppConfig,
|
|
4
|
+
type AppConfig,
|
|
5
|
+
type AppRoutes,
|
|
6
|
+
type LogoComponent,
|
|
7
|
+
} from "./AppConfigContext";
|
|
8
|
+
|
|
9
|
+
export { ThemeProvider, useTheme, type ThemePreference } from "./ThemeContext";
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
NavigationProvider,
|
|
13
|
+
useNavigation,
|
|
14
|
+
type AppRouter,
|
|
15
|
+
type NavigationProviderProps,
|
|
16
|
+
type RouteTarget,
|
|
17
|
+
} from "./NavigationContext";
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
NotificationProvider,
|
|
21
|
+
useNotificationContext,
|
|
22
|
+
type NotificationLevel,
|
|
23
|
+
type ToastOptions,
|
|
24
|
+
} from "./NotificationContext";
|