diff --git a/app/torrent/[hash].tsx b/app/torrent/[hash].tsx index 1aca037..2340e70 100644 --- a/app/torrent/[hash].tsx +++ b/app/torrent/[hash].tsx @@ -423,6 +423,7 @@ export default function TorrentDetail() { await new Promise(resolve => setTimeout(resolve, 250)); await loadTorrentData(); setActionLoading(false); + showToast('Reannounce sent', 'success'); } catch (error: any) { showToast(error.message || 'Failed to reannounce torrent', 'error'); setActionLoading(false); @@ -649,61 +650,59 @@ export default function TorrentDetail() { ); }; - const handleAddTags = () => { - Alert.prompt( - 'Add Tags', - 'Enter tags (comma-separated)', - [ - { text: 'Cancel', style: 'cancel' }, - { - text: 'Add', - onPress: async (value: string | undefined) => { - if (!value || value.trim() === '') return; - try { - setActionLoading(true); - const tags = value.split(',').map((tag: string) => tag.trim()).filter((tag: string) => tag !== ''); - await torrentsApi.addTorrentTags([torrent.hash], tags); - await new Promise(resolve => setTimeout(resolve, 500)); - await loadTorrentData(); - showToast(`Added ${tags.length} tag(s)`, 'success'); - } catch (error: any) { - showToast(error.message || 'Failed to add tags', 'error'); - } finally { - setActionLoading(false); - } - }, + const handleManageTags = () => { + const currentTags = torrent.tags ? torrent.tags.split(',').map((t: string) => t.trim()).filter(Boolean) : []; + + const options: Array<{ text: string; style?: 'cancel' | 'default' | 'destructive'; onPress?: () => void }> = [ + ...currentTags.map(tag => ({ + text: `✕ ${tag}`, + onPress: async () => { + try { + setActionLoading(true); + await torrentsApi.removeTorrentTags([torrent.hash], [tag]); + await new Promise(resolve => setTimeout(resolve, 500)); + await loadTorrentData(); + showToast(`Removed tag "${tag}"`, 'success'); + } catch (error: any) { + showToast(error.message || 'Failed to remove tag', 'error'); + } finally { + setActionLoading(false); + } }, - ], - 'plain-text' - ); - }; - - const handleRemoveTags = () => { - Alert.prompt( - 'Remove Tags', - 'Enter tags to remove (comma-separated)', - [ - { text: 'Cancel', style: 'cancel' }, - { - text: 'Remove', - onPress: async (value: string | undefined) => { - if (!value || value.trim() === '') return; - try { - setActionLoading(true); - const tags = value.split(',').map((tag: string) => tag.trim()).filter((tag: string) => tag !== ''); - await torrentsApi.removeTorrentTags([torrent.hash], tags); - await new Promise(resolve => setTimeout(resolve, 500)); - await loadTorrentData(); - showToast(`Removed ${tags.length} tag(s)`, 'success'); - } catch (error: any) { - showToast(error.message || 'Failed to remove tags', 'error'); - } finally { - setActionLoading(false); - } - }, + })), + { + text: '+ Add Tags', + onPress: () => { + Alert.prompt('Add Tags', 'Enter tags (comma-separated)', [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Add', + onPress: async (value: string | undefined) => { + if (!value || !value.trim()) return; + try { + setActionLoading(true); + const tags = value.split(',').map((t: string) => t.trim()).filter(Boolean); + await torrentsApi.addTorrentTags([torrent.hash], tags); + await new Promise(resolve => setTimeout(resolve, 500)); + await loadTorrentData(); + showToast(`Added ${tags.length} tag(s)`, 'success'); + } catch (error: any) { + showToast(error.message || 'Failed to add tags', 'error'); + } finally { + setActionLoading(false); + } + }, + }, + ], 'plain-text'); }, - ], - 'plain-text' + }, + { text: 'Cancel', style: 'cancel' }, + ]; + + Alert.alert( + 'Manage Tags', + currentTags.length > 0 ? 'Tap a tag to remove it' : 'No tags assigned', + options ); }; @@ -826,14 +825,6 @@ export default function TorrentDetail() { Recheck - - - Reannounce - router.push(`/torrent/files?hash=${hash}`)} @@ -881,11 +872,13 @@ export default function TorrentDetail() { {properties.save_path} - + Category - - {torrent.category || 'None'} - + + + {torrent.category || 'None'} + + Tags @@ -1067,40 +1060,40 @@ export default function TorrentDetail() { {/* Priority Controls */} - {torrent?.force_start ? 'Force Start ON' : 'Force Start'} + {torrent.force_start ? 'Force Start ✓' : 'Force Start'} - {torrent?.super_seeding ? 'Super Seed ON' : 'Super Seed'} + {torrent.super_seeding ? 'Super Seed ✓' : 'Super Seed'} - Sequential DL + {torrent.seq_dl ? 'Sequential DL ✓' : 'Sequential DL'} - First/Last Priority + {torrent.f_l_piece_prio ? 'First/Last Piece ✓' : 'First/Last Piece'} @@ -1157,14 +1150,23 @@ export default function TorrentDetail() { {/* Trackers & Metadata */} - - - Edit Trackers - + + + + + + + Trackers + + {/* Category & Tags */} - Add Tags - - - - Remove Tags + Tags {/* Location & Rename */} @@ -1358,6 +1352,15 @@ const styles = StyleSheet.create({ infoRowWithButton: { position: 'relative', }, + categoryBadge: { + paddingHorizontal: 10, + paddingVertical: 4, + borderRadius: 12, + }, + categoryBadgeText: { + fontSize: 13, + fontWeight: '600', + }, peersInfoBtn: { marginLeft: 8, padding: 4, @@ -1453,6 +1456,18 @@ const styles = StyleSheet.create({ gap: 6, marginTop: 6, }, + advancedToolFullRow: { + flexDirection: 'row', + width: '100%', + gap: 6, + }, + reannounceIconButton: { + width: 44, + borderRadius: 10, + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 8, + }, advancedToolButton: { flex: 1, minWidth: '47%',