Skip to content

Commit bf81948

Browse files
[Bug Fix] Duplicate link issues (#181)
* added duplicate link validation and scheme and authority check for link and replaced collectAsState with collectAsStateWithLifecycle * bug fix: saved link was not opening because of adding duplication check in validate function. now checking duplication only while saving * removed logs * refactoring and best practices change * fixed link duplicates while saving if https:// is removed * migrated dependency to catalogue version --------- Co-authored-by: anas shikoh <[email protected]>
1 parent 31a78d1 commit bf81948

File tree

15 files changed

+116
-173
lines changed

15 files changed

+116
-173
lines changed

app/build.gradle.kts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ plugins {
44
alias(libs.plugins.kotlin.compose)
55
alias(libs.plugins.kotlin.serialization)
66
alias(libs.plugins.sqldelight)
7-
id("org.jmailen.kotlinter")
8-
id("com.google.gms.google-services")
9-
id("com.google.firebase.crashlytics")
7+
alias(libs.plugins.kotlinter)
8+
alias(libs.plugins.googleServices)
9+
alias(libs.plugins.firebaseCrashlytics)
1010
}
1111

1212
android {
@@ -136,8 +136,8 @@ dependencies {
136136
implementation(libs.koin.compose)
137137
implementation(libs.zxing.scanner)
138138
ktlint(libs.ktlint)
139-
implementation("dev.chrisbanes.haze:haze:1.6.9")
140-
implementation("dev.chrisbanes.haze:haze-materials:1.6.9")
139+
implementation(libs.haze)
140+
implementation(libs.haze.materials)
141141
implementation(libs.jsoup)
142142
implementation(libs.ktor.client.core)
143143
implementation(libs.ktor.client.cio)
@@ -155,7 +155,7 @@ dependencies {
155155
implementation(libs.firebase.analytics)
156156
implementation(libs.firebase.crashlytics.ndk)
157157

158-
// Add Firebase dependencies to pro and freePlaystore flavors specifically
158+
// Add Firebase dependencies to pro and freePlayStore flavors specifically
159159
"proImplementation"(platform(libs.firebase.bom))
160160
"proImplementation"(libs.firebase.analytics)
161161
"proImplementation"(libs.firebase.crashlytics.ndk)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import com.yogeshpaliyal.deepr.ui.theme.DeeprTheme
3434
import com.yogeshpaliyal.deepr.util.LanguageUtil
3535
import kotlinx.coroutines.flow.MutableStateFlow
3636
import kotlinx.coroutines.flow.first
37+
import kotlinx.coroutines.flow.update
3738
import kotlinx.coroutines.runBlocking
3839

3940
data class SharedLink(
@@ -77,7 +78,7 @@ class MainActivity : ComponentActivity() {
7778
Surface {
7879
val sharedText by sharingLink.collectAsStateWithLifecycle()
7980
Dashboard(sharedText = sharedText) {
80-
sharingLink.value = null
81+
sharingLink.update { null }
8182
}
8283
}
8384
}
@@ -104,7 +105,7 @@ class MainActivity : ComponentActivity() {
104105

105106
else -> null
106107
}
107-
sharingLink.value = sharedText
108+
sharingLink.update { sharedText }
108109
}
109110

110111
override fun onNewIntent(intent: Intent) {

app/src/main/java/com/yogeshpaliyal/deepr/server/LocalServerRepositoryImpl.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import com.yogeshpaliyal.deepr.viewmodel.AccountViewModel
99
import io.ktor.http.ContentType
1010
import io.ktor.http.HttpStatusCode
1111
import io.ktor.serialization.kotlinx.json.json
12-
import io.ktor.server.application.call
1312
import io.ktor.server.application.install
1413
import io.ktor.server.cio.CIO
1514
import io.ktor.server.cio.CIOApplicationEngine

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

Lines changed: 0 additions & 114 deletions
This file was deleted.

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,13 @@ fun AboutUsScreen(
109109
)
110110
Spacer(modifier = Modifier.height(32.dp))
111111
Card(modifier = Modifier.fillMaxWidth()) {
112-
Column(modifier = Modifier.padding(16.dp).fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
112+
Column(
113+
modifier =
114+
Modifier
115+
.padding(16.dp)
116+
.fillMaxWidth(),
117+
horizontalAlignment = Alignment.CenterHorizontally,
118+
) {
113119
Text(
114120
text = stringResource(R.string.author),
115121
style = MaterialTheme.typography.titleLarge,
@@ -124,13 +130,22 @@ fun AboutUsScreen(
124130
val uriHandler = LocalUriHandler.current
125131

126132
IconButton(onClick = { uriHandler.openUri("https://twitter.com/yogeshpaliyal") }) {
127-
Icon(TablerIcons.BrandTwitter, contentDescription = stringResource(R.string.twitter))
133+
Icon(
134+
TablerIcons.BrandTwitter,
135+
contentDescription = stringResource(R.string.twitter),
136+
)
128137
}
129138
IconButton(onClick = { uriHandler.openUri("https://www.linkedin.com/in/yogeshpaliyal/") }) {
130-
Icon(TablerIcons.BrandLinkedin, contentDescription = stringResource(R.string.linkedin))
139+
Icon(
140+
TablerIcons.BrandLinkedin,
141+
contentDescription = stringResource(R.string.linkedin),
142+
)
131143
}
132144
IconButton(onClick = { uriHandler.openUri("https://github.com/yogeshpaliyal") }) {
133-
Icon(TablerIcons.BrandGithub, contentDescription = stringResource(R.string.github))
145+
Icon(
146+
TablerIcons.BrandGithub,
147+
contentDescription = stringResource(R.string.github),
148+
)
134149
}
135150
}
136151
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ import androidx.compose.material3.Text
3232
import androidx.compose.material3.TopAppBar
3333
import androidx.compose.runtime.Composable
3434
import androidx.compose.runtime.getValue
35+
import androidx.compose.runtime.mutableStateOf
36+
import androidx.compose.runtime.remember
37+
import androidx.compose.runtime.setValue
3538
import androidx.compose.runtime.snapshots.SnapshotStateList
3639
import androidx.compose.ui.Alignment
3740
import androidx.compose.ui.Modifier
@@ -74,14 +77,14 @@ fun LocalNetworkServerScreen(
7477
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
7578

7679
// Track if user wants to start the server (used for permission flow)
77-
val pendingStart = androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(false) }
80+
var pendingStart by remember { mutableStateOf(false) }
7881

7982
// Request notification permission for Android 13+
8083
val notificationPermissionState =
8184
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
8285
rememberPermissionState(Manifest.permission.POST_NOTIFICATIONS) {
83-
if (pendingStart.value) {
84-
pendingStart.value = false
86+
if (pendingStart) {
87+
pendingStart = false
8588
LocalServerService.startService(context)
8689
}
8790
}
@@ -164,7 +167,7 @@ fun LocalNetworkServerScreen(
164167
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
165168
notificationPermissionState?.status?.isGranted == false
166169
) {
167-
pendingStart.value = true
170+
pendingStart = true
168171
notificationPermissionState.launchPermissionRequest()
169172
} else {
170173
LocalServerService.startService(context)

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -466,9 +466,7 @@ fun SettingsScreen(
466466
viewModel.setLanguageCode(selectedLanguageCode)
467467
showLanguageDialog = false
468468
// Recreate activity to apply language change
469-
(context as? MainActivity)?.let { activity ->
470-
activity.recreate()
471-
}
469+
(context as? MainActivity)?.recreate()
472470
},
473471
onDismiss = { showLanguageDialog = false },
474472
)
@@ -531,9 +529,7 @@ private fun SettingsItem(
531529
if (shouldShowLoading) {
532530
isLoading = true
533531
}
534-
onClick({
535-
isLoading = false
536-
})
532+
onClick { isLoading = false }
537533
})
538534
} else {
539535
Modifier

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.yogeshpaliyal.deepr.ui.screens.home
22

3-
import android.R.attr.label
43
import android.widget.Toast
54
import androidx.activity.compose.rememberLauncherForActivityResult
65
import androidx.compose.animation.AnimatedVisibility
@@ -261,7 +260,12 @@ fun HomeScreen(
261260

262261
val favouriteFilter by viewModel.favouriteFilter.collectAsStateWithLifecycle()
263262
// Favourite filter tabs
264-
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
263+
SingleChoiceSegmentedButtonRow(
264+
modifier =
265+
Modifier
266+
.fillMaxWidth()
267+
.padding(8.dp),
268+
) {
265269
SegmentedButton(
266270
shape =
267271
SegmentedButtonDefaults.itemShape(
@@ -462,16 +466,19 @@ fun Content(
462466
viewModel.incrementOpenedCount(it.item.id)
463467
openDeeplink(context, it.item.link)
464468
}
469+
465470
is MenuItem.Delete -> showDeleteConfirmDialog = it.item
466471
is MenuItem.Edit -> editDeepr(it.item)
467472
is MenuItem.FavouriteClick -> viewModel.toggleFavourite(it.item.id)
468473
is MenuItem.ResetCounter -> {
469474
viewModel.resetOpenedCount(it.item.id)
470475
Toast.makeText(context, "Opened count reset", Toast.LENGTH_SHORT).show()
471476
}
477+
472478
is MenuItem.Shortcut -> {
473479
showShortcutDialog = it.item
474480
}
481+
475482
is MenuItem.ShowQrCode -> showQrCodeDialog = it.item
476483
}
477484
},

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import androidx.compose.material3.TextField
3434
import androidx.compose.material3.rememberModalBottomSheetState
3535
import androidx.compose.runtime.Composable
3636
import androidx.compose.runtime.LaunchedEffect
37-
import androidx.compose.runtime.collectAsState
3837
import androidx.compose.runtime.getValue
3938
import androidx.compose.runtime.mutableStateListOf
4039
import androidx.compose.runtime.mutableStateOf
@@ -46,6 +45,7 @@ import androidx.compose.ui.draw.clip
4645
import androidx.compose.ui.platform.LocalContext
4746
import androidx.compose.ui.res.stringResource
4847
import androidx.compose.ui.unit.dp
48+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
4949
import com.yogeshpaliyal.deepr.DeeprQueries
5050
import com.yogeshpaliyal.deepr.GetLinksAndTags
5151
import com.yogeshpaliyal.deepr.R
@@ -83,7 +83,7 @@ fun HomeBottomContent(
8383
var isFetchingMetadata by remember { mutableStateOf(false) }
8484
// Tags
8585
var newTagName by remember { mutableStateOf("") }
86-
val allTags by viewModel.allTags.collectAsState()
86+
val allTags by viewModel.allTags.collectAsStateWithLifecycle()
8787
val selectedTags = remember { mutableStateListOf<Tags>() }
8888
val initialSelectedTags = remember { mutableStateListOf<Tags>() }
8989
val isCreate = selectedLink.id == 0L
@@ -98,8 +98,7 @@ fun HomeBottomContent(
9898
selectedLink.tagsNames
9999
?.split(",")
100100
?.getOrNull(index)
101-
?.trim()
102-
?.toString() ?: "Unknown",
101+
?.trim() ?: "Unknown",
103102
)
104103
}
105104
selectedTags.clear()
@@ -111,10 +110,15 @@ fun HomeBottomContent(
111110
}
112111
}
113112

114-
val save: (executeAfterSave: Boolean) -> Unit = { executeAfterSave ->
113+
val save: (executeAfterSave: Boolean) -> Unit = save@{ executeAfterSave ->
115114
// Normalize the link before saving
116115
val normalizedLink = normalizeLink(deeprInfo.link)
117116

117+
if (deeprQueries.getDeeprByLink(normalizedLink).executeAsOneOrNull() != null) {
118+
Toast.makeText(context, deeplinkExistsText, Toast.LENGTH_SHORT).show()
119+
return@save
120+
}
121+
118122
// Remove unselected tags
119123
val initialTagIds = initialSelectedTags.map { it.id }.toSet()
120124
val currentTagIds = selectedTags.map { it.id }.toSet()
@@ -130,7 +134,12 @@ fun HomeBottomContent(
130134
// Edit
131135
viewModel.updateDeeplink(deeprInfo.id, normalizedLink, deeprInfo.name, selectedTags)
132136
}
133-
onSaveDialogInfoChange(SaveDialogInfo(deeprInfo.copy(link = normalizedLink), executeAfterSave))
137+
onSaveDialogInfoChange(
138+
SaveDialogInfo(
139+
deeprInfo.copy(link = normalizedLink),
140+
executeAfterSave,
141+
),
142+
)
134143
}
135144

136145
val modalBottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)

0 commit comments

Comments
 (0)