forked from melod1n/fast-messenger
some improvements, new feature
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
package dev.meloda.fast.common.extensions
|
package dev.meloda.fast.common.extensions
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -11,6 +13,7 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlin.reflect.KClass
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@@ -103,7 +106,7 @@ fun Any.asInt(): Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Any.asLong(): Long {
|
fun Any.asLong(): Long {
|
||||||
return when(this) {
|
return when (this) {
|
||||||
is Number -> this.toLong()
|
is Number -> this.toLong()
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Object is not numeric")
|
else -> throw IllegalArgumentException("Object is not numeric")
|
||||||
@@ -117,3 +120,19 @@ fun <T> Any.toList(mapper: (old: Any) -> T): List<T> {
|
|||||||
else -> emptyList()
|
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)
|
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
|
var enableHaptic: Boolean
|
||||||
get() = get(
|
get() = get(
|
||||||
SettingsKeys.KEY_ENABLE_HAPTIC,
|
SettingsKeys.KEY_ENABLE_HAPTIC,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ object SettingsKeys {
|
|||||||
const val DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON = false
|
const val DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON = false
|
||||||
const val KEY_SHOW_ATTACHMENT_BUTTON = "show_attachment_button"
|
const val KEY_SHOW_ATTACHMENT_BUTTON = "show_attachment_button"
|
||||||
const val DEFAULT_VALUE_SHOW_ATTACHMENT_BUTTON = false
|
const val DEFAULT_VALUE_SHOW_ATTACHMENT_BUTTON = false
|
||||||
const val KEY_SHOW_RECORD_VOICE_BUTTON = "show_record_voice_button"
|
const val KEY_SHOW_MANUAL_REFRESH_OPTIONS = "show_manual_refresh_options"
|
||||||
const val DEFAULT_VALUE_SHOW_RECORD_VOICE_BUTTON = false
|
const val DEFAULT_VALUE_SHOW_MANUAL_REFRESH_OPTIONS = false
|
||||||
|
|
||||||
const val KEY_APPEARANCE = "appearance"
|
const val KEY_APPEARANCE = "appearance"
|
||||||
const val KEY_APPEARANCE_MULTILINE = "appearance_multiline"
|
const val KEY_APPEARANCE_MULTILINE = "appearance_multiline"
|
||||||
|
|||||||
+32
-21
@@ -63,7 +63,9 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
|||||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
import dev.meloda.fast.conversations.model.ConversationsScreenState
|
import dev.meloda.fast.conversations.model.ConversationsScreenState
|
||||||
import dev.meloda.fast.conversations.navigation.ConversationsGraph
|
import dev.meloda.fast.conversations.navigation.ConversationsGraph
|
||||||
|
import dev.meloda.fast.datastore.AppSettings
|
||||||
import dev.meloda.fast.model.BaseError
|
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.FullScreenContainedLoader
|
||||||
import dev.meloda.fast.ui.components.NoItemsView
|
import dev.meloda.fast.ui.components.NoItemsView
|
||||||
import dev.meloda.fast.ui.components.VkErrorView
|
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 dev.meloda.fast.ui.util.isScrollingUp
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
import dev.meloda.fast.ui.R
|
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
ExperimentalMaterial3Api::class,
|
ExperimentalMaterial3Api::class,
|
||||||
@@ -215,11 +216,35 @@ fun ConversationsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton(onClick = { dropDownMenuExpanded = true }) {
|
val dropDownItems = mutableListOf<@Composable () -> Unit>()
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.MoreVert,
|
if (AppSettings.General.showManualRefreshOptions) {
|
||||||
contentDescription = null
|
dropDownItems += {
|
||||||
)
|
DropdownMenuItem(
|
||||||
|
onClick = {
|
||||||
|
onRefreshDropdownItemClicked()
|
||||||
|
dropDownMenuExpanded = false
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(id = R.string.action_refresh))
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Refresh,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropDownItems.isNotEmpty()) {
|
||||||
|
IconButton(onClick = { dropDownMenuExpanded = true }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.MoreVert,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
@@ -228,21 +253,7 @@ fun ConversationsScreen(
|
|||||||
onDismissRequest = { dropDownMenuExpanded = false },
|
onDismissRequest = { dropDownMenuExpanded = false },
|
||||||
offset = DpOffset(x = (-4).dp, y = (-60).dp)
|
offset = DpOffset(x = (-4).dp, y = (-60).dp)
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
dropDownItems.forEach { it.invoke() }
|
||||||
onClick = {
|
|
||||||
onRefreshDropdownItemClicked()
|
|
||||||
dropDownMenuExpanded = false
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = stringResource(id = R.string.action_refresh))
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Refresh,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
|
|||||||
+9
-13
@@ -31,6 +31,7 @@ import androidx.compose.runtime.mutableIntStateOf
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -69,7 +70,7 @@ fun FriendsRoute(
|
|||||||
val currentTheme = LocalThemeConfig.current
|
val currentTheme = LocalThemeConfig.current
|
||||||
val hazeState = LocalHazeState.current
|
val hazeState = LocalHazeState.current
|
||||||
|
|
||||||
var canScrollBackward by remember {
|
var canScrollBackward by rememberSaveable {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,35 +116,30 @@ fun FriendsRoute(
|
|||||||
derivedStateOf { pagerState.currentPage }
|
derivedStateOf { pagerState.currentPage }
|
||||||
}
|
}
|
||||||
|
|
||||||
var orderType: String by remember { mutableStateOf("hints") }
|
var orderType: String by rememberSaveable { mutableStateOf("hints") }
|
||||||
|
var showOrderDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
var showOrderDialog by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
val orderPriority = stringResource(R.string.friends_order_priority)
|
val orderPriority = stringResource(R.string.friends_order_priority)
|
||||||
val orderName = stringResource(R.string.friends_order_name)
|
val orderName = stringResource(R.string.friends_order_name)
|
||||||
val orderRandom = stringResource(R.string.friends_order_random)
|
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 {
|
val orderTitleItems = remember {
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
orderPriority,
|
orderPriority,
|
||||||
orderName,
|
orderName,
|
||||||
orderRandom,
|
orderRandom,
|
||||||
orderMobile,
|
|
||||||
orderSmart
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val orderItems = remember {
|
val orderItems = remember {
|
||||||
listOf("hints", "name", "random", "mobile", "smart")
|
listOf("hints", "name", "random")
|
||||||
}
|
|
||||||
|
|
||||||
var selectedIndex by remember {
|
|
||||||
mutableIntStateOf(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showOrderDialog) {
|
if (showOrderDialog) {
|
||||||
|
var selectedIndex by remember {
|
||||||
|
mutableIntStateOf(orderItems.indexOf(orderType))
|
||||||
|
}
|
||||||
|
|
||||||
MaterialDialog(
|
MaterialDialog(
|
||||||
onDismissRequest = { showOrderDialog = false },
|
onDismissRequest = { showOrderDialog = false },
|
||||||
confirmText = stringResource(R.string.ok),
|
confirmText = stringResource(R.string.ok),
|
||||||
|
|||||||
+3
-2
@@ -25,6 +25,7 @@ import coil.imageLoader
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.conena.nanokt.collections.indexOfFirstOrNull
|
import com.conena.nanokt.collections.indexOfFirstOrNull
|
||||||
import dev.meloda.fast.common.VkConstants
|
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.listenValue
|
||||||
import dev.meloda.fast.common.extensions.orDots
|
import dev.meloda.fast.common.extensions.orDots
|
||||||
import dev.meloda.fast.common.extensions.removeIfCompat
|
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.VkMessage
|
||||||
import dev.meloda.fast.model.api.domain.VkPhotoDomain
|
import dev.meloda.fast.model.api.domain.VkPhotoDomain
|
||||||
import dev.meloda.fast.network.VkErrorCode
|
import dev.meloda.fast.network.VkErrorCode
|
||||||
|
import dev.meloda.fast.ui.R
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@@ -67,7 +69,6 @@ import java.io.File
|
|||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import dev.meloda.fast.ui.R
|
|
||||||
|
|
||||||
interface MessagesHistoryViewModel {
|
interface MessagesHistoryViewModel {
|
||||||
|
|
||||||
@@ -267,7 +268,7 @@ class MessagesHistoryViewModelImpl(
|
|||||||
override fun onDialogItemPicked(dialog: MessageDialog, bundle: Bundle) {
|
override fun onDialogItemPicked(dialog: MessageDialog, bundle: Bundle) {
|
||||||
when (dialog) {
|
when (dialog) {
|
||||||
is MessageDialog.MessageOptions -> {
|
is MessageDialog.MessageOptions -> {
|
||||||
when (val option = bundle.getParcelable<MessageOption>("option")) {
|
when (val option = bundle.getParcelableCompat("option", MessageOption::class)) {
|
||||||
null -> Unit
|
null -> Unit
|
||||||
|
|
||||||
MessageOption.Retry -> {
|
MessageOption.Retry -> {
|
||||||
|
|||||||
+33
-24
@@ -51,11 +51,10 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
|||||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
import dev.meloda.fast.common.model.UiImage
|
import dev.meloda.fast.common.model.UiImage
|
||||||
import dev.meloda.fast.datastore.AppSettings
|
import dev.meloda.fast.datastore.AppSettings
|
||||||
|
import dev.meloda.fast.ui.R
|
||||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
import dev.meloda.fast.ui.util.getImage
|
import dev.meloda.fast.ui.util.getImage
|
||||||
|
|
||||||
import dev.meloda.fast.ui.R
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MessagesHistoryTopBar(
|
fun MessagesHistoryTopBar(
|
||||||
@@ -221,13 +220,37 @@ fun MessagesHistoryTopBar(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
IconButton(
|
val dropDownItems = mutableListOf<@Composable () -> Unit>()
|
||||||
onClick = { dropDownMenuExpanded = true }
|
|
||||||
) {
|
if (AppSettings.General.showManualRefreshOptions) {
|
||||||
Icon(
|
dropDownItems += {
|
||||||
imageVector = Icons.Outlined.MoreVert,
|
DropdownMenuItem(
|
||||||
contentDescription = "Options"
|
onClick = {
|
||||||
)
|
onRefresh()
|
||||||
|
dropDownMenuExpanded = false
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(R.string.action_refresh))
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Refresh,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropDownItems.isNotEmpty()) {
|
||||||
|
IconButton(
|
||||||
|
onClick = { dropDownMenuExpanded = true }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.MoreVert,
|
||||||
|
contentDescription = "Options"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
@@ -238,21 +261,7 @@ fun MessagesHistoryTopBar(
|
|||||||
},
|
},
|
||||||
offset = DpOffset(x = (-4).dp, y = (-60).dp)
|
offset = DpOffset(x = (-4).dp, y = (-60).dp)
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
dropDownItems.forEach { it.invoke() }
|
||||||
onClick = {
|
|
||||||
onRefresh()
|
|
||||||
dropDownMenuExpanded = false
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = stringResource(R.string.action_refresh))
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Refresh,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import dev.meloda.fast.settings.model.SettingsDialog
|
|||||||
import dev.meloda.fast.settings.model.SettingsItem
|
import dev.meloda.fast.settings.model.SettingsItem
|
||||||
import dev.meloda.fast.settings.model.SettingsScreenState
|
import dev.meloda.fast.settings.model.SettingsScreenState
|
||||||
import dev.meloda.fast.settings.model.TextProvider
|
import dev.meloda.fast.settings.model.TextProvider
|
||||||
|
import dev.meloda.fast.ui.R
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
@@ -37,7 +38,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import dev.meloda.fast.ui.R
|
|
||||||
|
|
||||||
class SettingsViewModel(
|
class SettingsViewModel(
|
||||||
private val loadUserByIdUseCase: LoadUserByIdUseCase,
|
private val loadUserByIdUseCase: LoadUserByIdUseCase,
|
||||||
@@ -351,6 +351,12 @@ class SettingsViewModel(
|
|||||||
text = UiText.Resource(R.string.settings_general_show_attachment_button_summary),
|
text = UiText.Resource(R.string.settings_general_show_attachment_button_summary),
|
||||||
defaultValue = SettingsKeys.DEFAULT_VALUE_SHOW_ATTACHMENT_BUTTON
|
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(
|
val generalEnableHaptic = SettingsItem.Switch(
|
||||||
key = SettingsKeys.KEY_ENABLE_HAPTIC,
|
key = SettingsKeys.KEY_ENABLE_HAPTIC,
|
||||||
defaultValue = SettingsKeys.DEFAULT_ENABLE_HAPTIC,
|
defaultValue = SettingsKeys.DEFAULT_ENABLE_HAPTIC,
|
||||||
@@ -538,6 +544,7 @@ class SettingsViewModel(
|
|||||||
generalUseContactNames,
|
generalUseContactNames,
|
||||||
generalShowEmojiButton,
|
generalShowEmojiButton,
|
||||||
generalShowAttachmentButton,
|
generalShowAttachmentButton,
|
||||||
|
generalShowManualRefreshOptions,
|
||||||
generalEnableHaptic
|
generalEnableHaptic
|
||||||
)
|
)
|
||||||
val appearanceList = listOf(
|
val appearanceList = listOf(
|
||||||
|
|||||||
Reference in New Issue
Block a user