Skip to content

Commit 7ee5eda

Browse files
feat: implement tag management functionality with edit and delete options (#110)
1 parent 50405e0 commit 7ee5eda

File tree

4 files changed

+223
-53
lines changed

4 files changed

+223
-53
lines changed

app/src/main/java/com/yogeshpaliyal/deepr/ui/screens/home/Home.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
8181
import dev.chrisbanes.haze.materials.HazeMaterials
8282
import dev.chrisbanes.haze.rememberHazeState
8383
import kotlinx.coroutines.launch
84+
import kotlinx.coroutines.runBlocking
8485
import org.koin.androidx.compose.koinViewModel
8586
import org.koin.compose.koinInject
8687

@@ -101,10 +102,12 @@ fun HomeScreen(
101102
resetSharedText: () -> Unit,
102103
) {
103104
var isTagsSelectionActive by remember { mutableStateOf(false) }
105+
104106
var selectedLink by remember { mutableStateOf<GetLinksAndTags?>(null) }
105107
val selectedTag = viewModel.selectedTagFilter.collectAsStateWithLifecycle()
106108
val hazeState = rememberHazeState()
107109
val context = LocalContext.current
110+
val contextCoroutine = rememberCoroutineScope()
108111
val scrollBehavior = SearchBarDefaults.enterAlwaysSearchBarScrollBehavior()
109112
val searchBarState = rememberSearchBarState()
110113
val textFieldState = rememberTextFieldState()
@@ -313,6 +316,20 @@ fun HomeScreen(
313316
isTagsSelectionActive = false
314317
},
315318
setTagFilter = { viewModel.setTagFilter(it) },
319+
editTag = { tag ->
320+
runBlocking {
321+
try {
322+
viewModel.updateTag(tag)
323+
Result.success(true)
324+
} catch (e: Exception) {
325+
return@runBlocking Result.failure(e)
326+
}
327+
}
328+
},
329+
deleteTag = {
330+
viewModel.deleteTag(it.id)
331+
Result.success(true)
332+
},
316333
)
317334
}
318335
}

app/src/main/java/com/yogeshpaliyal/deepr/ui/screens/home/TagSelectionBottomSheet.kt

Lines changed: 179 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,39 @@
11
package com.yogeshpaliyal.deepr.ui.screens.home
22

3+
import android.database.sqlite.SQLiteConstraintException
4+
import android.widget.Toast
35
import androidx.compose.foundation.clickable
46
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.Row
8+
import androidx.compose.foundation.lazy.LazyColumn
9+
import androidx.compose.foundation.lazy.items
10+
import androidx.compose.material3.AlertDialog
11+
import androidx.compose.material3.Button
12+
import androidx.compose.material3.ButtonDefaults
513
import androidx.compose.material3.ExperimentalMaterial3Api
614
import androidx.compose.material3.HorizontalDivider
15+
import androidx.compose.material3.Icon
16+
import androidx.compose.material3.IconButton
717
import androidx.compose.material3.ListItem
818
import androidx.compose.material3.ListItemDefaults
919
import androidx.compose.material3.ModalBottomSheet
1020
import androidx.compose.material3.Text
21+
import androidx.compose.material3.TextField
1122
import androidx.compose.material3.TopAppBar
1223
import androidx.compose.material3.TopAppBarDefaults
1324
import androidx.compose.material3.rememberModalBottomSheetState
1425
import androidx.compose.runtime.Composable
26+
import androidx.compose.runtime.getValue
27+
import androidx.compose.runtime.mutableStateOf
28+
import androidx.compose.runtime.remember
29+
import androidx.compose.runtime.setValue
1530
import androidx.compose.ui.Modifier
1631
import androidx.compose.ui.graphics.Color
32+
import androidx.compose.ui.platform.LocalContext
1733
import com.yogeshpaliyal.deepr.Tags
34+
import compose.icons.TablerIcons
35+
import compose.icons.tablericons.Edit
36+
import compose.icons.tablericons.Trash
1837

1938
@OptIn(ExperimentalMaterial3Api::class)
2039
@Composable
@@ -23,9 +42,112 @@ fun TagSelectionBottomSheet(
2342
selectedTag: Tags?,
2443
dismissBottomSheet: () -> Unit,
2544
setTagFilter: (Tags?) -> Unit,
45+
editTag: (Tags) -> Result<Boolean>,
46+
deleteTag: (Tags) -> Result<Boolean>,
2647
modifier: Modifier = Modifier,
2748
) {
2849
val modalBottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
50+
var isTagEditEnable by remember { mutableStateOf<Tags?>(null) }
51+
var isTagDeleteEnable by remember { mutableStateOf<Tags?>(null) }
52+
var tagEditError by remember { mutableStateOf<String?>(null) }
53+
val context = LocalContext.current
54+
55+
isTagEditEnable?.let { tag ->
56+
AlertDialog(
57+
onDismissRequest = {
58+
isTagEditEnable = null
59+
tagEditError = null
60+
},
61+
title = {
62+
Text(text = "Edit Tag")
63+
},
64+
text = {
65+
Column {
66+
TextField(
67+
value = tag.name,
68+
onValueChange = {
69+
isTagEditEnable = tag.copy(name = it)
70+
},
71+
isError = tagEditError != null,
72+
supportingText = {
73+
tagEditError?.let {
74+
Text(text = it)
75+
}
76+
},
77+
)
78+
}
79+
},
80+
confirmButton = {
81+
Button(onClick = {
82+
val result = editTag(tag)
83+
if (result.isFailure) {
84+
val exception = result.exceptionOrNull()
85+
when (exception) {
86+
is SQLiteConstraintException -> {
87+
tagEditError = "Tag with this name already exists"
88+
}
89+
90+
else -> {
91+
tagEditError = "Failed to edit tag"
92+
}
93+
}
94+
} else {
95+
isTagEditEnable = null
96+
tagEditError = null
97+
Toast
98+
.makeText(context, "Tag edited successfully", Toast.LENGTH_SHORT)
99+
.show()
100+
}
101+
}) {
102+
Text("Edit")
103+
}
104+
},
105+
dismissButton = {
106+
Button(onClick = {
107+
isTagEditEnable = null
108+
tagEditError = null
109+
}) {
110+
Text("Cancel")
111+
}
112+
},
113+
)
114+
}
115+
116+
isTagDeleteEnable?.let { tag ->
117+
AlertDialog(
118+
onDismissRequest = {
119+
isTagDeleteEnable = null
120+
},
121+
title = {
122+
Text(text = "Delete Tag")
123+
},
124+
text = {
125+
Text(text = "Are you sure you want to delete this tag?")
126+
},
127+
confirmButton = {
128+
Button(onClick = {
129+
val result = deleteTag(tag)
130+
if (result.isFailure) {
131+
Toast.makeText(context, "Failed to delete tag ${result.exceptionOrNull()}", Toast.LENGTH_SHORT).show()
132+
} else {
133+
isTagDeleteEnable = null
134+
Toast
135+
.makeText(context, "Tag deleted successfully", Toast.LENGTH_SHORT)
136+
.show()
137+
}
138+
}, colors = ButtonDefaults.buttonColors(containerColor = Color.Red)) {
139+
Text("Delete")
140+
}
141+
},
142+
dismissButton = {
143+
Button(onClick = {
144+
isTagDeleteEnable = null
145+
}) {
146+
Text("Cancel")
147+
}
148+
},
149+
)
150+
}
29151

30152
ModalBottomSheet(sheetState = modalBottomSheetState, onDismissRequest = dismissBottomSheet) {
31153
Column(modifier) {
@@ -36,39 +158,64 @@ fun TagSelectionBottomSheet(
36158
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
37159
)
38160
HorizontalDivider()
39-
ListItem(
40-
modifier =
41-
Modifier.clickable {
42-
setTagFilter(null)
43-
dismissBottomSheet()
44-
},
45-
headlineContent = { Text("All") },
46-
colors =
47-
if (selectedTag == null) {
48-
ListItemDefaults.colors(
49-
headlineColor = androidx.compose.material3.MaterialTheme.colorScheme.primary,
50-
)
51-
} else {
52-
ListItemDefaults.colors(containerColor = Color.Transparent)
53-
},
54-
)
55-
tags.forEach { tag ->
56-
ListItem(
57-
modifier =
58-
Modifier.clickable {
59-
setTagFilter(tag)
60-
dismissBottomSheet()
61-
},
62-
headlineContent = { Text(tag.name) },
63-
colors =
64-
if (selectedTag?.id == tag.id) {
65-
ListItemDefaults.colors(
66-
headlineColor = androidx.compose.material3.MaterialTheme.colorScheme.primary,
67-
)
68-
} else {
69-
ListItemDefaults.colors(containerColor = Color.Transparent)
161+
LazyColumn {
162+
item {
163+
ListItem(
164+
modifier =
165+
Modifier.clickable {
166+
setTagFilter(null)
167+
dismissBottomSheet()
168+
},
169+
headlineContent = { Text("All") },
170+
colors =
171+
if (selectedTag == null) {
172+
ListItemDefaults.colors(
173+
headlineColor = androidx.compose.material3.MaterialTheme.colorScheme.primary,
174+
)
175+
} else {
176+
ListItemDefaults.colors(containerColor = Color.Transparent)
177+
},
178+
)
179+
}
180+
items(tags) { tag ->
181+
ListItem(
182+
modifier =
183+
Modifier.clickable {
184+
setTagFilter(tag)
185+
dismissBottomSheet()
186+
},
187+
headlineContent = { Text(tag.name) },
188+
trailingContent = {
189+
Row {
190+
IconButton(onClick = {
191+
isTagEditEnable = tag
192+
}) {
193+
Icon(
194+
imageVector = TablerIcons.Edit,
195+
contentDescription = "Edit Tag",
196+
)
197+
}
198+
199+
IconButton(onClick = {
200+
isTagDeleteEnable = tag
201+
}) {
202+
Icon(
203+
imageVector = TablerIcons.Trash,
204+
contentDescription = "Clear Tag",
205+
)
206+
}
207+
}
70208
},
71-
)
209+
colors =
210+
if (selectedTag?.id == tag.id) {
211+
ListItemDefaults.colors(
212+
headlineColor = androidx.compose.material3.MaterialTheme.colorScheme.primary,
213+
)
214+
} else {
215+
ListItemDefaults.colors(containerColor = Color.Transparent)
216+
},
217+
)
218+
}
72219
}
73220
}
74221
}

app/src/main/java/com/yogeshpaliyal/deepr/viewmodel/AccountViewModel.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,20 @@ class AccountViewModel(
231231
fun deleteAccount(id: Long) {
232232
viewModelScope.launch(Dispatchers.IO) {
233233
deeprQueries.deleteDeeprById(id)
234+
deeprQueries.deleteLinkRelations(id)
235+
}
236+
}
237+
238+
fun deleteTag(id: Long) {
239+
viewModelScope.launch(Dispatchers.IO) {
240+
deeprQueries.deleteTag(id)
241+
deeprQueries.deleteTagRelations(id)
242+
}
243+
}
244+
245+
suspend fun updateTag(tag: Tags) {
246+
withContext(Dispatchers.IO) {
247+
deeprQueries.updateTag(tag.name, tag.id)
234248
}
235249
}
236250

app/src/main/sqldelight/com/yogeshpaliyal/deepr/Deepr.sq

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -66,33 +66,12 @@ ORDER BY
6666
END DESC;
6767

6868

69-
listDeeprDesc:
70-
SELECT * FROM Deepr ORDER BY createdAt DESC;
71-
7269
listDeeprAsc:
7370
SELECT * FROM Deepr ORDER BY createdAt ASC;
7471

7572
deleteDeeprById:
7673
DELETE FROM Deepr WHERE id = ?;
7774

78-
searchDeeprDesc:
79-
SELECT * FROM Deepr WHERE link LIKE '%' || ? || '%' ORDER BY createdAt DESC;
80-
81-
searchDeeprAsc:
82-
SELECT * FROM Deepr WHERE link LIKE '%' || ? || '%' ORDER BY createdAt ASC;
83-
84-
listDeeprByOpenedCountDesc:
85-
SELECT * FROM Deepr ORDER BY openedCount DESC;
86-
87-
listDeeprByOpenedCountAsc:
88-
SELECT * FROM Deepr ORDER BY openedCount ASC;
89-
90-
searchDeeprByOpenedCountDesc:
91-
SELECT * FROM Deepr WHERE link LIKE '%' || ? || '%' ORDER BY openedCount DESC;
92-
93-
searchDeeprByOpenedCountAsc:
94-
SELECT * FROM Deepr WHERE link LIKE '%' || ? || '%' ORDER BY openedCount ASC;
95-
9675
incrementOpenedCount:
9776
UPDATE Deepr SET openedCount = openedCount + 1 WHERE id = ?;
9877

@@ -124,4 +103,17 @@ INSERT OR IGNORE INTO LinkTags (linkId, tagId) VALUES (?, ?);
124103
removeTagFromLink:
125104
DELETE FROM LinkTags WHERE linkId = ? AND tagId = ?;
126105

106+
deleteLinkRelations:
107+
DELETE FROM LinkTags WHERE linkId = ?;
108+
109+
110+
deleteTag:
111+
DELETE FROM Tags WHERE id = ?;
112+
113+
deleteTagRelations:
114+
DELETE FROM LinkTags WHERE tagId = ?;
115+
116+
updateTag:
117+
UPDATE Tags SET name = ? WHERE id = ?;
118+
127119

0 commit comments

Comments
 (0)