Skip to content

Commit

Permalink
Add feed modal
Browse files Browse the repository at this point in the history
  • Loading branch information
jocmp committed Nov 28, 2023
1 parent 60e797d commit 1978ac7
Show file tree
Hide file tree
Showing 20 changed files with 355 additions and 163 deletions.
5 changes: 4 additions & 1 deletion app/src/main/java/com/jocmp/basilreader/ui/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ fun App() {
navController.navigateToArticles(account.id)
}
)
articleGraph(defaultAccountID = defaultAccountID)
articleGraph(
navController = navController,
defaultAccountID = defaultAccountID
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
package com.jocmp.basilreader.ui.articles

import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.runtime.neverEqualPolicy
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.jocmp.basil.Account
import com.jocmp.basil.AccountManager
import com.jocmp.basil.Feed
import com.jocmp.basil.Folder
import com.jocmp.basil.FeedFormEntry
import kotlinx.coroutines.launch
import java.util.UUID

class AccountViewModel(
savedStateHandle: SavedStateHandle,
accountManager: AccountManager
) : ViewModel() {
private val args = ArticleArgs(savedStateHandle)

private val accountState = mutableStateOf(accountManager.findByID(args.accountId))
private val account: Account
private val accountState = mutableStateOf(
accountManager.findByID(args.accountID),
policy = neverEqualPolicy()
)

val account: Account
get() = accountState.value

val feeds = account.feeds.toList()
val folders = account.folders.toList()

fun addFeed(url: String) {
fun addFeed(entry: FeedFormEntry, onSuccess: () -> Unit) {
viewModelScope.launch {
val nextAccount = accountState.value.copy()
nextAccount.addFeed(url = url)
accountState.value = nextAccount
account.addFeed(entry)
accountState.value = account
onSuccess()
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.jocmp.basilreader.ui.articles

import androidx.compose.runtime.Composable
import org.koin.androidx.compose.koinViewModel

@Composable
fun AddFeedScreen(
viewModel: AccountViewModel = koinViewModel(),
onSubmit: (accountID: String) -> Unit,
onCancel: () -> Unit
) {
AddFeedView(
folders = viewModel.folders,
onSubmit = { entry ->
viewModel.addFeed(entry) {
onSubmit(viewModel.account.id)
}
},
onCancel = onCancel
)
}
135 changes: 85 additions & 50 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/AddFeedView.kt
Original file line number Diff line number Diff line change
@@ -1,92 +1,127 @@
package com.jocmp.basilreader.ui.articles

import androidx.compose.ui.Alignment
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateMap
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.jocmp.basil.Feed
import androidx.compose.ui.unit.dp
import com.jocmp.basil.FeedFormEntry
import com.jocmp.basil.Folder
import com.jocmp.basilreader.R
import com.jocmp.basilreader.ui.components.TextField
import kotlin.math.exp

@Composable
fun AddFeedView(
folders: List<Folder>,
onSubmit: (feed: Feed) -> Unit,
onSubmit: (feed: FeedFormEntry) -> Unit,
onCancel: () -> Unit
) {
val (selectedFolder, selectFolder) = remember {
mutableStateOf<Folder?>(null)
}

val (url, setURL) = remember { mutableStateOf("") }
val (name, setName) = remember { mutableStateOf("") }
val (addedFolder, setAddedFolder) = remember { mutableStateOf("") }
val switchFolders = remember {
folders.map { it.title to false }.toMutableStateMap()
}

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

onSubmit(
FeedFormEntry(
url = url,
name = name,
folderTitles = folderNames
)
)
}

Column {
TextField(
value = url,
onValueChange = setURL,
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 (folders.isNotEmpty()) {
// FolderMenuBox(
// selected = selectedFolder,
// folders = folders,
// onFolderSelect = { selectFolder(it) }
// )
// }
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FolderMenuBox(
selected: Folder?,
folders: List<Folder>,
onFolderSelect: (folder: Folder) -> Unit,
) {
val (expanded, setExpanded) = mutableStateOf(false)

ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { setExpanded(!expanded) }
) {
TextField(
readOnly = true,
value = selected?.title ?: "",
onValueChange = {},
modifier = Modifier.menuAnchor()
value = addedFolder,
onValueChange = setAddedFolder,
placeholder = {
Text(stringResource(id = R.string.add_feed_new_folder_title))
}
)
ExposedDropdownMenu(
expanded = true,
onDismissRequest = {}
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()
) {
folders.forEach { folder ->
DropdownMenuItem(
text = { Text(folder.title) },
onClick = {
onFolderSelect(folder)
}
)
TextButton(onClick = onCancel) {
Text(stringResource(R.string.add_feed_cancel))
}
Button(onClick = { submitFeed() }) {
Text(stringResource(R.string.add_feed_submit))
}
}
}
}

fun collectFolders(
existingFolders: Set<String>,
addedFolder: String
): List<String> {
val folderNames = existingFolders.toMutableList()

if (addedFolder.isNotBlank()) {
folderNames.add(addedFolder)
}

return folderNames
}

@Preview
@Composable
fun AddFeedViewPreview() {
AddFeedView(
folders = emptyList(),
onSubmit = {}
folders = listOf(Folder(title = "Tech")),
onSubmit = {},
onCancel = {}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,46 @@ package com.jocmp.basilreader.ui.articles
import androidx.lifecycle.SavedStateHandle
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.compose.dialog
import androidx.navigation.navArgument
import com.jocmp.basil.AccountManager
import org.koin.compose.koinInject

private const val ACCOUNT_ID_KEY = "account_id"

internal class ArticleArgs(val accountId: String) {
internal class ArticleArgs(val accountID: String) {
constructor(savedStateHandle: SavedStateHandle) :
this(checkNotNull(savedStateHandle[ACCOUNT_ID_KEY]) as String)
}

fun NavController.navigateToArticles(accountId: String) =
navigate("articles?account_id=${accountId}")
fun NavController.navigateToArticles(accountID: String) =
navigate("articles?account_id=${accountID}")

fun NavGraphBuilder.articleGraph(defaultAccountID: String) {
fun NavController.navigateToAddFeed(accountID: String) =
navigate("feeds/new?account_id=${accountID}")

fun NavGraphBuilder.articleGraph(
navController: NavController,
defaultAccountID: String,
) {
composable(
route = "articles?account_id={${ACCOUNT_ID_KEY}}",
arguments = listOf(navArgument(ACCOUNT_ID_KEY) { defaultValue = defaultAccountID })
) {
ArticleScreen()
ArticleScreen(
onNewFeedNavigate = { accountID ->
navController.navigateToAddFeed(accountID)
}
)
}
composable(
route = "feeds/new?account_id={${ACCOUNT_ID_KEY}}",
) {
AddFeedScreen(
onCancel = {
navController.popBackStack()
},
onSubmit = { accountID ->
navController.navigateToArticles(accountID)
}
)
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package com.jocmp.basilreader.ui.articles

import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import org.koin.androidx.compose.koinViewModel

@Composable
fun ArticleScreen(
viewModel: AccountViewModel = koinViewModel(),
onNewFeedNavigate: (accountID: String) -> Unit,
) {
FeedList(
folders = viewModel.folders,
feeds = viewModel.feeds
)
Column {
FeedList(
folders = viewModel.folders,
feeds = viewModel.feeds,
onNewFeedNavigate = { onNewFeedNavigate(viewModel.account.id) }
)
}
}
Loading

0 comments on commit 1978ac7

Please sign in to comment.