Archived
1
0

feat/panels #6

Merged
mrqiz merged 5 commits from feat/panels into lord 2025-11-27 10:57:52 +03:00
4 changed files with 913 additions and 0 deletions
Showing only changes of commit 362172e832 - Show all commits

212
apps/frontend/app/add.tsx Normal file
View 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>
);
}