Move logout and report bug to Setting screen
This commit is contained in:
parent
0568ea7c28
commit
37daf12cd0
36 changed files with 429 additions and 175 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -82,3 +82,4 @@ lint/generated/
|
||||||
lint/outputs/
|
lint/outputs/
|
||||||
lint/tmp/
|
lint/tmp/
|
||||||
# lint/reports/
|
# lint/reports/
|
||||||
|
/.idea/deploymentTargetDropDown.xml
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ dependencies {
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
implementation(project(":features:onboarding"))
|
implementation(project(":features:onboarding"))
|
||||||
implementation(project(":features:login"))
|
implementation(project(":features:login"))
|
||||||
|
implementation(project(":features:logout"))
|
||||||
implementation(project(":features:roomlist"))
|
implementation(project(":features:roomlist"))
|
||||||
implementation(project(":features:messages"))
|
implementation(project(":features:messages"))
|
||||||
implementation(project(":features:rageshake"))
|
implementation(project(":features:rageshake"))
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,6 @@ fun ChangeServerScreenNavigation(navigator: DestinationsNavigator) {
|
||||||
@Destination
|
@Destination
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomListScreenNavigation(navigator: DestinationsNavigator) {
|
fun RoomListScreenNavigation(navigator: DestinationsNavigator) {
|
||||||
val sessionComponentsOwner = LocalContext.current.bindings<AppBindings>().sessionComponentsOwner()
|
|
||||||
RoomListScreen(
|
RoomListScreen(
|
||||||
onRoomClicked = { roomId: RoomId ->
|
onRoomClicked = { roomId: RoomId ->
|
||||||
navigator.navigate(MessagesScreenNavigationDestination(roomId = roomId.value))
|
navigator.navigate(MessagesScreenNavigationDestination(roomId = roomId.value))
|
||||||
|
|
@ -79,18 +78,6 @@ fun RoomListScreenNavigation(navigator: DestinationsNavigator) {
|
||||||
onOpenSettings = {
|
onOpenSettings = {
|
||||||
navigator.navigate(PreferencesScreenNavigationDestination())
|
navigator.navigate(PreferencesScreenNavigationDestination())
|
||||||
},
|
},
|
||||||
onSuccessLogout = {
|
|
||||||
sessionComponentsOwner.releaseActiveSession()
|
|
||||||
navigator.navigate(OnBoardingScreenNavigationDestination) {
|
|
||||||
popUpTo(RoomListScreenNavigationDestination) {
|
|
||||||
inclusive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Tmp entry point
|
|
||||||
onOpenRageShake = {
|
|
||||||
navigator.navigate(BugReportScreenNavigationDestination)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,8 +98,19 @@ fun BugReportScreenNavigation(navigator: DestinationsNavigator) {
|
||||||
@Destination
|
@Destination
|
||||||
@Composable
|
@Composable
|
||||||
fun PreferencesScreenNavigation(navigator: DestinationsNavigator) {
|
fun PreferencesScreenNavigation(navigator: DestinationsNavigator) {
|
||||||
|
val sessionComponentsOwner = LocalContext.current.bindings<AppBindings>().sessionComponentsOwner()
|
||||||
PreferencesScreen(
|
PreferencesScreen(
|
||||||
onBackPressed = navigator::navigateUp
|
onBackPressed = navigator::navigateUp,
|
||||||
|
onOpenRageShake = {
|
||||||
|
navigator.navigate(BugReportScreenNavigationDestination)
|
||||||
|
},
|
||||||
|
onSuccessLogout = {
|
||||||
|
sessionComponentsOwner.releaseActiveSession()
|
||||||
|
navigator.navigate(OnBoardingScreenNavigationDestination) {
|
||||||
|
popUpTo(RoomListScreenNavigationDestination) {
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1
features/logout/.gitignore
vendored
Normal file
1
features/logout/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
||||||
27
features/logout/build.gradle.kts
Normal file
27
features/logout/build.gradle.kts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
plugins {
|
||||||
|
id("io.element.android-compose-library")
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
alias(libs.plugins.anvil)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.element.android.x.features.logout"
|
||||||
|
}
|
||||||
|
|
||||||
|
anvil {
|
||||||
|
generateDaggerFactories.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":anvilannotations"))
|
||||||
|
anvil(project(":anvilcodegen"))
|
||||||
|
implementation(project(":libraries:di"))
|
||||||
|
implementation(project(":libraries:core"))
|
||||||
|
implementation(project(":libraries:matrix"))
|
||||||
|
implementation(project(":libraries:designsystem"))
|
||||||
|
implementation(project(":libraries:elementresources"))
|
||||||
|
implementation(libs.mavericks.compose)
|
||||||
|
ksp(libs.showkase.processor)
|
||||||
|
testImplementation(libs.test.junit)
|
||||||
|
androidTestImplementation(libs.test.junitext)
|
||||||
|
}
|
||||||
0
features/logout/consumer-rules.pro
Normal file
0
features/logout/consumer-rules.pro
Normal file
21
features/logout/proguard-rules.pro
vendored
Normal file
21
features/logout/proguard-rules.pro
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.kts.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package io.element.android.x.features.logout
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("io.element.android.x.features.login.test", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
features/logout/src/main/AndroidManifest.xml
Normal file
4
features/logout/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package io.element.android.x.features.logout
|
||||||
|
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Logout
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import com.airbnb.mvrx.Loading
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.airbnb.mvrx.compose.collectAsState
|
||||||
|
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||||
|
import io.element.android.x.designsystem.ElementXTheme
|
||||||
|
import io.element.android.x.designsystem.components.ProgressDialog
|
||||||
|
import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog
|
||||||
|
import io.element.android.x.designsystem.components.preferences.PreferenceCategory
|
||||||
|
import io.element.android.x.designsystem.components.preferences.PreferenceText
|
||||||
|
import io.element.android.x.element.resources.R as ElementR
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LogoutPreference(
|
||||||
|
viewModel: LogoutViewModel = mavericksViewModel(),
|
||||||
|
onSuccessLogout: () -> Unit = { },
|
||||||
|
) {
|
||||||
|
val state: LogoutViewState by viewModel.collectAsState()
|
||||||
|
if (state.logoutAction is Success) {
|
||||||
|
onSuccessLogout()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val openDialog = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
LogoutPreferenceContent(
|
||||||
|
onClick = {
|
||||||
|
openDialog.value = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log out confirmation dialog
|
||||||
|
if (openDialog.value) {
|
||||||
|
ConfirmationDialog(
|
||||||
|
title = stringResource(id = ElementR.string.action_sign_out),
|
||||||
|
content = stringResource(id = ElementR.string.action_sign_out_confirmation_simple),
|
||||||
|
submitText = stringResource(id = ElementR.string.action_sign_out),
|
||||||
|
onCancelClicked = {
|
||||||
|
openDialog.value = false
|
||||||
|
},
|
||||||
|
onSubmitClicked = {
|
||||||
|
openDialog.value = false
|
||||||
|
viewModel.logout()
|
||||||
|
},
|
||||||
|
onDismiss = {
|
||||||
|
openDialog.value = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.logoutAction is Loading) {
|
||||||
|
ProgressDialog(text = "Login out...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LogoutPreferenceContent(
|
||||||
|
onClick: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
PreferenceCategory(title = stringResource(id = ElementR.string.settings_general_title)) {
|
||||||
|
PreferenceText(
|
||||||
|
title = stringResource(id = ElementR.string.action_sign_out),
|
||||||
|
icon = Icons.Default.Logout,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview
|
||||||
|
fun LogoutContentPreview() {
|
||||||
|
ElementXTheme(darkTheme = false) {
|
||||||
|
LogoutPreference()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package io.element.android.x.features.logout
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.MavericksViewModel
|
||||||
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import io.element.android.x.anvilannotations.ContributesViewModel
|
||||||
|
import io.element.android.x.core.di.daggerMavericksViewModelFactory
|
||||||
|
import io.element.android.x.di.SessionScope
|
||||||
|
import io.element.android.x.matrix.MatrixClient
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@ContributesViewModel(SessionScope::class)
|
||||||
|
class LogoutViewModel @AssistedInject constructor(
|
||||||
|
private val client: MatrixClient,
|
||||||
|
@Assisted initialState: LogoutViewState
|
||||||
|
) : MavericksViewModel<LogoutViewState>(initialState) {
|
||||||
|
|
||||||
|
companion object : MavericksViewModelFactory<LogoutViewModel, LogoutViewState> by daggerMavericksViewModelFactory()
|
||||||
|
|
||||||
|
fun logout() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
suspend {
|
||||||
|
client.logout()
|
||||||
|
}.execute {
|
||||||
|
copy(logoutAction = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package io.element.android.x.features.logout
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.Async
|
||||||
|
import com.airbnb.mvrx.MavericksState
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
|
||||||
|
data class LogoutViewState(
|
||||||
|
val logoutAction: Async<Unit> = Uninitialized,
|
||||||
|
) : MavericksState
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.element.android.x.features.logout
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ dependencies {
|
||||||
implementation(project(":libraries:di"))
|
implementation(project(":libraries:di"))
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
implementation(project(":features:rageshake"))
|
implementation(project(":features:rageshake"))
|
||||||
|
implementation(project(":features:logout"))
|
||||||
implementation(project(":libraries:designsystem"))
|
implementation(project(":libraries:designsystem"))
|
||||||
implementation(project(":libraries:elementresources"))
|
implementation(project(":libraries:elementresources"))
|
||||||
implementation(libs.mavericks.compose)
|
implementation(libs.mavericks.compose)
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,43 @@
|
||||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
|
|
||||||
package io.element.android.x.features.preferences
|
package io.element.android.x.features.preferences
|
||||||
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import io.element.android.x.designsystem.components.preferences.PreferenceScreen
|
import io.element.android.x.designsystem.components.preferences.PreferenceScreen
|
||||||
import io.element.android.x.features.rageshake.preferences.RageshakePreferenceCategory
|
|
||||||
import io.element.android.x.element.resources.R as ElementR
|
import io.element.android.x.element.resources.R as ElementR
|
||||||
|
import io.element.android.x.features.logout.LogoutPreference
|
||||||
|
import io.element.android.x.features.rageshake.preferences.RageshakePreferences
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PreferencesScreen(
|
fun PreferencesScreen(
|
||||||
onBackPressed: () -> Unit = {},
|
onBackPressed: () -> Unit = {},
|
||||||
|
onOpenRageShake: () -> Unit = {},
|
||||||
|
onSuccessLogout: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
// TODO Hierarchy!
|
// TODO Hierarchy!
|
||||||
// TODO Move logout here
|
|
||||||
// Include pref from other modules
|
// Include pref from other modules
|
||||||
PreferencesContent(onBackPressed = onBackPressed)
|
PreferencesContent(
|
||||||
|
onBackPressed = onBackPressed,
|
||||||
|
onOpenRageShake = onOpenRageShake,
|
||||||
|
onSuccessLogout = onSuccessLogout,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PreferencesContent(
|
fun PreferencesContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onBackPressed: () -> Unit = {},
|
onBackPressed: () -> Unit = {},
|
||||||
|
onOpenRageShake: () -> Unit = {},
|
||||||
|
onSuccessLogout: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
PreferenceScreen(
|
PreferenceScreen(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onBackPressed = onBackPressed,
|
onBackPressed = onBackPressed,
|
||||||
title = stringResource(id = ElementR.string.settings)
|
title = stringResource(id = ElementR.string.settings)
|
||||||
) {
|
) {
|
||||||
RageshakePreferenceCategory()
|
LogoutPreference(onSuccessLogout = onSuccessLogout)
|
||||||
|
RageshakePreferences(onOpenRageShake = onOpenRageShake)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ package io.element.android.x.features.rageshake.bugreport
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
|
|
@ -14,7 +13,6 @@ import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Checkbox
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import io.element.android.x.core.bool.orFalse
|
import io.element.android.x.core.bool.orFalse
|
||||||
import io.element.android.x.di.ApplicationContext
|
import io.element.android.x.di.ApplicationContext
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_crash")
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_crash")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ package io.element.android.x.features.rageshake.crash
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import io.element.android.x.core.data.tryOrNull
|
import io.element.android.x.core.data.tryOrNull
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class VectorUncaughtExceptionHandler(
|
class VectorUncaughtExceptionHandler(
|
||||||
context: Context
|
context: Context
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@ package io.element.android.x.features.rageshake.logs
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import io.element.android.x.core.data.tryOrNull
|
import io.element.android.x.core.data.tryOrNull
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.util.logging.FileHandler
|
import java.util.logging.FileHandler
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will be planted in Timber.
|
* Will be planted in Timber.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
package io.element.android.x.features.rageshake.preferences
|
package io.element.android.x.features.rageshake.preferences
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.BugReport
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.airbnb.mvrx.compose.collectAsState
|
import com.airbnb.mvrx.compose.collectAsState
|
||||||
|
|
@ -10,44 +14,58 @@ import io.element.android.x.designsystem.components.preferences.PreferenceCatego
|
||||||
import io.element.android.x.designsystem.components.preferences.PreferenceSlide
|
import io.element.android.x.designsystem.components.preferences.PreferenceSlide
|
||||||
import io.element.android.x.designsystem.components.preferences.PreferenceSwitch
|
import io.element.android.x.designsystem.components.preferences.PreferenceSwitch
|
||||||
import io.element.android.x.designsystem.components.preferences.PreferenceText
|
import io.element.android.x.designsystem.components.preferences.PreferenceText
|
||||||
|
import io.element.android.x.element.resources.R as ElementR
|
||||||
import io.element.android.x.features.rageshake.detection.RageshakeDetectionViewModel
|
import io.element.android.x.features.rageshake.detection.RageshakeDetectionViewModel
|
||||||
import io.element.android.x.features.rageshake.detection.RageshakeDetectionViewState
|
import io.element.android.x.features.rageshake.detection.RageshakeDetectionViewState
|
||||||
import io.element.android.x.element.resources.R as ElementR
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RageshakePreferenceCategory() {
|
fun RageshakePreferences(
|
||||||
RageshakePreferenceContent()
|
onOpenRageShake: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
RageshakePreferencesContent(
|
||||||
|
onOpenRageShake = onOpenRageShake,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RageshakePreferenceContent(
|
fun RageshakePreferencesContent(
|
||||||
viewModel: RageshakeDetectionViewModel = mavericksViewModel()
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: RageshakeDetectionViewModel = mavericksViewModel(),
|
||||||
|
onOpenRageShake: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val state: RageshakeDetectionViewState by viewModel.collectAsState()
|
val state: RageshakeDetectionViewState by viewModel.collectAsState()
|
||||||
PreferenceCategory(title = stringResource(id = ElementR.string.settings_rageshake)) {
|
Column(modifier = modifier) {
|
||||||
if (state.isSupported) {
|
PreferenceCategory(title = stringResource(id = ElementR.string.send_bug_report)) {
|
||||||
PreferenceSwitch(
|
PreferenceText(
|
||||||
title = stringResource(id = ElementR.string.send_bug_report_rage_shake),
|
title = stringResource(id = ElementR.string.send_bug_report),
|
||||||
isChecked = state.isEnabled,
|
icon = Icons.Default.BugReport,
|
||||||
onCheckedChange = viewModel::onEnableClicked
|
onClick = onOpenRageShake
|
||||||
)
|
)
|
||||||
if (state.isEnabled) {
|
}
|
||||||
|
PreferenceCategory(title = stringResource(id = ElementR.string.settings_rageshake)) {
|
||||||
|
if (state.isSupported) {
|
||||||
|
PreferenceSwitch(
|
||||||
|
title = stringResource(id = ElementR.string.send_bug_report_rage_shake),
|
||||||
|
isChecked = state.isEnabled,
|
||||||
|
onCheckedChange = viewModel::onEnableClicked
|
||||||
|
)
|
||||||
PreferenceSlide(
|
PreferenceSlide(
|
||||||
title = stringResource(id = ElementR.string.settings_rageshake_detection_threshold),
|
title = stringResource(id = ElementR.string.settings_rageshake_detection_threshold),
|
||||||
// summary = stringResource(id = ElementR.string.settings_rageshake_detection_threshold_summary),
|
// summary = stringResource(id = ElementR.string.settings_rageshake_detection_threshold_summary),
|
||||||
value = state.sensitivity,
|
value = state.sensitivity,
|
||||||
|
enabled = state.isEnabled,
|
||||||
steps = 3 /* 5 possible values - steps are in ]0, 1[ */,
|
steps = 3 /* 5 possible values - steps are in ]0, 1[ */,
|
||||||
onValueChange = viewModel::onSensitivityChange
|
onValueChange = viewModel::onSensitivityChange
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
PreferenceText(title = "Rageshaking is not supported by your device")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
PreferenceText(title = "Rageshaking is not supported by your device")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun RageshakePreferenceCategoryPreview() {
|
fun RageshakePreferencePreview() {
|
||||||
RageshakePreferenceCategory()
|
RageshakePreferences()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ class RageShake @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sensitivity will be {0, O.25, 0.5, 0.75, 1} and converted to [ShakeDetector.SENSITIVITY_LIGHT (=11), ShakeDetector.SENSITIVITY_HARD (=15)]
|
* sensitivity will be {0, O.25, 0.5, 0.75, 1} and converted to
|
||||||
|
* [ShakeDetector.SENSITIVITY_LIGHT (=11), ShakeDetector.SENSITIVITY_HARD (=15)].
|
||||||
*/
|
*/
|
||||||
fun setSensitivity(sensitivity: Float) {
|
fun setSensitivity(sensitivity: Float) {
|
||||||
shakeDetector?.setSensitivity(
|
shakeDetector?.setSensitivity(
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import androidx.datastore.preferences.core.floatPreferencesKey
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import io.element.android.x.core.bool.orTrue
|
import io.element.android.x.core.bool.orTrue
|
||||||
import io.element.android.x.di.ApplicationContext
|
import io.element.android.x.di.ApplicationContext
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_rageshake")
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_rageshake")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ import io.element.android.x.features.rageshake.R
|
||||||
import io.element.android.x.features.rageshake.crash.CrashDataStore
|
import io.element.android.x.features.rageshake.crash.CrashDataStore
|
||||||
import io.element.android.x.features.rageshake.logs.VectorFileLogger
|
import io.element.android.x.features.rageshake.logs.VectorFileLogger
|
||||||
import io.element.android.x.features.rageshake.screenshot.ScreenshotHolder
|
import io.element.android.x.features.rageshake.screenshot.ScreenshotHolder
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.OutputStreamWriter
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.util.Locale
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
@ -24,12 +30,6 @@ import okhttp3.Response
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.OutputStreamWriter
|
|
||||||
import java.net.HttpURLConnection
|
|
||||||
import java.util.Locale
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BugReporter creates and sends the bug reports.
|
* BugReporter creates and sends the bug reports.
|
||||||
|
|
@ -121,6 +121,7 @@ class BugReporter @Inject constructor(
|
||||||
/**
|
/**
|
||||||
* Send a bug report.
|
* Send a bug report.
|
||||||
*
|
*
|
||||||
|
* @param coroutineScope The coroutine scope
|
||||||
* @param reportType The report type (bug, suggestion, feedback)
|
* @param reportType The report type (bug, suggestion, feedback)
|
||||||
* @param withDevicesLogs true to include the device log
|
* @param withDevicesLogs true to include the device log
|
||||||
* @param withCrashLogs true to include the crash logs
|
* @param withCrashLogs true to include the crash logs
|
||||||
|
|
@ -241,14 +242,14 @@ class BugReporter @Inject constructor(
|
||||||
.addFormDataPart("device", Build.MODEL.trim())
|
.addFormDataPart("device", Build.MODEL.trim())
|
||||||
// .addFormDataPart("verbose_log", vectorPreferences.labAllowedExtendedLogging().toOnOff())
|
// .addFormDataPart("verbose_log", vectorPreferences.labAllowedExtendedLogging().toOnOff())
|
||||||
.addFormDataPart("multi_window", inMultiWindowMode.toOnOff())
|
.addFormDataPart("multi_window", inMultiWindowMode.toOnOff())
|
||||||
//.addFormDataPart(
|
// .addFormDataPart(
|
||||||
// "os", Build.VERSION.RELEASE + " (API " + sdkIntProvider.get() + ") " +
|
// "os", Build.VERSION.RELEASE + " (API " + sdkIntProvider.get() + ") " +
|
||||||
// Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME
|
// Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME
|
||||||
//)
|
// )
|
||||||
.addFormDataPart("locale", Locale.getDefault().toString())
|
.addFormDataPart("locale", Locale.getDefault().toString())
|
||||||
//.addFormDataPart("app_language", vectorLocale.applicationLocale.toString())
|
// .addFormDataPart("app_language", vectorLocale.applicationLocale.toString())
|
||||||
//.addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString())
|
// .addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString())
|
||||||
//.addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
|
// .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
|
||||||
.addFormDataPart("server_version", serverVersion)
|
.addFormDataPart("server_version", serverVersion)
|
||||||
.apply {
|
.apply {
|
||||||
customFields?.forEach { (name, value) ->
|
customFields?.forEach { (name, value) ->
|
||||||
|
|
@ -442,19 +443,19 @@ class BugReporter @Inject constructor(
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//private fun logOtherInfo() {
|
// private fun logOtherInfo() {
|
||||||
// Timber.i("SyncThread state: " + activeSessionHolder.getSafeActiveSession()?.syncService()?.getSyncState())
|
// Timber.i("SyncThread state: " + activeSessionHolder.getSafeActiveSession()?.syncService()?.getSyncState())
|
||||||
//}
|
// }
|
||||||
|
|
||||||
//private fun logDbInfo() {
|
// private fun logDbInfo() {
|
||||||
// val dbInfo = matrix.debugService().getDbUsageInfo()
|
// val dbInfo = matrix.debugService().getDbUsageInfo()
|
||||||
// Timber.i(dbInfo)
|
// Timber.i(dbInfo)
|
||||||
//}
|
// }
|
||||||
|
|
||||||
//private fun logProcessInfo() {
|
// private fun logProcessInfo() {
|
||||||
// val pInfo = processInfo.getInfo()
|
// val pInfo = processInfo.getInfo()
|
||||||
// Timber.i(pInfo)
|
// Timber.i(pInfo)
|
||||||
//}
|
// }
|
||||||
|
|
||||||
private fun rageShakeAppNameForReport(reportType: ReportType): String {
|
private fun rageShakeAppNameForReport(reportType: ReportType): String {
|
||||||
// As per https://github.com/matrix-org/rageshake
|
// As per https://github.com/matrix-org/rageshake
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,10 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.Velocity
|
import androidx.compose.ui.unit.Velocity
|
||||||
import com.airbnb.mvrx.Loading
|
|
||||||
import com.airbnb.mvrx.Success
|
|
||||||
import com.airbnb.mvrx.compose.collectAsState
|
import com.airbnb.mvrx.compose.collectAsState
|
||||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||||
import io.element.android.x.core.compose.LogCompositions
|
import io.element.android.x.core.compose.LogCompositions
|
||||||
import io.element.android.x.designsystem.ElementXTheme
|
import io.element.android.x.designsystem.ElementXTheme
|
||||||
import io.element.android.x.designsystem.components.ProgressDialog
|
|
||||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.x.features.roomlist.components.RoomListTopBar
|
import io.element.android.x.features.roomlist.components.RoomListTopBar
|
||||||
import io.element.android.x.features.roomlist.components.RoomSummaryRow
|
import io.element.android.x.features.roomlist.components.RoomSummaryRow
|
||||||
|
|
@ -42,17 +39,10 @@ import kotlinx.collections.immutable.toImmutableList
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomListScreen(
|
fun RoomListScreen(
|
||||||
viewModel: RoomListViewModel = mavericksViewModel(),
|
viewModel: RoomListViewModel = mavericksViewModel(),
|
||||||
onSuccessLogout: () -> Unit = { },
|
|
||||||
onRoomClicked: (RoomId) -> Unit = { },
|
onRoomClicked: (RoomId) -> Unit = { },
|
||||||
onOpenRageShake: () -> Unit = { },
|
|
||||||
onOpenSettings: () -> Unit = { },
|
onOpenSettings: () -> Unit = { },
|
||||||
) {
|
) {
|
||||||
val logoutAction by viewModel.collectAsState(RoomListViewState::logoutAction)
|
|
||||||
val filter by viewModel.collectAsState(RoomListViewState::filter)
|
val filter by viewModel.collectAsState(RoomListViewState::filter)
|
||||||
if (logoutAction is Success) {
|
|
||||||
onSuccessLogout()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
LogCompositions(tag = "RoomListScreen", msg = "Root")
|
LogCompositions(tag = "RoomListScreen", msg = "Root")
|
||||||
val roomSummaries by viewModel.collectAsState(RoomListViewState::rooms)
|
val roomSummaries by viewModel.collectAsState(RoomListViewState::rooms)
|
||||||
val matrixUser by viewModel.collectAsState(RoomListViewState::user)
|
val matrixUser by viewModel.collectAsState(RoomListViewState::user)
|
||||||
|
|
@ -60,10 +50,7 @@ fun RoomListScreen(
|
||||||
roomSummaries = roomSummaries().orEmpty().toImmutableList(),
|
roomSummaries = roomSummaries().orEmpty().toImmutableList(),
|
||||||
matrixUser = matrixUser(),
|
matrixUser = matrixUser(),
|
||||||
onRoomClicked = onRoomClicked,
|
onRoomClicked = onRoomClicked,
|
||||||
onLogoutClicked = viewModel::logout,
|
|
||||||
onOpenSettings = onOpenSettings,
|
onOpenSettings = onOpenSettings,
|
||||||
onOpenRageShake = onOpenRageShake,
|
|
||||||
isLoginOut = logoutAction is Loading,
|
|
||||||
filter = filter,
|
filter = filter,
|
||||||
onFilterChanged = viewModel::filterRoom,
|
onFilterChanged = viewModel::filterRoom,
|
||||||
onScrollOver = viewModel::updateVisibleRange
|
onScrollOver = viewModel::updateVisibleRange
|
||||||
|
|
@ -75,13 +62,10 @@ fun RoomListContent(
|
||||||
roomSummaries: ImmutableList<RoomListRoomSummary>,
|
roomSummaries: ImmutableList<RoomListRoomSummary>,
|
||||||
matrixUser: MatrixUser?,
|
matrixUser: MatrixUser?,
|
||||||
filter: String,
|
filter: String,
|
||||||
isLoginOut: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onRoomClicked: (RoomId) -> Unit = {},
|
onRoomClicked: (RoomId) -> Unit = {},
|
||||||
onFilterChanged: (String) -> Unit = {},
|
onFilterChanged: (String) -> Unit = {},
|
||||||
onLogoutClicked: () -> Unit = {},
|
|
||||||
onOpenSettings: () -> Unit = {},
|
onOpenSettings: () -> Unit = {},
|
||||||
onOpenRageShake: () -> Unit = { },
|
|
||||||
onScrollOver: (IntRange) -> Unit = {},
|
onScrollOver: (IntRange) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
fun onRoomClicked(room: RoomListRoomSummary) {
|
fun onRoomClicked(room: RoomListRoomSummary) {
|
||||||
|
|
@ -118,9 +102,7 @@ fun RoomListContent(
|
||||||
matrixUser = matrixUser,
|
matrixUser = matrixUser,
|
||||||
filter = filter,
|
filter = filter,
|
||||||
onFilterChanged = onFilterChanged,
|
onFilterChanged = onFilterChanged,
|
||||||
onLogoutClicked = onLogoutClicked,
|
|
||||||
onOpenSettings = onOpenSettings,
|
onOpenSettings = onOpenSettings,
|
||||||
onOpenRageShake = onOpenRageShake,
|
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -142,9 +124,6 @@ fun RoomListContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (isLoginOut) {
|
|
||||||
ProgressDialog(text = "Login out...")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RoomListRoomSummary.contentType() = isPlaceholder
|
private fun RoomListRoomSummary.contentType() = isPlaceholder
|
||||||
|
|
@ -161,10 +140,8 @@ fun PreviewableRoomListContent() {
|
||||||
roomSummaries = stubbedRoomSummaries(),
|
roomSummaries = stubbedRoomSummaries(),
|
||||||
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
|
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
|
||||||
onRoomClicked = {},
|
onRoomClicked = {},
|
||||||
onLogoutClicked = {},
|
|
||||||
filter = "filter",
|
filter = "filter",
|
||||||
onFilterChanged = {},
|
onFilterChanged = {},
|
||||||
isLoginOut = false,
|
|
||||||
onScrollOver = {}
|
onScrollOver = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -178,10 +155,8 @@ fun PreviewableDarkRoomListContent() {
|
||||||
roomSummaries = stubbedRoomSummaries(),
|
roomSummaries = stubbedRoomSummaries(),
|
||||||
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
|
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
|
||||||
onRoomClicked = {},
|
onRoomClicked = {},
|
||||||
onLogoutClicked = {},
|
|
||||||
filter = "filter",
|
filter = "filter",
|
||||||
onFilterChanged = {},
|
onFilterChanged = {},
|
||||||
isLoginOut = true,
|
|
||||||
onScrollOver = {}
|
onScrollOver = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import io.element.android.x.matrix.MatrixClient
|
||||||
import io.element.android.x.matrix.media.MediaResolver
|
import io.element.android.x.matrix.media.MediaResolver
|
||||||
import io.element.android.x.matrix.room.RoomSummary
|
import io.element.android.x.matrix.room.RoomSummary
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
|
@ -33,8 +32,7 @@ private const val extendedRangeSize = 40
|
||||||
class RoomListViewModel @AssistedInject constructor(
|
class RoomListViewModel @AssistedInject constructor(
|
||||||
private val client: MatrixClient,
|
private val client: MatrixClient,
|
||||||
@Assisted initialState: RoomListViewState
|
@Assisted initialState: RoomListViewState
|
||||||
) :
|
) : MavericksViewModel<RoomListViewState>(initialState) {
|
||||||
MavericksViewModel<RoomListViewState>(initialState) {
|
|
||||||
|
|
||||||
companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by daggerMavericksViewModelFactory()
|
companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by daggerMavericksViewModelFactory()
|
||||||
|
|
||||||
|
|
@ -44,17 +42,6 @@ class RoomListViewModel @AssistedInject constructor(
|
||||||
handleInit()
|
handleInit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun logout() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
suspend {
|
|
||||||
delay(2000)
|
|
||||||
client.logout()
|
|
||||||
}.execute {
|
|
||||||
copy(logoutAction = it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filterRoom(filter: String) {
|
fun filterRoom(filter: String) {
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material.ContentAlpha
|
import androidx.compose.material.ContentAlpha
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.BugReport
|
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Logout
|
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
|
@ -34,25 +32,20 @@ import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import io.element.android.x.core.compose.LogCompositions
|
import io.element.android.x.core.compose.LogCompositions
|
||||||
import io.element.android.x.core.compose.textFieldState
|
import io.element.android.x.core.compose.textFieldState
|
||||||
import io.element.android.x.designsystem.components.avatar.Avatar
|
import io.element.android.x.designsystem.components.avatar.Avatar
|
||||||
import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog
|
|
||||||
import io.element.android.x.features.roomlist.model.MatrixUser
|
import io.element.android.x.features.roomlist.model.MatrixUser
|
||||||
import io.element.android.x.element.resources.R as ElementR
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomListTopBar(
|
fun RoomListTopBar(
|
||||||
matrixUser: MatrixUser?,
|
matrixUser: MatrixUser?,
|
||||||
filter: String,
|
filter: String,
|
||||||
onFilterChanged: (String) -> Unit,
|
onFilterChanged: (String) -> Unit,
|
||||||
onLogoutClicked: () -> Unit,
|
|
||||||
onOpenSettings: () -> Unit,
|
onOpenSettings: () -> Unit,
|
||||||
onOpenRageShake: () -> Unit,
|
|
||||||
scrollBehavior: TopAppBarScrollBehavior
|
scrollBehavior: TopAppBarScrollBehavior
|
||||||
) {
|
) {
|
||||||
LogCompositions(tag = "RoomListScreen", msg = "TopBar")
|
LogCompositions(tag = "RoomListScreen", msg = "TopBar")
|
||||||
|
|
@ -77,9 +70,7 @@ fun RoomListTopBar(
|
||||||
} else {
|
} else {
|
||||||
DefaultRoomListTopBar(
|
DefaultRoomListTopBar(
|
||||||
matrixUser = matrixUser,
|
matrixUser = matrixUser,
|
||||||
onLogoutClicked = onLogoutClicked,
|
|
||||||
onOpenSettings = onOpenSettings,
|
onOpenSettings = onOpenSettings,
|
||||||
onOpenRageShake = onOpenRageShake,
|
|
||||||
onSearchClicked = {
|
onSearchClicked = {
|
||||||
searchWidgetStateIsOpened = true
|
searchWidgetStateIsOpened = true
|
||||||
},
|
},
|
||||||
|
|
@ -168,13 +159,10 @@ fun SearchRoomListTopBar(
|
||||||
@Composable
|
@Composable
|
||||||
private fun DefaultRoomListTopBar(
|
private fun DefaultRoomListTopBar(
|
||||||
matrixUser: MatrixUser?,
|
matrixUser: MatrixUser?,
|
||||||
onLogoutClicked: () -> Unit,
|
|
||||||
onOpenRageShake: () -> Unit,
|
|
||||||
onOpenSettings: () -> Unit,
|
onOpenSettings: () -> Unit,
|
||||||
onSearchClicked: () -> Unit,
|
onSearchClicked: () -> Unit,
|
||||||
scrollBehavior: TopAppBarScrollBehavior
|
scrollBehavior: TopAppBarScrollBehavior
|
||||||
) {
|
) {
|
||||||
val openDialog = remember { mutableStateOf(false) }
|
|
||||||
MediumTopAppBar(
|
MediumTopAppBar(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
|
@ -197,16 +185,6 @@ private fun DefaultRoomListTopBar(
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Default.Search, contentDescription = "search")
|
Icon(Icons.Default.Search, contentDescription = "search")
|
||||||
}
|
}
|
||||||
IconButton(
|
|
||||||
onClick = onOpenRageShake
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.BugReport, contentDescription = stringResource(id = ElementR.string.send_bug_report))
|
|
||||||
}
|
|
||||||
IconButton(
|
|
||||||
onClick = { openDialog.value = true }
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.Logout, contentDescription = "logout")
|
|
||||||
}
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onOpenSettings
|
onClick = onOpenSettings
|
||||||
) {
|
) {
|
||||||
|
|
@ -215,19 +193,4 @@ private fun DefaultRoomListTopBar(
|
||||||
},
|
},
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
// Log out confirmation dialog
|
|
||||||
if (openDialog.value) {
|
|
||||||
ConfirmationDialog(
|
|
||||||
title = "Log out",
|
|
||||||
content = "Do you confirm you want to log out?",
|
|
||||||
submitText = "Log out",
|
|
||||||
onSubmitClicked = {
|
|
||||||
openDialog.value = false
|
|
||||||
onLogoutClicked()
|
|
||||||
},
|
|
||||||
onDismiss = {
|
|
||||||
openDialog.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,5 @@ data class RoomListViewState(
|
||||||
val rooms: Async<List<RoomListRoomSummary>> = Uninitialized,
|
val rooms: Async<List<RoomListRoomSummary>> = Uninitialized,
|
||||||
val filter: String = "",
|
val filter: String = "",
|
||||||
val canLoadMore: Boolean = false,
|
val canLoadMore: Boolean = false,
|
||||||
val logoutAction: Async<Unit> = Uninitialized,
|
|
||||||
val roomsById: Map<RoomId, RoomListRoomSummary> = emptyMap()
|
val roomsById: Map<RoomId, RoomListRoomSummary> = emptyMap()
|
||||||
) : MavericksState
|
) : MavericksState
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package io.element.android.x.core.file
|
package io.element.android.x.core.file
|
||||||
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.zip.GZIPOutputStream
|
import java.util.zip.GZIPOutputStream
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GZip a file.
|
* GZip a file.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package io.element.android.x.designsystem
|
||||||
|
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Boolean.toEnabledColor(): Color {
|
||||||
|
return if (this) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
import io.element.android.x.element.resources.R as ElementR
|
import io.element.android.x.element.resources.R as ElementR
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -20,9 +21,14 @@ fun PreferenceCategory(
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
) {
|
) {
|
||||||
|
Divider(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
thickness = 1.dp
|
||||||
|
)
|
||||||
Text(
|
Text(
|
||||||
|
modifier = Modifier.padding(top = 8.dp, start = 56.dp),
|
||||||
style = MaterialTheme.typography.titleSmall,
|
style = MaterialTheme.typography.titleSmall,
|
||||||
text = title
|
text = title
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package io.element.android.x.designsystem.components.preferences
|
||||||
import androidx.annotation.FloatRange
|
import androidx.annotation.FloatRange
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -11,7 +12,10 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import io.element.android.x.designsystem.components.preferences.components.PreferenceIcon
|
||||||
|
import io.element.android.x.designsystem.toEnabledColor
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PreferenceSlide(
|
fun PreferenceSlide(
|
||||||
|
|
@ -19,6 +23,8 @@ fun PreferenceSlide(
|
||||||
@FloatRange(0.0, 1.0)
|
@FloatRange(0.0, 1.0)
|
||||||
value: Float,
|
value: Float,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
icon: ImageVector? = null,
|
||||||
|
enabled: Boolean = true,
|
||||||
summary: String? = null,
|
summary: String? = null,
|
||||||
steps: Int = 0,
|
steps: Int = 0,
|
||||||
onValueChange: (Float) -> Unit = {},
|
onValueChange: (Float) -> Unit = {},
|
||||||
|
|
@ -29,27 +35,32 @@ fun PreferenceSlide(
|
||||||
.defaultMinSize(minHeight = preferenceMinHeight),
|
.defaultMinSize(minHeight = preferenceMinHeight),
|
||||||
contentAlignment = Alignment.CenterStart
|
contentAlignment = Alignment.CenterStart
|
||||||
) {
|
) {
|
||||||
Column(
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
modifier = modifier
|
PreferenceIcon(icon = icon)
|
||||||
.fillMaxWidth(),
|
Column(
|
||||||
) {
|
modifier = Modifier.weight(1f),
|
||||||
Text(
|
) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
text = title
|
|
||||||
)
|
|
||||||
summary?.let {
|
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
text = summary
|
color = enabled.toEnabledColor(),
|
||||||
|
text = title
|
||||||
|
)
|
||||||
|
summary?.let {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = enabled.toEnabledColor(),
|
||||||
|
text = summary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Slider(
|
||||||
|
value = value,
|
||||||
|
steps = steps,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
enabled = enabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Slider(
|
|
||||||
value = value,
|
|
||||||
steps = steps,
|
|
||||||
onValueChange = onValueChange
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,58 @@
|
||||||
package io.element.android.x.designsystem.components.preferences
|
package io.element.android.x.designsystem.components.preferences
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Announcement
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import io.element.android.x.designsystem.components.preferences.components.PreferenceIcon
|
||||||
|
import io.element.android.x.designsystem.toEnabledColor
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PreferenceSwitch(
|
fun PreferenceSwitch(
|
||||||
title: String,
|
title: String,
|
||||||
isChecked: Boolean,
|
isChecked: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
icon: ImageVector? = null,
|
||||||
onCheckedChange: (Boolean) -> Unit = {},
|
onCheckedChange: (Boolean) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.defaultMinSize(minHeight = preferenceMinHeight),
|
.defaultMinSize(minHeight = preferenceMinHeight)
|
||||||
|
.clickable { onCheckedChange(!isChecked) },
|
||||||
contentAlignment = Alignment.CenterStart
|
contentAlignment = Alignment.CenterStart
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.fillMaxWidth(),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
|
PreferenceIcon(
|
||||||
|
icon = icon,
|
||||||
|
enabled = enabled
|
||||||
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = modifier
|
modifier = Modifier.weight(1f),
|
||||||
.weight(1f),
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = enabled.toEnabledColor(),
|
||||||
text = title
|
text = title
|
||||||
)
|
)
|
||||||
Checkbox(checked = isChecked, onCheckedChange = onCheckedChange)
|
Checkbox(
|
||||||
|
checked = isChecked,
|
||||||
|
enabled = enabled,
|
||||||
|
onCheckedChange = onCheckedChange
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,6 +62,7 @@ fun PreferenceSwitch(
|
||||||
fun PreferenceSwitchPreview() {
|
fun PreferenceSwitchPreview() {
|
||||||
PreferenceSwitch(
|
PreferenceSwitch(
|
||||||
title = "Switch",
|
title = "Switch",
|
||||||
|
icon = Icons.Default.Announcement,
|
||||||
isChecked = true
|
isChecked = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,26 @@ package io.element.android.x.designsystem.components.preferences
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.BugReport
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import io.element.android.x.designsystem.components.preferences.components.PreferenceIcon
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PreferenceText(
|
fun PreferenceText(
|
||||||
title: String,
|
title: String,
|
||||||
// TODO subtitle
|
// TODO subtitle
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
icon: ImageVector? = null,
|
||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -25,12 +31,17 @@ fun PreferenceText(
|
||||||
.clickable { onClick() },
|
.clickable { onClick() },
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth()
|
||||||
.fillMaxWidth(),
|
) {
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
PreferenceIcon(icon = icon)
|
||||||
text = title
|
Text(
|
||||||
)
|
modifier = Modifier
|
||||||
|
.weight(1f),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
text = title
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,5 +50,6 @@ fun PreferenceText(
|
||||||
fun PreferenceTextPreview() {
|
fun PreferenceTextPreview() {
|
||||||
PreferenceText(
|
PreferenceText(
|
||||||
title = "Title",
|
title = "Title",
|
||||||
|
icon = Icons.Default.BugReport,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package io.element.android.x.designsystem.components.preferences.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.x.designsystem.toEnabledColor
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PreferenceIcon(
|
||||||
|
icon: ImageVector?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true
|
||||||
|
) {
|
||||||
|
if (icon != null) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = "",
|
||||||
|
tint = enabled.toEnabledColor(),
|
||||||
|
modifier = modifier
|
||||||
|
.padding(start = 8.dp)
|
||||||
|
.width(48.dp),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Spacer(modifier = modifier.width(56.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ include(":libraries:textcomposer")
|
||||||
include(":libraries:elementresources")
|
include(":libraries:elementresources")
|
||||||
include(":features:onboarding")
|
include(":features:onboarding")
|
||||||
include(":features:login")
|
include(":features:login")
|
||||||
|
include(":features:logout")
|
||||||
include(":features:roomlist")
|
include(":features:roomlist")
|
||||||
include(":features:messages")
|
include(":features:messages")
|
||||||
include(":features:rageshake")
|
include(":features:rageshake")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue