Skip to content

Commit

Permalink
Show source name in home top app bar
Browse files Browse the repository at this point in the history
  • Loading branch information
msasikanth committed Apr 17, 2024
1 parent 7739f69 commit 100a0fa
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ val DeTwineStrings =
postsAll = "Alle artikel",
postsUnread = "Ungelesen",
postsToday = "Heute",
postsLast24Hours = "24 Stunden",
postsLast24Hours = "Letzte 24 Stunden",
openSource = "Open Source",
openSourceDesc =
"Twine basiert auf Open-Source-Technologien und ist völlig kostenlos. Den Quellcode von Twine und einigen meiner anderen beliebten Projekte finden Sie auf GitHub. Klicken Sie hier, um dorthin zu gelangen.",
Expand Down Expand Up @@ -153,4 +153,5 @@ val DeTwineStrings =
},
actionGroupsTooltip = "Gruppen können nicht innerhalb anderer Gruppen sein.",
groupAddNew = "Add new",
appBarAllFeeds = "All feeds",
)
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ val EnTwineStrings =
postsAll = "All articles",
postsUnread = "Unread",
postsToday = "Today",
postsLast24Hours = "24 hours",
postsLast24Hours = "Last 24 hours",
openSource = "Support Open Source",
openSourceDesc =
"Twine is an open source project and is available for free to use. Click here to know more on how to support this project or, view source code of Twine or some of my other popular projects.",
Expand Down Expand Up @@ -160,4 +160,5 @@ val EnTwineStrings =
},
actionGroupsTooltip = "Groups cannot be inside other groups.",
groupAddNew = "Add new",
appBarAllFeeds = "All feeds",
)
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ val TrTwineStrings =
postsAll = "Tümü makaleler",
postsUnread = "Okunmamış",
postsToday = "Bugün",
postsLast24Hours = "24 saat",
postsLast24Hours = "Son 24 saat",
openSource = "Açık kaynağı destekleyin",
openSourceDesc =
"Twine açık kaynaklı bir projedir ve ücretsiz olarak kullanılabilir. Bu projeyi nasıl destekleyeceğiniz hakkında daha fazla bilgi edinmek için buraya tıklayın veya Twine'ın veya diğer popüler projelerimden bazılarının kaynak kodunu görüntüleyin.",
Expand Down Expand Up @@ -149,4 +149,5 @@ val TrTwineStrings =
},
actionGroupsTooltip = "Gruplar başka grupların içinde olamaz.",
groupAddNew = "Add new",
appBarAllFeeds = "All feeds",
)
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ data class TwineStrings(
val feedGroupFeeds: (Int) -> String,
val actionGroupsTooltip: String,
val groupAddNew: String,
val appBarAllFeeds: String,
)

object Locales {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ internal fun HomeScreen(homePresenter: HomePresenter, modifier: Modifier = Modif
scaffoldState = bottomSheetScaffoldState,
topBar = {
HomeTopAppBar(
hasFeeds = state.hasFeeds ?: false,
source = state.activeSource,
postsType = state.postsType,
listState = listState,
onSearchClicked = { homePresenter.dispatch(HomeEvent.SearchClicked) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,31 @@
package dev.sasikanth.rss.reader.home.ui

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material.icons.rounded.Search
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.derivedStateOf
Expand All @@ -48,13 +51,18 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import dev.sasikanth.rss.reader.components.DropdownMenu
import dev.sasikanth.rss.reader.components.DropdownMenuItem
import dev.sasikanth.rss.reader.components.image.AsyncImage
import dev.sasikanth.rss.reader.core.model.local.Feed
import dev.sasikanth.rss.reader.core.model.local.FeedGroup
import dev.sasikanth.rss.reader.core.model.local.Source
import dev.sasikanth.rss.reader.feeds.ui.FeedGroupIconGrid
import dev.sasikanth.rss.reader.resources.icons.Bookmarks
import dev.sasikanth.rss.reader.resources.icons.RSS
import dev.sasikanth.rss.reader.resources.icons.Tune
import dev.sasikanth.rss.reader.resources.icons.TwineIcons
import dev.sasikanth.rss.reader.resources.strings.LocalStrings
Expand All @@ -64,7 +72,7 @@ private const val APP_BAR_OPAQUE_THRESHOLD = 200f

@Composable
internal fun HomeTopAppBar(
hasFeeds: Boolean,
source: Source?,
postsType: PostsType,
listState: LazyListState,
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -93,16 +101,17 @@ internal fun HomeTopAppBar(
.windowInsetsPadding(
WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
)
.padding(start = 12.dp, end = 12.dp, top = 16.dp, bottom = 16.dp),
.padding(horizontal = 8.dp, vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
if (!hasFeeds) {
AppName()
} else {
PostsTypeSelector(postsType = postsType, onPostTypeChanged = onPostTypeChanged)
}
PostsFilter(
modifier = Modifier.weight(1f),
source = source,
postsType = postsType,
onPostTypeChanged = onPostTypeChanged
)

Spacer(Modifier.weight(1f))
Spacer(Modifier.requiredWidth(16.dp))

IconButton(
onClick = onSearchClicked,
Expand All @@ -114,80 +123,134 @@ internal fun HomeTopAppBar(
)
}

IconButton(
onClick = onBookmarksClicked,
) {
Icon(
imageVector = TwineIcons.Bookmarks,
contentDescription = LocalStrings.current.bookmarks,
tint = AppTheme.colorScheme.tintedForeground
)
}

OverflowMenu(onSettingsClicked)
OverflowMenu(
onSettingsClicked = onSettingsClicked,
onBookmarksClicked = onBookmarksClicked,
)
}
}

@Composable
fun PostsTypeSelector(
fun PostsFilter(
modifier: Modifier = Modifier,
source: Source?,
postsType: PostsType = PostsType.ALL,
onPostTypeChanged: (PostsType) -> Unit,
) {
var showDropdown by remember { mutableStateOf(false) }
val title = getPostTypeLabel(postsType)
val postsTypeLabel = getPostTypeLabel(postsType)

Row(
modifier = modifier.clip(MaterialTheme.shapes.large).clickable { showDropdown = true },
verticalAlignment = Alignment.CenterVertically
) {
SourceIcon(source)

Column(modifier = Modifier.padding(start = 16.dp, end = 8.dp)) {
val sourceLabel =
when (source) {
is FeedGroup -> source.name
is Feed -> source.name
else -> LocalStrings.current.appBarAllFeeds
}

Box {
TextButton(
onClick = { showDropdown = true },
modifier = modifier,
) {
Text(
modifier = Modifier.align(Alignment.CenterVertically),
text = title,
color = Color.White,
style = MaterialTheme.typography.titleLarge
text = sourceLabel,
style = MaterialTheme.typography.titleLarge,
color = AppTheme.colorScheme.textEmphasisHigh,
)

Spacer(Modifier.width(4.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = postsTypeLabel,
style = MaterialTheme.typography.bodyMedium,
color = AppTheme.colorScheme.textEmphasisHigh,
)

Icon(
modifier = Modifier.align(Alignment.CenterVertically),
imageVector = Icons.Filled.ArrowDropDown,
contentDescription = null,
tint = Color.White
)
Icon(
imageVector = Icons.Filled.ExpandMore,
contentDescription = null,
modifier = Modifier.requiredSize(20.dp),
tint = AppTheme.colorScheme.tintedForeground,
)
}
}
}

DropdownMenu(
modifier = Modifier.requiredWidth(158.dp),
expanded = showDropdown,
onDismissRequest = { showDropdown = false },
) {
PostsType.entries.forEach { type ->
val label = getPostTypeLabel(type)
val color =
if (postsType == type) {
AppTheme.colorScheme.tintedSurface
DropdownMenu(
modifier = Modifier.requiredWidth(158.dp),
expanded = showDropdown,
onDismissRequest = { showDropdown = false },
) {
PostsType.entries.forEach { type ->
val label = getPostTypeLabel(type)
val color =
if (postsType == type) {
AppTheme.colorScheme.tintedSurface
} else {
Color.Unspecified
}
val labelColor =
if (postsType == type) {
AppTheme.colorScheme.onSurface
} else {
AppTheme.colorScheme.textEmphasisHigh
}

DropdownMenuItem(
onClick = {
onPostTypeChanged(type)
showDropdown = false
},
modifier = Modifier.background(color)
) {
Text(text = label, style = MaterialTheme.typography.bodyLarge, color = labelColor)
}
}
}
}

@Composable
private fun SourceIcon(source: Source?, modifier: Modifier = Modifier) {
Row(modifier) {
if (source != null) {
Spacer(Modifier.requiredWidth(16.dp))
}

when (source) {
is FeedGroup -> {
val iconSize =
if (source.feedIcons.size > 2) {
18.dp
} else {
Color.Unspecified
20.dp
}
val labelColor =
if (postsType == type) {
AppTheme.colorScheme.onSurface

val iconSpacing =
if (source.feedIcons.size > 2) {
4.dp
} else {
AppTheme.colorScheme.textEmphasisHigh
0.dp
}

DropdownMenuItem(
onClick = {
onPostTypeChanged(type)
showDropdown = false
},
modifier = Modifier.background(color)
) {
Text(text = label, style = MaterialTheme.typography.bodyLarge, color = labelColor)
}
FeedGroupIconGrid(
icons = source.feedIcons,
iconSize = iconSize,
iconShape = RoundedCornerShape(percent = 30),
horizontalArrangement = Arrangement.spacedBy(iconSpacing),
verticalArrangement = Arrangement.spacedBy(iconSpacing)
)
}
is Feed -> {
AsyncImage(
url = source.icon,
contentDescription = null,
backgroundColor = Color.White,
modifier = Modifier.clip(MaterialTheme.shapes.small).requiredSize(24.dp)
)
}
else -> {
// no-op
}
}
}
Expand All @@ -204,22 +267,7 @@ private fun getPostTypeLabel(type: PostsType) =
}

@Composable
private fun AppName(modifier: Modifier = Modifier) {
Row(modifier = modifier.padding(start = 12.dp), verticalAlignment = Alignment.CenterVertically) {
Text(
text = LocalStrings.current.appName,
color = Color.White,
style = MaterialTheme.typography.titleLarge
)

Spacer(Modifier.width(4.dp))

Icon(imageVector = TwineIcons.RSS, contentDescription = null, tint = Color.White)
}
}

@Composable
private fun OverflowMenu(onSettingsClicked: () -> Unit) {
private fun OverflowMenu(onBookmarksClicked: () -> Unit, onSettingsClicked: () -> Unit) {
Box {
var dropdownExpanded by remember { mutableStateOf(false) }

Expand All @@ -235,6 +283,20 @@ private fun OverflowMenu(onSettingsClicked: () -> Unit) {

if (dropdownExpanded) {
DropdownMenu(expanded = dropdownExpanded, onDismissRequest = { dropdownExpanded = false }) {
DropdownMenuItem(
text = { Text(text = LocalStrings.current.bookmarks) },
leadingIcon = {
Icon(
imageVector = TwineIcons.Bookmarks,
contentDescription = LocalStrings.current.bookmarks
)
},
onClick = {
dropdownExpanded = false
onBookmarksClicked()
}
)

DropdownMenuItem(
text = { Text(text = LocalStrings.current.settings) },
leadingIcon = {
Expand Down

0 comments on commit 100a0fa

Please sign in to comment.