feat/panels #6
212
apps/frontend/app/add.tsx
Normal file
212
apps/frontend/app/add.tsx
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
TextInput,
|
||||||
|
TouchableOpacity,
|
||||||
|
ScrollView,
|
||||||
|
Alert,
|
||||||
|
ActivityIndicator,
|
||||||
|
Platform,
|
||||||
|
} from 'react-native';
|
||||||
|
import { useRouter } from 'expo-router';
|
||||||
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
|
import { Text, View } from '@/components/Themed';
|
||||||
|
import { savesApi } from '@/lib/api';
|
||||||
|
import { useColorScheme } from '@/components/useColorScheme';
|
||||||
|
import Colors from '@/constants/Colors';
|
||||||
|
|
||||||
|
export default function AddSaveScreen() {
|
||||||
|
const [mode, setMode] = useState<'upload' | 'url'>('upload');
|
||||||
|
const [url, setUrl] = useState('');
|
||||||
|
const [name, setName] = useState('');
|
||||||
|
const [description, setDescription] = useState('');
|
||||||
|
const [tags, setTags] = useState('');
|
||||||
|
const [visibility, setVisibility] = useState<'public' | 'link'>('link');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const router = useRouter();
|
||||||
|
const colorScheme = useColorScheme();
|
||||||
|
const colors = Colors[colorScheme ?? 'light'];
|
||||||
|
|
||||||
|
const handleUpload = async () => {
|
||||||
|
if (mode === 'url') {
|
||||||
|
if (!url) {
|
||||||
|
Alert.alert('Ошибка', 'Введите URL');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
if (mode === 'url') {
|
||||||
|
const tagsArray = tags
|
||||||
|
? tags.split(',').map((t) => t.trim()).filter(Boolean)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
await savesApi.createFromUrl({
|
||||||
|
url,
|
||||||
|
name: name || undefined,
|
||||||
|
description: description || undefined,
|
||||||
|
tags: tagsArray,
|
||||||
|
visibility,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Загрузка файла
|
||||||
|
const result = await ImagePicker.launchImageLibraryAsync({
|
||||||
|
mediaTypes: ImagePicker.MediaTypeOptions.All,
|
||||||
|
allowsEditing: false,
|
||||||
|
quality: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.canceled) {
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const asset = result.assets[0];
|
||||||
|
const file = {
|
||||||
|
uri: asset.uri,
|
||||||
|
type: asset.mimeType || 'image/jpeg',
|
||||||
|
name: asset.fileName || `image.${asset.uri.split('.').pop()}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagsArray = tags
|
||||||
|
? tags.split(',').map((t) => t.trim()).filter(Boolean)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
await savesApi.uploadFile(file, {
|
||||||
|
name: name || undefined,
|
||||||
|
description: description || undefined,
|
||||||
|
tags: tagsArray,
|
||||||
|
visibility,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Alert.alert('Успех', 'Сейв успешно создан', [
|
||||||
|
{ text: 'OK', onPress: () => router.back() },
|
||||||
|
]);
|
||||||
|
} catch (error: any) {
|
||||||
|
Alert.alert('Ошибка', error.message || 'Не удалось создать сейв');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView className="flex-1">
|
||||||
|
<View className="p-5 pt-16">
|
||||||
|
<Text className="text-3xl font-bold mb-6">Добавить сейв</Text>
|
||||||
|
|
||||||
|
<View className="flex-row gap-3 mb-5">
|
||||||
|
<TouchableOpacity
|
||||||
|
className="flex-1 p-3 rounded-lg border items-center"
|
||||||
|
style={mode === 'upload' ? { backgroundColor: colors.tint } : {}}
|
||||||
|
onPress={() => setMode('upload')}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="text-base font-semibold"
|
||||||
|
style={mode === 'upload' ? { color: '#fff' } : {}}
|
||||||
|
>
|
||||||
|
Загрузить файл
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
className="flex-1 p-3 rounded-lg border items-center"
|
||||||
|
style={mode === 'url' ? { backgroundColor: colors.tint } : {}}
|
||||||
|
onPress={() => setMode('url')}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="text-base font-semibold"
|
||||||
|
style={mode === 'url' ? { color: '#fff' } : {}}
|
||||||
|
>
|
||||||
|
По URL
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{mode === 'url' && (
|
||||||
|
<TextInput
|
||||||
|
className="h-12 border rounded-lg px-4 mb-4 text-base"
|
||||||
|
style={{ color: colors.text, borderColor: colors.tabIconDefault }}
|
||||||
|
placeholder="URL медиафайла"
|
||||||
|
placeholderTextColor={colors.tabIconDefault}
|
||||||
|
value={url}
|
||||||
|
onChangeText={setUrl}
|
||||||
|
keyboardType="url"
|
||||||
|
autoCapitalize="none"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
className="h-12 border rounded-lg px-4 mb-4 text-base"
|
||||||
|
style={{ color: colors.text, borderColor: colors.tabIconDefault }}
|
||||||
|
placeholder="Название (необязательно)"
|
||||||
|
placeholderTextColor={colors.tabIconDefault}
|
||||||
|
value={name}
|
||||||
|
onChangeText={setName}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
className="h-24 border rounded-lg px-4 pt-4 mb-4 text-base"
|
||||||
|
style={{ color: colors.text, borderColor: colors.tabIconDefault }}
|
||||||
|
placeholder="Описание (необязательно)"
|
||||||
|
placeholderTextColor={colors.tabIconDefault}
|
||||||
|
value={description}
|
||||||
|
onChangeText={setDescription}
|
||||||
|
multiline
|
||||||
|
numberOfLines={4}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
className="h-12 border rounded-lg px-4 mb-4 text-base"
|
||||||
|
style={{ color: colors.text, borderColor: colors.tabIconDefault }}
|
||||||
|
placeholder="Теги через запятую (необязательно)"
|
||||||
|
placeholderTextColor={colors.tabIconDefault}
|
||||||
|
value={tags}
|
||||||
|
onChangeText={setTags}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<View className="mb-5">
|
||||||
|
<Text className="text-base font-semibold mb-3">Видимость:</Text>
|
||||||
|
<TouchableOpacity
|
||||||
|
className="p-3 rounded-lg mb-2 border items-center"
|
||||||
|
style={visibility === 'public' ? { backgroundColor: colors.tint } : {}}
|
||||||
|
onPress={() => setVisibility('public')}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="text-base font-semibold"
|
||||||
|
style={visibility === 'public' ? { color: '#fff' } : {}}
|
||||||
|
>
|
||||||
|
Публичный
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
className="p-3 rounded-lg mb-2 border items-center"
|
||||||
|
style={visibility === 'link' ? { backgroundColor: colors.tint } : {}}
|
||||||
|
onPress={() => setVisibility('link')}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="text-base font-semibold"
|
||||||
|
style={visibility === 'link' ? { color: '#fff' } : {}}
|
||||||
|
>
|
||||||
|
По ссылке
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
className="h-12 rounded-lg justify-center items-center mt-2"
|
||||||
|
style={{ backgroundColor: colors.tint }}
|
||||||
|
onPress={handleUpload}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<ActivityIndicator color="#fff" />
|
||||||
|
) : (
|
||||||
|
<Text className="text-base font-semibold" style={{ color: '#fff' }}>
|
||||||
|
{mode === 'url' ? 'Создать из URL' : 'Выбрать файл'}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user