Skip to content

Commit

Permalink
Add initial article view (#22)
Browse files Browse the repository at this point in the history
* Add initial webview

* Avoid tree rerenders by hoisting scaffold

* Don't let webview capture back events
  • Loading branch information
jocmp authored Dec 21, 2023
1 parent 7b11ec0 commit 5aa8464
Show file tree
Hide file tree
Showing 9 changed files with 896 additions and 135 deletions.
17 changes: 0 additions & 17 deletions app/src/main/java/com/jocmp/basilreader/ui/EmptyView.kt

This file was deleted.

This file was deleted.

66 changes: 14 additions & 52 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleList.kt
Original file line number Diff line number Diff line change
@@ -1,83 +1,45 @@
package com.jocmp.basilreader.ui.articles

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.ThreePaneScaffoldValue
import androidx.compose.material3.adaptive.calculateListDetailPaneScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.paging.Pager
import androidx.paging.compose.collectAsLazyPagingItems
import com.jocmp.basil.db.Articles
import com.jocmp.basilreader.ui.EmptyView
import kotlinx.coroutines.launch


@ExperimentalMaterial3AdaptiveApi
@Composable
fun ArticleList(
pager: Pager<Int, Articles>,
article: Articles?,
onSelect: suspend (articleID: Long) -> Unit,
goBack: () -> Unit
) {
val composableScope = rememberCoroutineScope()
val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
val (destination, setDestination) = rememberSaveable { mutableStateOf(ListDetailPaneScaffoldRole.List) }
val scaffoldState = calculateListDetailPaneScaffoldState(currentPaneDestination = destination)

val navigateToDetail = {
setDestination(ListDetailPaneScaffoldRole.Detail)
}

BackHandler(article != null) {
setDestination(ListDetailPaneScaffoldRole.List)
goBack()
}

ListDetailPaneScaffold(
scaffoldState = scaffoldState,
listPane = {
LazyColumn(Modifier.fillMaxWidth()) {
items(count = lazyPagingItems.itemCount) { index ->
val item = lazyPagingItems[index]
Column(
modifier = Modifier
.padding(8.dp)
.clickable {
item?.let {
composableScope.launch {
onSelect(it.id)
navigateToDetail()
}
}
LazyColumn(Modifier.fillMaxWidth()) {
items(count = lazyPagingItems.itemCount) { index ->
val item = lazyPagingItems[index]
Column(
modifier = Modifier
.padding(8.dp)
.clickable {
item?.let {
composableScope.launch {
onSelect(it.id)
}
) {
Text(item?.title ?: "No title", fontSize = 20.sp)
}
}
}
}
},
detailPane = {
if (article != null) {
Text(article.title ?: "")
} else {
EmptyView(fullWidth = true)
) {
Text(item?.title ?: "No title", fontSize = 20.sp)
}
}
)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package com.jocmp.basilreader.ui.articles

import androidx.compose.material3.Text
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.jocmp.basilreader.ui.accounts.AccountViewModel
import org.koin.androidx.compose.koinViewModel

const val articlesRoute = "articles"

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

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.ThreePaneScaffoldState
import androidx.compose.material3.adaptive.calculateListDetailPaneScaffoldState
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import com.jocmp.basilreader.ui.theme.BasilReaderTheme

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ArticleScaffold(
drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
listDetailState: ThreePaneScaffoldState = calculateListDetailPaneScaffoldState(
currentPaneDestination = ListDetailPaneScaffoldRole.List
),
drawerPane: @Composable () -> Unit,
listPane: @Composable () -> Unit,
detailPane: @Composable () -> Unit,
) {
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
drawerPane()
}
}
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
ListDetailPaneScaffold(
scaffoldState = listDetailState,
listPane = {
listPane()
},
detailPane = {
detailPane()
}
)
}
}
}

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Preview(device = Devices.FOLDABLE)
@Composable
fun ArticlesLayoutPreview() {
BasilReaderTheme {
ArticleScaffold(
drawerPane = {
Text("List here!")
},
listPane = {
Text("Index list here...")
},
detailPane = {
Text("Detail!")
}
)
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package com.jocmp.basilreader.ui.articles

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.calculateListDetailPaneScaffoldState
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.runtime.saveable.rememberSaveable
import com.jocmp.basilreader.ui.accounts.AccountViewModel
import com.jocmp.basilreader.ui.components.EmptyView
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel

Expand All @@ -23,38 +22,50 @@ fun ArticleScreen(
) {
val drawerState = rememberDrawerState(DrawerValue.Closed)
val coroutineScope = rememberCoroutineScope()
val (destination, setDestination) = rememberSaveable { mutableStateOf(ListDetailPaneScaffoldRole.List) }
val scaffoldState = calculateListDetailPaneScaffoldState(currentPaneDestination = destination)

ArticleLayout(
val navigateToDetail = {
setDestination(ListDetailPaneScaffoldRole.Detail)
}

ArticleScaffold(
drawerState = drawerState,
list = {
listDetailState = scaffoldState,
drawerPane = {
FeedList(
folders = viewModel.folders,
feeds = viewModel.feeds,
onFeedAdd = onFeedAdd,
onFeedSelect = {
viewModel.selectFeed(it) {
coroutineScope.launch {
setDestination(ListDetailPaneScaffoldRole.List)
drawerState.close()
}
}
}
)
}
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
},
listPane = {
viewModel.articles()?.let { pager ->
ArticleList(
pager = pager,
article = viewModel.article,
goBack = viewModel::clearArticle,
onSelect = {
viewModel.selectArticle(it)
navigateToDetail()
}
)
}
} ?: EmptyView(fillSize = true)
},
detailPane = {
ArticleView(
article = viewModel.article,
onBackPressed = {
viewModel.clearArticle()
setDestination(ListDetailPaneScaffoldRole.List)
}
)
}
}
)
}
21 changes: 21 additions & 0 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.jocmp.basilreader.ui.articles

import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import com.jocmp.basil.db.Articles
import com.jocmp.basilreader.ui.components.WebView
import com.jocmp.basilreader.ui.components.rememberWebViewStateWithHTMLData

@Composable
fun ArticleView(
article: Articles?,
onBackPressed: () -> Unit
) {
val state = rememberWebViewStateWithHTMLData(article?.content_html ?: "<div />")

WebView(state)

BackHandler(article != null) {
onBackPressed()
}
}
17 changes: 17 additions & 0 deletions app/src/main/java/com/jocmp/basilreader/ui/components/EmptyView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.jocmp.basilreader.ui.components

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

@Composable
fun EmptyView(fillSize: Boolean = false) {
val modifier = if (fillSize) {
Modifier.fillMaxSize()
} else {
Modifier
}

Column(modifier) {}
}
Loading

0 comments on commit 5aa8464

Please sign in to comment.