parent
584838b98d
commit
c4aef08a63
|
|
@ -338,6 +338,10 @@ dependencies {
|
|||
// Shimmer - loading animations
|
||||
implementation("com.valentinilk.shimmer:compose-shimmer:1.3.1")
|
||||
|
||||
// Chucker - HTTP inspector
|
||||
debugImplementation("com.github.chuckerteam.chucker:library:4.0.0")
|
||||
releaseImplementation("com.github.chuckerteam.chucker:library-no-op:4.0.0")
|
||||
|
||||
// Testing
|
||||
androidTestImplementation("androidx.test:runner:$androidXTestVersion")
|
||||
androidTestImplementation("androidx.test:rules:$androidXTestVersion")
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.os.Looper
|
|||
import android.util.Log
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import chat.revolt.BuildConfig
|
||||
import chat.revolt.RevoltApplication
|
||||
import chat.revolt.api.internals.Members
|
||||
import chat.revolt.api.realtime.DisconnectionState
|
||||
import chat.revolt.api.realtime.RealtimeSocket
|
||||
|
|
@ -18,6 +19,9 @@ import chat.revolt.api.schemas.User
|
|||
import chat.revolt.api.unreads.Unreads
|
||||
import chat.revolt.persistence.Database
|
||||
import chat.revolt.persistence.SqlStorage
|
||||
import com.chuckerteam.chucker.api.ChuckerCollector
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import com.chuckerteam.chucker.api.RetentionManager
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.okhttp.OkHttp
|
||||
import io.ktor.client.plugins.DefaultRequest
|
||||
|
|
@ -103,6 +107,20 @@ val RevoltHttp = HttpClient(OkHttp) {
|
|||
|
||||
install(Logging) { level = LogLevel.INFO }
|
||||
|
||||
val chuckerCollector = ChuckerCollector(
|
||||
context = RevoltApplication.instance,
|
||||
showNotification = true,
|
||||
retentionPeriod = RetentionManager.Period.ONE_DAY
|
||||
)
|
||||
|
||||
val chuckerInterceptor = ChuckerInterceptor.Builder(RevoltApplication.instance)
|
||||
.collector(chuckerCollector)
|
||||
.maxContentLength(250_000L)
|
||||
.redactHeaders(RevoltAPI.TOKEN_HEADER_NAME)
|
||||
.alwaysReadResponseBody(true)
|
||||
.createShortcut(false)
|
||||
.build()
|
||||
|
||||
engine {
|
||||
addInterceptor { chain ->
|
||||
val request = chain.request().newBuilder()
|
||||
|
|
@ -114,6 +132,7 @@ val RevoltHttp = HttpClient(OkHttp) {
|
|||
.build()
|
||||
chain.proceed(request)
|
||||
}
|
||||
addInterceptor(chuckerInterceptor)
|
||||
}
|
||||
|
||||
defaultRequest {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.aspectRatio
|
|||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -14,7 +15,11 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.revolt.BuildConfig
|
||||
import chat.revolt.R
|
||||
|
||||
@Composable
|
||||
|
|
@ -42,6 +47,18 @@ fun NotificationRationaleDialog(
|
|||
Text(
|
||||
text = stringResource(id = R.string.spark_notifications_rationale_description)
|
||||
)
|
||||
if (BuildConfig.DEBUG) {
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
|
||||
append("Debug build:")
|
||||
pop()
|
||||
append(" Required to open Chucker for network debugging. ")
|
||||
append("You can show this dialogue again from Settings -> Debug.")
|
||||
},
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@ package chat.revolt.screens.chat
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -46,20 +50,24 @@ import androidx.compose.ui.platform.LocalView
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.BuildConfig
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.internals.DirectMessages
|
||||
import chat.revolt.api.realtime.DisconnectionState
|
||||
import chat.revolt.api.realtime.RealtimeSocket
|
||||
import chat.revolt.api.routes.push.subscribePush
|
||||
import chat.revolt.callbacks.Action
|
||||
import chat.revolt.callbacks.ActionChannel
|
||||
import chat.revolt.components.chat.DisconnectedNotice
|
||||
import chat.revolt.components.screens.chat.drawer.ChannelSideDrawer
|
||||
import chat.revolt.components.screens.voice.VoiceChannelOverlay
|
||||
import chat.revolt.dialogs.NotificationRationaleDialog
|
||||
import chat.revolt.internals.Changelogs
|
||||
import chat.revolt.internals.extensions.zero
|
||||
import chat.revolt.persistence.KVStorage
|
||||
|
|
@ -84,8 +92,11 @@ import com.airbnb.lottie.compose.LottieAnimation
|
|||
import com.airbnb.lottie.compose.LottieCompositionSpec
|
||||
import com.airbnb.lottie.compose.animateLottieCompositionAsState
|
||||
import com.airbnb.lottie.compose.rememberLottieComposition
|
||||
import com.google.android.gms.tasks.OnCompleteListener
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.sentry.Sentry
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
|
@ -137,6 +148,7 @@ class ChatRouterViewModel @Inject constructor(
|
|||
var latestChangelogRead by mutableStateOf(true)
|
||||
var latestChangelog by mutableStateOf("")
|
||||
var latestChangelogBody by mutableStateOf("")
|
||||
var showNotificationRationale by mutableStateOf(false)
|
||||
|
||||
private val changelogs = Changelogs(context, kvStorage)
|
||||
|
||||
|
|
@ -158,6 +170,13 @@ class ChatRouterViewModel @Inject constructor(
|
|||
if (!latestChangelogRead) {
|
||||
changelogs.markAsSeen()
|
||||
}
|
||||
|
||||
val hasNotificationPermission =
|
||||
NotificationManagerCompat.from(context).areNotificationsEnabled()
|
||||
// right now we only show this in debug builds so Chucker can show its notification
|
||||
if (!hasNotificationPermission && BuildConfig.DEBUG) {
|
||||
showNotificationRationale = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +200,32 @@ class ChatRouterViewModel @Inject constructor(
|
|||
sidebarSparkDisplayed = true
|
||||
}
|
||||
|
||||
fun setRegisterForNotifications() {
|
||||
showNotificationRationale = false
|
||||
FirebaseMessaging.getInstance().token.addOnCompleteListener(
|
||||
OnCompleteListener { task ->
|
||||
if (!task.isSuccessful) {
|
||||
Log.w("FCM", "Fetching FCM registration token failed", task.exception)
|
||||
task.exception?.let { Sentry.captureException(it) }
|
||||
return@OnCompleteListener
|
||||
}
|
||||
|
||||
val token = task.result
|
||||
viewModelScope.launch {
|
||||
kvStorage.set("fcmToken", token)
|
||||
subscribePush(auth = token)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun markNotificationsRejected() {
|
||||
showNotificationRationale = false
|
||||
viewModelScope.launch {
|
||||
kvStorage.set("pushNotificationsRejected", true)
|
||||
}
|
||||
}
|
||||
|
||||
fun navigateToServer(serverId: String) {
|
||||
viewModelScope.launch {
|
||||
val savedLastChannel = kvStorage.get("lastChannel/$serverId")
|
||||
|
|
@ -692,6 +737,33 @@ fun ChatRouterScreen(
|
|||
}
|
||||
}
|
||||
|
||||
val askNotificationsPermission =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
viewModel.setRegisterForNotifications()
|
||||
} else {
|
||||
viewModel.showNotificationRationale = false
|
||||
}
|
||||
}
|
||||
if (viewModel.showNotificationRationale) {
|
||||
NotificationRationaleDialog(
|
||||
onDismiss = {
|
||||
viewModel.showNotificationRationale = false
|
||||
},
|
||||
onSelected = { accepted ->
|
||||
if (accepted) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
askNotificationsPermission.launch(android.Manifest.permission.POST_NOTIFICATIONS)
|
||||
} else {
|
||||
viewModel.setRegisterForNotifications()
|
||||
}
|
||||
} else {
|
||||
viewModel.markNotificationsRejected()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
|
|||
|
|
@ -38,9 +38,12 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import chat.revolt.BuildConfig
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.REVOLT_MARKETING
|
||||
import chat.revolt.components.generic.AnyLink
|
||||
import chat.revolt.components.generic.Weblink
|
||||
import com.chuckerteam.chucker.api.Chucker
|
||||
|
||||
@Composable
|
||||
fun LoginGreetingScreen(navController: NavController) {
|
||||
|
|
@ -159,6 +162,16 @@ fun LoginGreetingScreen(navController: NavController) {
|
|||
text = stringResource(R.string.community_guidelines),
|
||||
url = "$REVOLT_MARKETING/aup"
|
||||
)
|
||||
if (BuildConfig.DEBUG) {
|
||||
AnyLink(
|
||||
text = "Debug: Chucker",
|
||||
action = {
|
||||
Chucker.getLaunchIntent(context).apply {
|
||||
context.startActivity(this)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue