Skip to content

Commit 1833bf5

Browse files
Add Thumbnail support (#232)
* Refactor metadata fetching logic to use a dedicated function and trigger on link selection * Enhance search functionality by including notes in query conditions and refactor API documentation layout * Add thumbnail support to Deepr entries and update related functionality * Add thumbnail display option and related settings functionality
1 parent ccd59f0 commit 1833bf5

File tree

12 files changed

+125
-11
lines changed

12 files changed

+125
-11
lines changed

app/src/main/java/com/yogeshpaliyal/deepr/backup/CsvWriter.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class CsvWriter {
2222
Constants.Header.NAME,
2323
Constants.Header.NOTES,
2424
Constants.Header.TAGS,
25+
Constants.Header.THUMBNAIL,
2526
),
2627
)
2728
// Write Data
@@ -34,6 +35,7 @@ class CsvWriter {
3435
item.name,
3536
item.notes,
3637
item.tagsNames ?: "",
38+
item.thumbnail,
3739
),
3840
)
3941
}

app/src/main/java/com/yogeshpaliyal/deepr/backup/ImportRepositoryImpl.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class ImportRepositoryImpl(
4747
val name = row.getOrNull(3)?.toString() ?: ""
4848
val notes = row.getOrNull(4)?.toString() ?: ""
4949
val tagsString = row.getOrNull(5)?.toString() ?: ""
50+
val thumbnail = row.getOrNull(6)?.toString() ?: ""
5051
val existing = deeprQueries.getDeeprByLink(link).executeAsOneOrNull()
5152
if (link.isNotBlank() && existing == null) {
5253
updatedCount++
@@ -56,6 +57,7 @@ class ImportRepositoryImpl(
5657
openedCount = openedCount,
5758
name = name,
5859
notes = notes,
60+
thumbnail = thumbnail,
5961
)
6062
val linkId = deeprQueries.lastInsertRowId().executeAsOne()
6163

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class AppPreferenceDataStore(
2929
private val AUTO_BACKUP_INTERVAL = longPreferencesKey("auto_backup_interval")
3030
private val LAST_BACKUP_TIME = longPreferencesKey("last_backup_time")
3131
private val DEFAULT_PAGE_FAVOURITES = booleanPreferencesKey("default_page_favourites")
32+
private val IS_THUMBNAIL_ENABLE = booleanPreferencesKey("is_thumbnail_enable")
3233
}
3334

3435
val getSortingOrder: Flow<@SortType String> =
@@ -81,6 +82,11 @@ class AppPreferenceDataStore(
8182
preferences[DEFAULT_PAGE_FAVOURITES] ?: false // Default to All (-1)
8283
}
8384

85+
val isThumbnailEnable: Flow<Boolean> =
86+
context.appDataStore.data.map { preferences ->
87+
preferences[IS_THUMBNAIL_ENABLE] ?: false
88+
}
89+
8490
suspend fun setSortingOrder(order: @SortType String) {
8591
context.appDataStore.edit { prefs ->
8692
prefs[SORTING_ORDER] = order
@@ -146,4 +152,10 @@ class AppPreferenceDataStore(
146152
prefs[DEFAULT_PAGE_FAVOURITES] = favourites
147153
}
148154
}
155+
156+
suspend fun setThumbnailEnable(thumbnail: Boolean) {
157+
context.appDataStore.edit { prefs ->
158+
prefs[IS_THUMBNAIL_ENABLE] = thumbnail
159+
}
160+
}
149161
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import compose.icons.tablericons.Download
6767
import compose.icons.tablericons.FileText
6868
import compose.icons.tablericons.InfoCircle
6969
import compose.icons.tablericons.Language
70+
import compose.icons.tablericons.Photo
7071
import compose.icons.tablericons.Refresh
7172
import compose.icons.tablericons.Server
7273
import compose.icons.tablericons.Settings
@@ -117,6 +118,7 @@ fun SettingsScreen(
117118

118119
// Collect default page preference state
119120
val defaultPageFavourites by viewModel.defaultPageFavouritesEnabled.collectAsStateWithLifecycle()
121+
val isThumbnailEnable by viewModel.isThumbnailEnable.collectAsStateWithLifecycle()
120122

121123
// Collect sync preference states
122124
val syncEnabled by viewModel.syncEnabled.collectAsStateWithLifecycle()
@@ -453,6 +455,21 @@ fun SettingsScreen(
453455
)
454456
},
455457
)
458+
459+
SettingsItem(
460+
TablerIcons.Photo,
461+
title = "Show thumbnails for links",
462+
description = "If enabled, thumbnails will be displayed for saved links where available",
463+
onClick = {
464+
viewModel.setIsThumbnailEnable(!isThumbnailEnable)
465+
},
466+
trailing = {
467+
Switch(
468+
checked = isThumbnailEnable,
469+
onCheckedChange = { viewModel.setIsThumbnailEnable(it) },
470+
)
471+
},
472+
)
456473
}
457474

458475
SettingsSection("About") {

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.FlowRow
1414
import androidx.compose.foundation.layout.PaddingValues
1515
import androidx.compose.foundation.layout.Row
1616
import androidx.compose.foundation.layout.Spacer
17+
import androidx.compose.foundation.layout.aspectRatio
1718
import androidx.compose.foundation.layout.fillMaxSize
1819
import androidx.compose.foundation.layout.fillMaxWidth
1920
import androidx.compose.foundation.layout.height
@@ -49,11 +50,14 @@ import androidx.compose.ui.Alignment
4950
import androidx.compose.ui.Modifier
5051
import androidx.compose.ui.draw.clip
5152
import androidx.compose.ui.graphics.Color
53+
import androidx.compose.ui.layout.ContentScale
5254
import androidx.compose.ui.platform.LocalContext
5355
import androidx.compose.ui.res.stringResource
5456
import androidx.compose.ui.text.font.FontWeight
5557
import androidx.compose.ui.text.style.TextOverflow
58+
import androidx.compose.ui.tooling.preview.Preview
5659
import androidx.compose.ui.unit.dp
60+
import coil3.compose.AsyncImage
5761
import com.yogeshpaliyal.deepr.GetLinksAndTags
5862
import com.yogeshpaliyal.deepr.R
5963
import com.yogeshpaliyal.deepr.Tags
@@ -101,13 +105,31 @@ sealed class MenuItem(
101105
) : MenuItem(item)
102106
}
103107

108+
@Composable
109+
@Preview
110+
private fun DeeprItemPreview() {
111+
DeeprItem(
112+
account =
113+
createDeeprObject(
114+
name = "Yogesh Paliyal",
115+
link = "https://yogeshpaliyal.com",
116+
thumbnail = "https://yogeshpaliyal.com/og.png",
117+
),
118+
{},
119+
{},
120+
listOf(),
121+
isThumbnailEnable = true,
122+
)
123+
}
124+
104125
@OptIn(ExperimentalFoundationApi::class)
105126
@Composable
106127
fun DeeprItem(
107128
account: GetLinksAndTags,
108129
onItemClick: (MenuItem) -> Unit,
109130
onTagClick: (tag: String) -> Unit,
110131
selectedTag: List<Tags>,
132+
isThumbnailEnable: Boolean,
111133
modifier: Modifier = Modifier,
112134
) {
113135
var expanded by remember { mutableStateOf(false) }
@@ -243,6 +265,20 @@ fun DeeprItem(
243265
},
244266
),
245267
) {
268+
if (account.thumbnail.isNotEmpty() && isThumbnailEnable) {
269+
AsyncImage(
270+
model = account.thumbnail,
271+
contentDescription = account.name,
272+
modifier =
273+
Modifier
274+
.fillMaxWidth()
275+
.aspectRatio(1.91f)
276+
.background(MaterialTheme.colorScheme.surfaceVariant),
277+
placeholder = null,
278+
error = null,
279+
contentScale = ContentScale.Crop,
280+
)
281+
}
246282
Column(
247283
modifier =
248284
Modifier

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ fun Content(
421421
editDeepr: (GetLinksAndTags) -> Unit = {},
422422
) {
423423
val accounts by viewModel.accounts.collectAsStateWithLifecycle()
424+
val isThumbnailEnable by viewModel.isThumbnailEnable.collectAsStateWithLifecycle()
424425

425426
if (accounts == null) {
426427
Column(
@@ -496,6 +497,7 @@ fun Content(
496497
// Toggle the tag in the filter by tag name
497498
viewModel.setSelectedTagByName(it)
498499
},
500+
isThumbnailEnable = isThumbnailEnable,
499501
)
500502
}
501503
}
@@ -507,6 +509,7 @@ fun DeeprList(
507509
contentPaddingValues: PaddingValues,
508510
onItemClick: (MenuItem) -> Unit,
509511
onTagClick: (String) -> Unit,
512+
isThumbnailEnable: Boolean,
510513
modifier: Modifier = Modifier,
511514
) {
512515
AnimatedVisibility(
@@ -575,6 +578,7 @@ fun DeeprList(
575578
selectedTag = selectedTag,
576579
onItemClick = onItemClick,
577580
onTagClick = onTagClick,
581+
isThumbnailEnable = isThumbnailEnable,
578582
)
579583
}
580584
}

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
package com.yogeshpaliyal.deepr.ui.screens.home
44

55
import android.widget.Toast
6+
import androidx.compose.foundation.background
67
import androidx.compose.foundation.layout.Arrangement
78
import androidx.compose.foundation.layout.Column
89
import androidx.compose.foundation.layout.FlowRow
910
import androidx.compose.foundation.layout.Row
1011
import androidx.compose.foundation.layout.Spacer
12+
import androidx.compose.foundation.layout.aspectRatio
1113
import androidx.compose.foundation.layout.fillMaxWidth
1214
import androidx.compose.foundation.layout.height
1315
import androidx.compose.foundation.layout.padding
@@ -42,10 +44,12 @@ import androidx.compose.runtime.setValue
4244
import androidx.compose.ui.Alignment
4345
import androidx.compose.ui.Modifier
4446
import androidx.compose.ui.draw.clip
47+
import androidx.compose.ui.layout.ContentScale
4548
import androidx.compose.ui.platform.LocalContext
4649
import androidx.compose.ui.res.stringResource
4750
import androidx.compose.ui.unit.dp
4851
import androidx.lifecycle.compose.collectAsStateWithLifecycle
52+
import coil3.compose.AsyncImage
4953
import com.yogeshpaliyal.deepr.DeeprQueries
5054
import com.yogeshpaliyal.deepr.GetLinksAndTags
5155
import com.yogeshpaliyal.deepr.R
@@ -86,14 +90,15 @@ fun HomeBottomContent(
8690
val allTags by viewModel.allTags.collectAsStateWithLifecycle()
8791
val selectedTags = remember { mutableStateListOf<Tags>() }
8892
val initialSelectedTags = remember { mutableStateListOf<Tags>() }
93+
val isThumbnailEnable by viewModel.isThumbnailEnable.collectAsStateWithLifecycle()
8994
val isCreate = selectedLink.id == 0L
9095

91-
val fetchTitle: () -> Unit = {
96+
val fetchMetadata: () -> Unit = {
9297
isFetchingMetadata = true
9398
viewModel.fetchMetaData(deeprInfo.link) {
9499
isFetchingMetadata = false
95100
if (it != null) {
96-
deeprInfo = deeprInfo.copy(name = it.title ?: "")
101+
deeprInfo = deeprInfo.copy(name = it.title ?: "", thumbnail = it.image ?: "")
97102
isNameError = false
98103
} else {
99104
Toast
@@ -108,7 +113,7 @@ fun HomeBottomContent(
108113

109114
LaunchedEffect(selectedLink) {
110115
if (isValidDeeplink(selectedLink.link) && selectedLink.name.isEmpty()) {
111-
fetchTitle()
116+
fetchMetadata()
112117
}
113118
}
114119

@@ -153,10 +158,10 @@ fun HomeBottomContent(
153158

154159
if (deeprInfo.id == 0L) {
155160
// New Account
156-
viewModel.insertAccount(normalizedLink, deeprInfo.name, executeAfterSave, selectedTags, deeprInfo.notes)
161+
viewModel.insertAccount(normalizedLink, deeprInfo.name, executeAfterSave, selectedTags, deeprInfo.notes, deeprInfo.thumbnail)
157162
} else {
158163
// Edit
159-
viewModel.updateDeeplink(deeprInfo.id, normalizedLink, deeprInfo.name, selectedTags, deeprInfo.notes)
164+
viewModel.updateDeeplink(deeprInfo.id, normalizedLink, deeprInfo.name, selectedTags, deeprInfo.notes, deeprInfo.thumbnail)
160165
}
161166
onSaveDialogInfoChange(
162167
SaveDialogInfo(
@@ -225,7 +230,7 @@ fun HomeBottomContent(
225230
OutlinedButton(
226231
modifier = Modifier.fillMaxWidth(),
227232
enabled = deeprInfo.link.isNotBlank() && !isFetchingMetadata,
228-
onClick = fetchTitle,
233+
onClick = fetchMetadata,
229234
) {
230235
if (isFetchingMetadata) {
231236
CircularProgressIndicator(
@@ -239,6 +244,23 @@ fun HomeBottomContent(
239244

240245
Spacer(modifier = Modifier.height(8.dp))
241246

247+
if (deeprInfo.thumbnail.isNotEmpty() && isThumbnailEnable) {
248+
AsyncImage(
249+
model = deeprInfo.thumbnail,
250+
contentDescription = deeprInfo.name,
251+
modifier =
252+
Modifier
253+
.fillMaxWidth()
254+
.aspectRatio(1.91f)
255+
.background(MaterialTheme.colorScheme.surfaceVariant),
256+
placeholder = null,
257+
error = null,
258+
contentScale = ContentScale.Crop,
259+
)
260+
}
261+
262+
Spacer(modifier = Modifier.height(8.dp))
263+
242264
TextField(
243265
value = deeprInfo.name,
244266
onValueChange = {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fun createDeeprObject(
1212
link: String = "",
1313
openedCount: Long = 0,
1414
notes: String = "",
15+
thumbnail: String = "",
1516
): GetLinksAndTags =
1617
GetLinksAndTags(
1718
id = 0,
@@ -24,4 +25,5 @@ fun createDeeprObject(
2425
lastOpenedAt = "",
2526
isFavourite = 0,
2627
notes = notes,
28+
thumbnail = thumbnail,
2729
)

app/src/main/java/com/yogeshpaliyal/deepr/util/Constants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ object Constants {
88
const val CREATED_AT = "CreatedAt"
99
const val NOTES = "Notes"
1010
const val TAGS = "Tags"
11+
const val THUMBNAIL = "Thumbnail"
1112
}
1213
}

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,10 @@ class AccountViewModel(
286286
executed: Boolean,
287287
tagsList: List<Tags>,
288288
notes: String = "",
289+
thumbnail: String = "",
289290
) {
290291
viewModelScope.launch(Dispatchers.IO) {
291-
deeprQueries.insertDeepr(link = link, name, if (executed) 1 else 0, notes)
292+
deeprQueries.insertDeepr(link = link, name, if (executed) 1 else 0, notes, thumbnail)
292293
deeprQueries.lastInsertRowId().executeAsOneOrNull()?.let {
293294
modifyTagsForLink(it, tagsList)
294295
}
@@ -371,9 +372,10 @@ class AccountViewModel(
371372
newName: String,
372373
tagsList: List<Tags>,
373374
notes: String = "",
375+
thumbnail: String = "",
374376
) {
375377
viewModelScope.launch(Dispatchers.IO) {
376-
deeprQueries.updateDeeplink(newLink, newName, notes, id)
378+
deeprQueries.updateDeeplink(newLink, newName, notes, thumbnail, id)
377379
modifyTagsForLink(id, tagsList)
378380
syncToMarkdown()
379381
}
@@ -440,12 +442,22 @@ class AccountViewModel(
440442
preferenceDataStore.getDefaultPageFavourites
441443
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
442444

445+
val isThumbnailEnable =
446+
preferenceDataStore.isThumbnailEnable
447+
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
448+
443449
fun setDefaultPageFavourites(favourites: Boolean) {
444450
viewModelScope.launch(Dispatchers.IO) {
445451
preferenceDataStore.setDefaultPageFavourites(favourites)
446452
}
447453
}
448454

455+
fun setIsThumbnailEnable(thumbnail: Boolean) {
456+
viewModelScope.launch(Dispatchers.IO) {
457+
preferenceDataStore.setThumbnailEnable(thumbnail)
458+
}
459+
}
460+
449461
// Auto backup preference methods
450462
val autoBackupEnabled =
451463
preferenceDataStore.getAutoBackupEnabled

0 commit comments

Comments
 (0)