Skip to content

Commit 4473564

Browse files
author
code3-dev
committed
Implement group functionality for favorites: create, rename, delete groups and multi-group selection for favorites
1 parent d11c3fe commit 4473564

File tree

2 files changed

+238
-24
lines changed

2 files changed

+238
-24
lines changed

app/src/main/java/com/pira/ccloud/data/model/FavoriteGroup.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,15 @@ data class FavoriteGroup(
3434
fun removeSeries(seriesId: Int) {
3535
seriesIds.remove(seriesId)
3636
}
37+
38+
// Copy function for renaming
39+
fun copy(name: String = this.name): FavoriteGroup {
40+
return FavoriteGroup(
41+
id = this.id,
42+
name = name,
43+
isDefault = this.isDefault,
44+
movieIds = this.movieIds.toMutableList(),
45+
seriesIds = this.seriesIds.toMutableList()
46+
)
47+
}
3748
}

app/src/main/java/com/pira/ccloud/screens/FavoritesScreen.kt

Lines changed: 227 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ fun FavoritesScreen(navController: NavController) {
133133
if (groupName.isNotBlank()) {
134134
// Check if group with same name already exists
135135
val existingGroup = groups.find {
136-
it.name.equals(groupName, ignoreCase = true)
136+
it.name.equals(groupName, ignoreCase = true) && !it.isDefault
137137
}
138138

139139
if (existingGroup == null) {
@@ -171,7 +171,7 @@ fun FavoritesScreen(navController: NavController) {
171171
if (groupName.isNotBlank()) {
172172
// Check if group with same name already exists
173173
val existingGroup = groups.find {
174-
it.name.equals(groupName, ignoreCase = true)
174+
it.name.equals(groupName, ignoreCase = true) && !it.isDefault
175175
}
176176

177177
if (existingGroup == null) {
@@ -215,6 +215,165 @@ fun FavoritesScreen(navController: NavController) {
215215
)
216216
}
217217

218+
// Dialog for renaming a group
219+
var showRenameGroupDialog by remember { mutableStateOf(false) }
220+
var groupToRename by remember { mutableStateOf<FavoriteGroup?>(null) }
221+
var newGroupName by remember { mutableStateOf("") }
222+
223+
if (showRenameGroupDialog && groupToRename != null) {
224+
AlertDialog(
225+
onDismissRequest = { showRenameGroupDialog = false },
226+
title = { Text("Rename Playlist") },
227+
text = {
228+
OutlinedTextField(
229+
value = newGroupName,
230+
onValueChange = { newGroupName = it },
231+
label = { Text("New Playlist Name") },
232+
modifier = Modifier.fillMaxWidth(),
233+
singleLine = true,
234+
keyboardOptions = KeyboardOptions(
235+
imeAction = ImeAction.Done
236+
),
237+
keyboardActions = KeyboardActions(
238+
onDone = {
239+
if (newGroupName.isNotBlank() && groupToRename != null) {
240+
// Check if another group with same name already exists
241+
val existingGroup = groups.find {
242+
it.id != groupToRename!!.id &&
243+
it.name.equals(newGroupName, ignoreCase = true) &&
244+
!it.isDefault
245+
}
246+
247+
if (existingGroup == null) {
248+
val updatedGroup = groupToRename!!.copy(name = newGroupName)
249+
StorageUtils.saveFavoriteGroup(context, updatedGroup)
250+
groups = StorageUtils.loadAllFavoriteGroups(context)
251+
showRenameGroupDialog = false
252+
groupToRename = null
253+
newGroupName = ""
254+
// Show success message
255+
android.widget.Toast.makeText(
256+
context,
257+
"Playlist renamed successfully",
258+
android.widget.Toast.LENGTH_SHORT
259+
).show()
260+
} else {
261+
// Show error message
262+
android.widget.Toast.makeText(
263+
context,
264+
"A playlist with this name already exists",
265+
android.widget.Toast.LENGTH_SHORT
266+
).show()
267+
}
268+
}
269+
}
270+
)
271+
)
272+
},
273+
confirmButton = {
274+
TextButton(
275+
onClick = {
276+
if (newGroupName.isNotBlank() && groupToRename != null) {
277+
// Check if another group with same name already exists
278+
val existingGroup = groups.find {
279+
it.id != groupToRename!!.id &&
280+
it.name.equals(newGroupName, ignoreCase = true) &&
281+
!it.isDefault
282+
}
283+
284+
if (existingGroup == null) {
285+
val updatedGroup = groupToRename!!.copy(name = newGroupName)
286+
StorageUtils.saveFavoriteGroup(context, updatedGroup)
287+
groups = StorageUtils.loadAllFavoriteGroups(context)
288+
showRenameGroupDialog = false
289+
groupToRename = null
290+
newGroupName = ""
291+
// Show success message
292+
android.widget.Toast.makeText(
293+
context,
294+
"Playlist renamed successfully",
295+
android.widget.Toast.LENGTH_SHORT
296+
).show()
297+
} else {
298+
// Show error message
299+
android.widget.Toast.makeText(
300+
context,
301+
"A playlist with this name already exists",
302+
android.widget.Toast.LENGTH_SHORT
303+
).show()
304+
}
305+
}
306+
},
307+
enabled = newGroupName.isNotBlank()
308+
) {
309+
Text("Rename")
310+
}
311+
},
312+
dismissButton = {
313+
TextButton(
314+
onClick = {
315+
showRenameGroupDialog = false
316+
groupToRename = null
317+
newGroupName = ""
318+
}
319+
) {
320+
Text("Cancel")
321+
}
322+
}
323+
)
324+
}
325+
326+
// Dialog for deleting a group
327+
var showDeleteGroupDialog by remember { mutableStateOf(false) }
328+
var groupToDelete by remember { mutableStateOf<FavoriteGroup?>(null) }
329+
330+
if (showDeleteGroupDialog && groupToDelete != null) {
331+
AlertDialog(
332+
onDismissRequest = { showDeleteGroupDialog = false },
333+
title = { Text("Delete Playlist") },
334+
text = {
335+
Text("Are you sure you want to delete the playlist \"${groupToDelete!!.name}\"? This action cannot be undone.")
336+
},
337+
confirmButton = {
338+
TextButton(
339+
onClick = {
340+
if (groupToDelete != null) {
341+
StorageUtils.removeFavoriteGroup(context, groupToDelete!!.id)
342+
groups = StorageUtils.loadAllFavoriteGroups(context)
343+
344+
// If the deleted group was the selected group, switch to default
345+
if (selectedGroup?.id == groupToDelete!!.id) {
346+
selectedGroup = groups.firstOrNull { it.isDefault }
347+
favorites = StorageUtils.loadAllFavorites(context)
348+
}
349+
350+
showDeleteGroupDialog = false
351+
groupToDelete = null
352+
// Show success message
353+
android.widget.Toast.makeText(
354+
context,
355+
"Playlist deleted successfully",
356+
android.widget.Toast.LENGTH_SHORT
357+
).show()
358+
}
359+
}
360+
) {
361+
Text("Delete")
362+
}
363+
},
364+
dismissButton = {
365+
TextButton(
366+
onClick = {
367+
showDeleteGroupDialog = false
368+
groupToDelete = null
369+
}
370+
) {
371+
Text("Cancel")
372+
}
373+
}
374+
)
375+
}
376+
218377
// Dialog for moving item to groups (multi-select)
219378
if (showMoveToGroupDialog && selectedItem != null) {
220379
val item = selectedItem!!
@@ -363,30 +522,74 @@ fun FavoritesScreen(navController: NavController) {
363522
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(8.dp)
364523
) {
365524
items(groups) { group ->
366-
Card(
367-
modifier = Modifier
368-
.padding(vertical = 4.dp)
369-
.clickable {
370-
selectedGroup = group
371-
// Load favorites for this group
372-
favorites = if (group.isDefault) {
373-
StorageUtils.loadAllFavorites(context)
374-
} else {
375-
StorageUtils.getFavoritesInGroup(context, group.id)
525+
Box {
526+
var showGroupMenu by remember { mutableStateOf(false) }
527+
528+
Card(
529+
modifier = Modifier
530+
.padding(vertical = 4.dp)
531+
.clickable {
532+
selectedGroup = group
533+
// Load favorites for this group
534+
favorites = if (group.isDefault) {
535+
StorageUtils.loadAllFavorites(context)
536+
} else {
537+
StorageUtils.getFavoritesInGroup(context, group.id)
538+
}
539+
},
540+
shape = RoundedCornerShape(16.dp),
541+
elevation = CardDefaults.cardElevation(defaultElevation = if (selectedGroup?.id == group.id) 4.dp else 0.dp),
542+
colors = if (selectedGroup?.id == group.id) {
543+
CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
544+
} else {
545+
CardDefaults.cardColors()
546+
}
547+
) {
548+
Row(
549+
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
550+
verticalAlignment = Alignment.CenterVertically
551+
) {
552+
Text(text = group.name)
553+
554+
// Show menu icon for non-default groups
555+
if (!group.isDefault) {
556+
IconButton(
557+
onClick = { showGroupMenu = true },
558+
modifier = Modifier.size(24.dp)
559+
) {
560+
Icon(
561+
imageVector = Icons.Default.MoreVert,
562+
contentDescription = "Group options",
563+
modifier = Modifier.size(16.dp)
564+
)
565+
}
376566
}
377-
},
378-
shape = RoundedCornerShape(16.dp),
379-
elevation = CardDefaults.cardElevation(defaultElevation = if (selectedGroup?.id == group.id) 4.dp else 0.dp),
380-
colors = if (selectedGroup?.id == group.id) {
381-
CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
382-
} else {
383-
CardDefaults.cardColors()
567+
}
568+
}
569+
570+
// Group menu for rename/delete options
571+
DropdownMenu(
572+
expanded = showGroupMenu,
573+
onDismissRequest = { showGroupMenu = false }
574+
) {
575+
DropdownMenuItem(
576+
text = { Text("Rename") },
577+
onClick = {
578+
groupToRename = group
579+
newGroupName = group.name
580+
showRenameGroupDialog = true
581+
showGroupMenu = false
582+
}
583+
)
584+
DropdownMenuItem(
585+
text = { Text("Delete") },
586+
onClick = {
587+
groupToDelete = group
588+
showDeleteGroupDialog = true
589+
showGroupMenu = false
590+
}
591+
)
384592
}
385-
) {
386-
Text(
387-
text = group.name,
388-
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp)
389-
)
390593
}
391594
}
392595
}

0 commit comments

Comments
 (0)