forked from melod1n/fast-messenger
some improvements, new feature
This commit is contained in:
@@ -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"
|
||||
|
||||
+24
-13
@@ -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(
|
||||
|
||||
+9
-13
@@ -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),
|
||||
|
||||
+3
-2
@@ -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 -> {
|
||||
|
||||
+27
-18
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user