Skip to content

Commit 8ca861e

Browse files
Add Tags support (#76)
* feat: add tag filtering and link-tag relationship management in AccountViewModel and database schema * feat: add tags and link-tags relationship management in database schema and update AccountViewModel for sorting * tags enhancements * feat: refactor Home and HomeBottomContent to improve tag display and inject AccountViewModel * feat: add tag display using InputChip in DeeprItem and improve layout with FlowRow * feat: implement tag filtering and selection UI with TagSelectionBottomSheet
1 parent d7f5011 commit 8ca861e

File tree

16 files changed

+730
-202
lines changed

16 files changed

+730
-202
lines changed

app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.yogeshpaliyal.deepr
22

33
import android.app.Application
4+
import android.util.Log
45
import app.cash.sqldelight.db.SqlDriver
56
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
7+
import app.cash.sqldelight.logs.LogSqliteDriver
68
import com.yogeshpaliyal.deepr.backup.ExportRepository
79
import com.yogeshpaliyal.deepr.backup.ExportRepositoryImpl
810
import com.yogeshpaliyal.deepr.backup.ImportRepository
@@ -24,7 +26,15 @@ class DeeprApplication : Application() {
2426
module {
2527
// Provide the Android-specific SqlDriver
2628
single<SqlDriver> {
27-
AndroidSqliteDriver(DeeprDB.Schema, this@DeeprApplication, "deepr.db")
29+
LogSqliteDriver(
30+
AndroidSqliteDriver(
31+
DeeprDB.Schema,
32+
this@DeeprApplication,
33+
"deepr.db",
34+
),
35+
) {
36+
Log.d("loggingDB", it)
37+
}
2838
}
2939

3040
// Provide the Database instance

app/src/main/java/com/yogeshpaliyal/deepr/preference/AppPreferenceDataStore.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import androidx.datastore.preferences.core.booleanPreferencesKey
77
import androidx.datastore.preferences.core.edit
88
import androidx.datastore.preferences.core.stringPreferencesKey
99
import androidx.datastore.preferences.preferencesDataStore
10-
import com.yogeshpaliyal.deepr.viewmodel.SortOrder
10+
import com.yogeshpaliyal.deepr.viewmodel.SortType
1111
import kotlinx.coroutines.flow.Flow
1212
import kotlinx.coroutines.flow.map
1313

@@ -23,9 +23,9 @@ class AppPreferenceDataStore(
2323
private val SYNC_FILE_PATH = stringPreferencesKey("sync_file_path")
2424
}
2525

26-
val getSortingOrder: Flow<String> =
26+
val getSortingOrder: Flow<@SortType String> =
2727
context.appDataStore.data.map { preferences ->
28-
preferences[SORTING_ORDER] ?: SortOrder.DESC.name
28+
preferences[SORTING_ORDER] ?: SortType.SORT_CREATED_BY_DESC
2929
}
3030

3131
val getUseLinkBasedIcons: Flow<Boolean> =
@@ -43,7 +43,7 @@ class AppPreferenceDataStore(
4343
preferences[SYNC_FILE_PATH] ?: "" // Default to empty path
4444
}
4545

46-
suspend fun setSortingOrder(order: String) {
46+
suspend fun setSortingOrder(order: @SortType String) {
4747
context.appDataStore.edit { prefs ->
4848
prefs[SORTING_ORDER] = order
4949
}

app/src/main/java/com/yogeshpaliyal/deepr/ui/components/CreateShortcutDialog.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import androidx.compose.runtime.remember
1111
import androidx.compose.runtime.setValue
1212
import androidx.compose.ui.platform.LocalContext
1313
import androidx.lifecycle.compose.collectAsStateWithLifecycle
14-
import com.yogeshpaliyal.deepr.Deepr
14+
import com.yogeshpaliyal.deepr.GetLinksAndTags
1515
import com.yogeshpaliyal.deepr.util.createShortcut
1616
import com.yogeshpaliyal.deepr.util.getShortcut
1717
import com.yogeshpaliyal.deepr.util.isShortcutSupported
@@ -20,7 +20,7 @@ import org.koin.androidx.compose.koinViewModel
2020

2121
@Composable
2222
fun CreateShortcutDialog(
23-
deepr: Deepr,
23+
deepr: GetLinksAndTags,
2424
onDismiss: () -> Unit,
2525
viewModel: AccountViewModel = koinViewModel(),
2626
) {

app/src/main/java/com/yogeshpaliyal/deepr/ui/components/QRCodeDialog.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ import androidx.compose.ui.unit.dp
2424
import com.lightspark.composeqr.DotShape
2525
import com.lightspark.composeqr.QrCodeColors
2626
import com.lightspark.composeqr.QrCodeView
27-
import com.yogeshpaliyal.deepr.Deepr
27+
import com.yogeshpaliyal.deepr.GetLinksAndTags
2828
import com.yogeshpaliyal.deepr.R
2929

3030
@Composable
3131
fun QrCodeDialog(
32-
deepr: Deepr,
32+
deepr: GetLinksAndTags,
3333
onDismiss: () -> Unit,
3434
) {
3535
AlertDialog(

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

Lines changed: 117 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import androidx.compose.foundation.combinedClickable
99
import androidx.compose.foundation.layout.Arrangement
1010
import androidx.compose.foundation.layout.Box
1111
import androidx.compose.foundation.layout.Column
12+
import androidx.compose.foundation.layout.FlowRow
1213
import androidx.compose.foundation.layout.Row
1314
import androidx.compose.foundation.layout.Spacer
1415
import androidx.compose.foundation.layout.fillMaxWidth
@@ -19,6 +20,7 @@ import androidx.compose.material3.DropdownMenu
1920
import androidx.compose.material3.DropdownMenuItem
2021
import androidx.compose.material3.Icon
2122
import androidx.compose.material3.IconButton
23+
import androidx.compose.material3.InputChip
2224
import androidx.compose.material3.MaterialTheme
2325
import androidx.compose.material3.Text
2426
import androidx.compose.runtime.Composable
@@ -31,7 +33,8 @@ import androidx.compose.ui.Modifier
3133
import androidx.compose.ui.platform.LocalContext
3234
import androidx.compose.ui.text.style.TextOverflow
3335
import androidx.compose.ui.unit.dp
34-
import com.yogeshpaliyal.deepr.Deepr
36+
import com.yogeshpaliyal.deepr.GetLinksAndTags
37+
import com.yogeshpaliyal.deepr.Tags
3538
import compose.icons.TablerIcons
3639
import compose.icons.tablericons.Copy
3740
import compose.icons.tablericons.DotsVertical
@@ -45,17 +48,21 @@ import java.util.TimeZone
4548
@OptIn(ExperimentalFoundationApi::class)
4649
@Composable
4750
fun DeeprItem(
48-
account: Deepr,
51+
account: GetLinksAndTags,
52+
selectedTag: Tags?,
4953
modifier: Modifier = Modifier,
50-
onItemClick: ((Deepr) -> Unit)? = null,
51-
onRemoveClick: ((Deepr) -> Unit)? = null,
52-
onShortcutClick: ((Deepr) -> Unit)? = null,
53-
onQrCodeClick: ((Deepr) -> Unit)? = null,
54-
onEditClick: ((Deepr) -> Unit)? = null,
55-
onItemLongClick: ((Deepr) -> Unit)? = null,
54+
onItemClick: ((GetLinksAndTags) -> Unit)? = null,
55+
onRemoveClick: ((GetLinksAndTags) -> Unit)? = null,
56+
onShortcutClick: ((GetLinksAndTags) -> Unit)? = null,
57+
onQrCodeClick: ((GetLinksAndTags) -> Unit)? = null,
58+
onEditClick: ((GetLinksAndTags) -> Unit)? = null,
59+
onItemLongClick: ((GetLinksAndTags) -> Unit)? = null,
60+
onTagClick: ((String) -> Unit)? = null,
5661
) {
5762
var expanded by remember { mutableStateOf(false) }
5863
val context = LocalContext.current
64+
val selectedTags =
65+
remember(account.tagsNames) { account.tagsNames?.split(",")?.toMutableList() }
5966

6067
Card(
6168
modifier =
@@ -81,95 +88,112 @@ fun DeeprItem(
8188
.weight(1f)
8289
.padding(end = 8.dp),
8390
) {
84-
if (account.name.isNotEmpty()) {
85-
Text(
86-
text = account.name,
87-
maxLines = 1,
88-
overflow = TextOverflow.Ellipsis,
89-
style = MaterialTheme.typography.labelLarge,
90-
)
91-
}
92-
Text(
93-
text = account.link,
94-
maxLines = 1,
95-
overflow = TextOverflow.Ellipsis,
96-
style = MaterialTheme.typography.bodySmall,
97-
)
98-
Spacer(modifier = Modifier.height(4.dp))
99-
Row(verticalAlignment = Alignment.CenterVertically) {
100-
Text(
101-
text = formatDateTime(account.createdAt),
102-
style = MaterialTheme.typography.bodySmall,
103-
color = MaterialTheme.colorScheme.onSurfaceVariant,
104-
)
105-
Spacer(modifier = Modifier.weight(1f))
106-
Text(
107-
text = "Opened: ${account.openedCount}",
108-
style = MaterialTheme.typography.bodySmall,
109-
color = MaterialTheme.colorScheme.onSurfaceVariant,
110-
)
111-
}
112-
}
113-
Box {
114-
IconButton(onClick = { expanded = true }) {
115-
Icon(TablerIcons.DotsVertical, contentDescription = "More options")
116-
}
91+
Row(modifier = Modifier.fillMaxWidth()) {
92+
Column(modifier = Modifier.weight(1f)) {
93+
if (account.name.isNotEmpty()) {
94+
Text(
95+
text = account.name,
96+
maxLines = 1,
97+
overflow = TextOverflow.Ellipsis,
98+
style = MaterialTheme.typography.labelLarge,
99+
)
100+
}
101+
Text(
102+
text = account.link,
103+
maxLines = 1,
104+
overflow = TextOverflow.Ellipsis,
105+
style = MaterialTheme.typography.bodySmall,
106+
)
107+
Spacer(modifier = Modifier.height(4.dp))
108+
Row(verticalAlignment = Alignment.CenterVertically) {
109+
Text(
110+
text = formatDateTime(account.createdAt),
111+
style = MaterialTheme.typography.bodySmall,
112+
color = MaterialTheme.colorScheme.onSurfaceVariant,
113+
)
114+
Spacer(modifier = Modifier.weight(1f))
115+
Text(
116+
text = "Opened: ${account.openedCount}",
117+
style = MaterialTheme.typography.bodySmall,
118+
color = MaterialTheme.colorScheme.onSurfaceVariant,
119+
)
120+
}
121+
}
122+
Box {
123+
IconButton(onClick = { expanded = true }) {
124+
Icon(TablerIcons.DotsVertical, contentDescription = "More options")
125+
}
117126

118-
DropdownMenu(
119-
expanded = expanded,
120-
onDismissRequest = { expanded = false },
121-
) {
122-
DropdownMenuItem(
123-
text = { Text("Copy link") },
124-
onClick = {
125-
val clipboard =
126-
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
127-
val clip = ClipData.newPlainText("Link copied", account.link)
128-
clipboard.setPrimaryClip(clip)
129-
Toast.makeText(context, "Link copied", Toast.LENGTH_SHORT).show()
130-
expanded = false
131-
},
132-
leadingIcon = {
133-
Icon(
134-
TablerIcons.Copy,
135-
contentDescription = "Copy link",
127+
DropdownMenu(
128+
expanded = expanded,
129+
onDismissRequest = { expanded = false },
130+
) {
131+
DropdownMenuItem(
132+
text = { Text("Copy link") },
133+
onClick = {
134+
val clipboard =
135+
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
136+
val clip = ClipData.newPlainText("Link copied", account.link)
137+
clipboard.setPrimaryClip(clip)
138+
Toast.makeText(context, "Link copied", Toast.LENGTH_SHORT).show()
139+
expanded = false
140+
},
141+
leadingIcon = {
142+
Icon(
143+
TablerIcons.Copy,
144+
contentDescription = "Copy link",
145+
)
146+
},
136147
)
137-
},
138-
)
139-
ShortcutMenuItem(account, {
140-
onShortcutClick?.invoke(it)
141-
expanded = false
142-
})
143-
ShowQRCodeMenuItem(account, {
144-
onQrCodeClick?.invoke(it)
145-
expanded = false
146-
})
147-
DropdownMenuItem(
148-
text = { Text("Edit") },
149-
onClick = {
150-
onEditClick?.invoke(account)
151-
expanded = false
152-
},
153-
leadingIcon = {
154-
Icon(
155-
TablerIcons.Edit,
156-
contentDescription = "Edit",
148+
ShortcutMenuItem(account, {
149+
onShortcutClick?.invoke(it)
150+
expanded = false
151+
})
152+
ShowQRCodeMenuItem(account, {
153+
onQrCodeClick?.invoke(it)
154+
expanded = false
155+
})
156+
DropdownMenuItem(
157+
text = { Text("Edit") },
158+
onClick = {
159+
onEditClick?.invoke(account)
160+
expanded = false
161+
},
162+
leadingIcon = {
163+
Icon(
164+
TablerIcons.Edit,
165+
contentDescription = "Edit",
166+
)
167+
},
157168
)
158-
},
159-
)
160-
DropdownMenuItem(
161-
text = { Text("Delete") },
162-
onClick = {
163-
onRemoveClick?.invoke(account)
164-
expanded = false
165-
},
166-
leadingIcon = {
167-
Icon(
168-
TablerIcons.Trash,
169-
contentDescription = "Delete",
169+
DropdownMenuItem(
170+
text = { Text("Delete") },
171+
onClick = {
172+
onRemoveClick?.invoke(account)
173+
expanded = false
174+
},
175+
leadingIcon = {
176+
Icon(
177+
TablerIcons.Trash,
178+
contentDescription = "Delete",
179+
)
180+
},
170181
)
171-
},
172-
)
182+
}
183+
}
184+
}
185+
186+
FlowRow(
187+
modifier = Modifier.fillMaxWidth(),
188+
horizontalArrangement = Arrangement.spacedBy(4.dp),
189+
) {
190+
selectedTags?.forEach { tag ->
191+
InputChip(
192+
selected = selectedTag?.name == tag.trim(),
193+
onClick = { onTagClick?.invoke(tag.trim()) },
194+
label = { Text(tag.trim()) },
195+
)
196+
}
173197
}
174198
}
175199
}

0 commit comments

Comments
 (0)