feat: retire PageHeader in favour of TopAppBar
- also make DisconnectionNotice borderless Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
452d2b164d
commit
5736aec64d
|
|
@ -36,6 +36,7 @@ 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.graphics.toArgb
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -70,6 +71,7 @@ class InviteActivity : ComponentActivity() {
|
|||
|
||||
val inviteCode = intent.data?.lastPathSegment
|
||||
|
||||
window.statusBarColor = Color.Transparent.toArgb()
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
setContent {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
|
|
@ -212,6 +213,7 @@ class MainActivity : FragmentActivity() {
|
|||
super.onResume()
|
||||
DynamicColors.applyToActivityIfAvailable(this)
|
||||
DynamicColors.applyToActivitiesIfAvailable(RevoltApplication.instance)
|
||||
window.statusBarColor = Color.Transparent.toArgb()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
||||
|
|
@ -223,6 +225,7 @@ class MainActivity : FragmentActivity() {
|
|||
options.release = BuildConfig.VERSION_NAME
|
||||
}
|
||||
|
||||
window.statusBarColor = Color.Transparent.toArgb()
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
installSplashScreen().apply {
|
||||
|
|
@ -273,7 +276,8 @@ fun AppEntrypoint(
|
|||
colourOverrides = SyncedSettings.android.colourOverrides
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
NavHost(
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ import androidx.activity.compose.setContent
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -28,6 +29,7 @@ import androidx.compose.material3.SnackbarHostState
|
|||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -38,6 +40,7 @@ import androidx.compose.ui.graphics.RectangleShape
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.core.view.WindowCompat
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.REVOLT_FILES
|
||||
|
|
@ -45,7 +48,6 @@ import chat.revolt.api.RevoltHttp
|
|||
import chat.revolt.api.schemas.AutumnResource
|
||||
import chat.revolt.api.settings.GlobalState
|
||||
import chat.revolt.api.settings.SyncedSettings
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.provider.getAttachmentContentUri
|
||||
import chat.revolt.ui.theme.RevoltTheme
|
||||
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
|
||||
|
|
@ -82,7 +84,7 @@ class ImageViewActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalGlideComposeApi::class)
|
||||
@OptIn(ExperimentalGlideComposeApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ImageViewScreen(resource: AutumnResource, onClose: () -> Unit = {}) {
|
||||
val resourceUrl = "$REVOLT_FILES/attachments/${resource.id}/${resource.filename}"
|
||||
|
|
@ -187,6 +189,81 @@ fun ImageViewScreen(resource: AutumnResource, onClose: () -> Unit = {}) {
|
|||
colourOverrides = SyncedSettings.android.colourOverrides
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.media_viewer_title_image,
|
||||
resource.filename ?: resource.id!!
|
||||
),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
onClose()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
shareSubmenuIsOpen.value = true
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_share_24dp),
|
||||
contentDescription = stringResource(id = R.string.share)
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = shareSubmenuIsOpen.value,
|
||||
onDismissRequest = {
|
||||
shareSubmenuIsOpen.value = false
|
||||
}
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
shareUrl()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(id = R.string.media_viewer_share_url)
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
shareImage()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(
|
||||
id = R.string.media_viewer_share_image
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = {
|
||||
saveToGallery()
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_download_24dp),
|
||||
contentDescription = stringResource(
|
||||
id = R.string.media_viewer_save
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
|
||||
) { pv ->
|
||||
Surface(
|
||||
|
|
@ -195,87 +272,22 @@ fun ImageViewScreen(resource: AutumnResource, onClose: () -> Unit = {}) {
|
|||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Column {
|
||||
PageHeader(
|
||||
text = stringResource(
|
||||
id = R.string.media_viewer_title_image,
|
||||
resource.filename ?: resource.id!!
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(RectangleShape)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
ZoomableGlideImage(
|
||||
model = resourceUrl,
|
||||
contentDescription = null,
|
||||
state = rememberZoomableImageState(
|
||||
rememberZoomableState(
|
||||
zoomSpec = ZoomSpec(maxZoomFactor = 10f)
|
||||
)
|
||||
),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = onClose,
|
||||
maxLines = 1,
|
||||
additionalButtons = {
|
||||
Row {
|
||||
IconButton(onClick = {
|
||||
shareSubmenuIsOpen.value = true
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_share_24dp),
|
||||
contentDescription = stringResource(id = R.string.share)
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = shareSubmenuIsOpen.value,
|
||||
onDismissRequest = {
|
||||
shareSubmenuIsOpen.value = false
|
||||
}
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
shareUrl()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(id = R.string.media_viewer_share_url)
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
shareImage()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(
|
||||
id = R.string.media_viewer_share_image
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = {
|
||||
saveToGallery()
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_download_24dp),
|
||||
contentDescription = stringResource(
|
||||
id = R.string.media_viewer_save
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(RectangleShape)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
ZoomableGlideImage(
|
||||
model = resourceUrl,
|
||||
contentDescription = null,
|
||||
state = rememberZoomableImageState(
|
||||
rememberZoomableState(
|
||||
zoomSpec = ZoomSpec(maxZoomFactor = 10f)
|
||||
)
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ import androidx.activity.compose.setContent
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -28,6 +29,7 @@ import androidx.compose.material3.SnackbarHostState
|
|||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -39,6 +41,7 @@ import androidx.compose.ui.graphics.RectangleShape
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.media3.common.MediaItem
|
||||
|
|
@ -50,7 +53,6 @@ import chat.revolt.api.RevoltHttp
|
|||
import chat.revolt.api.schemas.AutumnResource
|
||||
import chat.revolt.api.settings.GlobalState
|
||||
import chat.revolt.api.settings.SyncedSettings
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.provider.getAttachmentContentUri
|
||||
import chat.revolt.ui.theme.RevoltTheme
|
||||
import io.ktor.client.request.get
|
||||
|
|
@ -82,6 +84,7 @@ class VideoViewActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||
@Composable
|
||||
fun VideoViewScreen(resource: AutumnResource, onClose: () -> Unit = {}) {
|
||||
|
|
@ -199,6 +202,81 @@ fun VideoViewScreen(resource: AutumnResource, onClose: () -> Unit = {}) {
|
|||
colourOverrides = SyncedSettings.android.colourOverrides
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.media_viewer_title_video,
|
||||
resource.filename ?: resource.id!!
|
||||
),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
onClose()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
shareSubmenuIsOpen.value = true
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_share_24dp),
|
||||
contentDescription = stringResource(id = R.string.share)
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = shareSubmenuIsOpen.value,
|
||||
onDismissRequest = {
|
||||
shareSubmenuIsOpen.value = false
|
||||
}
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
shareUrl()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(id = R.string.media_viewer_share_url)
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
shareVideo()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(
|
||||
id = R.string.media_viewer_share_video
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = {
|
||||
saveToGallery()
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_download_24dp),
|
||||
contentDescription = stringResource(
|
||||
id = R.string.media_viewer_save
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
|
||||
) { pv ->
|
||||
Surface(
|
||||
|
|
@ -207,89 +285,24 @@ fun VideoViewScreen(resource: AutumnResource, onClose: () -> Unit = {}) {
|
|||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Column {
|
||||
PageHeader(
|
||||
text = stringResource(
|
||||
id = R.string.media_viewer_title_video,
|
||||
resource.filename ?: resource.id!!
|
||||
),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = onClose,
|
||||
maxLines = 1,
|
||||
additionalButtons = {
|
||||
Row {
|
||||
IconButton(onClick = {
|
||||
shareSubmenuIsOpen.value = true
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_share_24dp),
|
||||
contentDescription = stringResource(id = R.string.share)
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = shareSubmenuIsOpen.value,
|
||||
onDismissRequest = {
|
||||
shareSubmenuIsOpen.value = false
|
||||
}
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
shareUrl()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(id = R.string.media_viewer_share_url)
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
shareVideo()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(
|
||||
id = R.string.media_viewer_share_video
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = {
|
||||
saveToGallery()
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_download_24dp),
|
||||
contentDescription = stringResource(
|
||||
id = R.string.media_viewer_save
|
||||
)
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(RectangleShape)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
AndroidView(
|
||||
factory = { context ->
|
||||
PlayerView(context).apply {
|
||||
setShowBuffering(PlayerView.SHOW_BUFFERING_ALWAYS)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Box(
|
||||
},
|
||||
update = {
|
||||
it.player = player
|
||||
},
|
||||
modifier = Modifier
|
||||
.clip(RectangleShape)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
AndroidView(
|
||||
factory = { context ->
|
||||
PlayerView(context).apply {
|
||||
setShowBuffering(PlayerView.SHOW_BUFFERING_ALWAYS)
|
||||
}
|
||||
},
|
||||
update = {
|
||||
it.player = player
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
)
|
||||
}
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@ package chat.revolt.components.chat
|
|||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Done
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
|
|
@ -44,32 +48,35 @@ private fun DisconnectedNoticeBase(
|
|||
canTapToRetry: Boolean = false,
|
||||
onRetry: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(enabled = canTapToRetry, onClick = onRetry)
|
||||
.fillMaxWidth()
|
||||
.background(background)
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
imageVector = icon,
|
||||
tint = foreground,
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
color = foreground,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
if (canTapToRetry) {
|
||||
Text(
|
||||
text = stringResource(R.string.tap_to_reconnect),
|
||||
color = foreground,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
fontWeight = FontWeight.Normal
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(enabled = canTapToRetry, onClick = onRetry)
|
||||
.fillMaxWidth()
|
||||
.background(background)
|
||||
.windowInsetsPadding(WindowInsets.statusBars)
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
imageVector = icon,
|
||||
tint = foreground,
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
color = foreground,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
if (canTapToRetry) {
|
||||
Text(
|
||||
text = stringResource(R.string.tap_to_reconnect),
|
||||
color = foreground,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
package chat.revolt.components.generic
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowForward
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.revolt.R
|
||||
|
||||
@Composable
|
||||
fun PageHeader(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
showBackButton: Boolean = false,
|
||||
onBackButtonClicked: () -> Unit = {},
|
||||
startButtons: @Composable () -> Unit = {},
|
||||
additionalButtons: @Composable () -> Unit = {},
|
||||
maxLines: Int = Int.MAX_VALUE
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (showBackButton) {
|
||||
IconButton(onClick = onBackButtonClicked) {
|
||||
Icon(
|
||||
modifier = modifier,
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
}
|
||||
startButtons()
|
||||
Text(
|
||||
text = text,
|
||||
maxLines = maxLines,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.displaySmall.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Left,
|
||||
fontSize = 24.sp
|
||||
),
|
||||
modifier = modifier
|
||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||
.weight(1f)
|
||||
)
|
||||
additionalButtons()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun PageHeaderPreview() {
|
||||
PageHeader(text = "Page Header")
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun PageHeaderPreviewWithBackButton() {
|
||||
PageHeader(text = "Page Header", showBackButton = true)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun PageHeaderPreviewWithAdditionalButtons() {
|
||||
PageHeader(text = "Page Header", showBackButton = true, additionalButtons = {
|
||||
IconButton(onClick = {}) {
|
||||
Icon(
|
||||
modifier = Modifier,
|
||||
imageVector = Icons.Default.ArrowForward,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package chat.revolt.internals.extensions
|
||||
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.NonRestartableComposable
|
||||
|
||||
val WindowInsets.Companion.zero: WindowInsets
|
||||
@Composable
|
||||
@NonRestartableComposable
|
||||
get() = WindowInsets(left = 0, right = 0, top = 0, bottom = 0)
|
||||
|
|
@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.RevoltApplication
|
||||
|
|
@ -35,8 +37,10 @@ fun DefaultDestinationScreen(
|
|||
nextDestination?.let {
|
||||
// Fix for SDK >=31, where core-splashscreen accidentally removes dynamic colours
|
||||
// See the other one in MainActivity.kt
|
||||
DynamicColors.applyToActivityIfAvailable(context.getComponentActivity() as Activity)
|
||||
val activity = context.getComponentActivity() as Activity
|
||||
DynamicColors.applyToActivityIfAvailable(activity)
|
||||
DynamicColors.applyToActivitiesIfAvailable(RevoltApplication.instance)
|
||||
activity.window.statusBarColor = Color.Transparent.toArgb()
|
||||
|
||||
navController.popBackStack(navController.graph.startDestinationRoute!!, true)
|
||||
navController.navigate(it)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.os.Build
|
|||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Image
|
||||
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
|
||||
|
|
@ -12,15 +13,21 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
|
|
@ -38,6 +45,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
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.viewModelScope
|
||||
|
|
@ -49,7 +57,6 @@ import chat.revolt.api.REVOLT_BASE
|
|||
import chat.revolt.api.RevoltJson
|
||||
import chat.revolt.api.routes.misc.Root
|
||||
import chat.revolt.api.routes.misc.getRootRoute
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.generic.PrimaryTabs
|
||||
import chat.revolt.internals.Platform
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -126,6 +133,7 @@ fun DebugInfo(viewModel: AboutViewModel) {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AboutScreen(navController: NavController, viewModel: AboutViewModel = viewModel()) {
|
||||
val context = LocalContext.current
|
||||
|
|
@ -146,107 +154,126 @@ fun AboutScreen(navController: NavController, viewModel: AboutViewModel = viewMo
|
|||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(R.string.about),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = { navController.popBackStack() }
|
||||
)
|
||||
|
||||
PrimaryTabs(
|
||||
tabs = listOf(
|
||||
stringResource(R.string.about_tab_version),
|
||||
stringResource(R.string.about_tab_details)
|
||||
),
|
||||
currentIndex = viewModel.selectedTabIndex,
|
||||
onTabSelected = { viewModel.selectedTabIndex = it }
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
.weight(1f),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (viewModel.root == null) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
)
|
||||
} else {
|
||||
when (viewModel.selectedTabIndex) {
|
||||
0 -> {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.revolt_logo_wide),
|
||||
contentDescription = stringResource(R.string.about_full_name),
|
||||
colorFilter = ColorFilter.tint(LocalContentColor.current),
|
||||
modifier = Modifier
|
||||
.width(250.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.about_full_name),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = BuildConfig.VERSION_NAME,
|
||||
style = MaterialTheme.typography.labelMedium.copy(
|
||||
fontWeight = FontWeight.Normal
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.about_brought_to_you_by),
|
||||
style = MaterialTheme.typography.labelSmall.copy(
|
||||
fontWeight = FontWeight.Light
|
||||
),
|
||||
color = LocalContentColor.current.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.about),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Box(Modifier.padding(pv)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
PrimaryTabs(
|
||||
tabs = listOf(
|
||||
stringResource(R.string.about_tab_version),
|
||||
stringResource(R.string.about_tab_details)
|
||||
),
|
||||
currentIndex = viewModel.selectedTabIndex,
|
||||
onTabSelected = { viewModel.selectedTabIndex = it }
|
||||
)
|
||||
|
||||
1 -> {
|
||||
DebugInfo(viewModel)
|
||||
TextButton(onClick = ::copyDebugInformation) {
|
||||
Text(text = stringResource(id = R.string.copy))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
.weight(1f),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (viewModel.root == null) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
)
|
||||
} else {
|
||||
when (viewModel.selectedTabIndex) {
|
||||
0 -> {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.revolt_logo_wide),
|
||||
contentDescription = stringResource(R.string.about_full_name),
|
||||
colorFilter = ColorFilter.tint(LocalContentColor.current),
|
||||
modifier = Modifier
|
||||
.width(250.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.about_full_name),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = BuildConfig.VERSION_NAME,
|
||||
style = MaterialTheme.typography.labelMedium.copy(
|
||||
fontWeight = FontWeight.Normal
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.about_brought_to_you_by),
|
||||
style = MaterialTheme.typography.labelSmall.copy(
|
||||
fontWeight = FontWeight.Light
|
||||
),
|
||||
color = LocalContentColor.current.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
|
||||
1 -> {
|
||||
DebugInfo(viewModel)
|
||||
TextButton(onClick = ::copyDebugInformation) {
|
||||
Text(text = stringResource(id = R.string.copy))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp, vertical = 30.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
ElevatedButton(
|
||||
onClick = { navController.navigate("about/oss") },
|
||||
modifier = Modifier
|
||||
.testTag("view_oss_attribution")
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.oss_attribution))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp, vertical = 30.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
ElevatedButton(
|
||||
onClick = { navController.navigate("about/oss") },
|
||||
modifier = Modifier
|
||||
.testTag("view_oss_attribution")
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.oss_attribution))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,21 +2,28 @@ package chat.revolt.screens.about
|
|||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -27,13 +34,14 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.settings.AttributionItem
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
|
@ -144,66 +152,85 @@ fun AttributionScreen(navController: NavController) {
|
|||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(R.string.oss_attribution),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = { navController.popBackStack() }
|
||||
)
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
libraries?.let {
|
||||
LazyColumn {
|
||||
item {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.oss_attribution_body)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.oss_attribution_body_2)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.oss_attribution_warning),
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.oss_attribution_generation_date,
|
||||
libraries?.metadata?.generated ?: ""
|
||||
),
|
||||
color = LocalContentColor.current.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = it.libraries.sortedBy { library -> library.name }
|
||||
) { library ->
|
||||
AttributionItem(library = library) {
|
||||
licenceSheetOpen = true
|
||||
licenseSheetTarget = library.licenses.first()
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "cat") {
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = "🐈",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
textAlign = TextAlign.Center
|
||||
text = stringResource(R.string.oss_attribution),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Box(Modifier.padding(pv)) {
|
||||
libraries?.let {
|
||||
LazyColumn {
|
||||
item {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.oss_attribution_body)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.oss_attribution_body_2)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.oss_attribution_warning),
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.oss_attribution_generation_date,
|
||||
libraries?.metadata?.generated ?: ""
|
||||
),
|
||||
color = LocalContentColor.current.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = it.libraries.sortedBy { library -> library.name }
|
||||
) { library ->
|
||||
AttributionItem(library = library) {
|
||||
licenceSheetOpen = true
|
||||
licenseSheetTarget = library.licenses.first()
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "cat") {
|
||||
Text(
|
||||
text = "🐈",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,17 @@ import androidx.compose.animation.AnimatedVisibility
|
|||
import androidx.compose.animation.Crossfade
|
||||
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.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
|
@ -723,7 +727,6 @@ fun ChatRouterScreen(
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = RealtimeSocket.disconnectionState != DisconnectionState.Connected
|
||||
|
|
@ -737,10 +740,17 @@ fun ChatRouterScreen(
|
|||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = RealtimeSocket.disconnectionState == DisconnectionState.Connected
|
||||
) {
|
||||
Spacer(Modifier.windowInsetsPadding(WindowInsets.statusBars))
|
||||
}
|
||||
|
||||
if (useTabletAwareUI) {
|
||||
Row {
|
||||
DismissibleDrawerSheet(
|
||||
drawerContainerColor = Color.Transparent
|
||||
drawerContainerColor = Color.Transparent,
|
||||
windowInsets = WindowInsets.navigationBars
|
||||
) {
|
||||
Sidebar(
|
||||
viewModel = viewModel,
|
||||
|
|
@ -781,7 +791,8 @@ fun ChatRouterScreen(
|
|||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
DismissibleDrawerSheet(
|
||||
drawerContainerColor = Color.Transparent
|
||||
drawerContainerColor = Color.Transparent,
|
||||
windowInsets = WindowInsets.navigationBars
|
||||
) {
|
||||
Sidebar(
|
||||
viewModel = viewModel,
|
||||
|
|
@ -1044,7 +1055,6 @@ fun Sidebar(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ChannelNavigator(
|
||||
navController: NavHostController,
|
||||
|
|
@ -1111,7 +1121,7 @@ fun ChannelNavigator(
|
|||
toggleDrawer()
|
||||
}
|
||||
|
||||
NoCurrentChannelScreen()
|
||||
NoCurrentChannelScreen(useDrawer = useDrawer, onDrawerClicked = toggleDrawer)
|
||||
}
|
||||
|
||||
dialog("report/message/{messageId}") { backStackEntry ->
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package chat.revolt.screens.chat.views
|
|||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
|
|
@ -12,7 +14,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -23,6 +27,7 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.internals.FriendRequests
|
||||
import chat.revolt.api.routes.user.unfriendUser
|
||||
|
|
@ -30,8 +35,8 @@ import chat.revolt.callbacks.Action
|
|||
import chat.revolt.callbacks.ActionChannel
|
||||
import chat.revolt.components.chat.MemberListItem
|
||||
import chat.revolt.components.generic.CountableListHeader
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.generic.SheetClickable
|
||||
import chat.revolt.internals.extensions.zero
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -86,154 +91,168 @@ fun FriendsScreen(useDrawer: Boolean, onDrawerClicked: () -> Unit) {
|
|||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(R.string.friends),
|
||||
startButtons = {
|
||||
if (useDrawer) {
|
||||
IconButton(onClick = onDrawerClicked) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.friends),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
if (useDrawer) {
|
||||
IconButton(onClick = {
|
||||
onDrawerClicked()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Menu,
|
||||
contentDescription = stringResource(id = R.string.menu)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
optionsSheetShown = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Menu,
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = stringResource(R.string.menu)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
additionalButtons = {
|
||||
IconButton(onClick = {
|
||||
optionsSheetShown = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = stringResource(R.string.menu)
|
||||
},
|
||||
windowInsets = WindowInsets.zero
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(pv)
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
LazyColumn {
|
||||
stickyHeader(key = "incoming") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.friends_incoming_requests),
|
||||
count = FriendRequests.getIncoming().size
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
LazyColumn {
|
||||
stickyHeader(key = "incoming") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.friends_incoming_requests),
|
||||
count = FriendRequests.getIncoming().size
|
||||
)
|
||||
}
|
||||
|
||||
items(FriendRequests.getIncoming().size) {
|
||||
val item = FriendRequests.getIncoming()[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
items(FriendRequests.getIncoming().size) {
|
||||
val item = FriendRequests.getIncoming()[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
stickyHeader(key = "outgoing") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.friends_outgoing_requests),
|
||||
count = FriendRequests.getOutgoing().size
|
||||
)
|
||||
}
|
||||
stickyHeader(key = "outgoing") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.friends_outgoing_requests),
|
||||
count = FriendRequests.getOutgoing().size
|
||||
)
|
||||
}
|
||||
|
||||
items(FriendRequests.getOutgoing().size) {
|
||||
val item = FriendRequests.getOutgoing()[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
items(FriendRequests.getOutgoing().size) {
|
||||
val item = FriendRequests.getOutgoing()[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
stickyHeader(key = "online") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.status_online),
|
||||
count = FriendRequests.getOnlineFriends().size
|
||||
)
|
||||
}
|
||||
stickyHeader(key = "online") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.status_online),
|
||||
count = FriendRequests.getOnlineFriends().size
|
||||
)
|
||||
}
|
||||
|
||||
items(FriendRequests.getOnlineFriends().size) {
|
||||
val item = FriendRequests.getOnlineFriends()[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
items(FriendRequests.getOnlineFriends().size) {
|
||||
val item = FriendRequests.getOnlineFriends()[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
stickyHeader(key = "not_online") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.friends_all),
|
||||
count = FriendRequests.getFriends(true).size
|
||||
)
|
||||
}
|
||||
stickyHeader(key = "not_online") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.friends_all),
|
||||
count = FriendRequests.getFriends(true).size
|
||||
)
|
||||
}
|
||||
|
||||
items(FriendRequests.getFriends(true).size) {
|
||||
val item = FriendRequests.getFriends(true)[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
items(FriendRequests.getFriends(true).size) {
|
||||
val item = FriendRequests.getFriends(true)[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
stickyHeader(key = "blocked") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.friends_blocked),
|
||||
count = FriendRequests.getBlocked().size
|
||||
)
|
||||
}
|
||||
stickyHeader(key = "blocked") {
|
||||
CountableListHeader(
|
||||
text = stringResource(id = R.string.friends_blocked),
|
||||
count = FriendRequests.getBlocked().size
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
items(FriendRequests.getBlocked().size) {
|
||||
val item = FriendRequests.getBlocked()[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
items(FriendRequests.getBlocked().size) {
|
||||
val item = FriendRequests.getBlocked()[it]
|
||||
MemberListItem(
|
||||
member = null,
|
||||
user = item,
|
||||
serverId = null,
|
||||
userId = item.id ?: "",
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
item.id?.let { userId ->
|
||||
ActionChannel.send(Action.OpenUserSheet(userId, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,19 @@ import androidx.compose.animation.core.rememberInfiniteTransition
|
|||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -26,14 +30,16 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.activities.InviteActivity
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.home.LinkOnHome
|
||||
import chat.revolt.internals.extensions.zero
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HomeScreen(navController: NavController, useDrawer: Boolean, onDrawerClicked: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
|
@ -49,59 +55,75 @@ fun HomeScreen(navController: NavController, useDrawer: Boolean, onDrawerClicked
|
|||
label = "catRotation"
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(id = R.string.home),
|
||||
startButtons = {
|
||||
if (useDrawer) {
|
||||
IconButton(onClick = onDrawerClicked) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Menu,
|
||||
contentDescription = stringResource(R.string.menu)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "🐈",
|
||||
fontSize = 100.sp,
|
||||
modifier = Modifier.rotate(catRotation)
|
||||
)
|
||||
}
|
||||
|
||||
Column(Modifier.padding(16.dp)) {
|
||||
LinkOnHome(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Star,
|
||||
contentDescription = stringResource(id = R.string.home_join_jenvolt),
|
||||
modifier = modifier
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.home),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
heading = { Text(text = stringResource(id = R.string.home_join_jenvolt)) },
|
||||
description = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.home_join_jenvolt_description)
|
||||
navigationIcon = {
|
||||
if (useDrawer) {
|
||||
IconButton(onClick = {
|
||||
onDrawerClicked()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Menu,
|
||||
contentDescription = stringResource(id = R.string.menu)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.zero
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(pv)
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "🐈",
|
||||
fontSize = 100.sp,
|
||||
modifier = Modifier.rotate(catRotation)
|
||||
)
|
||||
}
|
||||
|
||||
Column(Modifier.padding(16.dp)) {
|
||||
LinkOnHome(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Star,
|
||||
contentDescription = stringResource(id = R.string.home_join_jenvolt),
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
heading = { Text(text = stringResource(id = R.string.home_join_jenvolt)) },
|
||||
description = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.home_join_jenvolt_description)
|
||||
)
|
||||
}
|
||||
) {
|
||||
context.startActivity(
|
||||
Intent(
|
||||
context,
|
||||
InviteActivity::class.java
|
||||
)
|
||||
.setData(Uri.parse("https://rvlt.gg/jen"))
|
||||
.setAction(Intent.ACTION_VIEW)
|
||||
)
|
||||
}
|
||||
) {
|
||||
context.startActivity(
|
||||
Intent(
|
||||
context,
|
||||
InviteActivity::class.java
|
||||
)
|
||||
.setData(Uri.parse("https://rvlt.gg/jen"))
|
||||
.setAction(Intent.ACTION_VIEW)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,18 @@ package chat.revolt.screens.chat.views
|
|||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
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.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -14,27 +22,51 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.revolt.R
|
||||
import chat.revolt.internals.extensions.zero
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NoCurrentChannelScreen() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(64.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.no_active_channel),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 24.sp,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.no_active_channel_body),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
fun NoCurrentChannelScreen(useDrawer: Boolean, onDrawerClicked: () -> Unit) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {},
|
||||
navigationIcon = {
|
||||
if (useDrawer) {
|
||||
IconButton(onClick = {
|
||||
onDrawerClicked()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Menu,
|
||||
contentDescription = stringResource(id = R.string.menu)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.zero
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(pv)
|
||||
.fillMaxSize()
|
||||
.padding(64.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.no_active_channel),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 24.sp,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.no_active_channel_body),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
|
|
@ -258,7 +260,7 @@ fun ChannelScreen(
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.imePadding()
|
||||
.safeDrawingPadding()
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
) {
|
||||
ChannelHeader(
|
||||
channel = channel ?: Channel(
|
||||
|
|
|
|||
|
|
@ -2,29 +2,55 @@ package chat.revolt.screens.services
|
|||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.services.DiscoverView
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DiscoverScreen(navController: NavController) {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(R.string.discover),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
DiscoverView()
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.discover),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { pv ->
|
||||
Column(
|
||||
Modifier
|
||||
.padding(pv)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
DiscoverView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import androidx.compose.animation.core.animateFloatAsState
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
|
|
@ -19,11 +20,11 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
|
|
@ -33,10 +34,14 @@ import androidx.compose.material.ripple.LocalRippleTheme
|
|||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -51,11 +56,13 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
|
@ -68,7 +75,6 @@ import chat.revolt.api.RevoltJson
|
|||
import chat.revolt.api.settings.GlobalState
|
||||
import chat.revolt.api.settings.SyncedSettings
|
||||
import chat.revolt.components.generic.ListHeader
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.settings.appearance.ColourChip
|
||||
import chat.revolt.ui.theme.ClearRippleTheme
|
||||
import chat.revolt.ui.theme.OverridableColourScheme
|
||||
|
|
@ -263,218 +269,234 @@ fun AppearanceSettingsScreen(
|
|||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(id = R.string.settings_appearance),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_appearance_theme))
|
||||
}
|
||||
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.settings_appearance),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Box(Modifier.padding(pv)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
ColourChip(
|
||||
color = Color(0xff191919),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_revolt),
|
||||
selected = GlobalState.theme == Theme.Revolt,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_revolt")
|
||||
) {
|
||||
viewModel.saveNewTheme(Theme.Revolt)
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_appearance_theme))
|
||||
}
|
||||
|
||||
ColourChip(
|
||||
color = Color(0xfff7f7f7),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_light),
|
||||
selected = GlobalState.theme == Theme.Light,
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_light")
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp)
|
||||
) {
|
||||
viewModel.saveNewTheme(Theme.Light)
|
||||
}
|
||||
|
||||
ColourChip(
|
||||
color = Color(0xff000000),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_amoled),
|
||||
selected = GlobalState.theme == Theme.Amoled,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_amoled")
|
||||
) {
|
||||
viewModel.saveNewTheme(Theme.Amoled)
|
||||
}
|
||||
|
||||
ColourChip(
|
||||
color = if (isSystemInDarkTheme()) Color(0xff191919) else Color(0xfff7f7f7),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_none),
|
||||
selected = GlobalState.theme == Theme.None,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_none")
|
||||
) {
|
||||
viewModel.saveNewTheme(Theme.None)
|
||||
}
|
||||
|
||||
if (systemSupportsDynamicColors()) {
|
||||
ColourChip(
|
||||
color = dynamicDarkColorScheme(LocalContext.current).primary,
|
||||
text = stringResource(id = R.string.settings_appearance_theme_m3dynamic),
|
||||
selected = GlobalState.theme == Theme.M3Dynamic,
|
||||
color = Color(0xff191919),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_revolt),
|
||||
selected = GlobalState.theme == Theme.Revolt,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_m3dynamic")
|
||||
.testTag("set_theme_revolt")
|
||||
) {
|
||||
viewModel.saveNewTheme(Theme.M3Dynamic)
|
||||
viewModel.saveNewTheme(Theme.Revolt)
|
||||
}
|
||||
} else {
|
||||
|
||||
ColourChip(
|
||||
color = Color(0xffa0a0a0),
|
||||
text = stringResource(
|
||||
id = R.string.settings_appearance_theme_m3dynamic_unsupported
|
||||
),
|
||||
selected = false,
|
||||
color = Color(0xfff7f7f7),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_light),
|
||||
selected = GlobalState.theme == Theme.Light,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_m3dynamic_unsupported")
|
||||
.testTag("set_theme_light")
|
||||
) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(
|
||||
R.string.settings_appearance_theme_m3dynamic_unsupported_toast
|
||||
),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
viewModel.saveNewTheme(Theme.Light)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
viewModel.showColourOverrides = !viewModel.showColourOverrides
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (LocalLayoutDirection.current == LayoutDirection.Ltr) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight,
|
||||
contentDescription = null,
|
||||
ColourChip(
|
||||
color = Color(0xff000000),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_amoled),
|
||||
selected = GlobalState.theme == Theme.Amoled,
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 4.dp)
|
||||
.rotate(colourOverridesOpenerArrowRotation)
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance_colour_overrides),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
)
|
||||
|
||||
if (LocalLayoutDirection.current == LayoutDirection.Rtl) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowLeft,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(start = 4.dp, end = 20.dp)
|
||||
.rotate(colourOverridesOpenerArrowRotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(viewModel.showColourOverrides) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp)
|
||||
.weight(1f)
|
||||
.testTag("set_theme_amoled")
|
||||
) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
filePicker.launch(arrayOf("*/*"))
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_folder_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance_colour_overrides_import)
|
||||
)
|
||||
}
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
fileSaver.launch("${SyncedSettings.android.theme}-colours.rato")
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_content_save_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance_colour_overrides_export)
|
||||
)
|
||||
}
|
||||
viewModel.saveNewTheme(Theme.Amoled)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OverridableColourScheme.fieldNames.forEach { fieldName ->
|
||||
val value =
|
||||
SyncedSettings.android.colourOverrides?.getFieldByName(fieldName)
|
||||
?: MaterialTheme.colorScheme.getFieldByName(fieldName)
|
||||
ColourChip(
|
||||
color = if (isSystemInDarkTheme()) Color(0xff191919) else Color(0xfff7f7f7),
|
||||
text = stringResource(id = R.string.settings_appearance_theme_none),
|
||||
selected = GlobalState.theme == Theme.None,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_none")
|
||||
) {
|
||||
viewModel.saveNewTheme(Theme.None)
|
||||
}
|
||||
|
||||
if (systemSupportsDynamicColors()) {
|
||||
ColourChip(
|
||||
color = Color(value ?: 0),
|
||||
text = OverridableColourScheme.fieldNameToResource[fieldName]
|
||||
?.let { context.getString(it) }
|
||||
?: fieldName,
|
||||
color = dynamicDarkColorScheme(LocalContext.current).primary,
|
||||
text = stringResource(id = R.string.settings_appearance_theme_m3dynamic),
|
||||
selected = GlobalState.theme == Theme.M3Dynamic,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_m3dynamic")
|
||||
) {
|
||||
viewModel.saveNewTheme(Theme.M3Dynamic)
|
||||
}
|
||||
} else {
|
||||
ColourChip(
|
||||
color = Color(0xffa0a0a0),
|
||||
text = stringResource(
|
||||
id = R.string.settings_appearance_theme_m3dynamic_unsupported
|
||||
),
|
||||
selected = false,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag("set_theme_m3dynamic_unsupported")
|
||||
) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(
|
||||
R.string.settings_appearance_theme_m3dynamic_unsupported_toast
|
||||
),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
viewModel.showColourOverrides = !viewModel.showColourOverrides
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (LocalLayoutDirection.current == LayoutDirection.Ltr) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 4.dp)
|
||||
.rotate(colourOverridesOpenerArrowRotation)
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance_colour_overrides),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
)
|
||||
|
||||
if (LocalLayoutDirection.current == LayoutDirection.Rtl) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowLeft,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(start = 4.dp, end = 20.dp)
|
||||
.rotate(colourOverridesOpenerArrowRotation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(viewModel.showColourOverrides) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp)
|
||||
.testTag("set_colour_override_$fieldName")
|
||||
.padding(horizontal = 20.dp)
|
||||
) {
|
||||
viewModel.selectedOverrideName = fieldName
|
||||
viewModel.selectedOverrideInitialValue = value
|
||||
viewModel.overridePickerSheetVisible = true
|
||||
TextButton(
|
||||
onClick = {
|
||||
filePicker.launch(arrayOf("*/*"))
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_folder_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance_colour_overrides_import)
|
||||
)
|
||||
}
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
fileSaver.launch("${SyncedSettings.android.theme}-colours.rato")
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_content_save_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance_colour_overrides_export)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OverridableColourScheme.fieldNames.forEach { fieldName ->
|
||||
val value =
|
||||
SyncedSettings.android.colourOverrides?.getFieldByName(fieldName)
|
||||
?: MaterialTheme.colorScheme.getFieldByName(fieldName)
|
||||
|
||||
ColourChip(
|
||||
color = Color(value ?: 0),
|
||||
text = OverridableColourScheme.fieldNameToResource[fieldName]
|
||||
?.let { context.getString(it) }
|
||||
?: fieldName,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp)
|
||||
.testTag("set_colour_override_$fieldName")
|
||||
) {
|
||||
viewModel.selectedOverrideName = fieldName
|
||||
viewModel.selectedOverrideInitialValue = value
|
||||
viewModel.overridePickerSheetVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,21 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -20,12 +27,13 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.R
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.internals.Changelogs
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import chat.revolt.sheets.ChangelogSheet
|
||||
|
|
@ -64,50 +72,70 @@ fun ChangelogsSettingsScreen(
|
|||
ChangelogSheet(version = currentChangelog)
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(R.string.settings_changelogs),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
|
||||
LazyColumn {
|
||||
items(
|
||||
viewModel.list.size,
|
||||
key = { viewModel.list.keys.elementAt(it) }
|
||||
) { index ->
|
||||
val version = viewModel.list.keys.elementAt(index)
|
||||
val changelog = viewModel.list[version]!!
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
currentChangelog = version
|
||||
sheetOpen = true
|
||||
}
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = changelog.version,
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Text(
|
||||
text = changelog.summary,
|
||||
)
|
||||
}
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.settings_changelogs),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Divider()
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(pv)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
LazyColumn {
|
||||
items(
|
||||
viewModel.list.size,
|
||||
key = { viewModel.list.keys.elementAt(it) }
|
||||
) { index ->
|
||||
val version = viewModel.list.keys.elementAt(index)
|
||||
val changelog = viewModel.list[version]!!
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
currentChangelog = version
|
||||
sheetOpen = true
|
||||
}
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = changelog.version,
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Text(
|
||||
text = changelog.summary,
|
||||
)
|
||||
}
|
||||
)
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ import android.util.Log
|
|||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
|
|
@ -21,9 +22,14 @@ import androidx.compose.material.icons.filled.Search
|
|||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
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.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
|
|
@ -32,21 +38,24 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
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.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.BuildConfig
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.RevoltHttp
|
||||
import chat.revolt.api.RevoltJson
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
|
|
@ -135,6 +144,7 @@ class ClosedBetaUpdaterScreenViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ClosedBetaUpdaterScreen(
|
||||
navController: NavController,
|
||||
|
|
@ -142,165 +152,181 @@ fun ClosedBetaUpdaterScreen(
|
|||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = "Closed Beta Updater",
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "Revolt ${BuildConfig.VERSION_NAME}/${BuildConfig.VERSION_CODE}",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
|
||||
when (viewModel.updateState) {
|
||||
UpdateState.NotChecked -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = "Closed Beta Updater",
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF585858),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Not yet checked for updates",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Box(Modifier.padding(pv)) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "Revolt ${BuildConfig.VERSION_NAME}/${BuildConfig.VERSION_CODE}",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
|
||||
UpdateState.UpdateAvailable -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFFF5252),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = AnnotatedString.Builder().apply {
|
||||
append("You are out of date\n\nBuild ")
|
||||
pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
|
||||
append("${viewModel.newestBuild}")
|
||||
pop()
|
||||
append(" is available")
|
||||
}.toAnnotatedString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = viewModel.updateState == UpdateState.UpdateAvailable
|
||||
when (viewModel.updateState) {
|
||||
UpdateState.NotChecked -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
ElevatedButton(onClick = {
|
||||
viewUrlInBrowser(
|
||||
ctx = context,
|
||||
url = "${BuildConfig.ANALYSIS_BASEURL}/api/distribution/android/download?build=${viewModel.newestBuild}&token=${viewModel.newestDownloadToken}"
|
||||
)
|
||||
}) {
|
||||
Text(text = "Download")
|
||||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF585858),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Not yet checked for updates",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
|
||||
UpdateState.UpdateAvailable -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFFF5252),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = AnnotatedString.Builder().apply {
|
||||
append("You are out of date\n\nBuild ")
|
||||
pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
|
||||
append("${viewModel.newestBuild}")
|
||||
pop()
|
||||
append(" is available")
|
||||
}.toAnnotatedString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = viewModel.updateState == UpdateState.UpdateAvailable
|
||||
) {
|
||||
ElevatedButton(onClick = {
|
||||
viewUrlInBrowser(
|
||||
ctx = context,
|
||||
url = "${BuildConfig.ANALYSIS_BASEURL}/api/distribution/android/download?build=${viewModel.newestBuild}&token=${viewModel.newestDownloadToken}"
|
||||
)
|
||||
}) {
|
||||
Text(text = "Download")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateState.Checking -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFEEFF41),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Checking for updates...",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
|
||||
UpdateState.UpToDate -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF00E676),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Up to date",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
|
||||
UpdateState.ErrorChecking -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFFF5252),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Error checking for updates",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
|
||||
UpdateState.RequestingUpdateToken -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFEEFF41),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Requesting update token...",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
UpdateState.Checking -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFEEFF41),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Checking for updates...",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
|
||||
UpdateState.UpToDate -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF00E676),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Up to date",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
|
||||
UpdateState.ErrorChecking -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFFF5252),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Error checking for updates",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
|
||||
UpdateState.RequestingUpdateToken -> Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFEEFF41),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Requesting update token...",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.checkForUpdates()
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(bottom = 10.dp)
|
||||
) {
|
||||
Text(text = "Check for updates")
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.checkForUpdates()
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(bottom = 10.dp)
|
||||
) {
|
||||
Text(text = "Check for updates")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,25 +5,35 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.R
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DebugSettingsScreenViewModel @Inject constructor(
|
||||
|
|
@ -46,56 +56,77 @@ class DebugSettingsScreenViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DebugSettingsScreen(
|
||||
navController: NavController,
|
||||
viewModel: DebugSettingsScreenViewModel = hiltViewModel()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = "Debug",
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = "Debug",
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(pv)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(20.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Sparks",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.horizontalScroll(rememberScrollState())
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(20.dp)
|
||||
) {
|
||||
TextButton(onClick = { viewModel.forgetSidebarSparkShown() }) {
|
||||
Text("Forget sidebar spark")
|
||||
Text(
|
||||
text = "Sparks",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.horizontalScroll(rememberScrollState())
|
||||
) {
|
||||
TextButton(onClick = { viewModel.forgetSidebarSparkShown() }) {
|
||||
Text("Forget sidebar spark")
|
||||
}
|
||||
ElevatedButton(onClick = { viewModel.forgetAllSparks() }) {
|
||||
Text("Forget all sparks")
|
||||
}
|
||||
}
|
||||
ElevatedButton(onClick = { viewModel.forgetAllSparks() }) {
|
||||
Text("Forget all sparks")
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "Changelogs",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.horizontalScroll(rememberScrollState())
|
||||
) {
|
||||
ElevatedButton(onClick = { viewModel.forgetLatestChangelog() }) {
|
||||
Text("Mark latest changelog as unread")
|
||||
Text(
|
||||
text = "Changelogs",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.horizontalScroll(rememberScrollState())
|
||||
) {
|
||||
ElevatedButton(onClick = { viewModel.forgetLatestChangelog() }) {
|
||||
Text("Mark latest changelog as unread")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
|
|
@ -12,20 +13,25 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
|
|
@ -33,7 +39,9 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
|
@ -46,7 +54,6 @@ import chat.revolt.api.routes.user.fetchUserProfile
|
|||
import chat.revolt.api.routes.user.patchSelf
|
||||
import chat.revolt.api.schemas.Profile
|
||||
import chat.revolt.components.generic.InlineMediaPicker
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.settings.RawUserOverview
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
|
|
@ -226,159 +233,90 @@ class ProfileSettingsScreenViewModel @Inject constructor(@ApplicationContext val
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ProfileSettingsScreen(
|
||||
navController: NavController,
|
||||
viewModel: ProfileSettingsScreenViewModel = hiltViewModel()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(id = R.string.settings_profile),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(
|
||||
if (viewModel.isLoading) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.verticalScroll(scrollState)
|
||||
}
|
||||
),
|
||||
verticalArrangement = if (viewModel.isLoading) {
|
||||
Arrangement.Center
|
||||
} else {
|
||||
Arrangement.Top
|
||||
},
|
||||
horizontalAlignment = if (viewModel.isLoading) {
|
||||
Alignment.CenterHorizontally
|
||||
} else {
|
||||
Alignment.Start
|
||||
}
|
||||
) {
|
||||
if (viewModel.isLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
)
|
||||
} else {
|
||||
RevoltAPI.userCache[RevoltAPI.selfId]?.let {
|
||||
RawUserOverview(
|
||||
it,
|
||||
viewModel.pendingProfile,
|
||||
viewModel.pfpModel?.toString(),
|
||||
viewModel.backgroundModel?.toString()
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = viewModel.uploadProgress > 0f) {
|
||||
LinearProgressIndicator(
|
||||
progress = viewModel.uploadProgress,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 0.dp)
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = viewModel.uploadError != null) {
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = viewModel.uploadError ?: "",
|
||||
style = MaterialTheme.typography.labelLarge.copy(
|
||||
color = MaterialTheme.colorScheme.error
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 0.dp)
|
||||
text = stringResource(R.string.settings_profile),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 0.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_profile_profile_picture),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Box(Modifier.padding(pv)) {
|
||||
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
InlineMediaPicker(
|
||||
currentModel = viewModel.pfpModel,
|
||||
circular = true,
|
||||
onPick = {
|
||||
viewModel.pfpModel = it.toString()
|
||||
viewModel.saveNewPfp()
|
||||
},
|
||||
canRemove = true,
|
||||
onRemove = {
|
||||
viewModel.removePfp()
|
||||
}
|
||||
val scrollState = rememberScrollState()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(
|
||||
if (viewModel.isLoading) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier.verticalScroll(scrollState)
|
||||
}
|
||||
),
|
||||
verticalArrangement = if (viewModel.isLoading) {
|
||||
Arrangement.Center
|
||||
} else {
|
||||
Arrangement.Top
|
||||
},
|
||||
horizontalAlignment = if (viewModel.isLoading) {
|
||||
Alignment.CenterHorizontally
|
||||
} else {
|
||||
Alignment.Start
|
||||
}
|
||||
) {
|
||||
if (viewModel.isLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
)
|
||||
} else {
|
||||
RevoltAPI.userCache[RevoltAPI.selfId]?.let {
|
||||
RawUserOverview(
|
||||
it,
|
||||
viewModel.pendingProfile,
|
||||
viewModel.pfpModel?.toString(),
|
||||
viewModel.backgroundModel?.toString()
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(20.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_profile_custom_background),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
InlineMediaPicker(
|
||||
currentModel = viewModel.backgroundModel,
|
||||
onPick = {
|
||||
viewModel.backgroundModel = it.toString()
|
||||
viewModel.saveNewBackground()
|
||||
},
|
||||
canRemove = true,
|
||||
onRemove = {
|
||||
viewModel.removeBackground()
|
||||
}
|
||||
AnimatedVisibility(visible = viewModel.uploadProgress > 0f) {
|
||||
LinearProgressIndicator(
|
||||
progress = viewModel.uploadProgress,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 0.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 20.dp, top = 0.dp, bottom = 20.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = viewModel.pendingProfile?.content ?: "",
|
||||
onValueChange = { value ->
|
||||
viewModel.pendingProfile?.let {
|
||||
viewModel.pendingProfile = it.copy(content = value)
|
||||
}
|
||||
},
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.user_info_sheet_category_bio),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
AnimatedVisibility(visible = viewModel.bioError != null) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
AnimatedVisibility(visible = viewModel.uploadError != null) {
|
||||
Text(
|
||||
text = viewModel.bioError ?: "",
|
||||
text = viewModel.uploadError ?: "",
|
||||
style = MaterialTheme.typography.labelLarge.copy(
|
||||
color = MaterialTheme.colorScheme.error
|
||||
),
|
||||
|
|
@ -387,26 +325,112 @@ fun ProfileSettingsScreen(
|
|||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.saveBio()
|
||||
},
|
||||
enabled = viewModel.pendingProfile?.content != viewModel.currentProfile?.content,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = null
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 0.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_profile_profile_picture),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
InlineMediaPicker(
|
||||
currentModel = viewModel.pfpModel,
|
||||
circular = true,
|
||||
onPick = {
|
||||
viewModel.pfpModel = it.toString()
|
||||
viewModel.saveNewPfp()
|
||||
},
|
||||
canRemove = true,
|
||||
onRemove = {
|
||||
viewModel.removePfp()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(20.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_profile_custom_background),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
InlineMediaPicker(
|
||||
currentModel = viewModel.backgroundModel,
|
||||
onPick = {
|
||||
viewModel.backgroundModel = it.toString()
|
||||
viewModel.saveNewBackground()
|
||||
},
|
||||
canRemove = true,
|
||||
onRemove = {
|
||||
viewModel.removeBackground()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 20.dp, top = 0.dp, bottom = 20.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = viewModel.pendingProfile?.content ?: "",
|
||||
onValueChange = { value ->
|
||||
viewModel.pendingProfile?.let {
|
||||
viewModel.pendingProfile = it.copy(content = value)
|
||||
}
|
||||
},
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.user_info_sheet_category_bio),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
AnimatedVisibility(visible = viewModel.bioError != null) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_profile_save),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
Text(
|
||||
text = viewModel.bioError ?: "",
|
||||
style = MaterialTheme.typography.labelLarge.copy(
|
||||
color = MaterialTheme.colorScheme.error
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 0.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.saveBio()
|
||||
},
|
||||
enabled = viewModel.pendingProfile?.content != viewModel.currentProfile?.content,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_profile_save),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.util.Log
|
|||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
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
|
||||
|
|
@ -11,16 +12,23 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -31,8 +39,10 @@ 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.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
|
@ -45,7 +55,6 @@ import chat.revolt.api.routes.auth.logoutAllSessions
|
|||
import chat.revolt.api.routes.auth.logoutSessionById
|
||||
import chat.revolt.api.schemas.Session
|
||||
import chat.revolt.components.generic.ListHeader
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.generic.UIMarkdown
|
||||
import chat.revolt.components.settings.sessions.SessionItem
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -84,7 +93,7 @@ class SessionSettingsScreenViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SessionSettingsScreen(
|
||||
navController: NavController,
|
||||
|
|
@ -126,125 +135,147 @@ fun SessionSettingsScreen(
|
|||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(id = R.string.settings_sessions),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
|
||||
if (viewModel.isLoading) {
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.settings_sessions),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Box(Modifier.padding(pv)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
.fillMaxSize()
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
LazyColumn {
|
||||
stickyHeader(key = "thisDevice") {
|
||||
ListHeader {
|
||||
Text(stringResource(id = R.string.settings_sessions_this_device))
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.currentSession?.let {
|
||||
item(key = it.id) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
SessionItem(
|
||||
session = it,
|
||||
currentSession = true,
|
||||
onLogout = {},
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
}
|
||||
} ?: run {
|
||||
item(key = "noCurrentSession") {
|
||||
UIMarkdown(
|
||||
text = stringResource(id = R.string.settings_sessions_this_device_unavailable),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
stickyHeader(key = "otherSessions") {
|
||||
ListHeader {
|
||||
Text(stringResource(id = R.string.settings_sessions_other_sessions))
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "logoutOtherSessions") {
|
||||
Row(
|
||||
if (viewModel.isLoading) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = MaterialTheme.shapes.medium)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp)
|
||||
)
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Column(
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(end = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.settings_sessions_log_out_other),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
.size(48.dp)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
LazyColumn {
|
||||
stickyHeader(key = "thisDevice") {
|
||||
ListHeader {
|
||||
Text(stringResource(id = R.string.settings_sessions_this_device))
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.settings_sessions_log_out_other_description
|
||||
),
|
||||
style = MaterialTheme.typography.bodySmall.copy(
|
||||
fontWeight = FontWeight.Normal
|
||||
viewModel.currentSession?.let {
|
||||
item(key = it.id) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
SessionItem(
|
||||
session = it,
|
||||
currentSession = true,
|
||||
onLogout = {},
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
}
|
||||
} ?: run {
|
||||
item(key = "noCurrentSession") {
|
||||
UIMarkdown(
|
||||
text = stringResource(id = R.string.settings_sessions_this_device_unavailable),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
stickyHeader(key = "otherSessions") {
|
||||
ListHeader {
|
||||
Text(stringResource(id = R.string.settings_sessions_other_sessions))
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "logoutOtherSessions") {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = MaterialTheme.shapes.medium)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp)
|
||||
)
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(end = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.settings_sessions_log_out_other),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.settings_sessions_log_out_other_description
|
||||
),
|
||||
style = MaterialTheme.typography.bodySmall.copy(
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FilledTonalButton(onClick = {
|
||||
viewModel.showLogoutOtherConfirmation = true
|
||||
}) {
|
||||
Text(stringResource(R.string.logout))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
items(viewModel.sessions.size) {
|
||||
val item = viewModel.sessions[it]
|
||||
|
||||
if (item.isCurrent()) {
|
||||
return@items
|
||||
}
|
||||
|
||||
SessionItem(
|
||||
session = item,
|
||||
onLogout = { session ->
|
||||
viewModel.logoutSession(session.id)
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
FilledTonalButton(onClick = {
|
||||
viewModel.showLogoutOtherConfirmation = true
|
||||
}) {
|
||||
Text(stringResource(R.string.logout))
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
items(viewModel.sessions.size) {
|
||||
val item = viewModel.sessions[it]
|
||||
|
||||
if (item.isCurrent()) {
|
||||
return@items
|
||||
}
|
||||
|
||||
SessionItem(
|
||||
session = item,
|
||||
onLogout = { session ->
|
||||
viewModel.logoutSession(session.id)
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,37 @@
|
|||
package chat.revolt.screens.settings
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowForward
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.DateRange
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
|
@ -35,7 +43,6 @@ import chat.revolt.api.settings.FeatureFlags
|
|||
import chat.revolt.api.settings.GlobalState
|
||||
import chat.revolt.api.settings.LabsAccessControlVariates
|
||||
import chat.revolt.components.generic.ListHeader
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.screens.settings.SelfUserOverview
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -55,152 +62,188 @@ class SettingsScreenViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
navController: NavController,
|
||||
viewModel: SettingsScreenViewModel = hiltViewModel()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
PageHeader(
|
||||
text = stringResource(id = R.string.settings),
|
||||
showBackButton = true,
|
||||
onBackButtonClicked = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
SelfUserOverview()
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.settings),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Box(Modifier.padding(pv)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(vertical = 10.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_category_account))
|
||||
}
|
||||
SelfUserOverview()
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_profile)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_card_account_details_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_profile")
|
||||
.clickable {
|
||||
navController.navigate("settings/profile")
|
||||
}
|
||||
)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = 10.dp)
|
||||
) {
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_category_account))
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_sessions)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_tablet_cellphone_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_sessions")
|
||||
.clickable {
|
||||
navController.navigate("settings/sessions")
|
||||
}
|
||||
)
|
||||
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_category_general))
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_palette_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_appearance")
|
||||
.clickable {
|
||||
navController.navigate("settings/appearance")
|
||||
}
|
||||
)
|
||||
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_category_miscellaneous))
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.about)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_about")
|
||||
.clickable {
|
||||
navController.navigate("about")
|
||||
}
|
||||
)
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = "Debug"
|
||||
text = stringResource(id = R.string.settings_profile)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
painter = painterResource(R.drawable.ic_card_account_details_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_debug")
|
||||
.testTag("settings_view_profile")
|
||||
.clickable {
|
||||
navController.navigate("settings/debug")
|
||||
navController.navigate("settings/profile")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (FeatureFlags.labsAccessControl is LabsAccessControlVariates.Restricted &&
|
||||
(FeatureFlags.labsAccessControl as LabsAccessControlVariates.Restricted).predicate()
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = "Labs"
|
||||
text = stringResource(id = R.string.settings_sessions)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_tablet_cellphone_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_sessions")
|
||||
.clickable {
|
||||
navController.navigate("settings/sessions")
|
||||
}
|
||||
)
|
||||
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_category_general))
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_appearance)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_palette_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_appearance")
|
||||
.clickable {
|
||||
navController.navigate("settings/appearance")
|
||||
}
|
||||
)
|
||||
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_category_miscellaneous))
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.about)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_about")
|
||||
.clickable {
|
||||
navController.navigate("about")
|
||||
}
|
||||
)
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = "Debug"
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_debug")
|
||||
.clickable {
|
||||
navController.navigate("settings/debug")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (FeatureFlags.labsAccessControl is LabsAccessControlVariates.Restricted &&
|
||||
(FeatureFlags.labsAccessControl as LabsAccessControlVariates.Restricted).predicate()
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = "Labs"
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowForward,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_labs")
|
||||
.clickable {
|
||||
navController.navigate("labs")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = "Closed Beta Updater"
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
|
|
@ -210,101 +253,87 @@ fun SettingsScreen(
|
|||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_labs")
|
||||
.testTag("settings_view_updater")
|
||||
.clickable {
|
||||
navController.navigate("labs")
|
||||
navController.navigate("settings/updater")
|
||||
}
|
||||
)
|
||||
|
||||
ListHeader {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.settings_category_last,
|
||||
BuildConfig.VERSION_NAME
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_changelogs)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.DateRange,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_changelogs")
|
||||
.clickable {
|
||||
navController.navigate("settings/changelogs")
|
||||
}
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_feedback)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowForward,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_feedback")
|
||||
.clickable {
|
||||
navController.navigate("settings/feedback")
|
||||
}
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.error) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.logout)
|
||||
)
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.error) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_logout")
|
||||
.clickable {
|
||||
viewModel.logout()
|
||||
navController.navigate("login/greeting") {
|
||||
popUpTo("chat") {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = "Closed Beta Updater"
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowForward,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_updater")
|
||||
.clickable {
|
||||
navController.navigate("settings/updater")
|
||||
}
|
||||
)
|
||||
|
||||
ListHeader {
|
||||
Text(stringResource(R.string.settings_category_last, BuildConfig.VERSION_NAME))
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_changelogs)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.DateRange,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_changelogs")
|
||||
.clickable {
|
||||
navController.navigate("settings/changelogs")
|
||||
}
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_feedback)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowForward,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_feedback")
|
||||
.clickable {
|
||||
navController.navigate("settings/feedback")
|
||||
}
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.error) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.logout)
|
||||
)
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.error) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_logout")
|
||||
.clickable {
|
||||
viewModel.logout()
|
||||
navController.navigate("login/greeting") {
|
||||
popUpTo("chat") {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.compose.material.icons.filled.ExitToApp
|
|||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -31,7 +32,6 @@ import chat.revolt.R
|
|||
import chat.revolt.activities.InviteActivity
|
||||
import chat.revolt.api.REVOLT_APP
|
||||
import chat.revolt.components.generic.FormTextField
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
|
||||
@Composable
|
||||
fun AddServerSheet() {
|
||||
|
|
@ -53,8 +53,9 @@ fun AddServerSheet() {
|
|||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
PageHeader(
|
||||
text = stringResource(id = R.string.add_server_sheet_title)
|
||||
Text(
|
||||
text = stringResource(id = R.string.add_server_sheet_title),
|
||||
style = MaterialTheme.typography.headlineMedium
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -23,7 +25,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import chat.revolt.R
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.generic.WebMarkdown
|
||||
import chat.revolt.internals.Changelog
|
||||
import chat.revolt.internals.Changelogs
|
||||
|
|
@ -67,8 +68,8 @@ fun ChangelogSheet(
|
|||
}
|
||||
|
||||
Column {
|
||||
PageHeader(
|
||||
if (new) {
|
||||
Text(
|
||||
text = if (new) {
|
||||
stringResource(R.string.settings_changelogs_new_header)
|
||||
} else {
|
||||
stringResource(
|
||||
|
|
@ -76,7 +77,8 @@ fun ChangelogSheet(
|
|||
viewModel.changelog?.version
|
||||
?: stringResource(R.string.settings_changelogs_historical_version_header_placeholder)
|
||||
)
|
||||
}
|
||||
},
|
||||
style = MaterialTheme.typography.headlineMedium
|
||||
)
|
||||
|
||||
if (viewModel.changelogContents == null) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.compose.material3.CircularProgressIndicator
|
|||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -42,7 +43,6 @@ import chat.revolt.api.schemas.Member
|
|||
import chat.revolt.api.schemas.User
|
||||
import chat.revolt.components.chat.MemberListItem
|
||||
import chat.revolt.components.generic.CountableListHeader
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.generic.Presence
|
||||
import chat.revolt.components.generic.presenceFromStatus
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -254,7 +254,10 @@ fun MemberListSheet(
|
|||
}
|
||||
|
||||
Column {
|
||||
PageHeader(text = stringResource(R.string.channel_info_sheet_options_members))
|
||||
Text(
|
||||
text = stringResource(R.string.channel_info_sheet_options_members),
|
||||
style = MaterialTheme.typography.headlineMedium
|
||||
)
|
||||
|
||||
LazyColumn {
|
||||
viewModel.fullItemList.forEachIndexed { index, item ->
|
||||
|
|
|
|||
Loading…
Reference in New Issue