@oxyhq/services 15.0.0 → 16.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/lib/commonjs/index.js +4 -4
- package/lib/commonjs/ui/hooks/mutations/mutationKeys.js +1 -1
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +9 -9
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/queryKeys.js +1 -1
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +7 -8
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/commonjs/ui/screens/ConnectedAppsScreen.js +17 -18
- package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/hooks/mutations/mutationKeys.js +1 -1
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +7 -7
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/queries/queryKeys.js +1 -1
- package/lib/module/ui/hooks/queries/useAccountQueries.js +5 -6
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/module/ui/screens/ConnectedAppsScreen.js +19 -20
- package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +4 -4
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts +6 -7
- package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/storageHelpers.d.ts +7 -7
- package/lib/typescript/module/index.d.ts +2 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +4 -4
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts +6 -7
- package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/storageHelpers.d.ts +7 -7
- package/package.json +4 -4
- package/src/index.ts +2 -2
- package/src/ui/hooks/mutations/mutationKeys.ts +1 -1
- package/src/ui/hooks/mutations/useAccountMutations.ts +7 -7
- package/src/ui/hooks/queries/queryKeys.ts +1 -1
- package/src/ui/hooks/queries/useAccountQueries.ts +8 -9
- package/src/ui/navigation/routes.ts +1 -1
- package/src/ui/screens/ConnectedAppsScreen.tsx +23 -24
- package/src/ui/utils/storageHelpers.ts +7 -7
|
@@ -10,13 +10,13 @@ export interface SessionStorageKeys {
|
|
|
10
10
|
language: string;
|
|
11
11
|
/**
|
|
12
12
|
* DURABLE "this device/app has had a signed-in Oxy session before" hint.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* Originally read at cold boot to drive the FedCM-era smart `sso-bounce`
|
|
14
|
+
* gate (a returning visitor still got one establish bounce; a first-time
|
|
15
|
+
* anonymous visitor was never force-redirected). That gate and
|
|
16
|
+
* `allowSsoBounce` were deleted in the device-first cutover — this storage
|
|
17
|
+
* key is currently defined but not read anywhere; kept rather than removed
|
|
18
|
+
* here since deleting a storage key is a logic change, out of scope for a
|
|
19
|
+
* comment sweep. Flag for a follow-up dead-field cleanup.
|
|
20
20
|
*/
|
|
21
21
|
priorSession: string;
|
|
22
22
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/services",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "16.0.0",
|
|
4
4
|
"description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -106,10 +106,10 @@
|
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
108
|
"dependencies": {
|
|
109
|
-
"@oxyhq/contracts": "0.
|
|
109
|
+
"@oxyhq/contracts": "0.10.0"
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
|
-
"@oxyhq/core": "^
|
|
112
|
+
"@oxyhq/core": "^7.0.0",
|
|
113
113
|
"nativewind": "5.0.0-preview.3",
|
|
114
114
|
"react-native-css": "^3.0.0",
|
|
115
115
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
"peerDependencies": {
|
|
154
154
|
"@expo/vector-icons": "^15.0.3",
|
|
155
155
|
"@oxyhq/bloom": ">=0.16.0",
|
|
156
|
-
"@oxyhq/core": "^
|
|
156
|
+
"@oxyhq/core": "^7.0.0",
|
|
157
157
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
158
158
|
"@react-native-community/netinfo": "^11.4.1",
|
|
159
159
|
"@tanstack/query-async-storage-persister": "^5.101",
|
package/src/index.ts
CHANGED
|
@@ -77,7 +77,7 @@ export {
|
|
|
77
77
|
useUserByUsername,
|
|
78
78
|
useUsersBySessions,
|
|
79
79
|
usePrivacySettings,
|
|
80
|
-
|
|
80
|
+
useConnectedApps,
|
|
81
81
|
} from './ui/hooks/queries/useAccountQueries';
|
|
82
82
|
export {
|
|
83
83
|
useSessions,
|
|
@@ -124,7 +124,7 @@ export {
|
|
|
124
124
|
useUpdatePrivacySettings,
|
|
125
125
|
useUpdateNotificationPreferences,
|
|
126
126
|
useUpdateUserPreferences,
|
|
127
|
-
|
|
127
|
+
useRevokeConnectedApp,
|
|
128
128
|
useUploadFile,
|
|
129
129
|
} from './ui/hooks/mutations/useAccountMutations';
|
|
130
130
|
export {
|
|
@@ -32,7 +32,7 @@ export const mutationKeys = {
|
|
|
32
32
|
removeDevice: ['mutation', 'session', 'removeDevice'] as const,
|
|
33
33
|
},
|
|
34
34
|
|
|
35
|
-
// Connected apps (
|
|
35
|
+
// Connected apps (OAuth grants)
|
|
36
36
|
connectedApps: {
|
|
37
37
|
revoke: ['mutation', 'connectedApps', 'revoke'] as const,
|
|
38
38
|
},
|
|
@@ -632,20 +632,20 @@ export const useUpdateUserPreferences = () => {
|
|
|
632
632
|
};
|
|
633
633
|
|
|
634
634
|
/**
|
|
635
|
-
* Revoke the
|
|
636
|
-
* Removes the
|
|
637
|
-
* "Connected apps" list — next sign-in
|
|
635
|
+
* Revoke the current user's grant for a connected application (by its
|
|
636
|
+
* `applicationId`). Removes the grant so the app no longer appears in the user's
|
|
637
|
+
* "Connected apps" list — next sign-in for that app will require explicit
|
|
638
638
|
* re-consent.
|
|
639
639
|
*/
|
|
640
|
-
export const
|
|
640
|
+
export const useRevokeConnectedApp = () => {
|
|
641
641
|
const { oxyServices, activeSessionId } = useOxy();
|
|
642
642
|
const queryClient = useQueryClient();
|
|
643
643
|
|
|
644
644
|
return useMutation({
|
|
645
645
|
mutationKey: [...mutationKeys.connectedApps.revoke],
|
|
646
|
-
mutationFn: async (
|
|
646
|
+
mutationFn: async (applicationId: string) => {
|
|
647
647
|
return authenticatedApiCall<void>(oxyServices, activeSessionId, () =>
|
|
648
|
-
oxyServices.
|
|
648
|
+
oxyServices.revokeAppGrant(applicationId)
|
|
649
649
|
);
|
|
650
650
|
},
|
|
651
651
|
onSuccess: () => {
|
|
@@ -653,7 +653,7 @@ export const useRevokeAuthorizedApp = () => {
|
|
|
653
653
|
},
|
|
654
654
|
onError: (error) => {
|
|
655
655
|
toast.error(
|
|
656
|
-
error instanceof Error ? error.message : 'Failed to revoke
|
|
656
|
+
error instanceof Error ? error.message : 'Failed to revoke connected app'
|
|
657
657
|
);
|
|
658
658
|
},
|
|
659
659
|
});
|
|
@@ -77,7 +77,7 @@ export const queryKeys = {
|
|
|
77
77
|
usage: (accountScope?: string) => [...queryKeys.storage.all, 'usage', accountScope] as const,
|
|
78
78
|
},
|
|
79
79
|
|
|
80
|
-
// Connected apps (
|
|
80
|
+
// Connected apps (OAuth grants the user has authorized)
|
|
81
81
|
connectedApps: {
|
|
82
82
|
all: ['connectedApps'] as const,
|
|
83
83
|
list: () => [...queryKeys.connectedApps.all, 'list'] as const,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import { useQuery, useQueries, useQueryClient } from '@tanstack/react-query';
|
|
3
3
|
import { authenticatedApiCall } from '@oxyhq/core';
|
|
4
|
-
import type {
|
|
4
|
+
import type { ConnectedApp, User } from '@oxyhq/core';
|
|
5
5
|
import { queryKeys } from './queryKeys';
|
|
6
6
|
import { mutationKeys } from '../mutations/mutationKeys';
|
|
7
7
|
import { useOxy } from '../../context/OxyContext';
|
|
@@ -205,21 +205,20 @@ export const useUsersBySessions = (sessionIds: string[], options?: { enabled?: b
|
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
/**
|
|
208
|
-
* List the
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* RP catalog. Drives the "Connected apps" management screen.
|
|
208
|
+
* List the third-party OAuth applications the current user has connected via
|
|
209
|
+
* the consent flow (`GET /auth/grants`). Drives the "Connected apps" management
|
|
210
|
+
* screen.
|
|
212
211
|
*/
|
|
213
|
-
export const
|
|
212
|
+
export const useConnectedApps = (options?: { enabled?: boolean }) => {
|
|
214
213
|
const { oxyServices, activeSessionId, isAuthenticated } = useOxy();
|
|
215
214
|
|
|
216
|
-
return useQuery<
|
|
215
|
+
return useQuery<ConnectedApp[]>({
|
|
217
216
|
queryKey: queryKeys.connectedApps.list(),
|
|
218
217
|
queryFn: async () => {
|
|
219
|
-
return authenticatedApiCall<
|
|
218
|
+
return authenticatedApiCall<ConnectedApp[]>(
|
|
220
219
|
oxyServices,
|
|
221
220
|
activeSessionId,
|
|
222
|
-
() => oxyServices.
|
|
221
|
+
() => oxyServices.listConnectedApps()
|
|
223
222
|
);
|
|
224
223
|
},
|
|
225
224
|
enabled: (options?.enabled !== false) && isAuthenticated,
|
|
@@ -43,7 +43,7 @@ export type RouteName =
|
|
|
43
43
|
| 'AccountSettings' // Per-account profile edit + members + danger zone
|
|
44
44
|
| 'AvatarCrop' // Square-crop editor presented before avatar upload
|
|
45
45
|
| 'Notifications' // Per-channel notification preferences
|
|
46
|
-
| 'ConnectedApps' //
|
|
46
|
+
| 'ConnectedApps' // OAuth-authorized third-party apps the user can revoke
|
|
47
47
|
| 'Preferences'; // General user preferences (theme, reduce-motion, etc.)
|
|
48
48
|
|
|
49
49
|
// Lazy screen loaders - functions that return screen components on-demand
|
|
@@ -3,7 +3,7 @@ import { ActivityIndicator, FlatList, RefreshControl, StyleSheet, View } from 'r
|
|
|
3
3
|
import { Dialog, toast, useDialogControl } from '@oxyhq/bloom';
|
|
4
4
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
5
5
|
import { SettingsListGroup, SettingsListItem } from '@oxyhq/bloom/settings-list';
|
|
6
|
-
import type {
|
|
6
|
+
import type { ConnectedApp } from '@oxyhq/core';
|
|
7
7
|
import { logger as loggerUtil } from '@oxyhq/core';
|
|
8
8
|
import type { BaseScreenProps } from '../types/navigation';
|
|
9
9
|
import Header from '../components/Header';
|
|
@@ -12,8 +12,8 @@ import EmptyState from '../components/EmptyState';
|
|
|
12
12
|
import LoadingState from '../components/LoadingState';
|
|
13
13
|
import { useI18n } from '../hooks/useI18n';
|
|
14
14
|
import { useOxy } from '../context/OxyContext';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { useConnectedApps } from '../hooks/queries/useAccountQueries';
|
|
16
|
+
import { useRevokeConnectedApp } from '../hooks/mutations/useAccountMutations';
|
|
17
17
|
|
|
18
18
|
const APP_ICON_SIZE = 40;
|
|
19
19
|
|
|
@@ -39,12 +39,11 @@ const formatRelative = (iso: string): string => {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* ConnectedAppsScreen — list and revoke
|
|
42
|
+
* ConnectedAppsScreen — list and revoke connected OAuth applications.
|
|
43
43
|
*
|
|
44
|
-
* Fetches via `
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* refreshes immediately.
|
|
44
|
+
* Fetches via `useConnectedApps` (drives `GET /auth/grants`) and exposes a
|
|
45
|
+
* "Revoke" action that hits `DELETE /auth/grants/:applicationId`. Each revoke
|
|
46
|
+
* invalidates the connected-apps query so the list refreshes immediately.
|
|
48
47
|
*/
|
|
49
48
|
const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) => {
|
|
50
49
|
const bloomTheme = useTheme();
|
|
@@ -55,14 +54,14 @@ const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) =>
|
|
|
55
54
|
isLoading,
|
|
56
55
|
refetch,
|
|
57
56
|
isRefetching,
|
|
58
|
-
} =
|
|
59
|
-
const revokeMutation =
|
|
57
|
+
} = useConnectedApps({ enabled: isAuthenticated });
|
|
58
|
+
const revokeMutation = useRevokeConnectedApp();
|
|
60
59
|
const revokeDialog = useDialogControl();
|
|
61
|
-
const [pendingRevoke, setPendingRevoke] = useState<
|
|
62
|
-
const [
|
|
60
|
+
const [pendingRevoke, setPendingRevoke] = useState<ConnectedApp | null>(null);
|
|
61
|
+
const [revokingAppId, setRevokingAppId] = useState<string | null>(null);
|
|
63
62
|
|
|
64
63
|
const confirmRevoke = useCallback(
|
|
65
|
-
(app:
|
|
64
|
+
(app: ConnectedApp) => {
|
|
66
65
|
setPendingRevoke(app);
|
|
67
66
|
revokeDialog.open();
|
|
68
67
|
},
|
|
@@ -74,16 +73,16 @@ const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) =>
|
|
|
74
73
|
return;
|
|
75
74
|
}
|
|
76
75
|
const target = pendingRevoke;
|
|
77
|
-
|
|
76
|
+
setRevokingAppId(target.applicationId);
|
|
78
77
|
try {
|
|
79
|
-
await revokeMutation.mutateAsync(target.
|
|
78
|
+
await revokeMutation.mutateAsync(target.applicationId);
|
|
80
79
|
toast.success(
|
|
81
80
|
t('connectedApps.toasts.revoked', { name: target.name })
|
|
82
81
|
|| `Revoked access for ${target.name}`,
|
|
83
82
|
);
|
|
84
83
|
} catch (error) {
|
|
85
84
|
loggerUtil.warn(
|
|
86
|
-
'Revoke
|
|
85
|
+
'Revoke connected app failed',
|
|
87
86
|
{ component: 'ConnectedAppsScreen' },
|
|
88
87
|
error,
|
|
89
88
|
);
|
|
@@ -92,7 +91,7 @@ const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) =>
|
|
|
92
91
|
|| 'Failed to revoke access',
|
|
93
92
|
);
|
|
94
93
|
} finally {
|
|
95
|
-
|
|
94
|
+
setRevokingAppId(null);
|
|
96
95
|
setPendingRevoke(null);
|
|
97
96
|
}
|
|
98
97
|
}, [pendingRevoke, revokeMutation, t]);
|
|
@@ -113,18 +112,18 @@ const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) =>
|
|
|
113
112
|
);
|
|
114
113
|
|
|
115
114
|
const renderItem = useCallback(
|
|
116
|
-
({ item }: { item:
|
|
117
|
-
const isRevoking =
|
|
115
|
+
({ item }: { item: ConnectedApp }) => {
|
|
116
|
+
const isRevoking = revokingAppId === item.applicationId;
|
|
118
117
|
return (
|
|
119
118
|
<SettingsListGroup>
|
|
120
119
|
<SettingsListItem
|
|
121
120
|
icon={<Avatar name={item.name} size={APP_ICON_SIZE} />}
|
|
122
121
|
title={item.name}
|
|
123
122
|
description={
|
|
124
|
-
t('connectedApps.item.
|
|
125
|
-
relative: formatRelative(item.
|
|
123
|
+
t('connectedApps.item.granted', {
|
|
124
|
+
relative: formatRelative(item.firstGrantedAt),
|
|
126
125
|
})
|
|
127
|
-
|| `
|
|
126
|
+
|| `Granted ${formatRelative(item.firstGrantedAt)}`
|
|
128
127
|
}
|
|
129
128
|
onPress={isRevoking ? undefined : () => confirmRevoke(item)}
|
|
130
129
|
disabled={isRevoking}
|
|
@@ -142,7 +141,7 @@ const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) =>
|
|
|
142
141
|
</SettingsListGroup>
|
|
143
142
|
);
|
|
144
143
|
},
|
|
145
|
-
[bloomTheme.colors.error, confirmRevoke,
|
|
144
|
+
[bloomTheme.colors.error, confirmRevoke, revokingAppId, t],
|
|
146
145
|
);
|
|
147
146
|
|
|
148
147
|
return (
|
|
@@ -158,7 +157,7 @@ const ConnectedAppsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) =>
|
|
|
158
157
|
) : (
|
|
159
158
|
<FlatList
|
|
160
159
|
data={apps ?? []}
|
|
161
|
-
keyExtractor={(item) => item.
|
|
160
|
+
keyExtractor={(item) => item.applicationId}
|
|
162
161
|
renderItem={renderItem}
|
|
163
162
|
contentContainerClassName="px-screen-margin py-space-16"
|
|
164
163
|
contentContainerStyle={styles.listContent}
|
|
@@ -11,13 +11,13 @@ export interface SessionStorageKeys {
|
|
|
11
11
|
language: string;
|
|
12
12
|
/**
|
|
13
13
|
* DURABLE "this device/app has had a signed-in Oxy session before" hint.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* Originally read at cold boot to drive the FedCM-era smart `sso-bounce`
|
|
15
|
+
* gate (a returning visitor still got one establish bounce; a first-time
|
|
16
|
+
* anonymous visitor was never force-redirected). That gate and
|
|
17
|
+
* `allowSsoBounce` were deleted in the device-first cutover — this storage
|
|
18
|
+
* key is currently defined but not read anywhere; kept rather than removed
|
|
19
|
+
* here since deleting a storage key is a logic change, out of scope for a
|
|
20
|
+
* comment sweep. Flag for a follow-up dead-field cleanup.
|
|
21
21
|
*/
|
|
22
22
|
priorSession: string;
|
|
23
23
|
}
|