@dubsdotapp/expo 0.3.0 → 0.3.2

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.
@@ -1,353 +0,0 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
2
- import {
3
- ActivityIndicator,
4
- Animated,
5
- FlatList,
6
- KeyboardAvoidingView,
7
- Modal,
8
- Platform,
9
- StyleSheet,
10
- Text,
11
- TouchableOpacity,
12
- View,
13
- } from 'react-native';
14
- import type { DeveloperCommission, DeveloperCommissionsSummary } from '../types';
15
- import { useDubs } from '../provider';
16
- import { useDubsTheme } from './theme';
17
-
18
- export interface DeveloperDashboardSheetProps {
19
- visible: boolean;
20
- onDismiss: () => void;
21
- }
22
-
23
- export function DeveloperDashboardSheet({ visible, onDismiss }: DeveloperDashboardSheetProps) {
24
- const t = useDubsTheme();
25
- const { client } = useDubs();
26
-
27
- const [summary, setSummary] = useState<DeveloperCommissionsSummary | null>(null);
28
- const [commissions, setCommissions] = useState<DeveloperCommission[]>([]);
29
- const [loading, setLoading] = useState(false);
30
- const [error, setError] = useState<string | null>(null);
31
-
32
- const overlayOpacity = useRef(new Animated.Value(0)).current;
33
-
34
- useEffect(() => {
35
- Animated.timing(overlayOpacity, {
36
- toValue: visible ? 1 : 0,
37
- duration: 250,
38
- useNativeDriver: true,
39
- }).start();
40
- }, [visible, overlayOpacity]);
41
-
42
- const fetchData = useCallback(async () => {
43
- setLoading(true);
44
- setError(null);
45
- try {
46
- const result = await client.getCommissions({ limit: 50 });
47
- setCommissions(result.commissions);
48
- setSummary(result.summary);
49
- } catch (err) {
50
- setError(err instanceof Error ? err.message : 'Failed to load commissions');
51
- } finally {
52
- setLoading(false);
53
- }
54
- }, [client]);
55
-
56
- useEffect(() => {
57
- if (visible) fetchData();
58
- }, [visible, fetchData]);
59
-
60
- const formatSol = (sol: number) => {
61
- if (sol === 0) return '0';
62
- if (sol < 0.001) return '<0.001';
63
- return sol.toFixed(3);
64
- };
65
-
66
- const renderCommission = ({ item }: { item: DeveloperCommission }) => (
67
- <View style={[styles.row, { backgroundColor: t.surface, borderColor: t.border }]}>
68
- <View style={styles.rowTop}>
69
- <Text style={[styles.rowGameId, { color: t.text }]} numberOfLines={1}>
70
- {item.gameId.slice(0, 16)}...
71
- </Text>
72
- <Text style={[styles.rowAmount, { color: t.success }]}>
73
- +{formatSol(item.amountSol)} SOL
74
- </Text>
75
- </View>
76
- <View style={styles.rowBottom}>
77
- <Text style={[styles.rowMeta, { color: t.textMuted }]}>
78
- {item.gameType || 'game'} | pot {formatSol(item.potSizeSol)} SOL
79
- </Text>
80
- <View style={[
81
- styles.statusBadge,
82
- { backgroundColor: item.status === 'paid' ? t.success + '22' : t.accent + '22' },
83
- ]}>
84
- <Text style={[
85
- styles.statusText,
86
- { color: item.status === 'paid' ? t.success : t.accent },
87
- ]}>
88
- {item.status}
89
- </Text>
90
- </View>
91
- </View>
92
- <Text style={[styles.rowDate, { color: t.textDim }]}>
93
- {new Date(item.createdAt).toLocaleDateString()}
94
- </Text>
95
- </View>
96
- );
97
-
98
- return (
99
- <Modal visible={visible} animationType="slide" transparent onRequestClose={onDismiss}>
100
- <Animated.View style={[styles.overlay, { opacity: overlayOpacity }]} />
101
- <KeyboardAvoidingView
102
- style={styles.flex}
103
- behavior={Platform.OS === 'ios' ? 'padding' : undefined}
104
- >
105
- <View style={styles.flex} />
106
- <View style={[styles.sheet, { backgroundColor: t.background }]}>
107
- {/* Drag handle */}
108
- <View style={styles.handleRow}>
109
- <View style={[styles.handle, { backgroundColor: t.textMuted }]} />
110
- </View>
111
-
112
- {/* Header */}
113
- <View style={styles.header}>
114
- <Text style={[styles.headerTitle, { color: t.text }]}>Commission Earnings</Text>
115
- <TouchableOpacity onPress={onDismiss} hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}>
116
- <Text style={[styles.closeButton, { color: t.textMuted }]}>{'\u2715'}</Text>
117
- </TouchableOpacity>
118
- </View>
119
-
120
- {/* Summary cards */}
121
- {summary && (
122
- <View style={styles.summaryRow}>
123
- <View style={[styles.summaryCard, { backgroundColor: t.surface }]}>
124
- <Text style={[styles.summaryLabel, { color: t.textMuted }]}>Total Earned</Text>
125
- <Text style={[styles.summaryValue, { color: t.success }]}>
126
- {formatSol(summary.totalEarnedSol)} SOL
127
- </Text>
128
- </View>
129
- <View style={[styles.summaryCard, { backgroundColor: t.surface }]}>
130
- <Text style={[styles.summaryLabel, { color: t.textMuted }]}>Games</Text>
131
- <Text style={[styles.summaryValue, { color: t.text }]}>
132
- {summary.totalGames}
133
- </Text>
134
- </View>
135
- <View style={[styles.summaryCard, { backgroundColor: t.surface }]}>
136
- <Text style={[styles.summaryLabel, { color: t.textMuted }]}>Paid</Text>
137
- <Text style={[styles.summaryValue, { color: t.accent }]}>
138
- {formatSol(summary.totalPaidSol)} SOL
139
- </Text>
140
- </View>
141
- </View>
142
- )}
143
-
144
- {summary?.commissionWallet && (
145
- <View style={[styles.walletRow, { backgroundColor: t.surface, borderColor: t.border }]}>
146
- <Text style={[styles.walletLabel, { color: t.textMuted }]}>Commission Wallet</Text>
147
- <Text style={[styles.walletAddress, { color: t.textSecondary }]} numberOfLines={1}>
148
- {summary.commissionWallet}
149
- </Text>
150
- </View>
151
- )}
152
-
153
- {/* Content */}
154
- {loading && !commissions.length ? (
155
- <View style={styles.centered}>
156
- <ActivityIndicator size="large" color={t.accent} />
157
- </View>
158
- ) : error ? (
159
- <View style={[styles.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }]}>
160
- <Text style={[styles.errorText, { color: t.errorText }]}>{error}</Text>
161
- <TouchableOpacity onPress={fetchData} style={[styles.retryButton, { borderColor: t.errorBorder }]}>
162
- <Text style={[styles.retryText, { color: t.errorText }]}>Retry</Text>
163
- </TouchableOpacity>
164
- </View>
165
- ) : commissions.length === 0 ? (
166
- <View style={styles.centered}>
167
- <Text style={[styles.emptyTitle, { color: t.textMuted }]}>No commissions yet</Text>
168
- <Text style={[styles.emptySubtitle, { color: t.textDim }]}>
169
- When games created through your app are resolved, your 1% commission will appear here.
170
- </Text>
171
- </View>
172
- ) : (
173
- <FlatList
174
- data={commissions}
175
- keyExtractor={(item) => String(item.id)}
176
- renderItem={renderCommission}
177
- contentContainerStyle={styles.listContent}
178
- showsVerticalScrollIndicator={false}
179
- />
180
- )}
181
- </View>
182
- </KeyboardAvoidingView>
183
- </Modal>
184
- );
185
- }
186
-
187
- const styles = StyleSheet.create({
188
- flex: { flex: 1 },
189
- overlay: {
190
- ...StyleSheet.absoluteFillObject,
191
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
192
- },
193
- sheet: {
194
- borderTopLeftRadius: 20,
195
- borderTopRightRadius: 20,
196
- maxHeight: '85%',
197
- minHeight: 400,
198
- paddingBottom: 34,
199
- },
200
- handleRow: {
201
- alignItems: 'center',
202
- paddingTop: 10,
203
- paddingBottom: 6,
204
- },
205
- handle: {
206
- width: 36,
207
- height: 4,
208
- borderRadius: 2,
209
- opacity: 0.4,
210
- },
211
- header: {
212
- flexDirection: 'row',
213
- alignItems: 'center',
214
- justifyContent: 'space-between',
215
- paddingHorizontal: 20,
216
- paddingVertical: 12,
217
- },
218
- headerTitle: {
219
- fontSize: 18,
220
- fontWeight: '700',
221
- },
222
- closeButton: {
223
- fontSize: 18,
224
- fontWeight: '600',
225
- },
226
- summaryRow: {
227
- flexDirection: 'row',
228
- paddingHorizontal: 16,
229
- gap: 8,
230
- marginBottom: 12,
231
- },
232
- summaryCard: {
233
- flex: 1,
234
- borderRadius: 12,
235
- padding: 12,
236
- alignItems: 'center',
237
- },
238
- summaryLabel: {
239
- fontSize: 11,
240
- fontWeight: '600',
241
- textTransform: 'uppercase',
242
- letterSpacing: 0.5,
243
- marginBottom: 4,
244
- },
245
- summaryValue: {
246
- fontSize: 16,
247
- fontWeight: '700',
248
- },
249
- walletRow: {
250
- marginHorizontal: 16,
251
- borderRadius: 10,
252
- borderWidth: 1,
253
- padding: 10,
254
- marginBottom: 12,
255
- },
256
- walletLabel: {
257
- fontSize: 11,
258
- fontWeight: '600',
259
- textTransform: 'uppercase',
260
- letterSpacing: 0.5,
261
- marginBottom: 2,
262
- },
263
- walletAddress: {
264
- fontSize: 12,
265
- fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
266
- },
267
- centered: {
268
- flex: 1,
269
- justifyContent: 'center',
270
- alignItems: 'center',
271
- paddingHorizontal: 32,
272
- paddingVertical: 48,
273
- },
274
- emptyTitle: {
275
- fontSize: 16,
276
- fontWeight: '600',
277
- marginBottom: 8,
278
- },
279
- emptySubtitle: {
280
- fontSize: 13,
281
- textAlign: 'center',
282
- lineHeight: 18,
283
- },
284
- errorBox: {
285
- margin: 16,
286
- borderRadius: 10,
287
- borderWidth: 1,
288
- padding: 16,
289
- alignItems: 'center',
290
- },
291
- errorText: {
292
- fontSize: 14,
293
- marginBottom: 12,
294
- },
295
- retryButton: {
296
- borderWidth: 1,
297
- borderRadius: 8,
298
- paddingHorizontal: 16,
299
- paddingVertical: 6,
300
- },
301
- retryText: {
302
- fontSize: 13,
303
- fontWeight: '600',
304
- },
305
- listContent: {
306
- paddingHorizontal: 16,
307
- paddingBottom: 16,
308
- },
309
- row: {
310
- borderRadius: 12,
311
- borderWidth: 1,
312
- padding: 12,
313
- marginBottom: 8,
314
- },
315
- rowTop: {
316
- flexDirection: 'row',
317
- justifyContent: 'space-between',
318
- alignItems: 'center',
319
- marginBottom: 4,
320
- },
321
- rowGameId: {
322
- fontSize: 14,
323
- fontWeight: '600',
324
- flex: 1,
325
- marginRight: 8,
326
- },
327
- rowAmount: {
328
- fontSize: 14,
329
- fontWeight: '700',
330
- },
331
- rowBottom: {
332
- flexDirection: 'row',
333
- justifyContent: 'space-between',
334
- alignItems: 'center',
335
- marginBottom: 2,
336
- },
337
- rowMeta: {
338
- fontSize: 12,
339
- },
340
- statusBadge: {
341
- borderRadius: 6,
342
- paddingHorizontal: 8,
343
- paddingVertical: 2,
344
- },
345
- statusText: {
346
- fontSize: 11,
347
- fontWeight: '600',
348
- textTransform: 'uppercase',
349
- },
350
- rowDate: {
351
- fontSize: 11,
352
- },
353
- });