some improvements, new feature

This commit is contained in:
2025-08-27 05:17:40 +03:00
parent 4677e484d9
commit 8cb3ed8784
8 changed files with 114 additions and 64 deletions
@@ -1,5 +1,7 @@
package dev.meloda.fast.common.extensions
import android.os.Build
import android.os.Bundle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@@ -11,6 +13,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.update
import kotlin.reflect.KClass
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
@@ -103,7 +106,7 @@ fun Any.asInt(): Int {
}
fun Any.asLong(): Long {
return when(this) {
return when (this) {
is Number -> this.toLong()
else -> throw IllegalArgumentException("Object is not numeric")
@@ -117,3 +120,19 @@ fun <T> Any.toList(mapper: (old: Any) -> T): List<T> {
else -> emptyList()
}
}
fun <T> Bundle.getParcelableCompat(key: String, clazz: Class<T>): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, clazz)
} else {
getParcelable(key)
}
}
fun <T : Any> Bundle.getParcelableCompat(key: String, clazz: KClass<T>): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, clazz.java)
} else {
getParcelable(key)
}
}
@@ -103,6 +103,13 @@ object AppSettings {
)
set(value) = put(SettingsKeys.KEY_SHOW_ATTACHMENT_BUTTON, value)
var showManualRefreshOptions: Boolean
get() = get(
SettingsKeys.KEY_SHOW_MANUAL_REFRESH_OPTIONS,
SettingsKeys.DEFAULT_VALUE_SHOW_MANUAL_REFRESH_OPTIONS
)
set(value) = put(SettingsKeys.KEY_SHOW_MANUAL_REFRESH_OPTIONS, value)
var enableHaptic: Boolean
get() = get(
SettingsKeys.KEY_ENABLE_HAPTIC,
@@ -13,8 +13,8 @@ object SettingsKeys {
const val DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON = false
const val KEY_SHOW_ATTACHMENT_BUTTON = "show_attachment_button"
const val DEFAULT_VALUE_SHOW_ATTACHMENT_BUTTON = false
const val KEY_SHOW_RECORD_VOICE_BUTTON = "show_record_voice_button"
const val DEFAULT_VALUE_SHOW_RECORD_VOICE_BUTTON = false
const val KEY_SHOW_MANUAL_REFRESH_OPTIONS = "show_manual_refresh_options"
const val DEFAULT_VALUE_SHOW_MANUAL_REFRESH_OPTIONS = false
const val KEY_APPEARANCE = "appearance"
const val KEY_APPEARANCE_MULTILINE = "appearance_multiline"
@@ -63,7 +63,9 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import dev.meloda.fast.conversations.model.ConversationsScreenState
import dev.meloda.fast.conversations.navigation.ConversationsGraph
import dev.meloda.fast.datastore.AppSettings
import dev.meloda.fast.model.BaseError
import dev.meloda.fast.ui.R
import dev.meloda.fast.ui.components.FullScreenContainedLoader
import dev.meloda.fast.ui.components.NoItemsView
import dev.meloda.fast.ui.components.VkErrorView
@@ -78,7 +80,6 @@ import dev.meloda.fast.ui.util.emptyImmutableList
import dev.meloda.fast.ui.util.isScrollingUp
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import dev.meloda.fast.ui.R
@OptIn(
ExperimentalMaterial3Api::class,
@@ -215,19 +216,10 @@ fun ConversationsScreen(
}
}
IconButton(onClick = { dropDownMenuExpanded = true }) {
Icon(
imageVector = Icons.Rounded.MoreVert,
contentDescription = null
)
}
val dropDownItems = mutableListOf<@Composable () -> Unit>()
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
expanded = dropDownMenuExpanded,
onDismissRequest = { dropDownMenuExpanded = false },
offset = DpOffset(x = (-4).dp, y = (-60).dp)
) {
if (AppSettings.General.showManualRefreshOptions) {
dropDownItems += {
DropdownMenuItem(
onClick = {
onRefreshDropdownItemClicked()
@@ -244,6 +236,25 @@ fun ConversationsScreen(
}
)
}
}
if (dropDownItems.isNotEmpty()) {
IconButton(onClick = { dropDownMenuExpanded = true }) {
Icon(
imageVector = Icons.Rounded.MoreVert,
contentDescription = null
)
}
}
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
expanded = dropDownMenuExpanded,
onDismissRequest = { dropDownMenuExpanded = false },
offset = DpOffset(x = (-4).dp, y = (-60).dp)
) {
dropDownItems.forEach { it.invoke() }
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = toolbarContainerColor.copy(
@@ -31,6 +31,7 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -69,7 +70,7 @@ fun FriendsRoute(
val currentTheme = LocalThemeConfig.current
val hazeState = LocalHazeState.current
var canScrollBackward by remember {
var canScrollBackward by rememberSaveable {
mutableStateOf(false)
}
@@ -115,35 +116,30 @@ fun FriendsRoute(
derivedStateOf { pagerState.currentPage }
}
var orderType: String by remember { mutableStateOf("hints") }
var showOrderDialog by remember { mutableStateOf(false) }
var orderType: String by rememberSaveable { mutableStateOf("hints") }
var showOrderDialog by rememberSaveable { mutableStateOf(false) }
val orderPriority = stringResource(R.string.friends_order_priority)
val orderName = stringResource(R.string.friends_order_name)
val orderRandom = stringResource(R.string.friends_order_random)
val orderMobile = stringResource(R.string.friends_order_mobile)
val orderSmart = stringResource(R.string.friends_order_smart)
val orderTitleItems = remember {
ImmutableList.of(
orderPriority,
orderName,
orderRandom,
orderMobile,
orderSmart
)
}
val orderItems = remember {
listOf("hints", "name", "random", "mobile", "smart")
}
var selectedIndex by remember {
mutableIntStateOf(0)
listOf("hints", "name", "random")
}
if (showOrderDialog) {
var selectedIndex by remember {
mutableIntStateOf(orderItems.indexOf(orderType))
}
MaterialDialog(
onDismissRequest = { showOrderDialog = false },
confirmText = stringResource(R.string.ok),
@@ -25,6 +25,7 @@ import coil.imageLoader
import coil.request.ImageRequest
import com.conena.nanokt.collections.indexOfFirstOrNull
import dev.meloda.fast.common.VkConstants
import dev.meloda.fast.common.extensions.getParcelableCompat
import dev.meloda.fast.common.extensions.listenValue
import dev.meloda.fast.common.extensions.orDots
import dev.meloda.fast.common.extensions.removeIfCompat
@@ -57,6 +58,7 @@ import dev.meloda.fast.model.api.domain.VkAttachment
import dev.meloda.fast.model.api.domain.VkMessage
import dev.meloda.fast.model.api.domain.VkPhotoDomain
import dev.meloda.fast.network.VkErrorCode
import dev.meloda.fast.ui.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -67,7 +69,6 @@ import java.io.File
import java.io.FileOutputStream
import kotlin.math.abs
import kotlin.random.Random
import dev.meloda.fast.ui.R
interface MessagesHistoryViewModel {
@@ -267,7 +268,7 @@ class MessagesHistoryViewModelImpl(
override fun onDialogItemPicked(dialog: MessageDialog, bundle: Bundle) {
when (dialog) {
is MessageDialog.MessageOptions -> {
when (val option = bundle.getParcelable<MessageOption>("option")) {
when (val option = bundle.getParcelableCompat("option", MessageOption::class)) {
null -> Unit
MessageOption.Retry -> {
@@ -51,11 +51,10 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import dev.meloda.fast.common.model.UiImage
import dev.meloda.fast.datastore.AppSettings
import dev.meloda.fast.ui.R
import dev.meloda.fast.ui.theme.LocalThemeConfig
import dev.meloda.fast.ui.util.getImage
import dev.meloda.fast.ui.R
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
@Composable
fun MessagesHistoryTopBar(
@@ -221,23 +220,10 @@ fun MessagesHistoryTopBar(
)
}
} else {
IconButton(
onClick = { dropDownMenuExpanded = true }
) {
Icon(
imageVector = Icons.Outlined.MoreVert,
contentDescription = "Options"
)
}
val dropDownItems = mutableListOf<@Composable () -> Unit>()
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
expanded = dropDownMenuExpanded,
onDismissRequest = {
dropDownMenuExpanded = false
},
offset = DpOffset(x = (-4).dp, y = (-60).dp)
) {
if (AppSettings.General.showManualRefreshOptions) {
dropDownItems += {
DropdownMenuItem(
onClick = {
onRefresh()
@@ -255,6 +241,29 @@ fun MessagesHistoryTopBar(
)
}
}
if (dropDownItems.isNotEmpty()) {
IconButton(
onClick = { dropDownMenuExpanded = true }
) {
Icon(
imageVector = Icons.Outlined.MoreVert,
contentDescription = "Options"
)
}
}
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
expanded = dropDownMenuExpanded,
onDismissRequest = {
dropDownMenuExpanded = false
},
offset = DpOffset(x = (-4).dp, y = (-60).dp)
) {
dropDownItems.forEach { it.invoke() }
}
}
}
)
}
@@ -30,6 +30,7 @@ import dev.meloda.fast.settings.model.SettingsDialog
import dev.meloda.fast.settings.model.SettingsItem
import dev.meloda.fast.settings.model.SettingsScreenState
import dev.meloda.fast.settings.model.TextProvider
import dev.meloda.fast.ui.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@@ -37,7 +38,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import dev.meloda.fast.ui.R
class SettingsViewModel(
private val loadUserByIdUseCase: LoadUserByIdUseCase,
@@ -351,6 +351,12 @@ class SettingsViewModel(
text = UiText.Resource(R.string.settings_general_show_attachment_button_summary),
defaultValue = SettingsKeys.DEFAULT_VALUE_SHOW_ATTACHMENT_BUTTON
)
val generalShowManualRefreshOptions = SettingsItem.Switch(
key = SettingsKeys.KEY_SHOW_MANUAL_REFRESH_OPTIONS,
defaultValue = SettingsKeys.DEFAULT_VALUE_SHOW_MANUAL_REFRESH_OPTIONS,
title = UiText.Simple("Refresh options"),
text = UiText.Simple("Show manual refresh options in some screens")
)
val generalEnableHaptic = SettingsItem.Switch(
key = SettingsKeys.KEY_ENABLE_HAPTIC,
defaultValue = SettingsKeys.DEFAULT_ENABLE_HAPTIC,
@@ -538,6 +544,7 @@ class SettingsViewModel(
generalUseContactNames,
generalShowEmojiButton,
generalShowAttachmentButton,
generalShowManualRefreshOptions,
generalEnableHaptic
)
val appearanceList = listOf(