@oxy.so/services 9.2.0 → 11.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/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +6 -6
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionModule.kt +106 -41
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionProvider.kt +45 -32
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionStore.kt +22 -27
- package/android/src/main/java/so/oxy/identity/OxyIdentityModule.kt +89 -43
- package/android/src/main/java/so/oxy/security/OxyCallerPolicy.kt +97 -0
- package/android/src/main/java/so/oxy/session/OxyBackgroundSessionStore.kt +4 -10
- package/android/src/main/java/so/oxy/storage/OxyEncryptedPrefs.kt +17 -18
- package/ios/OxyIdentityModule.swift +13 -14
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +2 -2
- package/lib/commonjs/ui/components/BenefitList.js +1 -1
- package/lib/commonjs/ui/components/BenefitList.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +2 -2
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/FollowTargetButton.js +2 -2
- package/lib/commonjs/ui/components/FollowTargetButton.js.map +1 -1
- package/lib/commonjs/ui/components/PeableButton.js +1 -2
- package/lib/commonjs/ui/components/PeableButton.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceHeaderAction.js +1 -1
- package/lib/commonjs/ui/components/SurfaceHeaderAction.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/primitives.js +1 -1
- package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js +1 -1
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +2 -2
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentMethodStep.js +2 -2
- package/lib/commonjs/ui/components/payment/PaymentMethodStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentReviewStep.js +2 -2
- package/lib/commonjs/ui/components/payment/PaymentReviewStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +2 -2
- package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js +1 -1
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js +2 -2
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/commonjs/ui/components/surfaces/ActionSheetSurface.js +2 -1
- package/lib/commonjs/ui/components/surfaces/ActionSheetSurface.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountMembersScreen.js +4 -4
- package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js +1 -1
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ConnectedAppsScreen.js +1 -1
- package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/DomainsScreen.js +1 -1
- package/lib/commonjs/ui/screens/DomainsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +4 -2
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +5 -5
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HistoryViewScreen.js +1 -1
- package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +1 -1
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +1 -1
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/NotificationsScreen.js +8 -8
- package/lib/commonjs/ui/screens/NotificationsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +1 -1
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PreferencesScreen.js +2 -2
- package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +37 -37
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SearchSettingsScreen.js +5 -5
- package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/UserListScreen.js +1 -1
- package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +11 -11
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/UploadBar.js +1 -1
- package/lib/commonjs/ui/screens/fileManagement/UploadBar.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +1 -1
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -1
- package/lib/commonjs/ui/session/tokenLossRecovery.js +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +2 -2
- package/lib/module/ui/components/BenefitList.js +1 -1
- package/lib/module/ui/components/BenefitList.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +2 -2
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/FollowTargetButton.js +2 -2
- package/lib/module/ui/components/FollowTargetButton.js.map +1 -1
- package/lib/module/ui/components/PeableButton.js +2 -3
- package/lib/module/ui/components/PeableButton.js.map +1 -1
- package/lib/module/ui/components/SurfaceHeaderAction.js +1 -1
- package/lib/module/ui/components/SurfaceHeaderAction.js.map +1 -1
- package/lib/module/ui/components/SurfaceScreen.js +1 -1
- package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/module/ui/components/authChooser/primitives.js +1 -1
- package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/module/ui/components/authChooser/requestSurfaces.js +1 -1
- package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentDetailsStep.js +2 -2
- package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentMethodStep.js +2 -2
- package/lib/module/ui/components/payment/PaymentMethodStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentReviewStep.js +2 -2
- package/lib/module/ui/components/payment/PaymentReviewStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentSuccessStep.js +1 -1
- package/lib/module/ui/components/payment/PaymentSuccessStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentSummaryStep.js +2 -2
- package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAuthScreen.js +1 -1
- package/lib/module/ui/components/signIn/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js +1 -1
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js +2 -2
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/module/ui/components/surfaces/ActionSheetSurface.js +2 -1
- package/lib/module/ui/components/surfaces/ActionSheetSurface.js.map +1 -1
- package/lib/module/ui/screens/AccountMembersScreen.js +4 -4
- package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/module/ui/screens/ConnectedAppsScreen.js +1 -1
- package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
- package/lib/module/ui/screens/DomainsScreen.js +1 -1
- package/lib/module/ui/screens/DomainsScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +4 -2
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +5 -5
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/HistoryViewScreen.js +1 -1
- package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/module/ui/screens/LegalDocumentsScreen.js +1 -1
- package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
- package/lib/module/ui/screens/NotificationsScreen.js +8 -8
- package/lib/module/ui/screens/NotificationsScreen.js.map +1 -1
- package/lib/module/ui/screens/PaymentGatewayScreen.js +1 -1
- package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
- package/lib/module/ui/screens/PreferencesScreen.js +2 -2
- package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +37 -37
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +1 -1
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/SearchSettingsScreen.js +5 -5
- package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/UserListScreen.js +1 -1
- package/lib/module/ui/screens/UserListScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +11 -11
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/UploadBar.js +1 -1
- package/lib/module/ui/screens/fileManagement/UploadBar.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustCenterScreen.js +1 -1
- package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustRulesScreen.js +1 -1
- package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -1
- package/lib/module/ui/session/tokenLossRecovery.js +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/BenefitList.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FollowButton.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/FollowTargetButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/PeableButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +2 -2
- package/lib/typescript/module/ui/components/BenefitList.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FollowButton.d.ts +1 -1
- package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FollowTargetButton.d.ts +1 -1
- package/lib/typescript/module/ui/components/FollowTargetButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/PeableButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/surfaces/ActionSheetSurface.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/package.json +7 -7
- package/plugins/withOxySharedPermissions.js +89 -0
- package/plugins/withSharedDeviceSessionProvider.js +26 -71
- package/src/ui/boot/runProviderColdBoot.ts +2 -2
- package/src/ui/components/BenefitList.tsx +1 -2
- package/src/ui/components/FollowButton.tsx +3 -3
- package/src/ui/components/FollowTargetButton.tsx +3 -3
- package/src/ui/components/PeableButton.tsx +3 -4
- package/src/ui/components/SurfaceHeaderAction.tsx +1 -1
- package/src/ui/components/SurfaceScreen.tsx +1 -1
- package/src/ui/components/__tests__/followButtonAccessibility.test.tsx +9 -0
- package/src/ui/components/authChooser/AccountsMenuView.tsx +1 -1
- package/src/ui/components/authChooser/primitives.tsx +1 -1
- package/src/ui/components/authChooser/requestSurfaces.tsx +1 -1
- package/src/ui/components/payment/PaymentDetailsStep.tsx +2 -2
- package/src/ui/components/payment/PaymentMethodStep.tsx +2 -2
- package/src/ui/components/payment/PaymentReviewStep.tsx +2 -2
- package/src/ui/components/payment/PaymentSuccessStep.tsx +1 -1
- package/src/ui/components/payment/PaymentSummaryStep.tsx +2 -2
- package/src/ui/components/signIn/OxyAuthScreen.tsx +1 -1
- package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +1 -1
- package/src/ui/components/signIn/OxyPasswordPanel.tsx +1 -1
- package/src/ui/components/surfaces/ActionSheetSurface.tsx +2 -1
- package/src/ui/screens/AccountMembersScreen.tsx +4 -4
- package/src/ui/screens/AccountVerificationScreen.tsx +1 -1
- package/src/ui/screens/ConnectedAppsScreen.tsx +1 -1
- package/src/ui/screens/DomainsScreen.tsx +1 -1
- package/src/ui/screens/EditProfileFieldScreen.tsx +1 -2
- package/src/ui/screens/FileManagementScreen.tsx +5 -5
- package/src/ui/screens/HistoryViewScreen.tsx +1 -1
- package/src/ui/screens/LanguageSelectorScreen.tsx +1 -1
- package/src/ui/screens/LegalDocumentsScreen.tsx +1 -1
- package/src/ui/screens/NotificationsScreen.tsx +8 -8
- package/src/ui/screens/PaymentGatewayScreen.tsx +1 -1
- package/src/ui/screens/PreferencesScreen.tsx +2 -2
- package/src/ui/screens/PrivacySettingsScreen.tsx +21 -21
- package/src/ui/screens/ProfileScreen.tsx +1 -1
- package/src/ui/screens/SearchSettingsScreen.tsx +5 -5
- package/src/ui/screens/UserListScreen.tsx +1 -1
- package/src/ui/screens/WelcomeNewUserScreen.tsx +11 -11
- package/src/ui/screens/fileManagement/UploadBar.tsx +1 -1
- package/src/ui/screens/trust/TrustCenterScreen.tsx +1 -1
- package/src/ui/screens/trust/TrustRulesScreen.tsx +1 -1
- package/src/ui/session/__tests__/tokenLossRecovery.test.ts +2 -2
- package/src/ui/session/tokenLossRecovery.ts +1 -1
- package/android/src/main/java/so/oxy/identity/OxyIdentityProvider.kt +0 -77
- package/android/src/main/java/so/oxy/identity/OxyIdentityStore.kt +0 -66
- package/plugins/withSharedDeviceSessionReader.js +0 -57
- package/plugins/withSharedIdentityProvider.js +0 -83
- package/plugins/withSharedIdentityReader.js +0 -46
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config plugin: withOxySharedPermissions (EVERY Oxy Android app).
|
|
3
|
+
*
|
|
4
|
+
* Oxy Android apps do not share a UID. They share the Oxy identity and the
|
|
5
|
+
* device session with Commons (and Accounts) through signature-protected
|
|
6
|
+
* ContentProvider `call()` IPC, the way apps use `AccountManager`. Crossing a UID
|
|
7
|
+
* needs two `signature`-level permissions:
|
|
8
|
+
*
|
|
9
|
+
* - `so.oxy.permission.IDENTITY` — Commons' identity host
|
|
10
|
+
* (`so.oxy.commons[.dev].identity`): public key, challenge proofs, scoped
|
|
11
|
+
* derivations. Never the private key.
|
|
12
|
+
* - `so.oxy.permission.DEVICE_SESSION` — the device-session hosts
|
|
13
|
+
* (`so.oxy.commons[.dev].devicesession`, `so.oxy.accounts[.dev].devicesession`).
|
|
14
|
+
*
|
|
15
|
+
* This plugin makes the app both DECLARE and REQUEST both, and adds `<queries>`
|
|
16
|
+
* for every host authority:
|
|
17
|
+
*
|
|
18
|
+
* - Declared by every app, not only by the hosts: a signature permission is
|
|
19
|
+
* granted when the requesting app is installed, if the declaring app is
|
|
20
|
+
* already there. With every app declaring it, install order never matters.
|
|
21
|
+
* Android accepts the same declaration from several packages signed with one
|
|
22
|
+
* certificate, and rejects it from a differently-signed one
|
|
23
|
+
* (`INSTALL_FAILED_DUPLICATE_PERMISSION`), which is the protection we want.
|
|
24
|
+
* Every declaration must be identical, which is why this ONE plugin owns
|
|
25
|
+
* them; the provider plugins do not declare anything.
|
|
26
|
+
* - `<queries>`: Android 11+ package visibility hides a provider the app has
|
|
27
|
+
* not declared an interest in, and `ContentResolver.call` then silently finds
|
|
28
|
+
* nothing.
|
|
29
|
+
*
|
|
30
|
+
* `signature` is the whole trust boundary at the manifest level; each provider
|
|
31
|
+
* also checks the calling package and its certificate itself.
|
|
32
|
+
*/
|
|
33
|
+
const { withAndroidManifest } = require('@expo/config-plugins');
|
|
34
|
+
|
|
35
|
+
const IDENTITY_PERMISSION = 'so.oxy.permission.IDENTITY';
|
|
36
|
+
const DEVICE_SESSION_PERMISSION = 'so.oxy.permission.DEVICE_SESSION';
|
|
37
|
+
const PERMISSIONS = [IDENTITY_PERMISSION, DEVICE_SESSION_PERMISSION];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Every host authority an Oxy app calls. Keep in step with `COMMONS_AUTHORITIES`
|
|
41
|
+
* in `OxyIdentityModule.kt` and `HOST_AUTHORITIES` in `OxyDeviceSessionModule.kt`
|
|
42
|
+
* (a test compares them).
|
|
43
|
+
*/
|
|
44
|
+
const HOST_AUTHORITIES = [
|
|
45
|
+
'so.oxy.commons.identity',
|
|
46
|
+
'so.oxy.commons.dev.identity',
|
|
47
|
+
'so.oxy.commons.devicesession',
|
|
48
|
+
'so.oxy.commons.dev.devicesession',
|
|
49
|
+
'so.oxy.accounts.devicesession',
|
|
50
|
+
'so.oxy.accounts.dev.devicesession',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
function withOxySharedPermissions(config) {
|
|
54
|
+
return withAndroidManifest(config, (modConfig) => {
|
|
55
|
+
const manifest = modConfig.modResults.manifest;
|
|
56
|
+
|
|
57
|
+
manifest.permission = manifest.permission ?? [];
|
|
58
|
+
manifest['uses-permission'] = manifest['uses-permission'] ?? [];
|
|
59
|
+
for (const name of PERMISSIONS) {
|
|
60
|
+
if (!manifest.permission.some((p) => p.$['android:name'] === name)) {
|
|
61
|
+
manifest.permission.push({
|
|
62
|
+
$: { 'android:name': name, 'android:protectionLevel': 'signature' },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (!manifest['uses-permission'].some((p) => p.$['android:name'] === name)) {
|
|
66
|
+
manifest['uses-permission'].push({ $: { 'android:name': name } });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
manifest.queries = manifest.queries ?? [];
|
|
71
|
+
if (manifest.queries.length === 0) {
|
|
72
|
+
manifest.queries.push({});
|
|
73
|
+
}
|
|
74
|
+
const queries = manifest.queries[0];
|
|
75
|
+
queries.provider = queries.provider ?? [];
|
|
76
|
+
for (const authority of HOST_AUTHORITIES) {
|
|
77
|
+
if (!queries.provider.some((p) => p.$['android:authorities'] === authority)) {
|
|
78
|
+
queries.provider.push({ $: { 'android:authorities': authority } });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return modConfig;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = withOxySharedPermissions;
|
|
87
|
+
module.exports.IDENTITY_PERMISSION = IDENTITY_PERMISSION;
|
|
88
|
+
module.exports.DEVICE_SESSION_PERMISSION = DEVICE_SESSION_PERMISSION;
|
|
89
|
+
module.exports.HOST_AUTHORITIES = HOST_AUTHORITIES;
|
|
@@ -1,86 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Config plugin: withSharedDeviceSessionProvider (
|
|
2
|
+
* Config plugin: withSharedDeviceSessionProvider (the device-session HOSTS:
|
|
3
|
+
* Commons and Accounts, prod and dev).
|
|
3
4
|
*
|
|
4
|
-
* The shared DeviceSession credential is how
|
|
5
|
+
* The shared DeviceSession credential is how the official Oxy apps end up on
|
|
5
6
|
* ONE `DeviceSession` — and therefore one globally active account context —
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
7
|
+
* without any of them touching the identity key. Oxy apps do not share a UID,
|
|
8
|
+
* so the credential lives in the hosts' own storage, and this plugin declares
|
|
9
|
+
* the provider every other app reads and writes it through:
|
|
10
|
+
* `OxyDeviceSessionProvider` at `${applicationId}.devicesession` (AGP
|
|
11
|
+
* substitutes `${applicationId}` at build), guarded by the `signature`-level
|
|
12
|
+
* `so.oxy.permission.DEVICE_SESSION`.
|
|
8
13
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* it — the trust boundary is the signing key, not the deprecated `sharedUserId`.
|
|
13
|
-
* - Requests that same permission so this app can also read cross-authority.
|
|
14
|
-
* - Declares `OxyDeviceSessionProvider` at authority
|
|
15
|
-
* `${applicationId}.devicesession` (AGP substitutes `${applicationId}` at
|
|
16
|
-
* build), guarded by that permission. The provider is READ-only across the
|
|
17
|
-
* process boundary: a sibling may join the device session, never overwrite it.
|
|
18
|
-
* - Adds `<queries>` entries so Android 11+ package-visibility filtering never
|
|
19
|
-
* hides the sibling providers from the resolver.
|
|
14
|
+
* It declares NO permission itself: every Oxy app, host or not, declares and
|
|
15
|
+
* requests both Oxy permissions through `withOxySharedPermissions`, so the
|
|
16
|
+
* declarations stay identical. Apply that plugin too.
|
|
20
17
|
*
|
|
21
|
-
* WHICH APPS USE THIS PLUGIN: exactly the apps listed in
|
|
22
|
-
*
|
|
23
|
-
* data directory, so they all serve the SAME file and the sweep in
|
|
24
|
-
* `OxyDeviceSessionModule` is deterministic whichever one answers. An app that is
|
|
25
|
-
* same-signature but outside that UID must use `withSharedDeviceSessionReader`
|
|
26
|
-
* instead — hosting from there would publish a second, independent copy of "the
|
|
27
|
-
* shared credential" and make the winner depend on list order.
|
|
28
|
-
*
|
|
29
|
-
* Keep this list identical to `PROVIDER_AUTHORITIES` in
|
|
30
|
-
* `android/src/main/java/so/oxy/devicesession/OxyDeviceSessionModule.kt`.
|
|
31
|
-
*
|
|
32
|
-
* This is deliberately a SEPARATE permission and a SEPARATE provider from
|
|
33
|
-
* `withSharedIdentityProvider`. Two secrets with different blast radii: the
|
|
34
|
-
* identity key is self-custody and irreplaceable, the device credential is
|
|
35
|
-
* server-revocable and re-publishable. An app that only needs a session gets only
|
|
36
|
-
* the second one.
|
|
18
|
+
* WHICH APPS USE THIS PLUGIN: exactly the apps whose authorities are listed in
|
|
19
|
+
* `HOST_AUTHORITIES` of `OxyDeviceSessionModule.kt` (a test compares them).
|
|
37
20
|
*/
|
|
38
21
|
const { withAndroidManifest } = require('@expo/config-plugins');
|
|
39
22
|
|
|
40
|
-
const
|
|
23
|
+
const DEVICE_SESSION_PERMISSION = 'so.oxy.permission.DEVICE_SESSION';
|
|
41
24
|
const PROVIDER_CLASS = 'so.oxy.devicesession.OxyDeviceSessionProvider';
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
25
|
+
|
|
26
|
+
/** The authorities this plugin can end up hosting, one per host app. */
|
|
27
|
+
const HOST_AUTHORITIES = [
|
|
45
28
|
'so.oxy.commons.devicesession',
|
|
46
29
|
'so.oxy.commons.dev.devicesession',
|
|
30
|
+
'so.oxy.accounts.devicesession',
|
|
31
|
+
'so.oxy.accounts.dev.devicesession',
|
|
47
32
|
];
|
|
48
33
|
|
|
49
|
-
|
|
34
|
+
function withSharedDeviceSessionProvider(config) {
|
|
50
35
|
return withAndroidManifest(config, (modConfig) => {
|
|
51
36
|
const manifest = modConfig.modResults.manifest;
|
|
52
|
-
|
|
53
|
-
// 1. Define the signature-level permission.
|
|
54
|
-
manifest['permission'] = manifest['permission'] ?? [];
|
|
55
|
-
if (!manifest['permission'].some((p) => p.$['android:name'] === READ_DEVICE_SESSION_PERMISSION)) {
|
|
56
|
-
manifest['permission'].push({
|
|
57
|
-
$: {
|
|
58
|
-
'android:name': READ_DEVICE_SESSION_PERMISSION,
|
|
59
|
-
'android:protectionLevel': 'signature',
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 2. Request it (a hub app reads its siblings too — prod ⇆ dev variant).
|
|
65
|
-
manifest['uses-permission'] = manifest['uses-permission'] ?? [];
|
|
66
|
-
if (!manifest['uses-permission'].some((p) => p.$['android:name'] === READ_DEVICE_SESSION_PERMISSION)) {
|
|
67
|
-
manifest['uses-permission'].push({ $: { 'android:name': READ_DEVICE_SESSION_PERMISSION } });
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 3. Make the sibling provider authorities visible under package filtering.
|
|
71
|
-
manifest['queries'] = manifest['queries'] ?? [];
|
|
72
|
-
if (manifest['queries'].length === 0) {
|
|
73
|
-
manifest['queries'].push({});
|
|
74
|
-
}
|
|
75
|
-
const queries = manifest['queries'][0];
|
|
76
|
-
queries.provider = queries.provider ?? [];
|
|
77
|
-
for (const authority of PROVIDER_AUTHORITIES) {
|
|
78
|
-
if (!queries.provider.some((p) => p.$['android:authorities'] === authority)) {
|
|
79
|
-
queries.provider.push({ $: { 'android:authorities': authority } });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 4. Host the provider.
|
|
84
37
|
const app = manifest.application?.[0];
|
|
85
38
|
if (!app) {
|
|
86
39
|
throw new Error('withSharedDeviceSessionProvider: AndroidManifest has no <application>');
|
|
@@ -92,11 +45,13 @@ module.exports = function withSharedDeviceSessionProvider(config) {
|
|
|
92
45
|
'android:name': PROVIDER_CLASS,
|
|
93
46
|
'android:authorities': '${applicationId}.devicesession',
|
|
94
47
|
'android:exported': 'true',
|
|
95
|
-
'android:permission':
|
|
48
|
+
'android:permission': DEVICE_SESSION_PERMISSION,
|
|
96
49
|
},
|
|
97
50
|
});
|
|
98
51
|
}
|
|
99
|
-
|
|
100
52
|
return modConfig;
|
|
101
53
|
});
|
|
102
|
-
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = withSharedDeviceSessionProvider;
|
|
57
|
+
module.exports.HOST_AUTHORITIES = HOST_AUTHORITIES;
|
|
@@ -91,8 +91,8 @@ export interface RunProviderColdBootOptions {
|
|
|
91
91
|
*
|
|
92
92
|
* Ordered pipeline:
|
|
93
93
|
* 1. Complete an OAuth authorization-code return already on the URL (web)
|
|
94
|
-
* 2. `runSessionColdBoot` — device-secret mint (+ native
|
|
95
|
-
* primary-identity-key lane in `sessionMode: 'identity'`)
|
|
94
|
+
* 2. `runSessionColdBoot` — device-secret mint (+ the native Commons-proof lane,
|
|
95
|
+
* or the primary-identity-key lane in `sessionMode: 'identity'`)
|
|
96
96
|
*
|
|
97
97
|
* Step 1 is ACCOUNT-MODE ONLY: it commits whichever account the IdP resolves,
|
|
98
98
|
* which for an identity-bound client is somebody else's account by construction.
|
|
@@ -98,13 +98,12 @@ export const BenefitList: React.FC<BenefitListProps> = ({
|
|
|
98
98
|
style,
|
|
99
99
|
}) => (
|
|
100
100
|
<Card
|
|
101
|
-
variant="outlined"
|
|
102
101
|
radius="radius-20"
|
|
103
102
|
border="hairline"
|
|
104
103
|
elevation="s"
|
|
105
104
|
className={className}
|
|
106
105
|
style={[{ padding: SPACING['space-16'], gap: SPACING['space-16'] }, style]}
|
|
107
|
-
accessibilityLabel={accessibilityLabel}
|
|
106
|
+
accessibilityLabel={accessibilityLabel} appearance="outline"
|
|
108
107
|
>
|
|
109
108
|
{children}
|
|
110
109
|
</Card>
|
|
@@ -19,7 +19,7 @@ const DEFAULT_FOLLOWED_ALL_LABEL = 'Following';
|
|
|
19
19
|
|
|
20
20
|
/** Props shared by both single- and multi-user follow modes. */
|
|
21
21
|
interface FollowButtonBaseProps {
|
|
22
|
-
size?: '
|
|
22
|
+
size?: 'sm' | 'md' | 'lg';
|
|
23
23
|
style?: StyleProp<ViewStyle>;
|
|
24
24
|
textStyle?: StyleProp<TextStyle>;
|
|
25
25
|
disabled?: boolean;
|
|
@@ -67,7 +67,7 @@ const FollowButtonInner = memo(function FollowButtonInner({
|
|
|
67
67
|
oxyServices,
|
|
68
68
|
initiallyFollowing,
|
|
69
69
|
username,
|
|
70
|
-
size = '
|
|
70
|
+
size = 'md',
|
|
71
71
|
onFollowChange,
|
|
72
72
|
style,
|
|
73
73
|
textStyle,
|
|
@@ -140,7 +140,7 @@ const FollowButtonInner = memo(function FollowButtonInner({
|
|
|
140
140
|
const FollowButtonMultiInner = memo(function FollowButtonMultiInner({
|
|
141
141
|
userIds,
|
|
142
142
|
initiallyAllFollowing = false,
|
|
143
|
-
size = '
|
|
143
|
+
size = 'md',
|
|
144
144
|
followAllLabel = DEFAULT_FOLLOW_ALL_LABEL,
|
|
145
145
|
followedAllLabel = DEFAULT_FOLLOWED_ALL_LABEL,
|
|
146
146
|
onFollowChange,
|
|
@@ -108,7 +108,7 @@ export interface FollowTargetButtonProps {
|
|
|
108
108
|
verb?: FollowVerb;
|
|
109
109
|
/** Full override, for a verb the vocabulary above does not carry. */
|
|
110
110
|
labels?: Partial<FollowLabels>;
|
|
111
|
-
size?: '
|
|
111
|
+
size?: 'sm' | 'md' | 'lg';
|
|
112
112
|
style?: StyleProp<ViewStyle>;
|
|
113
113
|
disabled?: boolean;
|
|
114
114
|
/**
|
|
@@ -152,7 +152,7 @@ export const FollowTargetButton = memo(function FollowTargetButton({
|
|
|
152
152
|
targetId,
|
|
153
153
|
verb = 'follow',
|
|
154
154
|
labels,
|
|
155
|
-
size = '
|
|
155
|
+
size = 'md',
|
|
156
156
|
style,
|
|
157
157
|
disabled = false,
|
|
158
158
|
showOptions = true,
|
|
@@ -309,7 +309,7 @@ export const FollowTargetButton = memo(function FollowTargetButton({
|
|
|
309
309
|
<DropdownMenuTrigger asChild disabled={disabled || isUnknown}>
|
|
310
310
|
<Button
|
|
311
311
|
appearance="subtle" tone="support"
|
|
312
|
-
size={size === '
|
|
312
|
+
size={size === 'lg' ? 'lg' : 'sm'}
|
|
313
313
|
icon={<ChevronDown width={16} />}
|
|
314
314
|
disabled={disabled || isUnknown}
|
|
315
315
|
accessibilityLabel={`${label} options`}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import type { ViewStyle, TextStyle, StyleProp } from 'react-native';
|
|
3
3
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
4
|
-
import {
|
|
4
|
+
import { InverseButton } from '@oxy.so/bloom/button';
|
|
5
5
|
import type { PaymentItem, PaymentGatewayResult } from '../screens/PaymentGatewayScreen';
|
|
6
6
|
import { LogoIcon } from './logo/LogoIcon';
|
|
7
7
|
|
|
@@ -46,8 +46,7 @@ const PeableButton: React.FC<PeableButtonProps> = ({
|
|
|
46
46
|
const textColor = variant === 'black' || (color && isColorDark(color)) ? theme.colors.background : '#1b1f0a';
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<
|
|
50
|
-
variant="inverse"
|
|
49
|
+
<InverseButton
|
|
51
50
|
onPress={handlePress}
|
|
52
51
|
disabled={disabled}
|
|
53
52
|
style={[{ backgroundColor, borderColor: textColor, borderWidth: 1 }, style]}
|
|
@@ -55,7 +54,7 @@ const PeableButton: React.FC<PeableButtonProps> = ({
|
|
|
55
54
|
icon={<LogoIcon height={16} color={textColor} style={{ marginRight: 6 }} />}
|
|
56
55
|
>
|
|
57
56
|
{text}
|
|
58
|
-
</
|
|
57
|
+
</InverseButton>
|
|
59
58
|
);
|
|
60
59
|
};
|
|
61
60
|
|
|
@@ -12,7 +12,7 @@ export interface SurfaceHeaderActionProps {
|
|
|
12
12
|
|
|
13
13
|
/** Header actions share Bloom's sizing, surface, disabled and loading behavior. */
|
|
14
14
|
export const SurfaceHeaderAction: React.FC<SurfaceHeaderActionProps> = ({ label, onPress, loading, disabled }) => (
|
|
15
|
-
<Button size="
|
|
15
|
+
<Button size="md" onPress={onPress} loading={loading} disabled={disabled}>
|
|
16
16
|
{label}
|
|
17
17
|
</Button>
|
|
18
18
|
);
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from '../followButtonAccessibility';
|
|
17
17
|
|
|
18
18
|
interface BloomFollowProps {
|
|
19
|
+
size?: 'sm' | 'md' | 'lg';
|
|
19
20
|
following: boolean;
|
|
20
21
|
label?: string;
|
|
21
22
|
followingLabel?: string;
|
|
@@ -189,3 +190,11 @@ describe('FollowTargetButton names the state it shows', () => {
|
|
|
189
190
|
expect(lastBloomProps()?.accessibilityLabel).toBe(label);
|
|
190
191
|
});
|
|
191
192
|
});
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
it.each(['sm', 'md', 'lg'] as const)('forwards canonical %s geometry in both follow modes', size => {
|
|
196
|
+
render(<FollowButton userId="nate-id" size={size} />);
|
|
197
|
+
expect(lastBloomProps()?.size).toBe(size);
|
|
198
|
+
render(<FollowButton userIds={['a', 'b']} size={size} />);
|
|
199
|
+
expect(lastBloomProps()?.size).toBe(size);
|
|
200
|
+
});
|
|
@@ -40,7 +40,7 @@ const ROUTE_GLYPH_SIZE = 44;
|
|
|
40
40
|
*/
|
|
41
41
|
export const PreparingSurface: React.FC = () => {
|
|
42
42
|
const { t } = useI18n();
|
|
43
|
-
return <Loading variant="spinner" size="
|
|
43
|
+
return <Loading variant="spinner" size="lg" accessibilityLabel={t('common.status.loading')} />;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
/** The leading glyph for a route whose surface is elsewhere (the phone, Commons). */
|
|
@@ -214,13 +214,13 @@ const PaymentDetailsStep: React.FC<PaymentDetailsStepProps> = ({
|
|
|
214
214
|
</View>
|
|
215
215
|
|
|
216
216
|
<View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
|
|
217
|
-
<Button appearance="subtle" tone="neutral" onPress={onBack} size="
|
|
217
|
+
<Button appearance="subtle" tone="neutral" onPress={onBack} size="sm" icon={<Ionicons name="arrow-back" size={16} />}>
|
|
218
218
|
{t('payment.actions.back')}
|
|
219
219
|
</Button>
|
|
220
220
|
<Button
|
|
221
221
|
appearance="solid" tone="accent"
|
|
222
222
|
onPress={onNext}
|
|
223
|
-
size="
|
|
223
|
+
size="sm"
|
|
224
224
|
icon={<Ionicons name="arrow-forward" size={16} />}
|
|
225
225
|
iconPosition="right"
|
|
226
226
|
disabled={paymentMethod === 'card' && !isCardValid}
|
|
@@ -77,10 +77,10 @@ const PaymentMethodStep: React.FC<PaymentMethodStepProps> = ({
|
|
|
77
77
|
</View>
|
|
78
78
|
|
|
79
79
|
<View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
|
|
80
|
-
<Button appearance="subtle" tone="neutral" onPress={onBack} size="
|
|
80
|
+
<Button appearance="subtle" tone="neutral" onPress={onBack} size="sm" icon={<Ionicons name="arrow-back" size={16} />}>
|
|
81
81
|
{t('payment.actions.back')}
|
|
82
82
|
</Button>
|
|
83
|
-
<Button appearance="solid" tone="accent" onPress={onNext} size="
|
|
83
|
+
<Button appearance="solid" tone="accent" onPress={onNext} size="sm" icon={<Ionicons name="arrow-forward" size={16} />} iconPosition="right">
|
|
84
84
|
{t('payment.actions.continue')}
|
|
85
85
|
</Button>
|
|
86
86
|
</View>
|
|
@@ -105,13 +105,13 @@ const PaymentReviewStep: React.FC<PaymentReviewStepProps> = ({
|
|
|
105
105
|
</View>
|
|
106
106
|
|
|
107
107
|
<View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
|
|
108
|
-
<Button appearance="subtle" tone="neutral" onPress={onBack} size="
|
|
108
|
+
<Button appearance="subtle" tone="neutral" onPress={onBack} size="sm" disabled={isPaying} icon={<Ionicons name="arrow-back" size={16} />}>
|
|
109
109
|
{t('payment.actions.back')}
|
|
110
110
|
</Button>
|
|
111
111
|
<Button
|
|
112
112
|
appearance="solid" tone="accent"
|
|
113
113
|
onPress={onPay}
|
|
114
|
-
size="
|
|
114
|
+
size="sm"
|
|
115
115
|
disabled={isPaying}
|
|
116
116
|
icon={isPaying ? <ActivityIndicator size="small" color="#FFFFFF" /> : <Ionicons name="checkmark" size={16} />}
|
|
117
117
|
iconPosition="right"
|
|
@@ -57,7 +57,7 @@ const PaymentSuccessStep: React.FC<PaymentSuccessStepProps> = ({
|
|
|
57
57
|
</View>
|
|
58
58
|
</View>
|
|
59
59
|
|
|
60
|
-
<Button appearance="solid" tone="accent" onPress={onDone} size="
|
|
60
|
+
<Button appearance="solid" tone="accent" onPress={onDone} size="sm" icon={<Ionicons name="checkmark" size={16} />} iconPosition="right">
|
|
61
61
|
{t('payment.actions.done')}
|
|
62
62
|
</Button>
|
|
63
63
|
</Animated.View>
|
|
@@ -145,10 +145,10 @@ const PaymentSummaryStep: React.FC<PaymentSummaryStepProps> = ({
|
|
|
145
145
|
</View>
|
|
146
146
|
|
|
147
147
|
<View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
|
|
148
|
-
<Button appearance="subtle" tone="neutral" onPress={onClose} size="
|
|
148
|
+
<Button appearance="subtle" tone="neutral" onPress={onClose} size="sm" icon={<Ionicons name="close" size={16} />}>
|
|
149
149
|
{t('payment.actions.close')}
|
|
150
150
|
</Button>
|
|
151
|
-
<Button appearance="solid" tone="accent" onPress={onNext} size="
|
|
151
|
+
<Button appearance="solid" tone="accent" onPress={onNext} size="sm" icon={<Ionicons name="arrow-forward" size={16} />} iconPosition="right">
|
|
152
152
|
{t('payment.actions.continue')}
|
|
153
153
|
</Button>
|
|
154
154
|
</View>
|
|
@@ -103,7 +103,7 @@ export const OxyAuthLoading: React.FC = () => {
|
|
|
103
103
|
const { t } = useI18n();
|
|
104
104
|
return (
|
|
105
105
|
<View className="min-h-[300px] items-center justify-center">
|
|
106
|
-
<Loading variant="spinner" size="
|
|
106
|
+
<Loading variant="spinner" size="lg" accessibilityLabel={t('common.status.loading')} testID="oxy-auth-loading" />
|
|
107
107
|
</View>
|
|
108
108
|
);
|
|
109
109
|
};
|
|
@@ -204,7 +204,7 @@ export const OxyLinkCommonsPanel: React.FC<OxyLinkCommonsPanelProps> = ({ onLink
|
|
|
204
204
|
<OxyAuthScreen>
|
|
205
205
|
<OxyAuthScreenHeader title={t('linkCommons.title')} description={t('linkCommons.subtitle')} />
|
|
206
206
|
<View style={styles.plateRow}>
|
|
207
|
-
<Loading variant="spinner" size="
|
|
207
|
+
<Loading variant="spinner" size="lg" accessibilityLabel={t('common.status.loading')} />
|
|
208
208
|
</View>
|
|
209
209
|
</OxyAuthScreen>
|
|
210
210
|
);
|
|
@@ -95,7 +95,7 @@ export const OxyPasswordPanel: React.FC<OxyPasswordPanelProps> = ({ onDone, onCa
|
|
|
95
95
|
{/* The field names the switch ("Sign out everywhere else, switch, off")
|
|
96
96
|
and, on web, its label toggles it. One row: label, then the switch. */}
|
|
97
97
|
<Field label={t('signInSecurity.password.signOutOthers')} style={styles.row}>
|
|
98
|
-
<Switch
|
|
98
|
+
<Switch checked={signOutOthers} onCheckedChange={setSignOutOthers} testID="password-sign-out-others" />
|
|
99
99
|
</Field>
|
|
100
100
|
</ReauthStep>
|
|
101
101
|
);
|
|
@@ -72,7 +72,8 @@ export function ActionSheetSurface<T extends string>({
|
|
|
72
72
|
{options.map((option) => (
|
|
73
73
|
<Button
|
|
74
74
|
key={option.value}
|
|
75
|
-
|
|
75
|
+
appearance="solid"
|
|
76
|
+
tone={option.destructive ? 'danger' : 'accent'}
|
|
76
77
|
onPress={() => surface.dismiss(option.value)}
|
|
77
78
|
>
|
|
78
79
|
{option.label}
|
|
@@ -291,7 +291,7 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
291
291
|
<Text className="text-caption font-caption text-text-secondary px-space-4">
|
|
292
292
|
{t('accounts.members.invite.roleLabel') || 'Role'}
|
|
293
293
|
</Text>
|
|
294
|
-
<SegmentedControl type="radio" label={t('accounts.members.invite.roleLabel') || 'Role'} value={inviteRole} onValueChange={setInviteRole} size="
|
|
294
|
+
<SegmentedControl type="radio" label={t('accounts.members.invite.roleLabel') || 'Role'} value={inviteRole} onValueChange={setInviteRole} size="sm">
|
|
295
295
|
{ASSIGNABLE_ROLES.map(role => <SegmentedControlItem key={role} value={role}>
|
|
296
296
|
<SegmentedControlItemText>{roleLabel(role)}</SegmentedControlItemText>
|
|
297
297
|
</SegmentedControlItem>)}
|
|
@@ -372,7 +372,7 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
372
372
|
{/* Role chips (editable) or static role label */}
|
|
373
373
|
{canEditThisRole ? (
|
|
374
374
|
<SegmentedControl type="radio" label={t('accounts.members.invite.roleLabel') || 'Role'}
|
|
375
|
-
value={member.role} onValueChange={role => handleChangeRole(member, role)} disabled={updateMutation.isPending} size="
|
|
375
|
+
value={member.role} onValueChange={role => handleChangeRole(member, role)} disabled={updateMutation.isPending} size="sm">
|
|
376
376
|
{ASSIGNABLE_ROLES.map(role => <SegmentedControlItem key={role} value={role}>
|
|
377
377
|
<SegmentedControlItemText>{roleLabel(role)}</SegmentedControlItemText>
|
|
378
378
|
</SegmentedControlItem>)}
|
|
@@ -387,7 +387,7 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
387
387
|
{(canTransferToThis || canRemoveThisMember) ? (
|
|
388
388
|
<View className="flex-row gap-space-16 pt-space-4">
|
|
389
389
|
{canTransferToThis ? (
|
|
390
|
-
<Button appearance="plain" tone="neutral" size="
|
|
390
|
+
<Button appearance="plain" tone="neutral" size="sm"
|
|
391
391
|
accessibilityRole="button"
|
|
392
392
|
accessibilityLabel={t('accounts.members.actions.transfer') || 'Transfer ownership'}
|
|
393
393
|
onPress={() => confirmTransfer(member)}
|
|
@@ -400,7 +400,7 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
400
400
|
</Button>
|
|
401
401
|
) : null}
|
|
402
402
|
{canRemoveThisMember ? (
|
|
403
|
-
<Button appearance="plain" tone="neutral" size="
|
|
403
|
+
<Button appearance="plain" tone="neutral" size="sm"
|
|
404
404
|
accessibilityRole="button"
|
|
405
405
|
accessibilityLabel={t('accounts.members.actions.remove') || 'Remove member'}
|
|
406
406
|
onPress={() => confirmRemove(member)}
|
|
@@ -146,7 +146,7 @@ const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) =>
|
|
|
146
146
|
return (
|
|
147
147
|
<View className="flex-1 bg-bg">
|
|
148
148
|
{isLoading && !apps ? (
|
|
149
|
-
<Loading size="
|
|
149
|
+
<Loading size="lg" color={bloomTheme.colors.primary} />
|
|
150
150
|
) : (
|
|
151
151
|
<FlatList
|
|
152
152
|
data={apps ?? []}
|
|
@@ -94,7 +94,7 @@ const DomainsScreen: React.FC<BaseScreenProps> = () => {
|
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
if (domains.isPending && isAuthenticated) {
|
|
97
|
-
return <Loading size="
|
|
97
|
+
return <Loading size="lg" color={bloomTheme.colors.primary} />;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
return (
|
|
@@ -704,11 +704,10 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
704
704
|
</TextField>
|
|
705
705
|
</View>
|
|
706
706
|
<Button
|
|
707
|
-
variant="icon"
|
|
708
707
|
onPress={handleAddItem}
|
|
709
708
|
disabled={!newItemValue.trim()}
|
|
710
709
|
accessibilityLabel={addLabel}
|
|
711
|
-
icon={<Ionicons name="add" size={20} color={bloomTheme.colors.primaryForeground} />}
|
|
710
|
+
icon={<Ionicons name="add" size={20} color={bloomTheme.colors.primaryForeground} />} iconOnly tone="neutral" appearance="outline"
|
|
712
711
|
/>
|
|
713
712
|
</View>
|
|
714
713
|
|
|
@@ -776,13 +776,13 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
776
776
|
// Hide action buttons when selecting (in selectMode or bulk operations mode)
|
|
777
777
|
rightElement: (!selectMode && selectedIds.size === 0) ? (
|
|
778
778
|
<View className="flex-row items-center gap-1.5 ml-3">
|
|
779
|
-
{(isImage || isVideo || file.contentType.includes('pdf')) ? <Button appearance="subtle" tone="neutral" size="
|
|
779
|
+
{(isImage || isVideo || file.contentType.includes('pdf')) ? <Button appearance="subtle" tone="neutral" size="sm" iconOnly stopPropagation
|
|
780
780
|
accessibilityLabel={`Preview ${file.filename}`} onPress={() => handleFileOpen(file)}
|
|
781
781
|
icon={<Ionicons name="eye" size={18} color={colors.text} />} /> : null}
|
|
782
|
-
<Button appearance="subtle" tone="neutral" size="
|
|
782
|
+
<Button appearance="subtle" tone="neutral" size="sm" iconOnly stopPropagation
|
|
783
783
|
accessibilityLabel={`Download ${file.filename}`} onPress={() => handleFileDownload(file.id, file.filename)}
|
|
784
784
|
icon={<Ionicons name="download" size={18} color={colors.text} />} />
|
|
785
|
-
<Button appearance="subtle" tone="danger" size="
|
|
785
|
+
<Button appearance="subtle" tone="danger" size="sm" iconOnly stopPropagation loading={deletingId === file.id}
|
|
786
786
|
accessibilityLabel={`Delete ${file.filename}`} onPress={() => confirmFileDelete(file.id, file.filename)}
|
|
787
787
|
icon={<Ionicons name="trash" size={18} color={colors.error} />} />
|
|
788
788
|
</View>
|
|
@@ -1144,7 +1144,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1144
1144
|
style={{ maxWidth: '80%' }}
|
|
1145
1145
|
>
|
|
1146
1146
|
<SegmentedControl type="radio" label={t('fileManagement.viewMode') || 'File type'}
|
|
1147
|
-
value={viewMode} onValueChange={setViewMode} size="
|
|
1147
|
+
value={viewMode} onValueChange={setViewMode} size="sm">
|
|
1148
1148
|
{(['all', 'photos', 'videos', 'documents', 'audio'] as const).map(mode => (
|
|
1149
1149
|
<SegmentedControlItem key={mode} value={mode} accessibilityLabel={mode}>
|
|
1150
1150
|
<MaterialCommunityIcons
|
|
@@ -1154,7 +1154,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1154
1154
|
))}
|
|
1155
1155
|
</SegmentedControl>
|
|
1156
1156
|
</ScrollView>
|
|
1157
|
-
<Button appearance="subtle" tone="neutral" size="
|
|
1157
|
+
<Button appearance="subtle" tone="neutral" size="sm"
|
|
1158
1158
|
accessibilityRole="button"
|
|
1159
1159
|
accessibilityLabel={t('fileManagement.a11y.sortBy', {
|
|
1160
1160
|
field: sortBy,
|