From 956026407dee1bab32934e95a435b58e66acc718 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 14 Mar 2026 21:22:04 +0000
Subject: [PATCH 1/2] Initial plan
From a5b8cd8af7757f7a46f97e1ae41bbf78de1ebecb Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 14 Mar 2026 21:29:43 +0000
Subject: [PATCH 2/2] UI improvements: merge tags, category badge, reannounce
badge icon, toast notification, advanced toggle state colors
Co-authored-by: taylorcox75 <10939863+taylorcox75@users.noreply.github.com>
---
app/torrent/[hash].tsx | 197 ++++++++++++++++++++++-------------------
1 file changed, 106 insertions(+), 91 deletions(-)
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%',