feat: remove old changelogs
This commit is contained in:
parent
779275ce62
commit
7b763d0e9c
|
|
@ -111,7 +111,6 @@ import chat.stoat.screens.register.RegisterGreetingScreen
|
|||
import chat.stoat.screens.register.RegisterVerifyScreen
|
||||
import chat.stoat.screens.services.DiscoverScreen
|
||||
import chat.stoat.screens.settings.AppearanceSettingsScreen
|
||||
import chat.stoat.screens.settings.ChangelogsSettingsScreen
|
||||
import chat.stoat.screens.settings.ChatSettingsScreen
|
||||
import chat.stoat.screens.settings.DebugSettingsScreen
|
||||
import chat.stoat.screens.settings.ExperimentsSettingsScreen
|
||||
|
|
@ -711,7 +710,6 @@ fun AppEntrypoint(
|
|||
composable("settings/chat") { ChatSettingsScreen(navController) }
|
||||
composable("settings/debug") { DebugSettingsScreen(navController) }
|
||||
composable("settings/experiments") { ExperimentsSettingsScreen(navController) }
|
||||
composable("settings/changelogs") { ChangelogsSettingsScreen(navController) }
|
||||
composable("settings/language") { LanguagePickerSettingsScreen(navController) }
|
||||
|
||||
composable("settings/channel/{channelId}") { backStackEntry ->
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ import chat.stoat.api.internals.Members
|
|||
import chat.stoat.api.realtime.DisconnectionState
|
||||
import chat.stoat.api.realtime.RealtimeSocket
|
||||
import chat.stoat.api.routes.user.fetchSelf
|
||||
import chat.stoat.core.model.util.ChannelVoiceState
|
||||
import chat.stoat.core.model.schemas.Emoji
|
||||
import chat.stoat.core.model.schemas.Message
|
||||
import chat.stoat.core.model.schemas.Server
|
||||
import chat.stoat.api.unreads.Unreads
|
||||
import chat.stoat.core.model.schemas.AutumnResource
|
||||
import chat.stoat.core.model.schemas.ChannelType
|
||||
import chat.stoat.core.model.schemas.Emoji
|
||||
import chat.stoat.core.model.schemas.Message
|
||||
import chat.stoat.core.model.schemas.Server
|
||||
import chat.stoat.core.model.schemas.User
|
||||
import chat.stoat.core.model.util.ChannelVoiceState
|
||||
import chat.stoat.persistence.Database
|
||||
import chat.stoat.persistence.SqlStorage
|
||||
import com.chuckerteam.chucker.api.ChuckerCollector
|
||||
|
|
@ -68,7 +68,6 @@ const val STOAT_WEB_APP = "https://stoat.chat"
|
|||
const val STOAT_INVITES = "https://stt.gg"
|
||||
val STOAT_WEBSOCKET =
|
||||
if (USE_ALPHA_API) "wss://alpha.revolt.chat/ws" else "wss://events.stoat.chat"
|
||||
const val STOAT_KJBOOK = "https://stoatchat.github.io/for-android"
|
||||
|
||||
fun String.api(): String {
|
||||
return "$STOAT_BASE$this"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import chat.stoat.screens.chat.views.channel.ChannelScreenViewModel
|
|||
import chat.stoat.screens.login.LoginViewModel
|
||||
import chat.stoat.screens.login.MfaScreenViewModel
|
||||
import chat.stoat.screens.settings.AppearanceSettingsScreenViewModel
|
||||
import chat.stoat.screens.settings.ChangelogsSettingsScreenViewModel
|
||||
import chat.stoat.screens.settings.DebugSettingsScreenViewModel
|
||||
import chat.stoat.screens.settings.ProfileSettingsScreenViewModel
|
||||
import chat.stoat.screens.settings.SettingsScreenViewModel
|
||||
|
|
@ -29,7 +28,6 @@ val viewModelModule = module {
|
|||
viewModel { DebugSettingsScreenViewModel(get()) }
|
||||
viewModel { LoginViewModel(get()) }
|
||||
viewModel { ProfileSettingsScreenViewModel(androidApplication()) }
|
||||
viewModel { ChangelogsSettingsScreenViewModel(get(), androidApplication()) }
|
||||
viewModel { AppearanceSettingsScreenViewModel(androidApplication()) }
|
||||
viewModel { ChannelSettingsOverviewViewModel(androidApplication()) }
|
||||
}
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
package chat.stoat.fragments
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.webkit.WebViewAssetLoader
|
||||
import chat.stoat.R
|
||||
import chat.stoat.activities.InviteActivity
|
||||
import chat.stoat.api.STOAT_WEB_APP
|
||||
import chat.stoat.core.model.schemas.isInviteUri
|
||||
import chat.stoat.databinding.SheetChangelogBinding
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.google.android.material.color.MaterialColors
|
||||
|
||||
|
||||
class ChangelogBottomSheetFragment(
|
||||
val onDismiss: () -> Unit
|
||||
) : BottomSheetDialogFragment() {
|
||||
private lateinit var binding: SheetChangelogBinding
|
||||
|
||||
private fun argbAsCssColour(argb: Int): String {
|
||||
val alpha = (argb shr 24 and 0xff) / 255.0f
|
||||
val red = argb shr 16 and 0xff
|
||||
val green = argb shr 8 and 0xff
|
||||
val blue = argb and 0xff
|
||||
return String.format("#%02x%02x%02x%02x", red, green, blue, (alpha * 255).toInt())
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = SheetChangelogBinding.inflate(inflater, container, false)
|
||||
requireArguments().run {
|
||||
binding.tvTitle.apply {
|
||||
text = when {
|
||||
getBoolean(ARG_HISTORICAL) -> requireContext().getString(
|
||||
R.string.settings_changelogs_historical_version_header,
|
||||
getString(ARG_VERSION_NAME)
|
||||
)
|
||||
|
||||
else -> requireContext().getString(R.string.settings_changelogs_new_header)
|
||||
}
|
||||
typeface = ResourcesCompat.getFont(requireContext(), R.font.inter_display_semibold)
|
||||
}
|
||||
|
||||
binding.wvChangelog.apply {
|
||||
val assetLoader = WebViewAssetLoader.Builder()
|
||||
.setDomain(Uri.parse(STOAT_WEB_APP).host!!)
|
||||
.addPathHandler(
|
||||
"/_android_assets/",
|
||||
WebViewAssetLoader.AssetsPathHandler(context)
|
||||
)
|
||||
.addPathHandler(
|
||||
"/_android_res/",
|
||||
WebViewAssetLoader.ResourcesPathHandler(context)
|
||||
)
|
||||
.build()
|
||||
|
||||
webChromeClient = object : WebChromeClient() {}
|
||||
webViewClient = object : WebViewClient() {
|
||||
override fun shouldInterceptRequest(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): WebResourceResponse? {
|
||||
return request?.let { assetLoader.shouldInterceptRequest(it.url) }
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
webResourceRequest: WebResourceRequest
|
||||
): Boolean {
|
||||
// Capture clicks on invite links
|
||||
if (webResourceRequest.url.isInviteUri()) {
|
||||
val intent = Intent(
|
||||
context,
|
||||
InviteActivity::class.java
|
||||
).setAction(Intent.ACTION_VIEW)
|
||||
|
||||
intent.data = webResourceRequest.url
|
||||
context.startActivity(intent)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise, open the link in the browser using androidx.browser
|
||||
val customTab = CustomTabsIntent.Builder()
|
||||
.setShowTitle(true)
|
||||
.setDefaultColorSchemeParams(
|
||||
CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(
|
||||
MaterialColors.getColor(
|
||||
binding.wvChangelog,
|
||||
com.google.android.material.R.attr.backgroundColor
|
||||
)
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
customTab.launchUrl(context, webResourceRequest.url)
|
||||
|
||||
// Prevent the WebView from navigating to the URL
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
loadUrl(
|
||||
"$STOAT_WEB_APP/_android_assets/changelogs/renderer.html"
|
||||
)
|
||||
|
||||
settings.apply {
|
||||
javaScriptEnabled = true
|
||||
setSupportZoom(false)
|
||||
setSupportMultipleWindows(false)
|
||||
isVerticalScrollBarEnabled = false
|
||||
isHorizontalScrollBarEnabled = false
|
||||
cacheMode = WebSettings.LOAD_NO_CACHE
|
||||
}
|
||||
|
||||
addJavascriptInterface(
|
||||
object {
|
||||
@JavascriptInterface
|
||||
fun getMarkdown(): String {
|
||||
return getString(ARG_RENDERED_CONTENTS) ?: ""
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun getContentColour(): String {
|
||||
return argbAsCssColour(
|
||||
MaterialColors.getColor(
|
||||
binding.wvChangelog,
|
||||
com.google.android.material.R.attr.colorOnSurface
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun getPrimaryColour(): String {
|
||||
return argbAsCssColour(
|
||||
MaterialColors.getColor(
|
||||
binding.wvChangelog,
|
||||
androidx.appcompat.R.attr.colorPrimary
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
"Bridge"
|
||||
)
|
||||
|
||||
setBackgroundColor(android.graphics.Color.TRANSPARENT)
|
||||
}
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCancel(dialog: DialogInterface) {
|
||||
super.onCancel(dialog)
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "ChangelogBottomSheetFragment"
|
||||
|
||||
private const val ARG_VERSION_NAME = "version_name"
|
||||
private const val ARG_HISTORICAL = "historical"
|
||||
private const val ARG_RENDERED_CONTENTS = "rendered_contents"
|
||||
|
||||
fun createArguments(
|
||||
versionName: String,
|
||||
historical: Boolean,
|
||||
renderedContents: String,
|
||||
): Bundle {
|
||||
return Bundle().apply {
|
||||
putString(ARG_VERSION_NAME, versionName)
|
||||
putBoolean(ARG_HISTORICAL, historical)
|
||||
putString(ARG_RENDERED_CONTENTS, renderedContents)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
package chat.stoat.internals
|
||||
|
||||
import android.content.Context
|
||||
import chat.stoat.api.STOAT_KJBOOK
|
||||
import chat.stoat.api.StoatHttp
|
||||
import chat.stoat.api.StoatJson
|
||||
import chat.stoat.internals.IndexHolder.cachedIndex
|
||||
import chat.stoat.persistence.KVStorage
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ChangelogIndex(
|
||||
val changelogs: List<ChangelogData> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChangelogData(
|
||||
val version: ChangelogVersion,
|
||||
val date: ChangelogDate,
|
||||
val summary: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChangelogDate(
|
||||
val publish: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChangelogVersion(
|
||||
val code: Long,
|
||||
val name: String,
|
||||
val title: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Changelog(
|
||||
val id: String,
|
||||
val slug: String,
|
||||
val body: String,
|
||||
val collection: String,
|
||||
val data: ChangelogData,
|
||||
val rendered: String
|
||||
)
|
||||
|
||||
object IndexHolder {
|
||||
var cachedIndex: ChangelogIndex? = null
|
||||
}
|
||||
|
||||
class Changelogs(val context: Context, val kvStorage: KVStorage? = null) {
|
||||
suspend fun fetchChangelogIndex(): ChangelogIndex {
|
||||
if (cachedIndex != null) {
|
||||
return cachedIndex as ChangelogIndex
|
||||
}
|
||||
|
||||
try {
|
||||
val response = StoatHttp.get("$STOAT_KJBOOK/changelogs.json")
|
||||
cachedIndex =
|
||||
StoatJson.decodeFromString(ChangelogIndex.serializer(), response.bodyAsText())
|
||||
return cachedIndex as ChangelogIndex
|
||||
} catch (e: Error) {
|
||||
return ChangelogIndex()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun fetchChangelogByVersionCode(versionCode: Long): Changelog {
|
||||
try {
|
||||
val response = StoatHttp.get("$STOAT_KJBOOK/changelogs/$versionCode.json")
|
||||
return StoatJson.decodeFromString(Changelog.serializer(), response.bodyAsText())
|
||||
} catch (e: Error) {
|
||||
return Changelog(
|
||||
id = "",
|
||||
slug = "",
|
||||
body = e.localizedMessage ?: "",
|
||||
collection = "",
|
||||
data = ChangelogData(
|
||||
version = ChangelogVersion(
|
||||
code = 0L,
|
||||
name = "",
|
||||
title = e.localizedMessage ?: "",
|
||||
),
|
||||
date = ChangelogDate(
|
||||
publish = "1970-01-01T00:00:00.000Z"
|
||||
),
|
||||
summary = e.localizedMessage ?: ""
|
||||
),
|
||||
rendered = e.localizedMessage ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getLatestChangelog(): ChangelogData {
|
||||
return fetchChangelogIndex().changelogs.maxByOrNull { it.version.code }!!
|
||||
}
|
||||
|
||||
suspend fun getLatestChangelogCode(): String {
|
||||
return getLatestChangelog().version.code.toString()
|
||||
}
|
||||
|
||||
suspend fun hasSeenCurrent(): Boolean {
|
||||
if (kvStorage == null) {
|
||||
throw IllegalStateException(
|
||||
"Not supported for non-KVStorage instances of Changelogs"
|
||||
)
|
||||
}
|
||||
|
||||
val latest = getLatestChangelog().version.code
|
||||
val lastRead = kvStorage.get("latestChangelogRead")
|
||||
|
||||
if (lastRead == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the last read changelog is >= the latest, it has been read
|
||||
return lastRead.toLong() >= latest
|
||||
}
|
||||
|
||||
suspend fun markAsSeen() {
|
||||
if (kvStorage == null) {
|
||||
throw IllegalStateException(
|
||||
"Not supported for non-KVStorage instances of Changelogs"
|
||||
)
|
||||
}
|
||||
|
||||
val index = fetchChangelogIndex()
|
||||
val latest = index.changelogs.maxByOrNull { it.version.code }!!.version.code.toString()
|
||||
kvStorage.set("latestChangelogRead", latest)
|
||||
}
|
||||
}
|
||||
|
|
@ -76,7 +76,6 @@ import chat.stoat.callbacks.ActionChannel
|
|||
import chat.stoat.composables.chat.DisconnectedNotice
|
||||
import chat.stoat.composables.screens.chat.drawer.ChannelSideDrawer
|
||||
import chat.stoat.dialogs.NotificationRationaleDialog
|
||||
import chat.stoat.internals.Changelogs
|
||||
import chat.stoat.internals.extensions.zero
|
||||
import chat.stoat.persistence.KVStorage
|
||||
import chat.stoat.screens.chat.dialogs.safety.ReportMessageDialog
|
||||
|
|
@ -87,7 +86,6 @@ import chat.stoat.screens.chat.views.NoCurrentChannelScreen
|
|||
import chat.stoat.screens.chat.views.OverviewScreen
|
||||
import chat.stoat.screens.chat.views.channel.ChannelScreen
|
||||
import chat.stoat.sheets.AddServerSheet
|
||||
import chat.stoat.sheets.ChangelogSheet
|
||||
import chat.stoat.sheets.EarlyAccessSheet
|
||||
import chat.stoat.sheets.EmoteInfoSheet
|
||||
import chat.stoat.sheets.LinkInfoSheet
|
||||
|
|
@ -153,25 +151,11 @@ class ChatRouterViewModel(
|
|||
var showEarlyAccessSpark by mutableStateOf(false)
|
||||
var showSwipeToReplySpark by mutableStateOf(false)
|
||||
|
||||
private val changelogs = Changelogs(context, kvStorage)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val current = kvStorage.get("currentDestination")
|
||||
setSaveDestination(ChatRouterDestination.fromString(current ?: ""))
|
||||
|
||||
try {
|
||||
latestChangelogRead = changelogs.hasSeenCurrent()
|
||||
latestChangelog = changelogs.getLatestChangelogCode()
|
||||
latestChangelogBody =
|
||||
changelogs.fetchChangelogByVersionCode(latestChangelog.toLong()).rendered
|
||||
if (!latestChangelogRead) {
|
||||
changelogs.markAsSeen()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
val seenEarlyAccess = kvStorage.getBoolean("spark/earlyAccess/dismissed")
|
||||
val seenSwipeToReply = kvStorage.getBoolean("spark/swipeToReply/dismissed")
|
||||
if (seenEarlyAccess == null) {
|
||||
|
|
@ -473,17 +457,6 @@ fun ChatRouterScreen(
|
|||
}
|
||||
}
|
||||
|
||||
if (!viewModel.latestChangelogRead) {
|
||||
ChangelogSheet(
|
||||
versionName = viewModel.latestChangelog,
|
||||
versionIsHistorical = false,
|
||||
renderedContents = viewModel.latestChangelogBody,
|
||||
onDismiss = {
|
||||
viewModel.latestChangelogRead = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (showPlatformModDMHint) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import androidx.compose.ui.geometry.Offset
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -61,10 +62,10 @@ import androidx.navigation.NavController
|
|||
import chat.stoat.R
|
||||
import chat.stoat.api.StoatAPI
|
||||
import chat.stoat.api.routes.user.fetchSelf
|
||||
import chat.stoat.core.model.schemas.User
|
||||
import chat.stoat.composables.generic.NonIdealState
|
||||
import chat.stoat.composables.screens.settings.UserOverview
|
||||
import chat.stoat.composables.skeletons.UserOverviewSkeleton
|
||||
import chat.stoat.core.model.schemas.User
|
||||
import chat.stoat.internals.extensions.zero
|
||||
import chat.stoat.screens.chat.LocalIsConnected
|
||||
import chat.stoat.sheets.UserCardSheet
|
||||
|
|
@ -79,6 +80,7 @@ fun OverviewScreen(
|
|||
includePadding: Boolean = true
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val resources = LocalResources.current
|
||||
|
||||
var isLoading by rememberSaveable { mutableStateOf(true) }
|
||||
var user by rememberSaveable { mutableStateOf<User?>(null) }
|
||||
|
|
@ -329,7 +331,8 @@ fun OverviewScreen(
|
|||
item(key = "changelog") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
navController.navigate("settings/changelogs")
|
||||
// navController.navigate("settings/changelogs")
|
||||
// TODO replace with new changelog
|
||||
},
|
||||
backgroundColour = MaterialTheme.colorScheme.errorContainer,
|
||||
foregroundColour = MaterialTheme.colorScheme.onErrorContainer,
|
||||
|
|
@ -349,12 +352,13 @@ fun OverviewScreen(
|
|||
body = { Text(stringResource(R.string.overview_screen_changelog_description)) }
|
||||
)
|
||||
}
|
||||
|
||||
item(key = "feedback") {
|
||||
OverviewScreenLink(
|
||||
onClick = {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.comingsoon_toast),
|
||||
resources.getString(R.string.comingsoon_toast),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
// navController.navigate("feedback")
|
||||
|
|
|
|||
|
|
@ -1,207 +0,0 @@
|
|||
package chat.stoat.screens.settings
|
||||
|
||||
import android.app.Application
|
||||
import android.text.format.DateUtils
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
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.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavController
|
||||
import chat.stoat.R
|
||||
import chat.stoat.internals.ChangelogIndex
|
||||
import chat.stoat.internals.Changelogs
|
||||
import chat.stoat.persistence.KVStorage
|
||||
import chat.stoat.sheets.ChangelogSheet
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.Instant
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
class ChangelogsSettingsScreenViewModel(
|
||||
val kvStorage: KVStorage,
|
||||
val context: Application
|
||||
) : ViewModel() {
|
||||
var index by mutableStateOf<ChangelogIndex?>(null)
|
||||
var renderedChangelog by mutableStateOf("")
|
||||
|
||||
suspend fun requestChangelog(version: String) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
renderedChangelog = Changelogs(
|
||||
context,
|
||||
kvStorage
|
||||
).fetchChangelogByVersionCode(version.toLong()).rendered
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun populate() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
index = Changelogs(context, kvStorage).fetchChangelogIndex()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ChangelogsSettingsScreen(
|
||||
navController: NavController,
|
||||
viewModel: ChangelogsSettingsScreenViewModel = koinViewModel()
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.populate()
|
||||
}
|
||||
|
||||
var currentChangelog by remember { mutableStateOf("") }
|
||||
var sheetOpen by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(currentChangelog) {
|
||||
if (currentChangelog.isNotEmpty())
|
||||
viewModel.requestChangelog(currentChangelog)
|
||||
}
|
||||
|
||||
if (sheetOpen) {
|
||||
val changelog =
|
||||
viewModel.index?.changelogs?.firstOrNull { it.version.code.toString() == currentChangelog }
|
||||
?: return
|
||||
|
||||
ChangelogSheet(
|
||||
versionName = changelog.version.name,
|
||||
versionIsHistorical = true,
|
||||
renderedContents = viewModel.renderedChangelog,
|
||||
onDismiss = {
|
||||
sheetOpen = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.settings_changelogs),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_arrow_back_24dp),
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { pv ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(pv)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Crossfade(targetState = viewModel.index, label = "index has items") { index ->
|
||||
if (index == null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(200.dp)
|
||||
) {
|
||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||
}
|
||||
} else {
|
||||
LazyColumn {
|
||||
items(
|
||||
viewModel.index?.changelogs?.size ?: 0,
|
||||
key = { index ->
|
||||
viewModel.index?.changelogs?.get(index)?.version?.name ?: ""
|
||||
}
|
||||
) { index ->
|
||||
val changelog = viewModel.index?.changelogs?.get(index) ?: return@items
|
||||
val relativeTimeString = DateUtils.getRelativeTimeSpanString(
|
||||
Instant.parse(changelog.date.publish).toEpochMilliseconds(),
|
||||
System.currentTimeMillis(),
|
||||
DateUtils.DAY_IN_MILLIS,
|
||||
DateUtils.FORMAT_ABBREV_ALL
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
currentChangelog = changelog.version.code.toString()
|
||||
sheetOpen = true
|
||||
}
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = changelog.version.title,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = changelog.summary,
|
||||
)
|
||||
Text(
|
||||
text = "${changelog.version.name} · $relativeTimeString",
|
||||
modifier = Modifier.alpha(0.7f),
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -315,27 +315,6 @@ fun SettingsScreen(
|
|||
)
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.settings_changelogs)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
SettingsIcon {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_wand_shine_24dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("settings_view_changelogs")
|
||||
.clickable {
|
||||
navController.navigate("settings/changelogs")
|
||||
}
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
|
|
@ -363,7 +342,7 @@ fun SettingsScreen(
|
|||
InviteActivity::class.java
|
||||
).setAction(Intent.ACTION_VIEW)
|
||||
|
||||
intent.data = "https://rvlt.gg/Testers".toUri()
|
||||
intent.data = "https://stt.gg/Testers".toUri()
|
||||
context.startActivity(intent)
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
package chat.stoat.sheets
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import chat.stoat.api.internals.getFragmentActivity
|
||||
import chat.stoat.fragments.ChangelogBottomSheetFragment
|
||||
|
||||
@Composable
|
||||
fun ChangelogSheet(
|
||||
versionName: String,
|
||||
versionIsHistorical: Boolean,
|
||||
renderedContents: String,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val activity = LocalContext.current.getFragmentActivity()
|
||||
|
||||
var lastRenderedContents by remember { mutableStateOf("") }
|
||||
|
||||
DisposableEffect(versionName, renderedContents) {
|
||||
if (lastRenderedContents == renderedContents) return@DisposableEffect onDispose {}
|
||||
if (renderedContents.isEmpty()) return@DisposableEffect onDispose {}
|
||||
|
||||
lastRenderedContents = renderedContents
|
||||
|
||||
val sheet = ChangelogBottomSheetFragment(onDismiss)
|
||||
sheet.arguments =
|
||||
ChangelogBottomSheetFragment.createArguments(
|
||||
versionName,
|
||||
versionIsHistorical,
|
||||
renderedContents,
|
||||
)
|
||||
|
||||
activity?.supportFragmentManager?.let {
|
||||
sheet.show(it, ChangelogBottomSheetFragment.TAG)
|
||||
}
|
||||
|
||||
onDispose {
|
||||
try {
|
||||
sheet.dismissAllowingStateLoss()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,5 +7,4 @@ const val STOAT_FILES = "https://cdn.stoatusercontent.com"
|
|||
const val STOAT_PROXY = "https://proxy.stoatusercontent.com"
|
||||
const val STOAT_WEB_APP = "https://stoat.chat"
|
||||
const val STOAT_INVITES = "https://stt.gg"
|
||||
const val STOAT_WEBSOCKET = "wss://events.stoat.chat"
|
||||
const val STOAT_KJBOOK = "https://stoatchat.github.io/for-android"
|
||||
const val STOAT_WEBSOCKET = "wss://events.stoat.chat"
|
||||
Loading…
Reference in New Issue