@oxyhq/services 5.16.26 → 5.16.27

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.
@@ -0,0 +1,201 @@
1
+ import { create } from 'zustand';
2
+ import { useShallow } from 'zustand/react/shallow';
3
+
4
+ export interface TransferCodeData {
5
+ code: string;
6
+ sourceDeviceId: string | null;
7
+ publicKey: string;
8
+ timestamp: number;
9
+ state: 'pending' | 'completed' | 'failed';
10
+ }
11
+
12
+ export interface TransferState {
13
+ // Transfer codes map: transferId -> TransferCodeData
14
+ transferCodes: Record<string, TransferCodeData>;
15
+
16
+ // Active transfer ID (only one active transfer at a time)
17
+ activeTransferId: string | null;
18
+
19
+ // Restoration flag to prevent duplicate restorations
20
+ isRestored: boolean;
21
+
22
+ // Actions
23
+ storeTransferCode: (transferId: string, code: string, sourceDeviceId: string | null, publicKey: string) => void;
24
+ getTransferCode: (transferId: string) => TransferCodeData | null;
25
+ clearTransferCode: (transferId: string) => void;
26
+ updateTransferState: (transferId: string, state: 'pending' | 'completed' | 'failed') => void;
27
+ getAllPendingTransfers: () => Array<{ transferId: string; data: TransferCodeData }>;
28
+ getActiveTransferId: () => string | null;
29
+ setActiveTransferId: (transferId: string | null) => void;
30
+ restoreFromStorage: (codes: Record<string, TransferCodeData>, activeTransferId: string | null) => void;
31
+ markRestored: () => void;
32
+ cleanupExpired: () => void;
33
+ reset: () => void;
34
+ }
35
+
36
+ const FIFTEEN_MINUTES = 15 * 60 * 1000;
37
+
38
+ const initialState = {
39
+ transferCodes: {} as Record<string, TransferCodeData>,
40
+ activeTransferId: null as string | null,
41
+ isRestored: false,
42
+ };
43
+
44
+ export const useTransferStore = create<TransferState>((set, get) => ({
45
+ ...initialState,
46
+
47
+ storeTransferCode: (transferId: string, code: string, sourceDeviceId: string | null, publicKey: string) => {
48
+ set((state) => ({
49
+ transferCodes: {
50
+ ...state.transferCodes,
51
+ [transferId]: {
52
+ code,
53
+ sourceDeviceId,
54
+ publicKey,
55
+ timestamp: Date.now(),
56
+ state: 'pending',
57
+ },
58
+ },
59
+ activeTransferId: transferId,
60
+ }));
61
+ },
62
+
63
+ getTransferCode: (transferId: string) => {
64
+ const state = get();
65
+ return state.transferCodes[transferId] || null;
66
+ },
67
+
68
+ clearTransferCode: (transferId: string) => {
69
+ set((state) => {
70
+ const { [transferId]: removed, ...rest } = state.transferCodes;
71
+ const newActiveTransferId = state.activeTransferId === transferId ? null : state.activeTransferId;
72
+ return {
73
+ transferCodes: rest,
74
+ activeTransferId: newActiveTransferId,
75
+ };
76
+ });
77
+ },
78
+
79
+ updateTransferState: (transferId: string, newState: 'pending' | 'completed' | 'failed') => {
80
+ set((state) => {
81
+ const existing = state.transferCodes[transferId];
82
+ if (!existing) {
83
+ return state;
84
+ }
85
+
86
+ const updated = {
87
+ ...existing,
88
+ state: newState,
89
+ };
90
+
91
+ // Clear active transfer if completed or failed
92
+ const newActiveTransferId =
93
+ (newState === 'completed' || newState === 'failed') && state.activeTransferId === transferId
94
+ ? null
95
+ : state.activeTransferId;
96
+
97
+ return {
98
+ transferCodes: {
99
+ ...state.transferCodes,
100
+ [transferId]: updated,
101
+ },
102
+ activeTransferId: newActiveTransferId,
103
+ };
104
+ });
105
+ },
106
+
107
+ getAllPendingTransfers: () => {
108
+ const state = get();
109
+ const pending: Array<{ transferId: string; data: TransferCodeData }> = [];
110
+
111
+ Object.entries(state.transferCodes).forEach(([transferId, data]) => {
112
+ if (data.state === 'pending') {
113
+ pending.push({ transferId, data });
114
+ }
115
+ });
116
+
117
+ return pending;
118
+ },
119
+
120
+ getActiveTransferId: () => {
121
+ return get().activeTransferId;
122
+ },
123
+
124
+ setActiveTransferId: (transferId: string | null) => {
125
+ set({ activeTransferId: transferId });
126
+ },
127
+
128
+ restoreFromStorage: (codes: Record<string, TransferCodeData>, activeTransferId: string | null) => {
129
+ const now = Date.now();
130
+ const validCodes: Record<string, TransferCodeData> = {};
131
+
132
+ // Only restore non-expired pending transfers
133
+ Object.entries(codes).forEach(([transferId, data]) => {
134
+ if (data.state === 'pending' && (now - data.timestamp) < FIFTEEN_MINUTES) {
135
+ validCodes[transferId] = data;
136
+ }
137
+ });
138
+
139
+ // Verify active transfer is still valid
140
+ let validActiveTransferId = activeTransferId;
141
+ if (activeTransferId && (!validCodes[activeTransferId] || validCodes[activeTransferId].state !== 'pending')) {
142
+ validActiveTransferId = null;
143
+ }
144
+
145
+ set({
146
+ transferCodes: validCodes,
147
+ activeTransferId: validActiveTransferId,
148
+ isRestored: true,
149
+ });
150
+ },
151
+
152
+ markRestored: () => {
153
+ set({ isRestored: true });
154
+ },
155
+
156
+ cleanupExpired: () => {
157
+ const now = Date.now();
158
+ set((state) => {
159
+ const validCodes: Record<string, TransferCodeData> = {};
160
+ let newActiveTransferId = state.activeTransferId;
161
+
162
+ Object.entries(state.transferCodes).forEach(([transferId, data]) => {
163
+ const age = now - data.timestamp;
164
+ if (age < FIFTEEN_MINUTES) {
165
+ validCodes[transferId] = data;
166
+ } else if (transferId === state.activeTransferId) {
167
+ newActiveTransferId = null;
168
+ }
169
+ });
170
+
171
+ return {
172
+ transferCodes: validCodes,
173
+ activeTransferId: newActiveTransferId,
174
+ };
175
+ });
176
+ },
177
+
178
+ reset: () => {
179
+ set(initialState);
180
+ },
181
+ }));
182
+
183
+ /**
184
+ * Hook to get transfer codes for persistence
185
+ */
186
+ export const useTransferCodesForPersistence = () => {
187
+ return useTransferStore(
188
+ useShallow((state) => ({
189
+ transferCodes: state.transferCodes,
190
+ activeTransferId: state.activeTransferId,
191
+ }))
192
+ );
193
+ };
194
+
195
+ /**
196
+ * Hook to check if store has been restored
197
+ */
198
+ export const useTransferStoreRestored = () => {
199
+ return useTransferStore((state) => state.isRestored);
200
+ };
201
+