Skip to content

Commit

Permalink
Prevent duplicate feeds
Browse files Browse the repository at this point in the history
Remove existing feeds before re-adding them via the importer
or the Add Feed screen.
  • Loading branch information
jocmp committed Feb 6, 2024
1 parent 3f0895b commit fe4ea80
Show file tree
Hide file tree
Showing 20 changed files with 255 additions and 125 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ android {

dependencies {
implementation("androidx.webkit:webkit:1.10.0")
implementation(project(":feedfinder"))
val sqldelightVersion = libs.versions.sqldelight.get()
val pagingVersion = libs.versions.androidx.paging.get()

Expand Down
5 changes: 3 additions & 2 deletions app/src/main/java/com/jocmp/basilreader/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ class MainActivity : ComponentActivity() {
}

private fun startDestination(): String {
val accountManager = get<AccountManager>()
val appPreferences = get<AppPreferences>()

val accountID = appPreferences.accountID.get()
val account = accountManager.findByID(appPreferences.accountID.get())

return if (accountID.isBlank()) {
return if (account == null) {
"accounts"
} else {
"articles"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.jocmp.basilreader.ui.accounts

import com.jocmp.basil.FeedSearch
import com.jocmp.feedfinder.DefaultFeedFinder
import com.jocmp.feedfinder.FeedFinder
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.jocmp.basil.Article
import com.jocmp.basil.ArticleFilter
import com.jocmp.basil.ArticleStatus
import com.jocmp.basil.Feed
import com.jocmp.basil.FeedSearch
import com.jocmp.basil.Folder
import com.jocmp.basil.buildPager
import com.jocmp.basil.unreadCounts
Expand Down Expand Up @@ -176,7 +177,7 @@ class AccountViewModel(
onFailure: (message: String) -> Unit
) {
viewModelScope.launch {
return@launch account.addFeed(entry).fold(
account.addFeed(entry).fold(
onSuccess = { feed ->
selectFeed(feed.id)
onSuccess()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package com.jocmp.basilreader.ui.articles

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import com.jocmp.basil.FeedSearch
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject

@Composable
fun AddFeedScreen(
viewModel: AccountViewModel = koinViewModel(),
feedSearch: FeedSearch = koinInject(),
onSubmit: () -> Unit,
onCancel: () -> Unit
) {
Expand All @@ -35,6 +35,7 @@ fun AddFeedScreen(
}
)
},
searchFeeds = { feedSearch.search(it).getOrNull() },
onCancel = onCancel
)
SnackbarHost(hostState = snackbarState)
Expand Down
132 changes: 82 additions & 50 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/AddFeedView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,94 +15,125 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.toMutableStateMap
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.jocmp.basil.AddFeedForm
import com.jocmp.basil.FeedSearch.SearchResult
import com.jocmp.basil.Folder
import com.jocmp.basil.shared.orEmpty
import com.jocmp.basilreader.R
import com.jocmp.basilreader.ui.components.TextField
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

@Composable
fun AddFeedView(
folders: List<Folder>,
onSubmit: (feed: AddFeedForm) -> Unit,
onCancel: () -> Unit
onCancel: () -> Unit,
searchFeeds: suspend (url: String) -> SearchResult?,
) {
val (url, setURL) = remember { mutableStateOf("") }
val scope = rememberCoroutineScope()
val (queryURL, setQueryURL) = remember { mutableStateOf("") }
val (searchResult, setSearchResult) = remember { mutableStateOf<SearchResult?>(null) }
val (name, setName) = remember { mutableStateOf("") }
val (addedFolder, setAddedFolder) = remember { mutableStateOf("") }
val switchFolders = remember {
folders.map { it.title to false }.toMutableStateMap()
}
val url = searchResult?.url.orEmpty

fun submitFeed() {
val search = {
scope.launch(Dispatchers.IO) {
val result = searchFeeds(queryURL)
if (result != null) {
if (result.name.isNotBlank()) {
setName(result.name)
}
setSearchResult(result)
}
}
}

val submitFeed = {
val existingFolderNames = switchFolders.filter { it.value }.keys
val folderNames = collectFolders(existingFolderNames, addedFolder)

onSubmit(
AddFeedForm(
url = url,
name = name,
folderTitles = folderNames
if (searchResult != null) {
onSubmit(
AddFeedForm(
url = searchResult.url,
siteURL = searchResult.siteURL,
name = name,
folderTitles = folderNames
)
)
)
}
}

Card(
shape = RoundedCornerShape(16.dp)
) {
Column(Modifier.padding(16.dp)) {
TextField(
value = url,
onValueChange = setURL,
value = queryURL,
onValueChange = setQueryURL,
readOnly = searchResult != null,
label = {
Text(stringResource(id = R.string.add_feed_url_title))
},
supportingText = {
Text(stringResource(R.string.required_placeholder))
}
)
TextField(
value = name,
onValueChange = setName,
label = {
Text(stringResource(id = R.string.add_feed_name_title))
if (url.isBlank()) {
Button(onClick = { search() }) {
Text("Search")
}
)
TextField(
value = addedFolder,
onValueChange = setAddedFolder,
placeholder = {
Text(stringResource(id = R.string.add_feed_new_folder_title))
}
)
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
switchFolders.forEach { (folderTitle, checked) ->
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(folderTitle)
Switch(
checked = checked,
onCheckedChange = { value -> switchFolders[folderTitle] = value }
)
} else {
TextField(
value = name,
onValueChange = setName,
label = {
Text(stringResource(id = R.string.add_feed_name_title))
},
supportingText = {
Text(url)
}
)
TextField(
value = addedFolder,
onValueChange = setAddedFolder,
placeholder = {
Text(stringResource(id = R.string.add_feed_new_folder_title))
}
)
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
switchFolders.forEach { (folderTitle, checked) ->
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(folderTitle)
Switch(
checked = checked,
onCheckedChange = { value -> switchFolders[folderTitle] = value }
)
}
}
}
}
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxWidth()
) {
TextButton(onClick = onCancel) {
Text(stringResource(R.string.feed_form_cancel))
}
Button(onClick = { submitFeed() }) {
Text(stringResource(R.string.add_feed_submit))
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxWidth()
) {
TextButton(onClick = onCancel) {
Text(stringResource(R.string.feed_form_cancel))
}
Button(onClick = { submitFeed() }) {
Text(stringResource(R.string.add_feed_submit))
}
}
}
}
Expand All @@ -128,6 +159,7 @@ fun AddFeedViewPreview() {
AddFeedView(
folders = listOf(Folder(title = "Tech")),
onSubmit = {},
onCancel = {}
onCancel = {},
searchFeeds = { null }
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.AnimatedPane
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
Expand Down Expand Up @@ -57,10 +58,14 @@ fun ArticleScaffold(
ListDetailPaneScaffold(
scaffoldState = listDetailState,
listPane = {
listPane()
AnimatedPane(Modifier) {
listPane()
}
},
detailPane = {
detailPane()
AnimatedPane(Modifier) {
detailPane()
}
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fun ArticleScreen(
val scaffoldState =
calculateListDetailPaneScaffoldState(
currentPaneDestination = destination,
scaffoldDirective = calculateArticleDirective()
scaffoldDirective = calculateArticleDirective(),
)

val context = LocalContext.current
Expand Down
12 changes: 10 additions & 2 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fun ArticleView(
onToggleRead: () -> Unit,
onToggleStar: () -> Unit
) {

if (article != null) {
ArticleLoadedView(
article = article,
Expand All @@ -42,6 +43,7 @@ fun ArticleView(
EmptyView()
}


BackHandler(article != null) {
onBackPressed()
}
Expand Down Expand Up @@ -71,10 +73,16 @@ fun ArticleLoadedView(
topBar = {
Row {
IconButton(onClick = { onToggleRead() }) {
Icon(painterResource(id = readIcon), contentDescription = stringResource(R.string.article_view_mark_as_read))
Icon(
painterResource(id = readIcon),
contentDescription = stringResource(R.string.article_view_mark_as_read)
)
}
IconButton(onClick = { onToggleStar() }) {
Icon(painterResource(id = starIcon), contentDescription = stringResource(R.string.article_view_bookmark))
Icon(
painterResource(id = starIcon),
contentDescription = stringResource(R.string.article_view_bookmark)
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package com.jocmp.basilreader.ui.articles

import com.jocmp.basil.FeedSearch
import com.jocmp.feedfinder.DefaultFeedFinder
import com.jocmp.feedfinder.FeedFinder
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.scope.get
import org.koin.dsl.module

internal val articlesModule = module {
single<FeedFinder> { DefaultFeedFinder() }
single { FeedSearch(get()) }
viewModel {
AccountViewModel(
accountManager = get(),
appPreferences = get()
appPreferences = get(),
)
}
viewModel {
Expand Down
Loading

0 comments on commit fe4ea80

Please sign in to comment.