@safercity/sdk-react 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -0
- package/dist/index.cjs +505 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +571 -0
- package/dist/index.d.ts +571 -0
- package/dist/index.js +473 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# @safercity/sdk-react
|
|
2
|
+
|
|
3
|
+
React hooks and components for SaferCity SDK with TanStack Query integration.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @safercity/sdk-react @tanstack/react-query
|
|
9
|
+
# or
|
|
10
|
+
bun add @safercity/sdk-react @tanstack/react-query
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### 1. Wrap Your App with Provider
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { SaferCityProvider } from '@safercity/sdk-react';
|
|
19
|
+
|
|
20
|
+
function App() {
|
|
21
|
+
return (
|
|
22
|
+
<SaferCityProvider
|
|
23
|
+
baseUrl="https://api.safercity.com"
|
|
24
|
+
token={userToken}
|
|
25
|
+
tenantId="tenant-123"
|
|
26
|
+
>
|
|
27
|
+
<YourApp />
|
|
28
|
+
</SaferCityProvider>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Use Hooks in Components
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { useUsers, usePanics, useCreatePanic } from '@safercity/sdk-react';
|
|
37
|
+
|
|
38
|
+
function Dashboard() {
|
|
39
|
+
const { data: users, isLoading } = useUsers();
|
|
40
|
+
const { data: panics } = usePanics({ status: 'active' });
|
|
41
|
+
|
|
42
|
+
if (isLoading) return <div>Loading...</div>;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div>
|
|
46
|
+
<h1>Users: {users?.data.users.length}</h1>
|
|
47
|
+
<h1>Active Panics: {panics?.data.panics.length}</h1>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function PanicButton({ userId }: { userId: string }) {
|
|
53
|
+
const createPanic = useCreatePanic();
|
|
54
|
+
|
|
55
|
+
const handlePanic = () => {
|
|
56
|
+
createPanic.mutate({
|
|
57
|
+
userId,
|
|
58
|
+
latitude: -26.2041,
|
|
59
|
+
longitude: 28.0473,
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<button onClick={handlePanic} disabled={createPanic.isPending}>
|
|
65
|
+
{createPanic.isPending ? 'Creating...' : 'Trigger Panic'}
|
|
66
|
+
</button>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Streaming Hook
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { usePanicStream } from '@safercity/sdk-react';
|
|
75
|
+
|
|
76
|
+
function PanicTracker({ panicId }: { panicId: string }) {
|
|
77
|
+
const { data, isConnected, error, events } = usePanicStream(panicId, {
|
|
78
|
+
keepHistory: true,
|
|
79
|
+
onEvent: (event) => console.log('Update:', event),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
83
|
+
if (!isConnected) return <div>Connecting...</div>;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div>
|
|
87
|
+
<p>Latest: {data?.data}</p>
|
|
88
|
+
<p>Total events: {events.length}</p>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Available Hooks
|
|
95
|
+
|
|
96
|
+
### Health & Auth
|
|
97
|
+
- `useHealthCheck()` - API health status
|
|
98
|
+
- `useWhoAmI()` - Current auth context
|
|
99
|
+
|
|
100
|
+
### Users
|
|
101
|
+
- `useUsers(filters?)` - List users
|
|
102
|
+
- `useUser(userId)` - Get user by ID
|
|
103
|
+
- `useCreateUser()` - Create user mutation
|
|
104
|
+
- `useUpdateUser()` - Update user mutation
|
|
105
|
+
- `useDeleteUser()` - Delete user mutation
|
|
106
|
+
|
|
107
|
+
### Panics
|
|
108
|
+
- `usePanics(filters?)` - List panics
|
|
109
|
+
- `usePanic(panicId, query?)` - Get panic by ID
|
|
110
|
+
- `useCreatePanic()` - Create panic mutation
|
|
111
|
+
- `useUpdatePanicLocation()` - Update location mutation
|
|
112
|
+
- `useCancelPanic()` - Cancel panic mutation
|
|
113
|
+
- `usePanicStream(panicId, options?)` - Stream panic updates
|
|
114
|
+
|
|
115
|
+
### Subscriptions
|
|
116
|
+
- `useSubscriptionTypes()` - List subscription types
|
|
117
|
+
- `useSubscriptions(filters?)` - List subscriptions
|
|
118
|
+
- `useSubscriptionStats()` - Get statistics
|
|
119
|
+
- `useCreateSubscription()` - Create subscription mutation
|
|
120
|
+
|
|
121
|
+
### Location Safety
|
|
122
|
+
- `useLocationSafety(lat, lng, radius?)` - Check location safety
|
|
123
|
+
|
|
124
|
+
### Crimes
|
|
125
|
+
- `useCrimes(filters?)` - List crimes
|
|
126
|
+
- `useCrimeCategories()` - Get crime categories
|
|
127
|
+
- `useCrimeTypes()` - Get crime types
|
|
128
|
+
|
|
129
|
+
## Query Keys
|
|
130
|
+
|
|
131
|
+
Use query keys for manual cache management:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { saferCityKeys, useQueryClient } from '@safercity/sdk-react';
|
|
135
|
+
|
|
136
|
+
function RefreshButton() {
|
|
137
|
+
const queryClient = useQueryClient();
|
|
138
|
+
|
|
139
|
+
const refresh = () => {
|
|
140
|
+
// Invalidate all user queries
|
|
141
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.users() });
|
|
142
|
+
|
|
143
|
+
// Invalidate specific user
|
|
144
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.usersDetail('user-123') });
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return <button onClick={refresh}>Refresh</button>;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Custom QueryClient
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { SaferCityProvider } from '@safercity/sdk-react';
|
|
155
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
156
|
+
|
|
157
|
+
const queryClient = new QueryClient({
|
|
158
|
+
defaultOptions: {
|
|
159
|
+
queries: {
|
|
160
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
function App() {
|
|
166
|
+
return (
|
|
167
|
+
<SaferCityProvider
|
|
168
|
+
baseUrl="https://api.safercity.com"
|
|
169
|
+
token={token}
|
|
170
|
+
queryClient={queryClient}
|
|
171
|
+
>
|
|
172
|
+
<YourApp />
|
|
173
|
+
</SaferCityProvider>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Access Raw Client
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { useSaferCityClient } from '@safercity/sdk-react';
|
|
182
|
+
|
|
183
|
+
function CustomComponent() {
|
|
184
|
+
const client = useSaferCityClient();
|
|
185
|
+
|
|
186
|
+
const customRequest = async () => {
|
|
187
|
+
// Use raw client for custom requests
|
|
188
|
+
const response = await client._client.get('/custom-endpoint');
|
|
189
|
+
return response.data;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sdk = require('@safercity/sdk');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var reactQuery = require('@tanstack/react-query');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
var SaferCityContext = react.createContext(null);
|
|
9
|
+
function SaferCityProvider({
|
|
10
|
+
children,
|
|
11
|
+
queryClient: externalQueryClient,
|
|
12
|
+
...clientOptions
|
|
13
|
+
}) {
|
|
14
|
+
const client = react.useMemo(
|
|
15
|
+
() => sdk.createSaferCityClient(clientOptions),
|
|
16
|
+
// Only recreate if baseUrl or tenantId changes
|
|
17
|
+
// Token updates should use client.setToken()
|
|
18
|
+
[clientOptions.baseUrl, clientOptions.tenantId]
|
|
19
|
+
);
|
|
20
|
+
react.useMemo(() => {
|
|
21
|
+
client.setToken(clientOptions.token);
|
|
22
|
+
}, [client, clientOptions.token]);
|
|
23
|
+
const queryClient = react.useMemo(
|
|
24
|
+
() => externalQueryClient ?? new reactQuery.QueryClient({
|
|
25
|
+
defaultOptions: {
|
|
26
|
+
queries: {
|
|
27
|
+
staleTime: 1e3 * 60,
|
|
28
|
+
// 1 minute
|
|
29
|
+
gcTime: 1e3 * 60 * 5,
|
|
30
|
+
// 5 minutes
|
|
31
|
+
retry: 1,
|
|
32
|
+
refetchOnWindowFocus: false
|
|
33
|
+
},
|
|
34
|
+
mutations: {
|
|
35
|
+
retry: 0
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}),
|
|
39
|
+
[externalQueryClient]
|
|
40
|
+
);
|
|
41
|
+
const contextValue = react.useMemo(() => ({ client }), [client]);
|
|
42
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsxRuntime.jsx(SaferCityContext.Provider, { value: contextValue, children }) });
|
|
43
|
+
}
|
|
44
|
+
function useSaferCity() {
|
|
45
|
+
const context = react.useContext(SaferCityContext);
|
|
46
|
+
if (!context) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
"useSaferCity must be used within a SaferCityProvider. Make sure your component is wrapped with <SaferCityProvider>."
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return context;
|
|
52
|
+
}
|
|
53
|
+
function useSaferCityClient() {
|
|
54
|
+
return useSaferCity().client;
|
|
55
|
+
}
|
|
56
|
+
var saferCityKeys = {
|
|
57
|
+
all: ["safercity"],
|
|
58
|
+
// Health
|
|
59
|
+
health: () => [...saferCityKeys.all, "health"],
|
|
60
|
+
// Auth
|
|
61
|
+
auth: () => [...saferCityKeys.all, "auth"],
|
|
62
|
+
authWhoami: () => [...saferCityKeys.auth(), "whoami"],
|
|
63
|
+
// Users
|
|
64
|
+
users: () => [...saferCityKeys.all, "users"],
|
|
65
|
+
usersList: (filters) => [...saferCityKeys.users(), "list", filters],
|
|
66
|
+
usersDetail: (userId) => [...saferCityKeys.users(), "detail", userId],
|
|
67
|
+
// Panics
|
|
68
|
+
panics: () => [...saferCityKeys.all, "panics"],
|
|
69
|
+
panicsList: (filters) => [...saferCityKeys.panics(), "list", filters],
|
|
70
|
+
panicsDetail: (panicId) => [...saferCityKeys.panics(), "detail", panicId],
|
|
71
|
+
// Subscriptions
|
|
72
|
+
subscriptions: () => [...saferCityKeys.all, "subscriptions"],
|
|
73
|
+
subscriptionsList: (filters) => [...saferCityKeys.subscriptions(), "list", filters],
|
|
74
|
+
subscriptionsTypes: () => [...saferCityKeys.subscriptions(), "types"],
|
|
75
|
+
subscriptionsStats: () => [...saferCityKeys.subscriptions(), "stats"],
|
|
76
|
+
// Location Safety
|
|
77
|
+
locationSafety: (lat, lng) => [...saferCityKeys.all, "location-safety", lat, lng],
|
|
78
|
+
// Crimes
|
|
79
|
+
crimes: () => [...saferCityKeys.all, "crimes"],
|
|
80
|
+
crimesList: (filters) => [...saferCityKeys.crimes(), "list", filters],
|
|
81
|
+
crimesCategories: () => [...saferCityKeys.crimes(), "categories"],
|
|
82
|
+
crimesTypes: () => [...saferCityKeys.crimes(), "types"]
|
|
83
|
+
};
|
|
84
|
+
function useHealthCheck(options) {
|
|
85
|
+
const client = useSaferCityClient();
|
|
86
|
+
return reactQuery.useQuery({
|
|
87
|
+
queryKey: saferCityKeys.health(),
|
|
88
|
+
queryFn: () => client.health.check(),
|
|
89
|
+
...options
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function useWhoAmI(options) {
|
|
93
|
+
const client = useSaferCityClient();
|
|
94
|
+
return reactQuery.useQuery({
|
|
95
|
+
queryKey: saferCityKeys.authWhoami(),
|
|
96
|
+
queryFn: () => client.auth.whoami(),
|
|
97
|
+
...options
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function useUsers(filters, options) {
|
|
101
|
+
const client = useSaferCityClient();
|
|
102
|
+
return reactQuery.useQuery({
|
|
103
|
+
queryKey: saferCityKeys.usersList(filters),
|
|
104
|
+
queryFn: () => client.users.list(filters),
|
|
105
|
+
...options
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function useUser(userId, options) {
|
|
109
|
+
const client = useSaferCityClient();
|
|
110
|
+
return reactQuery.useQuery({
|
|
111
|
+
queryKey: saferCityKeys.usersDetail(userId),
|
|
112
|
+
queryFn: () => client.users.get(userId),
|
|
113
|
+
enabled: !!userId,
|
|
114
|
+
...options
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function useCreateUser(options) {
|
|
118
|
+
const client = useSaferCityClient();
|
|
119
|
+
const queryClient = reactQuery.useQueryClient();
|
|
120
|
+
return reactQuery.useMutation({
|
|
121
|
+
mutationFn: (data) => client.users.create(data),
|
|
122
|
+
onSuccess: () => {
|
|
123
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.users() });
|
|
124
|
+
},
|
|
125
|
+
...options
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function useUpdateUser(options) {
|
|
129
|
+
const client = useSaferCityClient();
|
|
130
|
+
const queryClient = reactQuery.useQueryClient();
|
|
131
|
+
return reactQuery.useMutation({
|
|
132
|
+
mutationFn: ({ userId, data }) => client.users.update(userId, data),
|
|
133
|
+
onSuccess: (_, { userId }) => {
|
|
134
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.usersDetail(userId) });
|
|
135
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.usersList() });
|
|
136
|
+
},
|
|
137
|
+
...options
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function useDeleteUser(options) {
|
|
141
|
+
const client = useSaferCityClient();
|
|
142
|
+
const queryClient = reactQuery.useQueryClient();
|
|
143
|
+
return reactQuery.useMutation({
|
|
144
|
+
mutationFn: (userId) => client.users.delete(userId),
|
|
145
|
+
onSuccess: () => {
|
|
146
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.users() });
|
|
147
|
+
},
|
|
148
|
+
...options
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function usePanics(filters, options) {
|
|
152
|
+
const client = useSaferCityClient();
|
|
153
|
+
return reactQuery.useQuery({
|
|
154
|
+
queryKey: saferCityKeys.panicsList(filters),
|
|
155
|
+
queryFn: () => client.panics.list(filters),
|
|
156
|
+
...options
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function usePanic(panicId, query, options) {
|
|
160
|
+
const client = useSaferCityClient();
|
|
161
|
+
return reactQuery.useQuery({
|
|
162
|
+
queryKey: saferCityKeys.panicsDetail(panicId),
|
|
163
|
+
queryFn: () => client.panics.get(panicId, query),
|
|
164
|
+
enabled: !!panicId,
|
|
165
|
+
...options
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function useCreatePanic(options) {
|
|
169
|
+
const client = useSaferCityClient();
|
|
170
|
+
const queryClient = reactQuery.useQueryClient();
|
|
171
|
+
return reactQuery.useMutation({
|
|
172
|
+
mutationFn: (data) => client.panics.create(data),
|
|
173
|
+
onSuccess: () => {
|
|
174
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.panics() });
|
|
175
|
+
},
|
|
176
|
+
...options
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function useUpdatePanicLocation(options) {
|
|
180
|
+
const client = useSaferCityClient();
|
|
181
|
+
const queryClient = reactQuery.useQueryClient();
|
|
182
|
+
return reactQuery.useMutation({
|
|
183
|
+
mutationFn: ({ panicId, data }) => client.panics.updateLocation(panicId, data),
|
|
184
|
+
onSuccess: (_, { panicId }) => {
|
|
185
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.panicsDetail(panicId) });
|
|
186
|
+
},
|
|
187
|
+
...options
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function useCancelPanic(options) {
|
|
191
|
+
const client = useSaferCityClient();
|
|
192
|
+
const queryClient = reactQuery.useQueryClient();
|
|
193
|
+
return reactQuery.useMutation({
|
|
194
|
+
mutationFn: ({ panicId, data }) => client.panics.cancel(panicId, data),
|
|
195
|
+
onSuccess: (_, { panicId }) => {
|
|
196
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.panicsDetail(panicId) });
|
|
197
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.panicsList() });
|
|
198
|
+
},
|
|
199
|
+
...options
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function useSubscriptionTypes(options) {
|
|
203
|
+
const client = useSaferCityClient();
|
|
204
|
+
return reactQuery.useQuery({
|
|
205
|
+
queryKey: saferCityKeys.subscriptionsTypes(),
|
|
206
|
+
queryFn: () => client.subscriptions.listTypes(),
|
|
207
|
+
staleTime: 1e3 * 60 * 10,
|
|
208
|
+
// 10 minutes - types don't change often
|
|
209
|
+
...options
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function useSubscriptions(filters, options) {
|
|
213
|
+
const client = useSaferCityClient();
|
|
214
|
+
return reactQuery.useQuery({
|
|
215
|
+
queryKey: saferCityKeys.subscriptionsList(filters),
|
|
216
|
+
queryFn: () => client.subscriptions.list(filters),
|
|
217
|
+
...options
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function useSubscriptionStats(options) {
|
|
221
|
+
const client = useSaferCityClient();
|
|
222
|
+
return reactQuery.useQuery({
|
|
223
|
+
queryKey: saferCityKeys.subscriptionsStats(),
|
|
224
|
+
queryFn: () => client.subscriptions.stats(),
|
|
225
|
+
...options
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function useCreateSubscription(options) {
|
|
229
|
+
const client = useSaferCityClient();
|
|
230
|
+
const queryClient = reactQuery.useQueryClient();
|
|
231
|
+
return reactQuery.useMutation({
|
|
232
|
+
mutationFn: (data) => client.subscriptions.create(data),
|
|
233
|
+
onSuccess: () => {
|
|
234
|
+
queryClient.invalidateQueries({ queryKey: saferCityKeys.subscriptions() });
|
|
235
|
+
},
|
|
236
|
+
...options
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function useLocationSafety(latitude, longitude, radius, options) {
|
|
240
|
+
const client = useSaferCityClient();
|
|
241
|
+
return reactQuery.useQuery({
|
|
242
|
+
queryKey: saferCityKeys.locationSafety(latitude, longitude),
|
|
243
|
+
queryFn: () => client.locationSafety.check({ latitude, longitude, radius }),
|
|
244
|
+
enabled: latitude !== void 0 && longitude !== void 0,
|
|
245
|
+
staleTime: 1e3 * 60 * 5,
|
|
246
|
+
// 5 minutes
|
|
247
|
+
...options
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
function useCrimes(filters, options) {
|
|
251
|
+
const client = useSaferCityClient();
|
|
252
|
+
return reactQuery.useQuery({
|
|
253
|
+
queryKey: saferCityKeys.crimesList(filters),
|
|
254
|
+
queryFn: () => client.crimes.list(filters),
|
|
255
|
+
...options
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function useCrimeCategories(options) {
|
|
259
|
+
const client = useSaferCityClient();
|
|
260
|
+
return reactQuery.useQuery({
|
|
261
|
+
queryKey: saferCityKeys.crimesCategories(),
|
|
262
|
+
queryFn: () => client.crimes.categories(),
|
|
263
|
+
staleTime: 1e3 * 60 * 30,
|
|
264
|
+
// 30 minutes - categories rarely change
|
|
265
|
+
...options
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
function useCrimeTypes(options) {
|
|
269
|
+
const client = useSaferCityClient();
|
|
270
|
+
return reactQuery.useQuery({
|
|
271
|
+
queryKey: saferCityKeys.crimesTypes(),
|
|
272
|
+
queryFn: () => client.crimes.types(),
|
|
273
|
+
staleTime: 1e3 * 60 * 30,
|
|
274
|
+
// 30 minutes
|
|
275
|
+
...options
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function usePanicStream(panicId, options = {}) {
|
|
279
|
+
const client = useSaferCityClient();
|
|
280
|
+
const {
|
|
281
|
+
autoConnect = true,
|
|
282
|
+
keepHistory = false,
|
|
283
|
+
maxHistory = 100,
|
|
284
|
+
transform,
|
|
285
|
+
onOpen,
|
|
286
|
+
onEvent,
|
|
287
|
+
onError,
|
|
288
|
+
onClose
|
|
289
|
+
} = options;
|
|
290
|
+
const [state, setState] = react.useState({
|
|
291
|
+
data: null,
|
|
292
|
+
events: [],
|
|
293
|
+
isConnected: false,
|
|
294
|
+
isConnecting: false,
|
|
295
|
+
error: null
|
|
296
|
+
});
|
|
297
|
+
const abortControllerRef = react.useRef(null);
|
|
298
|
+
const isActiveRef = react.useRef(false);
|
|
299
|
+
const connect = react.useCallback(async () => {
|
|
300
|
+
if (!panicId || isActiveRef.current) return;
|
|
301
|
+
isActiveRef.current = true;
|
|
302
|
+
abortControllerRef.current = new AbortController();
|
|
303
|
+
setState((prev) => ({
|
|
304
|
+
...prev,
|
|
305
|
+
isConnecting: true,
|
|
306
|
+
error: null
|
|
307
|
+
}));
|
|
308
|
+
try {
|
|
309
|
+
const eventSourceOptions = {
|
|
310
|
+
signal: abortControllerRef.current.signal,
|
|
311
|
+
onOpen: () => {
|
|
312
|
+
setState((prev) => ({
|
|
313
|
+
...prev,
|
|
314
|
+
isConnected: true,
|
|
315
|
+
isConnecting: false
|
|
316
|
+
}));
|
|
317
|
+
onOpen?.();
|
|
318
|
+
},
|
|
319
|
+
onError: (err) => {
|
|
320
|
+
setState((prev) => ({
|
|
321
|
+
...prev,
|
|
322
|
+
isConnected: false,
|
|
323
|
+
isConnecting: false,
|
|
324
|
+
error: err
|
|
325
|
+
}));
|
|
326
|
+
onError?.(err);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
const stream = client.panics.streamUpdates(panicId, eventSourceOptions);
|
|
330
|
+
for await (const event of stream) {
|
|
331
|
+
if (!isActiveRef.current) break;
|
|
332
|
+
const transformedEvent = transform ? transform(event) : event;
|
|
333
|
+
setState((prev) => {
|
|
334
|
+
const newEvents = keepHistory ? [...prev.events, transformedEvent].slice(-maxHistory) : [];
|
|
335
|
+
return {
|
|
336
|
+
...prev,
|
|
337
|
+
data: transformedEvent,
|
|
338
|
+
events: newEvents
|
|
339
|
+
};
|
|
340
|
+
});
|
|
341
|
+
onEvent?.(transformedEvent);
|
|
342
|
+
}
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (isActiveRef.current) {
|
|
345
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
346
|
+
setState((prev) => ({
|
|
347
|
+
...prev,
|
|
348
|
+
isConnected: false,
|
|
349
|
+
isConnecting: false,
|
|
350
|
+
error
|
|
351
|
+
}));
|
|
352
|
+
onError?.(error);
|
|
353
|
+
}
|
|
354
|
+
} finally {
|
|
355
|
+
isActiveRef.current = false;
|
|
356
|
+
setState((prev) => ({
|
|
357
|
+
...prev,
|
|
358
|
+
isConnected: false,
|
|
359
|
+
isConnecting: false
|
|
360
|
+
}));
|
|
361
|
+
onClose?.();
|
|
362
|
+
}
|
|
363
|
+
}, [panicId, client, transform, keepHistory, maxHistory, onOpen, onEvent, onError, onClose]);
|
|
364
|
+
const disconnect = react.useCallback(() => {
|
|
365
|
+
isActiveRef.current = false;
|
|
366
|
+
abortControllerRef.current?.abort();
|
|
367
|
+
abortControllerRef.current = null;
|
|
368
|
+
}, []);
|
|
369
|
+
react.useEffect(() => {
|
|
370
|
+
if (autoConnect && panicId) {
|
|
371
|
+
connect();
|
|
372
|
+
}
|
|
373
|
+
return () => {
|
|
374
|
+
disconnect();
|
|
375
|
+
};
|
|
376
|
+
}, [autoConnect, panicId, connect, disconnect]);
|
|
377
|
+
return {
|
|
378
|
+
...state,
|
|
379
|
+
connect,
|
|
380
|
+
disconnect
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function useStream(createStream, options = {}) {
|
|
384
|
+
const {
|
|
385
|
+
autoConnect = true,
|
|
386
|
+
keepHistory = false,
|
|
387
|
+
maxHistory = 100,
|
|
388
|
+
transform,
|
|
389
|
+
onOpen,
|
|
390
|
+
onEvent,
|
|
391
|
+
onError,
|
|
392
|
+
onClose
|
|
393
|
+
} = options;
|
|
394
|
+
const [state, setState] = react.useState({
|
|
395
|
+
data: null,
|
|
396
|
+
events: [],
|
|
397
|
+
isConnected: false,
|
|
398
|
+
isConnecting: false,
|
|
399
|
+
error: null
|
|
400
|
+
});
|
|
401
|
+
const isActiveRef = react.useRef(false);
|
|
402
|
+
const createStreamRef = react.useRef(createStream);
|
|
403
|
+
createStreamRef.current = createStream;
|
|
404
|
+
const connect = react.useCallback(async () => {
|
|
405
|
+
if (isActiveRef.current) return;
|
|
406
|
+
isActiveRef.current = true;
|
|
407
|
+
setState((prev) => ({
|
|
408
|
+
...prev,
|
|
409
|
+
isConnecting: true,
|
|
410
|
+
error: null
|
|
411
|
+
}));
|
|
412
|
+
try {
|
|
413
|
+
const stream = createStreamRef.current();
|
|
414
|
+
setState((prev) => ({
|
|
415
|
+
...prev,
|
|
416
|
+
isConnected: true,
|
|
417
|
+
isConnecting: false
|
|
418
|
+
}));
|
|
419
|
+
onOpen?.();
|
|
420
|
+
for await (const event of stream) {
|
|
421
|
+
if (!isActiveRef.current) break;
|
|
422
|
+
const transformedEvent = transform ? transform(event) : event;
|
|
423
|
+
setState((prev) => {
|
|
424
|
+
const newEvents = keepHistory ? [...prev.events, transformedEvent].slice(-maxHistory) : [];
|
|
425
|
+
return {
|
|
426
|
+
...prev,
|
|
427
|
+
data: transformedEvent,
|
|
428
|
+
events: newEvents
|
|
429
|
+
};
|
|
430
|
+
});
|
|
431
|
+
onEvent?.(transformedEvent);
|
|
432
|
+
}
|
|
433
|
+
} catch (err) {
|
|
434
|
+
if (isActiveRef.current) {
|
|
435
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
436
|
+
setState((prev) => ({
|
|
437
|
+
...prev,
|
|
438
|
+
isConnected: false,
|
|
439
|
+
isConnecting: false,
|
|
440
|
+
error
|
|
441
|
+
}));
|
|
442
|
+
onError?.(error);
|
|
443
|
+
}
|
|
444
|
+
} finally {
|
|
445
|
+
isActiveRef.current = false;
|
|
446
|
+
setState((prev) => ({
|
|
447
|
+
...prev,
|
|
448
|
+
isConnected: false,
|
|
449
|
+
isConnecting: false
|
|
450
|
+
}));
|
|
451
|
+
onClose?.();
|
|
452
|
+
}
|
|
453
|
+
}, [transform, keepHistory, maxHistory, onOpen, onEvent, onError, onClose]);
|
|
454
|
+
const disconnect = react.useCallback(() => {
|
|
455
|
+
isActiveRef.current = false;
|
|
456
|
+
}, []);
|
|
457
|
+
react.useEffect(() => {
|
|
458
|
+
if (autoConnect) {
|
|
459
|
+
connect();
|
|
460
|
+
}
|
|
461
|
+
return () => {
|
|
462
|
+
disconnect();
|
|
463
|
+
};
|
|
464
|
+
}, [autoConnect, connect, disconnect]);
|
|
465
|
+
return {
|
|
466
|
+
...state,
|
|
467
|
+
connect,
|
|
468
|
+
disconnect
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
exports.SaferCityProvider = SaferCityProvider;
|
|
473
|
+
exports.saferCityKeys = saferCityKeys;
|
|
474
|
+
exports.useCancelPanic = useCancelPanic;
|
|
475
|
+
exports.useCreatePanic = useCreatePanic;
|
|
476
|
+
exports.useCreateSubscription = useCreateSubscription;
|
|
477
|
+
exports.useCreateUser = useCreateUser;
|
|
478
|
+
exports.useCrimeCategories = useCrimeCategories;
|
|
479
|
+
exports.useCrimeTypes = useCrimeTypes;
|
|
480
|
+
exports.useCrimes = useCrimes;
|
|
481
|
+
exports.useDeleteUser = useDeleteUser;
|
|
482
|
+
exports.useHealthCheck = useHealthCheck;
|
|
483
|
+
exports.useLocationSafety = useLocationSafety;
|
|
484
|
+
exports.usePanic = usePanic;
|
|
485
|
+
exports.usePanicStream = usePanicStream;
|
|
486
|
+
exports.usePanics = usePanics;
|
|
487
|
+
exports.useSaferCity = useSaferCity;
|
|
488
|
+
exports.useSaferCityClient = useSaferCityClient;
|
|
489
|
+
exports.useStream = useStream;
|
|
490
|
+
exports.useSubscriptionStats = useSubscriptionStats;
|
|
491
|
+
exports.useSubscriptionTypes = useSubscriptionTypes;
|
|
492
|
+
exports.useSubscriptions = useSubscriptions;
|
|
493
|
+
exports.useUpdatePanicLocation = useUpdatePanicLocation;
|
|
494
|
+
exports.useUpdateUser = useUpdateUser;
|
|
495
|
+
exports.useUser = useUser;
|
|
496
|
+
exports.useUsers = useUsers;
|
|
497
|
+
exports.useWhoAmI = useWhoAmI;
|
|
498
|
+
Object.keys(sdk).forEach(function (k) {
|
|
499
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
500
|
+
enumerable: true,
|
|
501
|
+
get: function () { return sdk[k]; }
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
//# sourceMappingURL=index.cjs.map
|
|
505
|
+
//# sourceMappingURL=index.cjs.map
|