@umituz/react-native-ai-fal-provider 3.1.6 → 3.2.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/package.json +1 -1
- package/src/exports/infrastructure.ts +0 -45
- package/src/exports/presentation.ts +2 -11
- package/src/index.ts +2 -3
- package/src/infrastructure/services/fal-queue-operations.ts +1 -1
- package/src/infrastructure/services/index.ts +0 -2
- package/src/infrastructure/utils/index.ts +7 -53
- package/src/infrastructure/utils/input-validator.util.ts +1 -1
- package/src/infrastructure/utils/parsers/index.ts +1 -4
- package/src/init/createAiProviderInitModule.ts +0 -56
- package/src/init/initializeFalProvider.ts +34 -0
- package/src/presentation/hooks/index.ts +0 -3
- package/src/infrastructure/services/fal-models.service.ts +0 -40
- package/src/infrastructure/utils/base-builders.util.ts +0 -28
- package/src/infrastructure/utils/collections/array-filters.util.ts +0 -63
- package/src/infrastructure/utils/collections/array-sorters.util.ts +0 -94
- package/src/infrastructure/utils/collections/index.ts +0 -7
- package/src/infrastructure/utils/date-format.util.ts +0 -30
- package/src/infrastructure/utils/error-categorizer.ts +0 -9
- package/src/infrastructure/utils/job-metadata/index.ts +0 -26
- package/src/infrastructure/utils/job-metadata/job-metadata-format.util.ts +0 -78
- package/src/infrastructure/utils/job-metadata/job-metadata-lifecycle.util.ts +0 -66
- package/src/infrastructure/utils/job-metadata/job-metadata-queries.util.ts +0 -57
- package/src/infrastructure/utils/job-metadata/job-metadata.types.ts +0 -19
- package/src/infrastructure/utils/job-storage/index.ts +0 -19
- package/src/infrastructure/utils/job-storage/job-storage-crud.util.ts +0 -64
- package/src/infrastructure/utils/job-storage/job-storage-interface.ts +0 -44
- package/src/infrastructure/utils/job-storage/job-storage-queries.util.ts +0 -81
- package/src/infrastructure/utils/number-format.util.ts +0 -86
- package/src/infrastructure/utils/parsers/object-validators.util.ts +0 -38
- package/src/infrastructure/utils/parsers/value-parsers.util.ts +0 -45
- package/src/infrastructure/utils/string-format.util.ts +0 -72
- package/src/infrastructure/validators/README.md +0 -290
- package/src/init/registerWithWizard.ts +0 -28
- package/src/presentation/hooks/README.md +0 -626
- package/src/presentation/hooks/use-models.ts +0 -56
|
@@ -1,626 +0,0 @@
|
|
|
1
|
-
# React Hooks
|
|
2
|
-
|
|
3
|
-
FAL AI işlemleri için React Hook'ları.
|
|
4
|
-
|
|
5
|
-
## useFalGeneration
|
|
6
|
-
|
|
7
|
-
AI içerik oluşturma işlemleri için React hook'u.
|
|
8
|
-
|
|
9
|
-
### Parametreler
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
interface UseFalGenerationOptions {
|
|
13
|
-
timeoutMs?: number; // Zaman aşımı (ms)
|
|
14
|
-
onProgress?: (status: FalQueueStatus) => void; // İlerleme callback
|
|
15
|
-
onError?: (error: FalErrorInfo) => void; // Hata callback
|
|
16
|
-
}
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
### Dönüş Değeri
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
interface UseFalGenerationResult<T> {
|
|
23
|
-
data: T | null; // Oluşturulan veri
|
|
24
|
-
error: FalErrorInfo | null; // Hata bilgisi
|
|
25
|
-
isLoading: boolean; // Yükleme durumu
|
|
26
|
-
isRetryable: boolean; // Retry edilebilir mi?
|
|
27
|
-
requestId: string | null; // İstek ID'si
|
|
28
|
-
isCancelling: boolean; // İptal ediliyor mu?
|
|
29
|
-
|
|
30
|
-
generate: (modelEndpoint: string, input: FalJobInput) => Promise<T | null>;
|
|
31
|
-
retry: () => Promise<T | null>;
|
|
32
|
-
cancel: () => void;
|
|
33
|
-
reset: () => void;
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Temel Kullanım
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
import { useFalGeneration } from '@umituz/react-native-ai-fal-provider';
|
|
41
|
-
|
|
42
|
-
function ImageGenerator() {
|
|
43
|
-
const { data, error, isLoading, generate, retry, cancel } = useFalGeneration({
|
|
44
|
-
timeoutMs: 120000,
|
|
45
|
-
onProgress: (status) => {
|
|
46
|
-
console.log('Durum:', status.status);
|
|
47
|
-
console.log('Sıra:', status.queuePosition);
|
|
48
|
-
},
|
|
49
|
-
onError: (error) => {
|
|
50
|
-
console.error('Hata:', error.messageKey);
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const handleGenerate = async () => {
|
|
55
|
-
await generate('fal-ai/flux/schnell', {
|
|
56
|
-
prompt: 'Güneş batımında sahil kenarında yürüyen iki kişi',
|
|
57
|
-
image_size: 'landscape_16_9',
|
|
58
|
-
});
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<View>
|
|
63
|
-
<Button onPress={handleGenerate} disabled={isLoading} />
|
|
64
|
-
{isLoading && (
|
|
65
|
-
<>
|
|
66
|
-
<ActivityIndicator />
|
|
67
|
-
<Button title="İptal" onPress={cancel} />
|
|
68
|
-
</>
|
|
69
|
-
)}
|
|
70
|
-
{error && (
|
|
71
|
-
<>
|
|
72
|
-
<Text>Hata: {error.messageKey}</Text>
|
|
73
|
-
{error.retryable && <Button title="Tekrar Dene" onPress={retry} />}
|
|
74
|
-
</>
|
|
75
|
-
)}
|
|
76
|
-
{data?.images?.[0]?.url && (
|
|
77
|
-
<Image source={{ uri: data.images[0].url }} />
|
|
78
|
-
)}
|
|
79
|
-
</View>
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Video Oluşturma
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
function VideoGenerator() {
|
|
88
|
-
const { data, isLoading, generate } = useFalGeneration({
|
|
89
|
-
timeoutMs: 300000, // 5 dakika
|
|
90
|
-
onProgress: (status) => {
|
|
91
|
-
console.log(`İlerleme: ${status.status}`);
|
|
92
|
-
if (status.queuePosition) {
|
|
93
|
-
console.log(`Sırada: ${status.queuePosition}`);
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
const handleGenerate = async () => {
|
|
99
|
-
await generate('fal-ai/minimax-video', {
|
|
100
|
-
prompt: 'Yağmurlu bir Tokyo sokakta yürüyen insanlar',
|
|
101
|
-
aspect_ratio: '16:9',
|
|
102
|
-
});
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<View>
|
|
107
|
-
<Button onPress={handleGenerate} disabled={isLoading} />
|
|
108
|
-
{isLoading && <ActivityIndicator />}
|
|
109
|
-
{data?.video?.url && <Video source={{ uri: data.video.url }} />}
|
|
110
|
-
</View>
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### İptal Edilebilir İşlem
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
function CancellableGeneration() {
|
|
119
|
-
const { data, isLoading, isCancelling, generate, cancel, reset } = useFalGeneration();
|
|
120
|
-
|
|
121
|
-
const handleGenerate = async () => {
|
|
122
|
-
await generate('fal-ai/flux/dev', {
|
|
123
|
-
prompt: 'Profesyonel bir iş ortamı',
|
|
124
|
-
});
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<View>
|
|
129
|
-
<Button onPress={handleGenerate} disabled={isLoading} />
|
|
130
|
-
{isLoading && !isCancelling && (
|
|
131
|
-
<Button title="İptal Et" onPress={cancel} color="red" />
|
|
132
|
-
)}
|
|
133
|
-
{isCancelling && <Text>İptal ediliyor...</Text>}
|
|
134
|
-
<Button title="Sıfırla" onPress={reset} />
|
|
135
|
-
{data && <Image source={{ uri: data.images[0].url }} />}
|
|
136
|
-
</View>
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### İlerleme Göstergesi
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
function ProgressIndicator() {
|
|
145
|
-
const { isLoading, generate } = useFalGeneration({
|
|
146
|
-
onProgress: (status) => {
|
|
147
|
-
switch (status.status) {
|
|
148
|
-
case 'IN_QUEUE':
|
|
149
|
-
console.log(`Sırada: ${status.queuePosition ?? 'bilinmiyor'}`);
|
|
150
|
-
break;
|
|
151
|
-
case 'IN_PROGRESS':
|
|
152
|
-
console.log('İşleniyor...');
|
|
153
|
-
break;
|
|
154
|
-
case 'COMPLETED':
|
|
155
|
-
console.log('Tamamlandı!');
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Logları göster
|
|
160
|
-
status.logs?.forEach((log) => {
|
|
161
|
-
console.log(`[${log.level}] ${log.message}`);
|
|
162
|
-
});
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
return (
|
|
167
|
-
<View>
|
|
168
|
-
<Button onPress={() => generate(...)} />
|
|
169
|
-
{isLoading && <ActivityIndicator />}
|
|
170
|
-
</View>
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## useModels
|
|
176
|
-
|
|
177
|
-
Model seçimi ve yönetimi için React hook'u.
|
|
178
|
-
|
|
179
|
-
### Parametreler
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
interface UseModelsProps {
|
|
183
|
-
readonly type: ModelType; // Model tipi
|
|
184
|
-
readonly config?: ModelSelectionConfig; // Opsiyonel yapılandırma
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
interface ModelSelectionConfig {
|
|
188
|
-
readonly initialModelId?: string; // Başlangıç modeli
|
|
189
|
-
readonly defaultModelId?: string; // Varsayılan model ID
|
|
190
|
-
readonly defaultCreditCost?: number; // Varsayılan kredi maliyeti
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Dönüş Değeri
|
|
195
|
-
|
|
196
|
-
```typescript
|
|
197
|
-
interface UseModelsReturn {
|
|
198
|
-
models: FalModelConfig[]; // Mevcut modeller
|
|
199
|
-
selectedModel: FalModelConfig | null; // Seçili model
|
|
200
|
-
selectModel: (id: string) => void; // Model seçme fonksiyonu
|
|
201
|
-
creditCost: number; // Seçili modelin maliyeti
|
|
202
|
-
modelId: string; // Seçili modelin ID'si
|
|
203
|
-
isLoading: boolean; // Yükleme durumu
|
|
204
|
-
error: string | null; // Hata mesajı
|
|
205
|
-
refreshModels: () => void; // Modelleri yenile
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Model Seçimi
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
function ModelSelector() {
|
|
213
|
-
const {
|
|
214
|
-
models,
|
|
215
|
-
selectedModel,
|
|
216
|
-
selectModel,
|
|
217
|
-
creditCost,
|
|
218
|
-
modelId,
|
|
219
|
-
isLoading,
|
|
220
|
-
} = useModels({
|
|
221
|
-
type: 'text-to-image',
|
|
222
|
-
config: {
|
|
223
|
-
defaultCreditCost: 2,
|
|
224
|
-
defaultModelId: 'fal-ai/flux/schnell',
|
|
225
|
-
initialModelId: 'fal-ai/flux/dev',
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
if (isLoading) return <ActivityIndicator />;
|
|
230
|
-
|
|
231
|
-
return (
|
|
232
|
-
<View>
|
|
233
|
-
<Text>Model Seçin:</Text>
|
|
234
|
-
<Picker selectedValue={modelId} onValueChange={selectModel}>
|
|
235
|
-
{models.map((model) => (
|
|
236
|
-
<Picker.Item
|
|
237
|
-
key={model.id}
|
|
238
|
-
label={`${model.name} (${model.pricing?.freeUserCost} kredi)`}
|
|
239
|
-
value={model.id}
|
|
240
|
-
/>
|
|
241
|
-
))}
|
|
242
|
-
</Picker>
|
|
243
|
-
|
|
244
|
-
<Text>Seçili: {selectedModel?.name}</Text>
|
|
245
|
-
<Text>Maliyet: {creditCost} kredi</Text>
|
|
246
|
-
<Text>{selectedModel?.description}</Text>
|
|
247
|
-
</View>
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### Model Karşılaştırma
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
function ModelComparison() {
|
|
256
|
-
const { models, selectModel, modelId } = useModels({
|
|
257
|
-
type: 'text-to-video',
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
return (
|
|
261
|
-
<View>
|
|
262
|
-
<Text>Modelleri Karşılaştırın:</Text>
|
|
263
|
-
{models.map((model) => (
|
|
264
|
-
<TouchableOpacity
|
|
265
|
-
key={model.id}
|
|
266
|
-
onPress={() => selectModel(model.id)}
|
|
267
|
-
style={[
|
|
268
|
-
styles.modelCard,
|
|
269
|
-
model.id === modelId && styles.selected,
|
|
270
|
-
]}
|
|
271
|
-
>
|
|
272
|
-
<Text style={styles.name}>{model.name}</Text>
|
|
273
|
-
<Text style={styles.cost}>
|
|
274
|
-
Ücretsiz: {model.pricing?.freeUserCost} kredi
|
|
275
|
-
</Text>
|
|
276
|
-
<Text style={styles.cost}>
|
|
277
|
-
Premium: {model.pricing?.premiumUserCost} kredi
|
|
278
|
-
</Text>
|
|
279
|
-
<Text style={styles.description}>{model.description}</Text>
|
|
280
|
-
</TouchableOpacity>
|
|
281
|
-
))}
|
|
282
|
-
</View>
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## Birlikte Kullanım
|
|
288
|
-
|
|
289
|
-
### useFalGeneration + useModels
|
|
290
|
-
|
|
291
|
-
```typescript
|
|
292
|
-
function AIImageGenerator() {
|
|
293
|
-
// Model seçimi
|
|
294
|
-
const {
|
|
295
|
-
models,
|
|
296
|
-
selectedModel,
|
|
297
|
-
selectModel,
|
|
298
|
-
creditCost,
|
|
299
|
-
modelId,
|
|
300
|
-
} = useModels({
|
|
301
|
-
type: 'text-to-image',
|
|
302
|
-
config: {
|
|
303
|
-
defaultCreditCost: 2,
|
|
304
|
-
initialModelId: 'fal-ai/flux/schnell',
|
|
305
|
-
},
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// Görsel oluşturma
|
|
309
|
-
const { data, error, isLoading, generate, retry } = useFalGeneration({
|
|
310
|
-
onError: (error) => {
|
|
311
|
-
Alert.alert('Hata', error.messageKey);
|
|
312
|
-
},
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
const handleGenerate = async (prompt: string) => {
|
|
316
|
-
await generate(modelId, {
|
|
317
|
-
prompt,
|
|
318
|
-
image_size: 'landscape_16_9',
|
|
319
|
-
});
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
return (
|
|
323
|
-
<View>
|
|
324
|
-
{/* Model Seçimi */}
|
|
325
|
-
<Text>Model: {selectedModel?.name}</Text>
|
|
326
|
-
<Text>Maliyet: {creditCost} kredi</Text>
|
|
327
|
-
|
|
328
|
-
<Picker selectedValue={modelId} onValueChange={selectModel}>
|
|
329
|
-
{models.map((model) => (
|
|
330
|
-
<Picker.Item
|
|
331
|
-
key={model.id}
|
|
332
|
-
label={`${model.name} - ${model.pricing?.freeUserCost} kredi`}
|
|
333
|
-
value={model.id}
|
|
334
|
-
/>
|
|
335
|
-
))}
|
|
336
|
-
</Picker>
|
|
337
|
-
|
|
338
|
-
{/* Prompt Girişi */}
|
|
339
|
-
<TextInput
|
|
340
|
-
placeholder="Görsel açıklaması girin..."
|
|
341
|
-
onChangeText={setPrompt}
|
|
342
|
-
/>
|
|
343
|
-
|
|
344
|
-
{/* Oluştur Butonu */}
|
|
345
|
-
<Button
|
|
346
|
-
title="Oluştur"
|
|
347
|
-
onPress={() => handleGenerate(prompt)}
|
|
348
|
-
disabled={isLoading || !prompt}
|
|
349
|
-
/>
|
|
350
|
-
|
|
351
|
-
{/* Sonuç */}
|
|
352
|
-
{isLoading && <ActivityIndicator />}
|
|
353
|
-
{error && error.retryable && (
|
|
354
|
-
<Button title="Tekrar Dene" onPress={retry} />
|
|
355
|
-
)}
|
|
356
|
-
{data?.images?.[0]?.url && (
|
|
357
|
-
<Image source={{ uri: data.images[0].url }} style={{ width: '100%', height: 300 }} />
|
|
358
|
-
)}
|
|
359
|
-
</View>
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
## Örnek Uygulama
|
|
365
|
-
|
|
366
|
-
### Tam Özellikli AI Oluşturucu
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
import React, { useState } from 'react';
|
|
370
|
-
import {
|
|
371
|
-
View,
|
|
372
|
-
Text,
|
|
373
|
-
TextInput,
|
|
374
|
-
Button,
|
|
375
|
-
ActivityIndicator,
|
|
376
|
-
Image,
|
|
377
|
-
Picker,
|
|
378
|
-
Alert,
|
|
379
|
-
} from 'react-native';
|
|
380
|
-
import { useFalGeneration, useModels } from '@umituz/react-native-ai-fal-provider';
|
|
381
|
-
|
|
382
|
-
function AIGenerator() {
|
|
383
|
-
const [prompt, setPrompt] = useState('');
|
|
384
|
-
const [imageSize, setImageSize] = useState('landscape_16_9');
|
|
385
|
-
|
|
386
|
-
const {
|
|
387
|
-
models,
|
|
388
|
-
selectedModel,
|
|
389
|
-
selectModel,
|
|
390
|
-
creditCost,
|
|
391
|
-
modelId,
|
|
392
|
-
} = useModels({
|
|
393
|
-
type: 'text-to-image',
|
|
394
|
-
config: {
|
|
395
|
-
defaultCreditCost: 2,
|
|
396
|
-
initialModelId: 'fal-ai/flux/schnell',
|
|
397
|
-
},
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
const {
|
|
401
|
-
data,
|
|
402
|
-
error,
|
|
403
|
-
isLoading,
|
|
404
|
-
isRetryable,
|
|
405
|
-
generate,
|
|
406
|
-
retry,
|
|
407
|
-
cancel,
|
|
408
|
-
reset,
|
|
409
|
-
} = useFalGeneration({
|
|
410
|
-
timeoutMs: 120000,
|
|
411
|
-
onProgress: (status) => {
|
|
412
|
-
console.log('Durum:', status.status);
|
|
413
|
-
if (status.queuePosition) {
|
|
414
|
-
console.log('Sırada:', status.queuePosition);
|
|
415
|
-
}
|
|
416
|
-
},
|
|
417
|
-
onError: (error) => {
|
|
418
|
-
Alert.alert('Hata', error.messageKey);
|
|
419
|
-
},
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
const handleGenerate = async () => {
|
|
423
|
-
if (!prompt.trim()) {
|
|
424
|
-
Alert.alert('Uyarı', 'Lütfen bir prompt girin');
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
await generate(modelId, {
|
|
429
|
-
prompt: prompt.trim(),
|
|
430
|
-
image_size: imageSize,
|
|
431
|
-
num_inference_steps: 4,
|
|
432
|
-
});
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
return (
|
|
436
|
-
<View style={{ padding: 20 }}>
|
|
437
|
-
<Text style={{ fontSize: 24, fontWeight: 'bold', marginBottom: 20 }}>
|
|
438
|
-
AI Görsel Oluşturucu
|
|
439
|
-
</Text>
|
|
440
|
-
|
|
441
|
-
{/* Model Seçimi */}
|
|
442
|
-
<View style={{ marginBottom: 20 }}>
|
|
443
|
-
<Text>Model Seçin:</Text>
|
|
444
|
-
<Picker
|
|
445
|
-
selectedValue={modelId}
|
|
446
|
-
onValueChange={selectModel}
|
|
447
|
-
enabled={!isLoading}
|
|
448
|
-
>
|
|
449
|
-
{models.map((model) => (
|
|
450
|
-
<Picker.Item
|
|
451
|
-
key={model.id}
|
|
452
|
-
label={`${model.name} (${model.pricing?.freeUserCost} kredi)`}
|
|
453
|
-
value={model.id}
|
|
454
|
-
/>
|
|
455
|
-
))}
|
|
456
|
-
</Picker>
|
|
457
|
-
<Text>Seçili: {selectedModel?.name}</Text>
|
|
458
|
-
<Text>Maliyet: {creditCost} kredi</Text>
|
|
459
|
-
</View>
|
|
460
|
-
|
|
461
|
-
{/* Image Size Seçimi */}
|
|
462
|
-
<View style={{ marginBottom: 20 }}>
|
|
463
|
-
<Text>Boyut:</Text>
|
|
464
|
-
<Picker
|
|
465
|
-
selectedValue={imageSize}
|
|
466
|
-
onValueChange={setImageSize}
|
|
467
|
-
enabled={!isLoading}
|
|
468
|
-
>
|
|
469
|
-
<Picker.Item label="Kare (1:1)" value="square_hd" />
|
|
470
|
-
<Picker.Item label="Yatay (16:9)" value="landscape_16_9" />
|
|
471
|
-
<Picker.Item label="Dikey (9:16)" value="portrait_9_16" />
|
|
472
|
-
</Picker>
|
|
473
|
-
</View>
|
|
474
|
-
|
|
475
|
-
{/* Prompt Girişi */}
|
|
476
|
-
<TextInput
|
|
477
|
-
style={{
|
|
478
|
-
borderWidth: 1,
|
|
479
|
-
borderColor: '#ccc',
|
|
480
|
-
borderRadius: 8,
|
|
481
|
-
padding: 12,
|
|
482
|
-
marginBottom: 20,
|
|
483
|
-
minHeight: 100,
|
|
484
|
-
}}
|
|
485
|
-
placeholder="Oluşturmak istediğiniz görseli açıklayın..."
|
|
486
|
-
value={prompt}
|
|
487
|
-
onChangeText={setPrompt}
|
|
488
|
-
multiline
|
|
489
|
-
editable={!isLoading}
|
|
490
|
-
maxLength: 1000
|
|
491
|
-
/>
|
|
492
|
-
|
|
493
|
-
{/* Butonlar */}
|
|
494
|
-
<View style={{ flexDirection: 'row', marginBottom: 20 }}>
|
|
495
|
-
<View style={{ flex: 1, marginRight: 10 }}>
|
|
496
|
-
<Button
|
|
497
|
-
title="Oluştur"
|
|
498
|
-
onPress={handleGenerate}
|
|
499
|
-
disabled={isLoading || !prompt.trim()}
|
|
500
|
-
/>
|
|
501
|
-
</View>
|
|
502
|
-
{isLoading && (
|
|
503
|
-
<View style={{ flex: 1 }}>
|
|
504
|
-
<Button title="İptal" onPress={cancel} color="red" />
|
|
505
|
-
</View>
|
|
506
|
-
)}
|
|
507
|
-
{error && isRetryable && (
|
|
508
|
-
<View style={{ flex: 1 }}>
|
|
509
|
-
<Button title="Tekrar Dene" onPress={retry} color="orange" />
|
|
510
|
-
</View>
|
|
511
|
-
)}
|
|
512
|
-
{data && (
|
|
513
|
-
<View style={{ flex: 1 }}>
|
|
514
|
-
<Button title="Sıfırla" onPress={reset} color="gray" />
|
|
515
|
-
</View>
|
|
516
|
-
)}
|
|
517
|
-
</View>
|
|
518
|
-
|
|
519
|
-
{/* Durum */}
|
|
520
|
-
{isLoading && (
|
|
521
|
-
<View style={{ alignItems: 'center', marginBottom: 20 }}>
|
|
522
|
-
<ActivityIndicator size="large" />
|
|
523
|
-
<Text style={{ marginTop: 10 }}>
|
|
524
|
-
Görsel oluşturuluyor...
|
|
525
|
-
</Text>
|
|
526
|
-
</View>
|
|
527
|
-
)}
|
|
528
|
-
|
|
529
|
-
{/* Sonuç */}
|
|
530
|
-
{data?.images?.[0]?.url && (
|
|
531
|
-
<View>
|
|
532
|
-
<Text style={{ marginBottom: 10 }}>Sonuç:</Text>
|
|
533
|
-
<Image
|
|
534
|
-
source={{ uri: data.images[0].url }}
|
|
535
|
-
style={{ width: '100%', height: 300, borderRadius: 8 }}
|
|
536
|
-
resizeMode="contain"
|
|
537
|
-
/>
|
|
538
|
-
</View>
|
|
539
|
-
)}
|
|
540
|
-
</View>
|
|
541
|
-
);
|
|
542
|
-
}
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
## İpuçları
|
|
546
|
-
|
|
547
|
-
### 1. Prompt Doğrulama
|
|
548
|
-
|
|
549
|
-
```typescript
|
|
550
|
-
const isValidPrompt = (prompt: string) => {
|
|
551
|
-
return prompt.trim().length >= 10 && prompt.trim().length <= 1000;
|
|
552
|
-
};
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
### 2. Debounced Generate
|
|
556
|
-
|
|
557
|
-
```typescript
|
|
558
|
-
import { debounce } from '@umituz/react-native-ai-fal-provider';
|
|
559
|
-
|
|
560
|
-
const debouncedGenerate = debounce(
|
|
561
|
-
(prompt) => generate(modelId, { prompt }),
|
|
562
|
-
1000
|
|
563
|
-
);
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
### 3. Kredi Bakiyesi Kontrolü
|
|
567
|
-
|
|
568
|
-
```typescript
|
|
569
|
-
const handleGenerate = async () => {
|
|
570
|
-
const userCredits = await getUserCredits();
|
|
571
|
-
|
|
572
|
-
if (userCredits < creditCost) {
|
|
573
|
-
Alert.alert(
|
|
574
|
-
'Yetersiz Bakiye',
|
|
575
|
-
`Bu işlem ${creditCost} kredi gerektirir. Sizin bakiyeniz: ${userCredits}`
|
|
576
|
-
);
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
await generate(modelId, { prompt });
|
|
581
|
-
};
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
### 4. Multiple Generations
|
|
585
|
-
|
|
586
|
-
```typescript
|
|
587
|
-
const [generatedImages, setGeneratedImages] = useState<string[]>([]);
|
|
588
|
-
|
|
589
|
-
const handleGenerate = async () => {
|
|
590
|
-
const result = await generate(modelId, { prompt });
|
|
591
|
-
|
|
592
|
-
if (result?.images) {
|
|
593
|
-
setGeneratedImages(prev => [
|
|
594
|
-
...prev,
|
|
595
|
-
...result.images.map((img: any) => img.url)
|
|
596
|
-
]);
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
## TypeScript Desteği
|
|
602
|
-
|
|
603
|
-
```typescript
|
|
604
|
-
import type {
|
|
605
|
-
UseFalGenerationOptions,
|
|
606
|
-
UseFalGenerationResult,
|
|
607
|
-
UseModelsProps,
|
|
608
|
-
UseModelsReturn,
|
|
609
|
-
} from '@umituz/react-native-ai-fal-provider';
|
|
610
|
-
|
|
611
|
-
// Tip tanımları
|
|
612
|
-
interface ImageGenerationResult {
|
|
613
|
-
images: Array<{ url: string }>;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const { data } = useFalGeneration<ImageGenerationResult>({
|
|
617
|
-
timeoutMs: 120000,
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
// data artık ImageGenerationResult tipinde
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
## Daha Fazla Bilgi
|
|
624
|
-
|
|
625
|
-
- [React Hooks Documentation](https://react.dev/reference/react)
|
|
626
|
-
- [FAL AI Documentation](https://fal.ai/docs)
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useModels Hook - Model selection management
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { useState, useCallback, useMemo, useEffect } from "react";
|
|
6
|
-
import { falModelsService, type FalModelConfig } from "../../infrastructure/services/fal-models.service";
|
|
7
|
-
|
|
8
|
-
export interface UseModelsProps {
|
|
9
|
-
readonly models: FalModelConfig[];
|
|
10
|
-
readonly initialModelId?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface UseModelsReturn {
|
|
14
|
-
readonly models: FalModelConfig[];
|
|
15
|
-
readonly selectedModel: FalModelConfig | null;
|
|
16
|
-
readonly selectModel: (modelId: string) => void;
|
|
17
|
-
readonly modelId: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function useModels(props: UseModelsProps): UseModelsReturn {
|
|
21
|
-
const { models, initialModelId } = props;
|
|
22
|
-
|
|
23
|
-
const sortedModels = useMemo(() => falModelsService.sortModels(models), [models]);
|
|
24
|
-
|
|
25
|
-
const [selectedModel, setSelectedModel] = useState<FalModelConfig | null>(() => {
|
|
26
|
-
if (initialModelId) {
|
|
27
|
-
const initial = falModelsService.findById(initialModelId, sortedModels);
|
|
28
|
-
if (initial) return initial;
|
|
29
|
-
}
|
|
30
|
-
return falModelsService.getDefaultModel(sortedModels) ?? null;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if (initialModelId) {
|
|
35
|
-
const model = falModelsService.findById(initialModelId, sortedModels);
|
|
36
|
-
if (model) setSelectedModel(model);
|
|
37
|
-
}
|
|
38
|
-
}, [initialModelId, sortedModels]);
|
|
39
|
-
|
|
40
|
-
const selectModel = useCallback(
|
|
41
|
-
(modelId: string) => {
|
|
42
|
-
const model = falModelsService.findById(modelId, sortedModels);
|
|
43
|
-
if (model) setSelectedModel(model);
|
|
44
|
-
},
|
|
45
|
-
[sortedModels]
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const modelId = useMemo(() => selectedModel?.id ?? "", [selectedModel]);
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
models: sortedModels,
|
|
52
|
-
selectedModel,
|
|
53
|
-
selectModel,
|
|
54
|
-
modelId,
|
|
55
|
-
};
|
|
56
|
-
}
|