feat: catch up screen (unfinished)
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
71c8b4581a
commit
dc8f8d9815
|
|
@ -97,6 +97,7 @@ import chat.stoat.screens.DefaultDestinationScreen
|
|||
import chat.stoat.screens.about.AboutScreen
|
||||
import chat.stoat.screens.about.AttributionScreen
|
||||
import chat.stoat.screens.chat.ChatRouterScreen
|
||||
import chat.stoat.screens.chat.standalone.CatchUpScreen
|
||||
import chat.stoat.screens.chat.views.channel.ChannelScreen
|
||||
import chat.stoat.screens.create.CreateGroupScreen
|
||||
import chat.stoat.screens.labs.LabsRootScreen
|
||||
|
|
@ -703,6 +704,8 @@ fun AppEntrypoint(
|
|||
)
|
||||
}
|
||||
|
||||
composable("catchup") { CatchUpScreen(navController) }
|
||||
|
||||
composable("create/group") { CreateGroupScreen(navController) }
|
||||
|
||||
composable("discover") { DiscoverScreen(navController) }
|
||||
|
|
|
|||
|
|
@ -94,6 +94,46 @@ class Unreads {
|
|||
}
|
||||
}
|
||||
|
||||
fun getAllUnreads(): List<ChannelUnread> {
|
||||
if (!hasLoaded.value) return emptyList()
|
||||
return channels.values.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are any unreads in any of the channels that are not muted.
|
||||
* **SLOW:** Run in a background coroutine.
|
||||
*/
|
||||
fun hasAnyUnreads(): Boolean {
|
||||
if (!hasLoaded.value) return false
|
||||
|
||||
for ((channelId, unread) in StoatAPI.channelCache) {
|
||||
if (channelId !in channels) continue
|
||||
if (NotificationSettingsProvider.isChannelMuted(channelId, unread.server)) continue
|
||||
if (hasUnread(channelId, unread.lastMessageID ?: "", unread.server)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of channels with unreads that are not muted.
|
||||
* **SLOW:** Run in a background coroutine.
|
||||
*/
|
||||
fun countChannelsWithUnreads(): Int? {
|
||||
if (!hasLoaded.value) return null
|
||||
|
||||
var count = 0
|
||||
for ((channelId, unread) in StoatAPI.channelCache) {
|
||||
if (channelId !in channels) continue
|
||||
if (NotificationSettingsProvider.isChannelMuted(channelId, unread.server)) continue
|
||||
if (hasUnread(channelId, unread.lastMessageID ?: "", unread.server)) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
channels.clear()
|
||||
hasLoaded.value = false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,234 @@
|
|||
package chat.stoat.screens.chat.standalone
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SwipeToDismissBox
|
||||
import androidx.compose.material3.SwipeToDismissBoxValue
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.rememberSwipeToDismissBoxState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import chat.stoat.R
|
||||
import chat.stoat.api.StoatAPI
|
||||
import chat.stoat.api.schemas.ChannelUnread
|
||||
import logcat.LogPriority
|
||||
import logcat.logcat
|
||||
|
||||
sealed class CatchUpCard {
|
||||
data class UnreadMessageInChannel(
|
||||
val channelId: String,
|
||||
val lastReadMessageId: String,
|
||||
val newestMessageId: String
|
||||
) : CatchUpCard()
|
||||
}
|
||||
|
||||
class CatchUpScreenViewModel : ViewModel() {
|
||||
private val deck = ArrayDeque<CatchUpCard>(initialCapacity = 3)
|
||||
private var dataSource: Iterator<ChannelUnread> = emptyList<ChannelUnread>().iterator()
|
||||
var initComplete = false
|
||||
private set
|
||||
|
||||
private val _cards = mutableStateOf<List<CatchUpCard>>(emptyList())
|
||||
val cards: State<List<CatchUpCard>> = _cards
|
||||
|
||||
fun initWith(unreads: List<ChannelUnread>) {
|
||||
dataSource = unreads.iterator()
|
||||
|
||||
repeat(3) {
|
||||
dealNewCard()
|
||||
}
|
||||
|
||||
initComplete = true
|
||||
}
|
||||
|
||||
fun dealNewCard() {
|
||||
if (dataSource.hasNext()) {
|
||||
val unread = dataSource.next()
|
||||
unread.last_id?.let { lastId ->
|
||||
deck.addLast(
|
||||
CatchUpCard.UnreadMessageInChannel(
|
||||
channelId = unread.id,
|
||||
lastReadMessageId = lastId,
|
||||
newestMessageId = lastId // TODO: Replace with actual newest message ID
|
||||
)
|
||||
)
|
||||
deckUpdated()
|
||||
}
|
||||
} else {
|
||||
logcat(LogPriority.WARN) { "No more unreads to deal!" }
|
||||
}
|
||||
}
|
||||
|
||||
fun swipedCard() {
|
||||
if (deck.isNotEmpty()) {
|
||||
deck.removeFirst()
|
||||
deckUpdated()
|
||||
}
|
||||
dealNewCard()
|
||||
}
|
||||
|
||||
fun deckUpdated() {
|
||||
_cards.value = deck.toList()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CatchUpScreen(navController: NavController, viewModel: CatchUpScreenViewModel = viewModel()) {
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.initWith(StoatAPI.unreads.getAllUnreads())
|
||||
}
|
||||
|
||||
val primaryContainer = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.1f)
|
||||
val colourKeep = remember { Color(0xFFF84848).copy(alpha = 0.5f) }
|
||||
val colourRead = remember { Color(0xFF3ABF7E).copy(alpha = 0.5f) }
|
||||
|
||||
var currentColour by remember { mutableStateOf(primaryContainer) }
|
||||
val gradientColour by animateColorAsState(
|
||||
targetValue = currentColour,
|
||||
animationSpec = tween(durationMillis = 500)
|
||||
)
|
||||
|
||||
val cards by viewModel.cards
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.catch_up),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_arrow_back_24dp),
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { pv ->
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
Box(
|
||||
Modifier
|
||||
.background(
|
||||
Brush.linearGradient(
|
||||
0.2f to MaterialTheme.colorScheme.background,
|
||||
1.0f to gradientColour,
|
||||
end = Offset.Infinite.copy(x = 0f)
|
||||
)
|
||||
)
|
||||
.fillMaxSize()
|
||||
)
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.padding(pv)
|
||||
.imePadding()
|
||||
) {
|
||||
for (card in cards.reversed()) {
|
||||
when (card) {
|
||||
is CatchUpCard.UnreadMessageInChannel -> {
|
||||
val state = rememberSwipeToDismissBoxState()
|
||||
|
||||
LaunchedEffect(state.currentValue) {
|
||||
if (state.currentValue == SwipeToDismissBoxValue.StartToEnd) {
|
||||
// Start to end is mark read
|
||||
viewModel.swipedCard()
|
||||
state.reset()
|
||||
} else if (state.currentValue == SwipeToDismissBoxValue.EndToStart) {
|
||||
// End to start is skip
|
||||
viewModel.swipedCard()
|
||||
state.reset()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(state.dismissDirection) {
|
||||
when (state.dismissDirection) {
|
||||
SwipeToDismissBoxValue.StartToEnd -> {
|
||||
currentColour = colourRead
|
||||
}
|
||||
|
||||
SwipeToDismissBoxValue.EndToStart -> {
|
||||
currentColour = colourKeep
|
||||
}
|
||||
|
||||
SwipeToDismissBoxValue.Settled -> {
|
||||
currentColour = primaryContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwipeToDismissBox(
|
||||
state = state,
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxSize(),
|
||||
backgroundContent = {
|
||||
if (state.dismissDirection == SwipeToDismissBoxValue.StartToEnd) {
|
||||
Text(
|
||||
"Mark as read",
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color.Green)
|
||||
)
|
||||
} else if (state.dismissDirection == SwipeToDismissBoxValue.EndToStart) {
|
||||
Text(
|
||||
"Keep unread",
|
||||
textAlign = TextAlign.Right,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color.Red)
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(Modifier.background(Color.Black)) {
|
||||
Text("Channel ID: ${card.channelId}")
|
||||
Text("Last Read Message ID: ${card.lastReadMessageId}")
|
||||
Text("Newest Message ID: ${card.newestMessageId}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import android.util.Log
|
|||
import android.widget.Toast
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -22,6 +23,8 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
|
|
@ -45,7 +48,10 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -107,6 +113,26 @@ fun OverviewScreen(
|
|||
}
|
||||
}
|
||||
|
||||
var hasUnreads by rememberSaveable { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
hasUnreads = StoatAPI.unreads.hasAnyUnreads()
|
||||
}
|
||||
|
||||
var countUnreads by rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
LaunchedEffect(hasUnreads) {
|
||||
countUnreads = if (hasUnreads) {
|
||||
StoatAPI.unreads.countChannelsWithUnreads()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
val lazyStaggeredGridState = rememberLazyStaggeredGridState()
|
||||
val scrollabilityIndicatorOpacity by animateFloatAsState(
|
||||
if (!lazyStaggeredGridState.canScrollBackward && lazyStaggeredGridState.canScrollForward) 1.0f else 0.0f,
|
||||
label = "scrollabilityIndicatorOpacity"
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
Column {
|
||||
|
|
@ -197,109 +223,175 @@ fun OverviewScreen(
|
|||
visible = !isLoading,
|
||||
enter = fadeIn(),
|
||||
) {
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Adaptive(300.dp),
|
||||
verticalItemSpacing = 16.dp,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
item(key = "settings") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
navController.navigate("settings")
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.primaryContainer,
|
||||
foregroundColour = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_settings_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
Box {
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Adaptive(300.dp),
|
||||
verticalItemSpacing = 16.dp,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
state = lazyStaggeredGridState
|
||||
) {
|
||||
item(key = "unreads") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
navController.navigate("catchup")
|
||||
},
|
||||
clickable = hasUnreads,
|
||||
backgroundColour = if (hasUnreads) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surfaceVariant,
|
||||
foregroundColour = if (hasUnreads) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = if (hasUnreads) painterResource(R.drawable.icn_move_to_inbox_24dp) else painterResource(
|
||||
R.drawable.icn_inbox_24dp
|
||||
),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Text(
|
||||
if (hasUnreads) stringResource(R.string.overview_screen_catch_up) else stringResource(
|
||||
R.string.overview_screen_catch_up_none
|
||||
)
|
||||
)
|
||||
|
||||
if (hasUnreads) {
|
||||
Spacer(Modifier.weight(1f))
|
||||
Badge {
|
||||
Text(
|
||||
countUnreads?.toString()
|
||||
?: stringResource(R.string.overview_screen_catch_up_amount_badge_loading)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
body = {
|
||||
Text(
|
||||
if (hasUnreads) stringResource(R.string.overview_screen_catch_up_description) else stringResource(
|
||||
R.string.overview_screen_catch_up_none_description
|
||||
)
|
||||
)
|
||||
Text(stringResource(R.string.overview_screen_settings))
|
||||
}
|
||||
},
|
||||
body = { Text(stringResource(R.string.overview_screen_settings_description)) }
|
||||
)
|
||||
}
|
||||
item(key = "shareProfile") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
showUserCardSheet = true
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
foregroundColour = MaterialTheme.colorScheme.onTertiaryContainer,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_ios_share_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Text(stringResource(R.string.overview_screen_share_profile))
|
||||
}
|
||||
},
|
||||
body = { Text(stringResource(R.string.overview_screen_share_profile_description)) }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
item(key = "settings") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
navController.navigate("settings")
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.primaryContainer,
|
||||
foregroundColour = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_settings_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Text(stringResource(R.string.overview_screen_settings))
|
||||
}
|
||||
},
|
||||
body = { Text(stringResource(R.string.overview_screen_settings_description)) }
|
||||
)
|
||||
}
|
||||
item(key = "shareProfile") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
showUserCardSheet = true
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
foregroundColour = MaterialTheme.colorScheme.onTertiaryContainer,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_ios_share_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Text(stringResource(R.string.overview_screen_share_profile))
|
||||
}
|
||||
},
|
||||
body = { Text(stringResource(R.string.overview_screen_share_profile_description)) }
|
||||
)
|
||||
}
|
||||
|
||||
item(key = "changelog") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
navController.navigate("settings/changelogs")
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.errorContainer,
|
||||
foregroundColour = MaterialTheme.colorScheme.onErrorContainer,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_wand_shine_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Text(stringResource(R.string.overview_screen_changelog))
|
||||
}
|
||||
},
|
||||
body = { Text(stringResource(R.string.overview_screen_changelog_description)) }
|
||||
)
|
||||
}
|
||||
item(key = "feedback") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.comingsoon_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
// navController.navigate("feedback")
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.primary,
|
||||
foregroundColour = MaterialTheme.colorScheme.onPrimary,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_star_shine_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Text(stringResource(R.string.overview_screen_feedback))
|
||||
}
|
||||
},
|
||||
body = { Text(stringResource(R.string.overview_screen_feedback_description)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "changelog") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
navController.navigate("settings/changelogs")
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.errorContainer,
|
||||
foregroundColour = MaterialTheme.colorScheme.onErrorContainer,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_wand_shine_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Text(stringResource(R.string.overview_screen_changelog))
|
||||
}
|
||||
},
|
||||
body = { Text(stringResource(R.string.overview_screen_changelog_description)) }
|
||||
)
|
||||
}
|
||||
item(key = "feedback") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.comingsoon_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
// navController.navigate("feedback")
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.primary,
|
||||
foregroundColour = MaterialTheme.colorScheme.onPrimary,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_star_shine_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Text(stringResource(R.string.overview_screen_feedback))
|
||||
}
|
||||
},
|
||||
body = { Text(stringResource(R.string.overview_screen_feedback_description)) }
|
||||
)
|
||||
}
|
||||
Box(
|
||||
Modifier
|
||||
.alpha(scrollabilityIndicatorOpacity)
|
||||
.background(
|
||||
Brush.linearGradient(
|
||||
0.0f to MaterialTheme.colorScheme.background.copy(alpha = 0.0f),
|
||||
0.5f to MaterialTheme.colorScheme.background.copy(alpha = 0.3f),
|
||||
1.0f to MaterialTheme.colorScheme.background,
|
||||
end = Offset.Infinite.copy(x = .0f)
|
||||
)
|
||||
)
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.height(60.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -311,13 +403,14 @@ fun OverviewScreenLink(
|
|||
onClick: () -> Unit,
|
||||
backgroundColour: Color,
|
||||
foregroundColour: Color,
|
||||
clickable: Boolean = true,
|
||||
title: @Composable () -> Unit,
|
||||
body: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.extraLarge)
|
||||
.clickable(onClick = onClick)
|
||||
.then(if (clickable) Modifier.clickable(onClick = onClick) else Modifier)
|
||||
.background(backgroundColour)
|
||||
.padding(vertical = 32.dp)
|
||||
.fillMaxWidth()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,640L640,640Q610,678 568.5,699Q527,720 480,720Q433,720 391.5,699Q350,678 320,640L200,640L200,760Q200,760 200,760Q200,760 200,760ZM480,640Q518,640 549,618Q580,596 592,560L760,560L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,560L368,560Q380,596 411,618Q442,640 480,640ZM200,760Q200,760 200,760Q200,760 200,760L200,760L320,760Q350,760 391.5,760Q433,760 480,760Q527,760 568.5,760Q610,760 640,760L760,760L760,760Q760,760 760,760Q760,760 760,760L200,760Z"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,640L640,640Q610,678 568.5,699Q527,720 480,720Q433,720 391.5,699Q350,678 320,640L200,640L200,760Q200,760 200,760Q200,760 200,760ZM480,640Q518,640 549,618Q580,596 592,560L760,560L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,560L368,560Q380,596 411,618Q442,640 480,640ZM480,560L320,400L376,342L440,406L440,240L520,240L520,406L584,342L640,400L480,560ZM200,760Q200,760 200,760Q200,760 200,760L200,760L320,760Q350,760 391.5,760Q433,760 480,760Q527,760 568.5,760Q610,760 640,760L760,760L760,760Q760,760 760,760Q760,760 760,760L200,760Z"/>
|
||||
</vector>
|
||||
|
|
@ -156,6 +156,13 @@
|
|||
<string name="overview_screen_changelog_description">Stoat never sleeps, take a look to see what\'s been in the works</string>
|
||||
<string name="overview_screen_feedback">Provide Feedback</string>
|
||||
<string name="overview_screen_feedback_description">Got something to say? We\'re all ears</string>
|
||||
<string name="overview_screen_catch_up">Catch Up</string>
|
||||
<string name="overview_screen_catch_up_description">See what you\'ve missed while you were away</string>
|
||||
<string name="overview_screen_catch_up_none">All caught up</string>
|
||||
<string name="overview_screen_catch_up_none_description">You\'ve seen all new messages since your last visit.</string>
|
||||
<string name="overview_screen_catch_up_amount_badge_loading">…</string>
|
||||
|
||||
<string name="catch_up">Catch Up</string>
|
||||
|
||||
<string name="user_card_tap_to_copy">Tap to copy</string>
|
||||
<string name="user_card_error_sharing">An error occurred while sharing your user card.</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue