feat: revamp about screen
Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
parent
98aa10c39e
commit
5981f064b5
|
|
@ -180,6 +180,7 @@ dependencies {
|
|||
implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha10"
|
||||
implementation 'com.github.MikeOrtiz:TouchImageView:3.3'
|
||||
implementation "androidx.appcompat:appcompat:1.7.0-alpha02"
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
|
||||
// hCaptcha - captcha provider
|
||||
implementation "com.github.hcaptcha:hcaptcha-android-sdk:3.8.1"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
package chat.revolt
|
||||
|
||||
import android.app.Application
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class RevoltApplication : Application() {
|
||||
init {
|
||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ import io.ktor.serialization.kotlinx.json.json
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -108,7 +109,7 @@ object RevoltAPI {
|
|||
var sessionToken: String = ""
|
||||
private set
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
|
||||
val realtimeContext = newSingleThreadContext("RealtimeContext")
|
||||
val wsFrameChannel = Channel<Any>(Channel.UNLIMITED)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,32 +2,45 @@ package chat.revolt.screens.about
|
|||
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.ClipboardManager
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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.compose.ui.viewinterop.AndroidView
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
|
@ -38,23 +51,24 @@ 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.api.settings.GlobalState
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.ui.theme.Theme
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.encodeToString
|
||||
import java.net.URI
|
||||
|
||||
class AboutViewModel(
|
||||
) : ViewModel() {
|
||||
private val _root = mutableStateOf<Root?>(null)
|
||||
val root: State<Root?>
|
||||
get() = _root
|
||||
var root by mutableStateOf<Root?>(null)
|
||||
var selectedTabIndex by mutableStateOf(0)
|
||||
|
||||
fun getDebugInformation(): Map<String, String> {
|
||||
return mapOf(
|
||||
"App ID" to BuildConfig.APPLICATION_ID,
|
||||
"App Version" to BuildConfig.VERSION_NAME,
|
||||
"API Host" to URI(REVOLT_BASE).host,
|
||||
"API Version" to (root.value?.revolt ?: "Unknown"),
|
||||
"API Version" to (root?.revolt ?: "Unknown"),
|
||||
"Runtime SDK" to Build.VERSION.SDK_INT.toString(),
|
||||
"Model" to "${Build.MANUFACTURER} ${
|
||||
Build.DEVICE.replaceFirstChar {
|
||||
|
|
@ -66,7 +80,7 @@ class AboutViewModel(
|
|||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
_root.value = getRootRoute().copy()
|
||||
root = getRootRoute().copy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -149,6 +163,63 @@ fun AboutScreen(
|
|||
showBackButton = true,
|
||||
onBackButtonClicked = { navController.popBackStack() })
|
||||
|
||||
// TODO this should be a reusable "tabs" component
|
||||
when (GlobalState.theme) {
|
||||
Theme.M3Dynamic -> AndroidView(
|
||||
factory = {
|
||||
com.google.android.material.tabs.TabLayout(it).apply {
|
||||
tabMode = com.google.android.material.tabs.TabLayout.MODE_FIXED
|
||||
tabGravity = com.google.android.material.tabs.TabLayout.GRAVITY_FILL
|
||||
|
||||
addTab(newTab().setText(it.getString(R.string.about_tab_version)))
|
||||
addTab(newTab().setText(it.getString(R.string.about_tab_details)))
|
||||
|
||||
addOnTabSelectedListener(object :
|
||||
com.google.android.material.tabs.TabLayout.OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: com.google.android.material.tabs.TabLayout.Tab?) {
|
||||
viewModel.selectedTabIndex = tab?.position ?: 0
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: com.google.android.material.tabs.TabLayout.Tab?) {
|
||||
}
|
||||
|
||||
override fun onTabReselected(tab: com.google.android.material.tabs.TabLayout.Tab?) {
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
update = {
|
||||
it.getTabAt(viewModel.selectedTabIndex)?.select()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
else -> TabRow(selectedTabIndex = viewModel.selectedTabIndex) {
|
||||
Tab(
|
||||
selected = viewModel.selectedTabIndex == 0,
|
||||
onClick = { viewModel.selectedTabIndex = 0 },
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.about_tab_version),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
)
|
||||
Tab(
|
||||
selected = viewModel.selectedTabIndex == 1,
|
||||
onClick = { viewModel.selectedTabIndex = 1 },
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.about_tab_details),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -157,22 +228,58 @@ fun AboutScreen(
|
|||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
|
||||
if (viewModel.root.value == null) {
|
||||
Text(
|
||||
text = stringResource(R.string.loading),
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
if (viewModel.root == null) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
)
|
||||
} else {
|
||||
DebugInfo(viewModel)
|
||||
TextButton(onClick = ::copyDebugInformation) {
|
||||
Text(text = stringResource(id = R.string.copy))
|
||||
when (viewModel.selectedTabIndex) {
|
||||
0 -> {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.revolt_logo_wide),
|
||||
contentDescription = stringResource(R.string.about_full_name),
|
||||
colorFilter = ColorFilter.tint(LocalContentColor.current)
|
||||
)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Revolt" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<style name="Theme.Revolt" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowSplashScreenBackground">@color/background</item>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,10 @@
|
|||
<string name="mfa_password_lead">Enter your password to continue.</string>
|
||||
|
||||
<string name="about">About</string>
|
||||
<string name="app_full_name">Revolt on Android</string>
|
||||
<string name="about_full_name">Revolt on Android</string>
|
||||
<string name="about_brought_to_you_by">Brought to you with ❤ by the Revolt team.</string>
|
||||
<string name="about_tab_version">Version</string>
|
||||
<string name="about_tab_details">Details</string>
|
||||
|
||||
<string name="oss_attribution">OSS Attribution</string>
|
||||
<string name="oss_attribution_body">Revolt is built with the help of these awesome open-source projects.</string>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Revolt" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<style name="Theme.Revolt" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue