parent
48e20d5464
commit
a302c2d13c
|
|
@ -43,10 +43,13 @@ import chat.revolt.api.HitRateLimitException
|
||||||
import chat.revolt.api.RevoltAPI
|
import chat.revolt.api.RevoltAPI
|
||||||
import chat.revolt.api.RevoltHttp
|
import chat.revolt.api.RevoltHttp
|
||||||
import chat.revolt.api.api
|
import chat.revolt.api.api
|
||||||
|
import chat.revolt.api.routes.microservices.health.healthCheck
|
||||||
import chat.revolt.api.routes.onboard.needsOnboarding
|
import chat.revolt.api.routes.onboard.needsOnboarding
|
||||||
|
import chat.revolt.api.schemas.HealthNotice
|
||||||
import chat.revolt.api.settings.Experiments
|
import chat.revolt.api.settings.Experiments
|
||||||
import chat.revolt.api.settings.LoadedSettings
|
import chat.revolt.api.settings.LoadedSettings
|
||||||
import chat.revolt.api.settings.SyncedSettings
|
import chat.revolt.api.settings.SyncedSettings
|
||||||
|
import chat.revolt.components.generic.HealthAlert
|
||||||
import chat.revolt.ndk.NativeLibraries
|
import chat.revolt.ndk.NativeLibraries
|
||||||
import chat.revolt.persistence.KVStorage
|
import chat.revolt.persistence.KVStorage
|
||||||
import chat.revolt.screens.DefaultDestinationScreen
|
import chat.revolt.screens.DefaultDestinationScreen
|
||||||
|
|
@ -132,6 +135,8 @@ class MainActivityViewModel @Inject constructor(
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
Log.d("MainActivity", "Hydrating Experiments from KV")
|
Log.d("MainActivity", "Hydrating Experiments from KV")
|
||||||
Experiments.hydrateWithKv()
|
Experiments.hydrateWithKv()
|
||||||
|
Log.d("MainActivity", "Performing health check")
|
||||||
|
doHealthCheck()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,6 +224,30 @@ class MainActivityViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val activeAlert = MutableStateFlow<HealthNotice?>(null)
|
||||||
|
val isAlertActive = MutableStateFlow(false)
|
||||||
|
|
||||||
|
private fun doHealthCheck() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val health = healthCheck()
|
||||||
|
if (health.alert != null) {
|
||||||
|
activeAlert.emit(health)
|
||||||
|
isAlertActive.emit(true)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("MainActivity", "Failed to perform health check", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDismissHealthAlert() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
activeAlert.emit(null)
|
||||||
|
isAlertActive.emit(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Log.d("MainActivity", "Starting up")
|
Log.d("MainActivity", "Starting up")
|
||||||
doPreStartupTasks()
|
doPreStartupTasks()
|
||||||
|
|
@ -273,6 +302,9 @@ class MainActivity : FragmentActivity() {
|
||||||
windowSizeClass,
|
windowSizeClass,
|
||||||
viewModel.nextDestination.collectAsState().value,
|
viewModel.nextDestination.collectAsState().value,
|
||||||
viewModel.isConnected.collectAsState().value,
|
viewModel.isConnected.collectAsState().value,
|
||||||
|
viewModel.activeAlert.collectAsState().value,
|
||||||
|
viewModel.isAlertActive.collectAsState().value,
|
||||||
|
viewModel::onDismissHealthAlert,
|
||||||
viewModel::checkLoggedInState,
|
viewModel::checkLoggedInState,
|
||||||
viewModel::updateNextDestination
|
viewModel::updateNextDestination
|
||||||
)
|
)
|
||||||
|
|
@ -299,6 +331,9 @@ fun AppEntrypoint(
|
||||||
windowSizeClass: WindowSizeClass,
|
windowSizeClass: WindowSizeClass,
|
||||||
nextDestination: String?,
|
nextDestination: String?,
|
||||||
isConnected: Boolean,
|
isConnected: Boolean,
|
||||||
|
healthNotice: HealthNotice?,
|
||||||
|
isHealthAlertActive: Boolean,
|
||||||
|
onDismissHealthAlert: () -> Unit = {},
|
||||||
onRetryConnection: () -> Unit,
|
onRetryConnection: () -> Unit,
|
||||||
onUpdateNextDestination: (String) -> Unit = {}
|
onUpdateNextDestination: (String) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
|
@ -313,6 +348,12 @@ fun AppEntrypoint(
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
color = MaterialTheme.colorScheme.background
|
color = MaterialTheme.colorScheme.background
|
||||||
) {
|
) {
|
||||||
|
if (isHealthAlertActive) {
|
||||||
|
healthNotice?.let {
|
||||||
|
HealthAlert(notice = healthNotice, onDismiss = onDismissHealthAlert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = "default",
|
startDestination = "default",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package chat.revolt.api.routes.microservices.health
|
||||||
|
|
||||||
|
import chat.revolt.api.RevoltHttp
|
||||||
|
import chat.revolt.api.RevoltJson
|
||||||
|
import chat.revolt.api.schemas.HealthNotice
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
|
||||||
|
suspend fun healthCheck(): HealthNotice {
|
||||||
|
val response = RevoltHttp.get("https://health.revolt.chat/api/health").bodyAsText()
|
||||||
|
return RevoltJson.decodeFromString(HealthNotice.serializer(), response)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package chat.revolt.api.schemas
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class HealthNotice(
|
||||||
|
val version: String? = null,
|
||||||
|
val alert: Alert? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Alert(
|
||||||
|
val text: String? = null,
|
||||||
|
@SerialName("dismissable")
|
||||||
|
val dismissible: Boolean? = null,
|
||||||
|
val actions: List<Action>? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Action(
|
||||||
|
val text: String? = null,
|
||||||
|
val type: String? = null,
|
||||||
|
val href: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
package chat.revolt.components.generic
|
||||||
|
|
||||||
|
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||||
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import chat.revolt.R
|
||||||
|
import chat.revolt.api.schemas.HealthNotice
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun HealthAlert(notice: HealthNotice, onDismiss: () -> Unit) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val backgroundColour = MaterialTheme.colorScheme.background
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {
|
||||||
|
// Purposefully empty
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(stringResource(R.string.service_health_alert))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(notice.alert?.text ?: stringResource(R.string.service_health_alert_body_default))
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
notice.alert?.actions?.firstOrNull()?.let { action ->
|
||||||
|
when (action.type) {
|
||||||
|
"external" -> TextButton(
|
||||||
|
onClick = {
|
||||||
|
val customTab = CustomTabsIntent.Builder()
|
||||||
|
.setShowTitle(true)
|
||||||
|
.setDefaultColorSchemeParams(
|
||||||
|
CustomTabColorSchemeParams.Builder()
|
||||||
|
.setToolbarColor(backgroundColour.toArgb())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
customTab.launchUrl(context, action.href?.toUri() ?: return@TextButton)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
action.text
|
||||||
|
?: stringResource(R.string.service_health_alert_actions_default)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text(stringResource(R.string.service_health_alert_actions_dismiss))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,11 @@
|
||||||
<string name="resend">Resend</string>
|
<string name="resend">Resend</string>
|
||||||
<string name="send_email">Send email</string>
|
<string name="send_email">Send email</string>
|
||||||
|
|
||||||
|
<string name="service_health_alert">Currently ongoing</string>
|
||||||
|
<string name="service_health_alert_body_default">We are currently experiencing issues with our services. Please check our status page for more information.</string>
|
||||||
|
<string name="service_health_alert_actions_default">Learn more</string>
|
||||||
|
<string name="service_health_alert_actions_dismiss">Dismiss</string>
|
||||||
|
|
||||||
<string name="login_heading">Let\'s log you in</string>
|
<string name="login_heading">Let\'s log you in</string>
|
||||||
<string name="password_forgot">Forgot password?</string>
|
<string name="password_forgot">Forgot password?</string>
|
||||||
<string name="resend_verification">Resend a verification email</string>
|
<string name="resend_verification">Resend a verification email</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue