forked from melod1n/fast-messenger
refactor default alerts
This commit is contained in:
@@ -3,7 +3,13 @@ package com.meloda.app.fast.designsystem
|
||||
import android.content.res.Configuration
|
||||
import android.view.KeyEvent
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -77,3 +83,21 @@ fun Modifier.handleEnterKey(
|
||||
action.invoke()
|
||||
} else false
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LazyListState.isScrollingUp(): Boolean {
|
||||
var previousIndex by remember(this) { mutableIntStateOf(firstVisibleItemIndex) }
|
||||
var previousScrollOffset by remember(this) { mutableIntStateOf(firstVisibleItemScrollOffset) }
|
||||
return remember(this) {
|
||||
derivedStateOf {
|
||||
if (previousIndex != firstVisibleItemIndex) {
|
||||
previousIndex > firstVisibleItemIndex
|
||||
} else {
|
||||
previousScrollOffset >= firstVisibleItemScrollOffset
|
||||
}.also {
|
||||
previousIndex = firstVisibleItemIndex
|
||||
previousScrollOffset = firstVisibleItemScrollOffset
|
||||
}
|
||||
}
|
||||
}.value
|
||||
}
|
||||
|
||||
+213
-20
@@ -37,6 +37,208 @@ import com.meloda.app.fast.common.UiText
|
||||
import com.meloda.app.fast.common.parseString
|
||||
import com.meloda.app.fast.designsystem.ImmutableList.Companion.toImmutableList
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MaterialDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
confirmText: String? = null,
|
||||
confirmAction: (() -> Unit)? = null,
|
||||
cancelText: String? = null,
|
||||
cancelAction: (() -> Unit)? = null,
|
||||
neutralText: String? = null,
|
||||
neutralAction: (() -> Unit)? = null,
|
||||
title: String? = null,
|
||||
text: String? = null,
|
||||
itemsSelectionType: ItemsSelectionType = ItemsSelectionType.None,
|
||||
items: ImmutableList<String> = ImmutableList.empty(),
|
||||
preSelectedItems: ImmutableList<Int> = ImmutableList.empty(),
|
||||
onItemClick: ((index: Int) -> Unit)? = null,
|
||||
properties: DialogProperties = DialogProperties(),
|
||||
actionInvokeDismiss: ActionInvokeDismiss = ActionInvokeDismiss.IfNoAction,
|
||||
customContent: (ColumnScope.() -> Unit)? = null
|
||||
) {
|
||||
var alertItems by remember {
|
||||
mutableStateOf(
|
||||
items.mapIndexed { index, title ->
|
||||
DialogItem(
|
||||
title,
|
||||
preSelectedItems.contains(index)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
modifier = modifier,
|
||||
properties = properties
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
val canScrollBackward by remember { derivedStateOf { scrollState.value > 0 } }
|
||||
val canScrollForward by remember { derivedStateOf { scrollState.value < scrollState.maxValue } }
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = AlertDialogDefaults.containerColor,
|
||||
shape = AlertDialogDefaults.shape,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation
|
||||
) {
|
||||
Column(modifier = Modifier.padding(bottom = 10.dp)) {
|
||||
if (title != null) {
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Row {
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (canScrollBackward) {
|
||||
HorizontalDivider(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f, fill = false)
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
if (text != null && title == null) {
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
}
|
||||
|
||||
if (text != null) {
|
||||
Row {
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
if (alertItems.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
AlertItems(
|
||||
selectionType = itemsSelectionType,
|
||||
items = alertItems,
|
||||
onItemClick = { index ->
|
||||
onItemClick?.invoke(index)
|
||||
|
||||
if (itemsSelectionType == ItemsSelectionType.None) {
|
||||
onDismissRequest.invoke()
|
||||
} else {
|
||||
val newItems =
|
||||
alertItems.mapIndexed { itemIndex, item ->
|
||||
item.copy(isSelected = itemIndex == index)
|
||||
}
|
||||
|
||||
alertItems = newItems
|
||||
}
|
||||
},
|
||||
onItemCheckedChanged = { index ->
|
||||
val newItems = alertItems.toMutableList()
|
||||
val oldItem = newItems[index]
|
||||
newItems[index] =
|
||||
oldItem.copy(isSelected = !oldItem.isSelected)
|
||||
|
||||
alertItems = newItems.toImmutableList()
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
} else {
|
||||
if (customContent != null) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
customContent.invoke(this)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canScrollForward) {
|
||||
HorizontalDivider(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
|
||||
Row {
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
if (neutralText != null) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
neutralAction?.invoke() ?: kotlin.run {
|
||||
if (actionInvokeDismiss == ActionInvokeDismiss.IfNoAction) {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
|
||||
if (actionInvokeDismiss == ActionInvokeDismiss.Always) {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = neutralText)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
if (cancelText != null) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
cancelAction?.invoke() ?: kotlin.run {
|
||||
if (actionInvokeDismiss == ActionInvokeDismiss.IfNoAction) {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
|
||||
if (actionInvokeDismiss == ActionInvokeDismiss.Always) {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = cancelText)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
|
||||
if (confirmText != null) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
confirmAction?.invoke() ?: kotlin.run {
|
||||
if (actionInvokeDismiss == ActionInvokeDismiss.IfNoAction) {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
|
||||
if (actionInvokeDismiss == ActionInvokeDismiss.Always) {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = confirmText)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 08.04.2023, Danil Nikolaev: refactor this
|
||||
@Deprecated("need refactoring")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -84,7 +286,7 @@ fun MaterialDialog(
|
||||
)
|
||||
}
|
||||
|
||||
AlertAnimation(visible = isVisible) {
|
||||
if (isVisible) {
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
properties = properties
|
||||
@@ -250,21 +452,6 @@ fun MaterialDialog(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AlertAnimation(
|
||||
visible: Boolean,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
if (visible) content()
|
||||
// AnimatedVisibility(
|
||||
// visible = visible,
|
||||
// enter = fadeIn(animationSpec = tween(400)) +
|
||||
// scaleIn(animationSpec = tween(400)),
|
||||
// exit = fadeOut(animationSpec = tween(150)),
|
||||
// content = content
|
||||
// )
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AlertItems(
|
||||
selectionType: ItemsSelectionType,
|
||||
@@ -324,8 +511,14 @@ data class DialogItem(
|
||||
val isSelected: Boolean
|
||||
)
|
||||
|
||||
sealed interface ItemsSelectionType {
|
||||
data object Single : ItemsSelectionType
|
||||
data object Multi : ItemsSelectionType
|
||||
data object None : ItemsSelectionType
|
||||
sealed class ActionInvokeDismiss {
|
||||
data object Never : ActionInvokeDismiss()
|
||||
data object IfNoAction : ActionInvokeDismiss()
|
||||
data object Always : ActionInvokeDismiss()
|
||||
}
|
||||
|
||||
sealed class ItemsSelectionType {
|
||||
data object Single : ItemsSelectionType()
|
||||
data object Multi : ItemsSelectionType()
|
||||
data object None : ItemsSelectionType()
|
||||
}
|
||||
|
||||
@@ -259,4 +259,7 @@
|
||||
|
||||
<string name="notification_channel_long_polling_service_name">Message update service</string>
|
||||
<string name="notification_channel_long_polling_service_description">Message update service notifications</string>
|
||||
<string name="warning_confirmation">Confirmation</string>
|
||||
<string name="captcha_exit_warning">Are you sure? Captcha process will be cancelled</string>
|
||||
<string name="validation_exit_warning">Are you sure? Validation process will be cancelled</string>
|
||||
</resources>
|
||||
|
||||
+10
-10
@@ -41,6 +41,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -50,7 +51,7 @@ import coil.request.ImageRequest
|
||||
import com.meloda.app.fast.auth.captcha.CaptchaViewModel
|
||||
import com.meloda.app.fast.auth.captcha.CaptchaViewModelImpl
|
||||
import com.meloda.app.fast.auth.captcha.model.CaptchaScreenState
|
||||
import com.meloda.app.fast.common.UiText
|
||||
import com.meloda.app.fast.designsystem.ActionInvokeDismiss
|
||||
import com.meloda.app.fast.designsystem.MaterialDialog
|
||||
import com.meloda.app.fast.designsystem.TextFieldErrorText
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
@@ -89,7 +90,7 @@ fun CaptchaScreen(
|
||||
onTextFieldDoneAction: () -> Unit = {},
|
||||
onDoneButtonClicked: () -> Unit = {}
|
||||
) {
|
||||
var confirmedExit by rememberSaveable {
|
||||
var confirmedExit by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
@@ -111,14 +112,13 @@ fun CaptchaScreen(
|
||||
|
||||
if (showExitAlert) {
|
||||
MaterialDialog(
|
||||
onDismissAction = { showExitAlert = false },
|
||||
title = UiText.Simple("Confirmation"),
|
||||
text = UiText.Simple("Are you sure? Captcha process will be cancelled."),
|
||||
confirmText = UiText.Resource(UiR.string.yes),
|
||||
confirmAction = {
|
||||
confirmedExit = true
|
||||
},
|
||||
cancelText = UiText.Resource(UiR.string.no)
|
||||
onDismissRequest = { showExitAlert = false },
|
||||
title = stringResource(id = UiR.string.warning_confirmation),
|
||||
text = stringResource(id = UiR.string.captcha_exit_warning),
|
||||
confirmAction = { confirmedExit = true },
|
||||
confirmText = stringResource(id = UiR.string.yes),
|
||||
cancelText = stringResource(id = UiR.string.no),
|
||||
actionInvokeDismiss = ActionInvokeDismiss.Always
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -38,6 +38,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
@@ -46,7 +47,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.meloda.app.fast.auth.validation.ValidationViewModel
|
||||
import com.meloda.app.fast.auth.validation.ValidationViewModelImpl
|
||||
import com.meloda.app.fast.auth.validation.model.ValidationScreenState
|
||||
import com.meloda.app.fast.common.UiText
|
||||
import com.meloda.app.fast.designsystem.ActionInvokeDismiss
|
||||
import com.meloda.app.fast.designsystem.MaterialDialog
|
||||
import com.meloda.app.fast.designsystem.TextFieldErrorText
|
||||
import com.meloda.app.fast.designsystem.getString
|
||||
@@ -96,7 +97,7 @@ fun ValidationScreen(
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
var confirmedExit by rememberSaveable {
|
||||
var confirmedExit by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
@@ -118,14 +119,13 @@ fun ValidationScreen(
|
||||
|
||||
if (showExitAlert) {
|
||||
MaterialDialog(
|
||||
onDismissAction = { showExitAlert = false },
|
||||
title = UiText.Simple("Confirmation"),
|
||||
text = UiText.Simple("Are you sure? Authorization process will be cancelled."),
|
||||
confirmText = UiText.Resource(UiR.string.yes),
|
||||
confirmAction = {
|
||||
confirmedExit = true
|
||||
},
|
||||
cancelText = UiText.Resource(UiR.string.no)
|
||||
onDismissRequest = { showExitAlert = false },
|
||||
title = stringResource(id = UiR.string.warning_confirmation),
|
||||
text = stringResource(id = UiR.string.validation_exit_warning),
|
||||
confirmAction = { confirmedExit = true },
|
||||
confirmText = stringResource(id = UiR.string.yes),
|
||||
cancelText = stringResource(id = UiR.string.no),
|
||||
actionInvokeDismiss = ActionInvokeDismiss.Always
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+11
-6
@@ -47,8 +47,7 @@ interface ConversationsViewModel {
|
||||
fun onPaginationConditionsMet()
|
||||
|
||||
fun onDeleteDialogDismissed()
|
||||
|
||||
fun onDeleteDialogPositiveClick(conversationId: Int)
|
||||
fun onDeleteDialogPositiveClick()
|
||||
|
||||
fun onRefresh()
|
||||
|
||||
@@ -56,7 +55,8 @@ interface ConversationsViewModel {
|
||||
fun onConversationItemLongClick(conversation: UiConversation)
|
||||
|
||||
fun onPinDialogDismissed()
|
||||
fun onPinDialogPositiveClick(conversation: UiConversation)
|
||||
fun onPinDialogPositiveClick()
|
||||
|
||||
fun onOptionClicked(conversation: UiConversation, option: ConversationOption)
|
||||
|
||||
fun onErrorConsumed()
|
||||
@@ -104,9 +104,11 @@ class ConversationsViewModelImpl(
|
||||
emitShowOptions { old -> old.copy(showDeleteDialog = null) }
|
||||
}
|
||||
|
||||
override fun onDeleteDialogPositiveClick(conversationId: Int) {
|
||||
override fun onDeleteDialogPositiveClick() {
|
||||
val conversationId = screenState.value.showOptions.showDeleteDialog ?: return
|
||||
deleteConversation(conversationId)
|
||||
hideOptions(conversationId)
|
||||
onDeleteDialogDismissed()
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
@@ -168,9 +170,11 @@ class ConversationsViewModelImpl(
|
||||
emitShowOptions { old -> old.copy(showPinDialog = null) }
|
||||
}
|
||||
|
||||
override fun onPinDialogPositiveClick(conversation: UiConversation) {
|
||||
override fun onPinDialogPositiveClick() {
|
||||
val conversation = screenState.value.showOptions.showPinDialog ?: return
|
||||
pinConversation(conversation.id, !conversation.isPinned)
|
||||
hideOptions(conversation.id)
|
||||
onPinDialogDismissed()
|
||||
}
|
||||
|
||||
override fun onOptionClicked(conversation: UiConversation, option: ConversationOption) {
|
||||
@@ -222,7 +226,8 @@ class ConversationsViewModelImpl(
|
||||
private fun loadConversations(
|
||||
offset: Int = currentOffset.value
|
||||
) {
|
||||
conversationsUseCase.getConversations(count = LOAD_COUNT, offset = offset).listenValue { state ->
|
||||
conversationsUseCase.getConversations(count = LOAD_COUNT, offset = offset)
|
||||
.listenValue { state ->
|
||||
state.processState(
|
||||
error = { error ->
|
||||
if (error is State.Error.ApiError) {
|
||||
|
||||
+16
-59
@@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.MoreVert
|
||||
@@ -68,7 +67,6 @@ import androidx.core.view.HapticFeedbackConstantsCompat
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil.imageLoader
|
||||
import coil.request.ImageRequest
|
||||
import com.meloda.app.fast.common.UiText
|
||||
import com.meloda.app.fast.conversations.ConversationsViewModel
|
||||
import com.meloda.app.fast.conversations.ConversationsViewModelImpl
|
||||
import com.meloda.app.fast.conversations.model.ConversationOption
|
||||
@@ -79,6 +77,7 @@ import com.meloda.app.fast.designsystem.LocalHazeState
|
||||
import com.meloda.app.fast.designsystem.LocalTheme
|
||||
import com.meloda.app.fast.designsystem.MaterialDialog
|
||||
import com.meloda.app.fast.designsystem.components.FullScreenLoader
|
||||
import com.meloda.app.fast.designsystem.isScrollingUp
|
||||
import com.meloda.app.fast.model.BaseError
|
||||
import com.meloda.app.fast.ui.ErrorView
|
||||
import dev.chrisbanes.haze.haze
|
||||
@@ -398,72 +397,30 @@ fun HandleDialogs(
|
||||
val showOptions = screenState.showOptions
|
||||
|
||||
if (showOptions.showDeleteDialog != null) {
|
||||
val conversationId = showOptions.showDeleteDialog
|
||||
DeleteDialog(
|
||||
conversationId = conversationId,
|
||||
viewModel = viewModel
|
||||
)
|
||||
}
|
||||
|
||||
showOptions.showPinDialog?.let { conversation ->
|
||||
PinDialog(
|
||||
conversation = conversation,
|
||||
viewModel = viewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeleteDialog(
|
||||
conversationId: Int,
|
||||
viewModel: ConversationsViewModel
|
||||
) {
|
||||
MaterialDialog(
|
||||
title = UiText.Resource(UiR.string.confirm_delete_conversation),
|
||||
confirmText = UiText.Resource(UiR.string.action_delete),
|
||||
confirmAction = { viewModel.onDeleteDialogPositiveClick(conversationId) },
|
||||
cancelText = UiText.Resource(UiR.string.cancel),
|
||||
onDismissAction = viewModel::onDeleteDialogDismissed
|
||||
onDismissRequest = viewModel::onDeleteDialogDismissed,
|
||||
title = stringResource(id = UiR.string.confirm_delete_conversation),
|
||||
confirmAction = viewModel::onDeleteDialogPositiveClick,
|
||||
confirmText = stringResource(id = UiR.string.action_delete),
|
||||
cancelText = stringResource(id = UiR.string.cancel)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PinDialog(
|
||||
conversation: UiConversation,
|
||||
viewModel: ConversationsViewModel
|
||||
) {
|
||||
if (showOptions.showPinDialog != null) {
|
||||
val conversation = showOptions.showPinDialog
|
||||
|
||||
MaterialDialog(
|
||||
title = UiText.Resource(
|
||||
if (conversation.isPinned) UiR.string.confirm_unpin_conversation
|
||||
onDismissRequest = viewModel::onPinDialogDismissed,
|
||||
title = stringResource(
|
||||
id = if (conversation.isPinned) UiR.string.confirm_unpin_conversation
|
||||
else UiR.string.confirm_pin_conversation
|
||||
),
|
||||
confirmText = UiText.Resource(
|
||||
if (conversation.isPinned) UiR.string.action_unpin
|
||||
confirmAction = viewModel::onPinDialogPositiveClick,
|
||||
confirmText = stringResource(
|
||||
id = if (conversation.isPinned) UiR.string.action_unpin
|
||||
else UiR.string.action_pin
|
||||
),
|
||||
confirmAction = {
|
||||
viewModel.onPinDialogPositiveClick(conversation)
|
||||
},
|
||||
cancelText = UiText.Resource(UiR.string.cancel),
|
||||
onDismissAction = viewModel::onPinDialogDismissed
|
||||
cancelText = stringResource(id = UiR.string.cancel)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun LazyListState.isScrollingUp(): Boolean {
|
||||
var previousIndex by remember(this) { mutableIntStateOf(firstVisibleItemIndex) }
|
||||
var previousScrollOffset by remember(this) { mutableIntStateOf(firstVisibleItemScrollOffset) }
|
||||
return remember(this) {
|
||||
derivedStateOf {
|
||||
if (previousIndex != firstVisibleItemIndex) {
|
||||
previousIndex > firstVisibleItemIndex
|
||||
} else {
|
||||
previousScrollOffset >= firstVisibleItemScrollOffset
|
||||
}.also {
|
||||
previousIndex = firstVisibleItemIndex
|
||||
previousScrollOffset = firstVisibleItemScrollOffset
|
||||
}
|
||||
}
|
||||
}.value
|
||||
}
|
||||
|
||||
@@ -32,15 +32,10 @@ interface SettingsViewModel {
|
||||
val isLongPollBackgroundEnabled: StateFlow<Boolean?>
|
||||
|
||||
fun onLogOutAlertDismissed()
|
||||
|
||||
fun onPerformCrashAlertDismissed()
|
||||
|
||||
fun onPerformCrashPositiveButtonClicked()
|
||||
|
||||
fun onLogOutAlertPositiveClick()
|
||||
|
||||
fun onLongPollingAlertPositiveClicked()
|
||||
fun onLongPollingAlertDismissed()
|
||||
fun onPerformCrashAlertDismissed()
|
||||
fun onPerformCrashPositiveButtonClicked()
|
||||
|
||||
fun onSettingsItemClicked(key: String)
|
||||
fun onSettingsItemLongClicked(key: String)
|
||||
@@ -68,14 +63,6 @@ class SettingsViewModelImpl(
|
||||
emitShowOptions { old -> old.copy(showLogOut = false) }
|
||||
}
|
||||
|
||||
override fun onPerformCrashAlertDismissed() {
|
||||
emitShowOptions { old -> old.copy(showPerformCrash = false) }
|
||||
}
|
||||
|
||||
override fun onPerformCrashPositiveButtonClicked() {
|
||||
throw Exception("Test exception")
|
||||
}
|
||||
|
||||
override fun onLogOutAlertPositiveClick() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
accountsRepository.storeAccounts(
|
||||
@@ -93,18 +80,12 @@ class SettingsViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongPollingAlertPositiveClicked() {
|
||||
screenState.setValue { old -> old.copy(isNeedToRequestNotificationPermission = true) }
|
||||
override fun onPerformCrashAlertDismissed() {
|
||||
emitShowOptions { old -> old.copy(showPerformCrash = false) }
|
||||
}
|
||||
|
||||
override fun onLongPollingAlertDismissed() {
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
showOptions = old.showOptions.copy(
|
||||
showLongPollNotifications = false
|
||||
)
|
||||
)
|
||||
}
|
||||
override fun onPerformCrashPositiveButtonClicked() {
|
||||
throw Exception("Test exception")
|
||||
}
|
||||
|
||||
override fun onSettingsItemClicked(key: String) {
|
||||
|
||||
-2
@@ -3,14 +3,12 @@ package com.meloda.app.fast.settings.model
|
||||
data class SettingsShowOptions(
|
||||
val showLogOut: Boolean,
|
||||
val showPerformCrash: Boolean,
|
||||
val showLongPollNotifications: Boolean
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val EMPTY: SettingsShowOptions = SettingsShowOptions(
|
||||
showLogOut = false,
|
||||
showPerformCrash = false,
|
||||
showLongPollNotifications = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+19
-49
@@ -34,11 +34,11 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.meloda.app.fast.common.UiText
|
||||
import com.meloda.app.fast.common.UserConfig
|
||||
import com.meloda.app.fast.datastore.SettingsKeys
|
||||
import com.meloda.app.fast.datastore.UserSettings
|
||||
import com.meloda.app.fast.datastore.isUsingDarkMode
|
||||
import com.meloda.app.fast.designsystem.ActionInvokeDismiss
|
||||
import com.meloda.app.fast.designsystem.LocalTheme
|
||||
import com.meloda.app.fast.designsystem.MaterialDialog
|
||||
import com.meloda.app.fast.settings.HapticType
|
||||
@@ -118,8 +118,6 @@ fun SettingsRoute(
|
||||
onLogOutButtonClicked()
|
||||
},
|
||||
logoutDismissed = viewModel::onLogOutAlertDismissed,
|
||||
longPollingPositiveClick = viewModel::onLongPollingAlertPositiveClicked,
|
||||
longPollingDismissed = viewModel::onLongPollingAlertDismissed,
|
||||
screenState = screenState
|
||||
)
|
||||
}
|
||||
@@ -273,8 +271,6 @@ fun HandlePopups(
|
||||
performCrashDismissed: () -> Unit,
|
||||
logoutPositiveClick: () -> Unit,
|
||||
logoutDismissed: () -> Unit,
|
||||
longPollingPositiveClick: () -> Unit,
|
||||
longPollingDismissed: () -> Unit,
|
||||
screenState: SettingsScreenState
|
||||
) {
|
||||
val showOptions = screenState.showOptions
|
||||
@@ -290,12 +286,6 @@ fun HandlePopups(
|
||||
dismiss = logoutDismissed,
|
||||
show = showOptions.showLogOut
|
||||
)
|
||||
|
||||
LongPollingNotificationsPermission(
|
||||
positiveClick = longPollingPositiveClick,
|
||||
dismiss = longPollingDismissed,
|
||||
show = showOptions.showLongPollNotifications
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -306,12 +296,13 @@ fun PerformCrashDialog(
|
||||
) {
|
||||
if (show) {
|
||||
MaterialDialog(
|
||||
title = UiText.Simple("Perform Crash"),
|
||||
text = UiText.Simple("App will be crashed. Are you sure?"),
|
||||
confirmText = UiText.Resource(UiR.string.yes),
|
||||
onDismissRequest = dismiss,
|
||||
title = "Perform crash",
|
||||
text = "App will be crashed. Are you sure?",
|
||||
confirmAction = positiveClick,
|
||||
cancelText = UiText.Resource(UiR.string.cancel),
|
||||
onDismissAction = dismiss
|
||||
confirmText = stringResource(id = UiR.string.yes),
|
||||
cancelText = stringResource(id = UiR.string.cancel),
|
||||
actionInvokeDismiss = ActionInvokeDismiss.Always
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -325,41 +316,20 @@ fun LogOutDialog(
|
||||
if (show) {
|
||||
val isEasterEgg = UserConfig.userId == SettingsKeys.ID_DMITRY
|
||||
|
||||
val title = UiText.Resource(
|
||||
if (isEasterEgg) UiR.string.easter_egg_log_out_dmitry
|
||||
MaterialDialog(
|
||||
onDismissRequest = dismiss,
|
||||
title = stringResource(
|
||||
id = if (isEasterEgg) UiR.string.easter_egg_log_out_dmitry
|
||||
else UiR.string.sign_out_confirm_title
|
||||
)
|
||||
|
||||
val positiveText = UiText.Resource(
|
||||
if (isEasterEgg) UiR.string.easter_egg_log_out_dmitry
|
||||
),
|
||||
text = stringResource(id = UiR.string.sign_out_confirm),
|
||||
confirmAction = positiveClick,
|
||||
confirmText = stringResource(
|
||||
id = if (isEasterEgg) UiR.string.easter_egg_log_out_dmitry
|
||||
else UiR.string.action_sign_out
|
||||
)
|
||||
|
||||
MaterialDialog(
|
||||
title = title,
|
||||
text = UiText.Resource(UiR.string.sign_out_confirm),
|
||||
confirmText = positiveText,
|
||||
confirmAction = positiveClick,
|
||||
cancelText = UiText.Resource(UiR.string.cancel),
|
||||
onDismissAction = dismiss
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LongPollingNotificationsPermission(
|
||||
positiveClick: () -> Unit,
|
||||
dismiss: () -> Unit,
|
||||
show: Boolean
|
||||
) {
|
||||
if (show) {
|
||||
MaterialDialog(
|
||||
title = UiText.Resource(UiR.string.warning),
|
||||
text = UiText.Simple("Long polling in background required notifications permission on Android 13 and up"),
|
||||
confirmText = UiText.Simple("Grant"),
|
||||
confirmAction = positiveClick,
|
||||
cancelText = UiText.Resource(UiR.string.cancel),
|
||||
onDismissAction = dismiss
|
||||
),
|
||||
cancelText = stringResource(id = UiR.string.no),
|
||||
actionInvokeDismiss = ActionInvokeDismiss.Always
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user