forked from melod1n/fast-messenger
Add theme option to disable animations and fix account avatar loading in bottom bar
This commit is contained in:
@@ -24,6 +24,7 @@ import dev.meloda.fast.datastore.UserSettings
|
|||||||
import dev.meloda.fast.domain.GetCurrentAccountUseCase
|
import dev.meloda.fast.domain.GetCurrentAccountUseCase
|
||||||
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
||||||
import dev.meloda.fast.model.BaseError
|
import dev.meloda.fast.model.BaseError
|
||||||
|
import dev.meloda.fast.model.api.domain.VkUser
|
||||||
import dev.meloda.fast.navigation.Main
|
import dev.meloda.fast.navigation.Main
|
||||||
import dev.meloda.fast.settings.navigation.Settings
|
import dev.meloda.fast.settings.navigation.Settings
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -36,14 +37,13 @@ interface MainViewModel {
|
|||||||
|
|
||||||
val startDestination: StateFlow<Any?>
|
val startDestination: StateFlow<Any?>
|
||||||
val isNeedToReplaceWithAuth: StateFlow<Boolean>
|
val isNeedToReplaceWithAuth: StateFlow<Boolean>
|
||||||
|
val currentUser: StateFlow<VkUser?>
|
||||||
|
|
||||||
val isNeedToShowNotificationsDeniedDialog: StateFlow<Boolean>
|
val isNeedToShowNotificationsDeniedDialog: StateFlow<Boolean>
|
||||||
val isNeedToShowNotificationsRationaleDialog: StateFlow<Boolean>
|
val isNeedToShowNotificationsRationaleDialog: StateFlow<Boolean>
|
||||||
val isNeedToCheckNotificationsPermission: StateFlow<Boolean>
|
val isNeedToCheckNotificationsPermission: StateFlow<Boolean>
|
||||||
val isNeedToRequestNotifications: StateFlow<Boolean>
|
val isNeedToRequestNotifications: StateFlow<Boolean>
|
||||||
|
|
||||||
val profileImageUrl: StateFlow<String?>
|
|
||||||
|
|
||||||
fun onError(error: BaseError)
|
fun onError(error: BaseError)
|
||||||
|
|
||||||
fun onNavigatedToAuth()
|
fun onNavigatedToAuth()
|
||||||
@@ -59,6 +59,8 @@ interface MainViewModel {
|
|||||||
fun onNotificationsDeniedDialogDismissed()
|
fun onNotificationsDeniedDialogDismissed()
|
||||||
fun onNotificationsRationaleDialogDismissed()
|
fun onNotificationsRationaleDialogDismissed()
|
||||||
fun onNotificationsRationaleDialogCancelClicked()
|
fun onNotificationsRationaleDialogCancelClicked()
|
||||||
|
|
||||||
|
fun onUserAuthenticated()
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainViewModelImpl(
|
class MainViewModelImpl(
|
||||||
@@ -70,14 +72,13 @@ class MainViewModelImpl(
|
|||||||
|
|
||||||
override val startDestination = MutableStateFlow<Any?>(null)
|
override val startDestination = MutableStateFlow<Any?>(null)
|
||||||
override val isNeedToReplaceWithAuth = MutableStateFlow(false)
|
override val isNeedToReplaceWithAuth = MutableStateFlow(false)
|
||||||
|
override val currentUser = MutableStateFlow<VkUser?>(null)
|
||||||
|
|
||||||
override val isNeedToShowNotificationsDeniedDialog = MutableStateFlow(false)
|
override val isNeedToShowNotificationsDeniedDialog = MutableStateFlow(false)
|
||||||
override val isNeedToShowNotificationsRationaleDialog = MutableStateFlow(false)
|
override val isNeedToShowNotificationsRationaleDialog = MutableStateFlow(false)
|
||||||
override val isNeedToCheckNotificationsPermission = MutableStateFlow(false)
|
override val isNeedToCheckNotificationsPermission = MutableStateFlow(false)
|
||||||
override val isNeedToRequestNotifications = MutableStateFlow(false)
|
override val isNeedToRequestNotifications = MutableStateFlow(false)
|
||||||
|
|
||||||
override val profileImageUrl = MutableStateFlow<String?>(null)
|
|
||||||
|
|
||||||
private var openNotificationsSettings = false
|
private var openNotificationsSettings = false
|
||||||
private var openAppSettings = false
|
private var openAppSettings = false
|
||||||
|
|
||||||
@@ -170,17 +171,20 @@ class MainViewModelImpl(
|
|||||||
disableBackgroundLongPoll()
|
disableBackgroundLongPoll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onUserAuthenticated() {
|
||||||
|
loadProfile()
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadProfile() {
|
private fun loadProfile() {
|
||||||
loadUserByIdUseCase(userId = null)
|
loadUserByIdUseCase(userId = null)
|
||||||
.listenValue(viewModelScope) { state ->
|
.listenValue(viewModelScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = { error ->
|
||||||
profileImageUrl.emit(null)
|
currentUser.emit(null)
|
||||||
},
|
},
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val user = response ?: return@listenValue
|
val user = response ?: return@listenValue
|
||||||
|
currentUser.emit(user)
|
||||||
profileImageUrl.emit(user.photo100)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package dev.meloda.fast.navigation
|
|||||||
|
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import dev.meloda.fast.MainViewModel
|
|
||||||
import dev.meloda.fast.conversations.navigation.Conversations
|
import dev.meloda.fast.conversations.navigation.Conversations
|
||||||
import dev.meloda.fast.friends.navigation.Friends
|
import dev.meloda.fast.friends.navigation.Friends
|
||||||
import dev.meloda.fast.model.BaseError
|
import dev.meloda.fast.model.BaseError
|
||||||
@@ -25,8 +24,7 @@ fun NavGraphBuilder.mainScreen(
|
|||||||
onConversationClicked: (conversationId: Int) -> Unit,
|
onConversationClicked: (conversationId: Int) -> Unit,
|
||||||
onPhotoClicked: (url: String) -> Unit,
|
onPhotoClicked: (url: String) -> Unit,
|
||||||
onMessageClicked: (userId: Int) -> Unit,
|
onMessageClicked: (userId: Int) -> Unit,
|
||||||
onCreateChatClicked: () -> Unit,
|
onCreateChatClicked: () -> Unit
|
||||||
viewModel: MainViewModel
|
|
||||||
) {
|
) {
|
||||||
val navigationItems = ImmutableList.of(
|
val navigationItems = ImmutableList.of(
|
||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
@@ -57,8 +55,7 @@ fun NavGraphBuilder.mainScreen(
|
|||||||
onConversationItemClicked = onConversationClicked,
|
onConversationItemClicked = onConversationClicked,
|
||||||
onPhotoClicked = onPhotoClicked,
|
onPhotoClicked = onPhotoClicked,
|
||||||
onMessageClicked = onMessageClicked,
|
onMessageClicked = onMessageClicked,
|
||||||
onCreateChatClicked = onCreateChatClicked,
|
onCreateChatClicked = onCreateChatClicked
|
||||||
viewModel = viewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import dev.meloda.fast.common.LongPollController
|
|||||||
import dev.meloda.fast.common.model.LongPollState
|
import dev.meloda.fast.common.model.LongPollState
|
||||||
import dev.meloda.fast.datastore.AppSettings
|
import dev.meloda.fast.datastore.AppSettings
|
||||||
import dev.meloda.fast.datastore.UserSettings
|
import dev.meloda.fast.datastore.UserSettings
|
||||||
|
import dev.meloda.fast.model.api.domain.VkUser
|
||||||
import dev.meloda.fast.service.OnlineService
|
import dev.meloda.fast.service.OnlineService
|
||||||
import dev.meloda.fast.service.longpolling.LongPollingService
|
import dev.meloda.fast.service.longpolling.LongPollingService
|
||||||
import dev.meloda.fast.ui.model.DeviceSize
|
import dev.meloda.fast.ui.model.DeviceSize
|
||||||
@@ -46,6 +47,7 @@ import dev.meloda.fast.ui.model.ThemeConfig
|
|||||||
import dev.meloda.fast.ui.theme.AppTheme
|
import dev.meloda.fast.ui.theme.AppTheme
|
||||||
import dev.meloda.fast.ui.theme.LocalSizeConfig
|
import dev.meloda.fast.ui.theme.LocalSizeConfig
|
||||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
|
import dev.meloda.fast.ui.theme.LocalUser
|
||||||
import dev.meloda.fast.ui.util.isNeedToEnableDarkMode
|
import dev.meloda.fast.ui.util.isNeedToEnableDarkMode
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.koin.compose.KoinContext
|
import org.koin.compose.KoinContext
|
||||||
@@ -98,6 +100,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
||||||
|
|
||||||
|
val currentUser: VkUser? by viewModel.currentUser.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LifecycleResumeEffect(true) {
|
LifecycleResumeEffect(true) {
|
||||||
viewModel.onAppResumed(intent)
|
viewModel.onAppResumed(intent)
|
||||||
onPauseOrDispose {}
|
onPauseOrDispose {}
|
||||||
@@ -202,6 +206,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val enableBlur by userSettings.useBlur.collectAsStateWithLifecycle()
|
val enableBlur by userSettings.useBlur.collectAsStateWithLifecycle()
|
||||||
val enableMultiline by userSettings.enableMultiline.collectAsStateWithLifecycle()
|
val enableMultiline by userSettings.enableMultiline.collectAsStateWithLifecycle()
|
||||||
val useSystemFont by userSettings.useSystemFont.collectAsStateWithLifecycle()
|
val useSystemFont by userSettings.useSystemFont.collectAsStateWithLifecycle()
|
||||||
|
val enableAnimations by userSettings.enableAnimations.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val setDarkMode = isNeedToEnableDarkMode(darkMode = darkMode)
|
val setDarkMode = isNeedToEnableDarkMode(darkMode = darkMode)
|
||||||
|
|
||||||
@@ -214,7 +219,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setDarkMode,
|
setDarkMode,
|
||||||
useSystemFont
|
useSystemFont
|
||||||
) {
|
) {
|
||||||
mutableStateOf(
|
derivedStateOf {
|
||||||
ThemeConfig(
|
ThemeConfig(
|
||||||
darkMode = setDarkMode,
|
darkMode = setDarkMode,
|
||||||
dynamicColors = dynamicColors,
|
dynamicColors = dynamicColors,
|
||||||
@@ -222,14 +227,16 @@ class MainActivity : AppCompatActivity() {
|
|||||||
amoledDark = amoledDark,
|
amoledDark = amoledDark,
|
||||||
enableBlur = enableBlur,
|
enableBlur = enableBlur,
|
||||||
enableMultiline = enableMultiline,
|
enableMultiline = enableMultiline,
|
||||||
useSystemFont = useSystemFont
|
useSystemFont = useSystemFont,
|
||||||
|
enableAnimations = enableAnimations
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalThemeConfig provides themeConfig,
|
LocalThemeConfig provides themeConfig,
|
||||||
LocalSizeConfig provides sizeConfig
|
LocalSizeConfig provides sizeConfig,
|
||||||
|
LocalUser provides currentUser
|
||||||
) {
|
) {
|
||||||
AppTheme(
|
AppTheme(
|
||||||
useDarkTheme = themeConfig.darkMode,
|
useDarkTheme = themeConfig.darkMode,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import androidx.compose.material3.NavigationBarItem
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -27,7 +28,6 @@ import androidx.compose.ui.draw.alpha
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.navigation
|
import androidx.navigation.compose.navigation
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
@@ -36,7 +36,6 @@ import dev.chrisbanes.haze.HazeState
|
|||||||
import dev.chrisbanes.haze.hazeEffect
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
import dev.meloda.fast.MainViewModel
|
|
||||||
import dev.meloda.fast.conversations.navigation.conversationsScreen
|
import dev.meloda.fast.conversations.navigation.conversationsScreen
|
||||||
import dev.meloda.fast.friends.navigation.friendsScreen
|
import dev.meloda.fast.friends.navigation.friendsScreen
|
||||||
import dev.meloda.fast.model.BaseError
|
import dev.meloda.fast.model.BaseError
|
||||||
@@ -45,6 +44,7 @@ import dev.meloda.fast.navigation.MainGraph
|
|||||||
import dev.meloda.fast.profile.navigation.profileScreen
|
import dev.meloda.fast.profile.navigation.profileScreen
|
||||||
import dev.meloda.fast.ui.theme.LocalHazeState
|
import dev.meloda.fast.ui.theme.LocalHazeState
|
||||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
|
import dev.meloda.fast.ui.theme.LocalUser
|
||||||
import dev.meloda.fast.ui.util.ImmutableList
|
import dev.meloda.fast.ui.util.ImmutableList
|
||||||
|
|
||||||
@OptIn(ExperimentalHazeMaterialsApi::class)
|
@OptIn(ExperimentalHazeMaterialsApi::class)
|
||||||
@@ -56,19 +56,21 @@ fun MainScreen(
|
|||||||
onConversationItemClicked: (conversationId: Int) -> Unit = {},
|
onConversationItemClicked: (conversationId: Int) -> Unit = {},
|
||||||
onPhotoClicked: (url: String) -> Unit = {},
|
onPhotoClicked: (url: String) -> Unit = {},
|
||||||
onMessageClicked: (userId: Int) -> Unit = {},
|
onMessageClicked: (userId: Int) -> Unit = {},
|
||||||
onCreateChatClicked: () -> Unit = {},
|
onCreateChatClicked: () -> Unit = {}
|
||||||
viewModel: MainViewModel
|
|
||||||
) {
|
) {
|
||||||
val currentTheme = LocalThemeConfig.current
|
val currentTheme = LocalThemeConfig.current
|
||||||
val hazeState = remember { HazeState() }
|
val hazeState = remember { HazeState() }
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|
||||||
val profileImageUrl by viewModel.profileImageUrl.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
var selectedItemIndex by rememberSaveable {
|
var selectedItemIndex by rememberSaveable {
|
||||||
mutableIntStateOf(1)
|
mutableIntStateOf(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val user = LocalUser.current
|
||||||
|
val profileImageUrl by remember(user) {
|
||||||
|
derivedStateOf { user?.photo100 }
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
@@ -122,9 +124,7 @@ fun MainScreen(
|
|||||||
.size(24.dp)
|
.size(24.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.alpha(if (isLoading) 0f else 1f),
|
.alpha(if (isLoading) 0f else 1f),
|
||||||
onSuccess = {
|
onSuccess = { isLoading = false }
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
@@ -118,7 +118,10 @@ fun RootScreen(
|
|||||||
exitTransition = { fadeOut(animationSpec = tween(200)) }
|
exitTransition = { fadeOut(animationSpec = tween(200)) }
|
||||||
) {
|
) {
|
||||||
authNavGraph(
|
authNavGraph(
|
||||||
onNavigateToMain = navController::navigateToMain,
|
onNavigateToMain = {
|
||||||
|
viewModel.onUserAuthenticated()
|
||||||
|
navController.navigateToMain()
|
||||||
|
},
|
||||||
navController = navController
|
navController = navController
|
||||||
)
|
)
|
||||||
mainScreen(
|
mainScreen(
|
||||||
@@ -127,8 +130,7 @@ fun RootScreen(
|
|||||||
onConversationClicked = navController::navigateToMessagesHistory,
|
onConversationClicked = navController::navigateToMessagesHistory,
|
||||||
onPhotoClicked = { url -> navController.navigateToPhotoView(listOf(url)) },
|
onPhotoClicked = { url -> navController.navigateToPhotoView(listOf(url)) },
|
||||||
onMessageClicked = navController::navigateToMessagesHistory,
|
onMessageClicked = navController::navigateToMessagesHistory,
|
||||||
onCreateChatClicked = navController::navigateToCreateChat,
|
onCreateChatClicked = navController::navigateToCreateChat
|
||||||
viewModel = viewModel
|
|
||||||
)
|
)
|
||||||
|
|
||||||
messagesHistoryScreen(
|
messagesHistoryScreen(
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ interface UserSettings {
|
|||||||
val showEmojiButton: StateFlow<Boolean>
|
val showEmojiButton: StateFlow<Boolean>
|
||||||
val showTimeInActionMessages: StateFlow<Boolean>
|
val showTimeInActionMessages: StateFlow<Boolean>
|
||||||
val useSystemFont: StateFlow<Boolean>
|
val useSystemFont: StateFlow<Boolean>
|
||||||
|
val enableAnimations: StateFlow<Boolean>
|
||||||
val showDebugCategory: StateFlow<Boolean>
|
val showDebugCategory: StateFlow<Boolean>
|
||||||
|
|
||||||
fun onUseContactNamesChanged(use: Boolean)
|
fun onUseContactNamesChanged(use: Boolean)
|
||||||
@@ -68,6 +69,7 @@ class UserSettingsImpl : UserSettings {
|
|||||||
override val showTimeInActionMessages =
|
override val showTimeInActionMessages =
|
||||||
MutableStateFlow(AppSettings.Experimental.showTimeInActionMessages)
|
MutableStateFlow(AppSettings.Experimental.showTimeInActionMessages)
|
||||||
override val useSystemFont = MutableStateFlow(AppSettings.Appearance.useSystemFont)
|
override val useSystemFont = MutableStateFlow(AppSettings.Appearance.useSystemFont)
|
||||||
|
override val enableAnimations = MutableStateFlow(AppSettings.Experimental.moreAnimations)
|
||||||
override val showDebugCategory = MutableStateFlow(AppSettings.Debug.showDebugCategory)
|
override val showDebugCategory = MutableStateFlow(AppSettings.Debug.showDebugCategory)
|
||||||
|
|
||||||
override fun onUseContactNamesChanged(use: Boolean) {
|
override fun onUseContactNamesChanged(use: Boolean) {
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ data class ThemeConfig(
|
|||||||
val amoledDark: Boolean,
|
val amoledDark: Boolean,
|
||||||
val enableBlur: Boolean,
|
val enableBlur: Boolean,
|
||||||
val enableMultiline: Boolean,
|
val enableMultiline: Boolean,
|
||||||
val useSystemFont: Boolean
|
val useSystemFont: Boolean,
|
||||||
|
val enableAnimations: Boolean
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
|
import dev.meloda.fast.model.api.domain.VkUser
|
||||||
import dev.meloda.fast.ui.R
|
import dev.meloda.fast.ui.R
|
||||||
import dev.meloda.fast.ui.model.DeviceSize
|
import dev.meloda.fast.ui.model.DeviceSize
|
||||||
import dev.meloda.fast.ui.model.SizeConfig
|
import dev.meloda.fast.ui.model.SizeConfig
|
||||||
@@ -113,7 +114,8 @@ val LocalThemeConfig = compositionLocalOf {
|
|||||||
amoledDark = false,
|
amoledDark = false,
|
||||||
enableBlur = false,
|
enableBlur = false,
|
||||||
enableMultiline = false,
|
enableMultiline = false,
|
||||||
useSystemFont = false
|
useSystemFont = false,
|
||||||
|
enableAnimations = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,13 +126,9 @@ val LocalSizeConfig = compositionLocalOf {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val LocalHazeState = compositionLocalOf {
|
val LocalHazeState = compositionLocalOf { HazeState() }
|
||||||
HazeState()
|
val LocalBottomPadding = compositionLocalOf { 0.dp }
|
||||||
}
|
val LocalUser = compositionLocalOf<VkUser?> { null }
|
||||||
|
|
||||||
val LocalBottomPadding = compositionLocalOf {
|
|
||||||
0.dp
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppTheme(
|
fun AppTheme(
|
||||||
|
|||||||
+16
-3
@@ -26,7 +26,7 @@ import dev.meloda.fast.conversations.model.ConversationsScreenState
|
|||||||
import dev.meloda.fast.data.UserConfig
|
import dev.meloda.fast.data.UserConfig
|
||||||
import dev.meloda.fast.ui.model.api.ConversationOption
|
import dev.meloda.fast.ui.model.api.ConversationOption
|
||||||
import dev.meloda.fast.ui.model.api.UiConversation
|
import dev.meloda.fast.ui.model.api.UiConversation
|
||||||
import dev.meloda.fast.ui.theme.LocalBottomPadding
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ fun ConversationsList(
|
|||||||
onOptionClicked: (UiConversation, ConversationOption) -> Unit,
|
onOptionClicked: (UiConversation, ConversationOption) -> Unit,
|
||||||
padding: PaddingValues
|
padding: PaddingValues
|
||||||
) {
|
) {
|
||||||
|
val theme = LocalThemeConfig.current
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@@ -68,7 +69,12 @@ fun ConversationsList(
|
|||||||
maxLines = maxLines,
|
maxLines = maxLines,
|
||||||
isUserAccount = isUserAccount,
|
isUserAccount = isUserAccount,
|
||||||
conversation = conversation,
|
conversation = conversation,
|
||||||
modifier = Modifier.animateItem(fadeInSpec = null, fadeOutSpec = null)
|
modifier =
|
||||||
|
if (theme.enableAnimations) Modifier.animateItem(
|
||||||
|
fadeInSpec = null,
|
||||||
|
fadeOutSpec = null
|
||||||
|
)
|
||||||
|
else Modifier
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
@@ -78,7 +84,14 @@ fun ConversationsList(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.animateItem(fadeInSpec = null, fadeOutSpec = null),
|
.then(
|
||||||
|
if (theme.enableAnimations)
|
||||||
|
Modifier.animateItem(
|
||||||
|
fadeInSpec = null,
|
||||||
|
fadeOutSpec = null
|
||||||
|
)
|
||||||
|
else Modifier
|
||||||
|
),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
if (screenState.isPaginating) {
|
if (screenState.isPaginating) {
|
||||||
|
|||||||
+10
-20
@@ -5,10 +5,6 @@ import androidx.compose.animation.animateColorAsState
|
|||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.animateIntAsState
|
import androidx.compose.animation.core.animateIntAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.slideIn
|
|
||||||
import androidx.compose.animation.slideOut
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
@@ -283,23 +279,17 @@ fun ConversationsScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
// AnimatedVisibility(
|
FloatingActionButton(
|
||||||
// visible = listState.isScrollingUp(),
|
onClick = onCreateChatButtonClicked,
|
||||||
// enter = slideIn { IntOffset(0, 600) } + fadeIn(tween(200)),
|
modifier = Modifier.offset {
|
||||||
// exit = slideOut { IntOffset(0, 600) } + fadeOut(tween(200))
|
IntOffset(0, offsetY)
|
||||||
// ) {
|
|
||||||
FloatingActionButton(
|
|
||||||
onClick = onCreateChatButtonClicked,
|
|
||||||
modifier = Modifier.offset {
|
|
||||||
IntOffset(0, offsetY)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(id = UiR.drawable.ic_baseline_create_24),
|
|
||||||
contentDescription = "Add chat button"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
// }
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = UiR.drawable.ic_baseline_create_24),
|
||||||
|
contentDescription = "Add chat button"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(LocalBottomPadding.current))
|
Spacer(modifier = Modifier.height(LocalBottomPadding.current))
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -27,14 +27,21 @@ import androidx.compose.ui.unit.dp
|
|||||||
import coil.compose.rememberAsyncImagePainter
|
import coil.compose.rememberAsyncImagePainter
|
||||||
import coil.imageLoader
|
import coil.imageLoader
|
||||||
import dev.meloda.fast.messageshistory.model.UiItem
|
import dev.meloda.fast.messageshistory.model.UiItem
|
||||||
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun IncomingMessageBubble(
|
fun IncomingMessageBubble(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
message: UiItem.Message,
|
message: UiItem.Message,
|
||||||
animate: Boolean,
|
|
||||||
) {
|
) {
|
||||||
Row(modifier = modifier.fillMaxWidth().then(if (animate) Modifier.animateContentSize() else Modifier),) {
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.then(
|
||||||
|
if (LocalThemeConfig.current.enableAnimations) Modifier.animateContentSize()
|
||||||
|
else Modifier
|
||||||
|
),
|
||||||
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth(0.85f)
|
.fillMaxWidth(0.85f)
|
||||||
@@ -81,13 +88,12 @@ fun IncomingMessageBubble(
|
|||||||
isOut = false,
|
isOut = false,
|
||||||
date = message.date,
|
date = message.date,
|
||||||
edited = message.isEdited,
|
edited = message.isEdited,
|
||||||
animate = animate,
|
|
||||||
isRead = message.isRead,
|
isRead = message.isRead,
|
||||||
sendingStatus = message.sendingStatus,
|
sendingStatus = message.sendingStatus,
|
||||||
pinned = message.isPinned
|
pinned = message.isPinned
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier=Modifier.fillMaxWidth(0.25f))
|
Spacer(modifier = Modifier.fillMaxWidth(0.25f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -30,6 +30,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.meloda.fast.messageshistory.model.SendingStatus
|
import dev.meloda.fast.messageshistory.model.SendingStatus
|
||||||
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
import dev.meloda.fast.ui.R as UiR
|
import dev.meloda.fast.ui.R as UiR
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -39,11 +40,11 @@ fun MessageBubble(
|
|||||||
isOut: Boolean,
|
isOut: Boolean,
|
||||||
date: String?,
|
date: String?,
|
||||||
edited: Boolean,
|
edited: Boolean,
|
||||||
animate: Boolean,
|
|
||||||
isRead: Boolean,
|
isRead: Boolean,
|
||||||
sendingStatus: SendingStatus,
|
sendingStatus: SendingStatus,
|
||||||
pinned: Boolean
|
pinned: Boolean
|
||||||
) {
|
) {
|
||||||
|
val theme = LocalThemeConfig.current
|
||||||
val backgroundColor = if (!isOut) {
|
val backgroundColor = if (!isOut) {
|
||||||
MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp)
|
MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp)
|
||||||
} else {
|
} else {
|
||||||
@@ -65,7 +66,7 @@ fun MessageBubble(
|
|||||||
horizontal = 8.dp,
|
horizontal = 8.dp,
|
||||||
vertical = 6.dp
|
vertical = 6.dp
|
||||||
)
|
)
|
||||||
.then(if (animate) Modifier.animateContentSize() else Modifier),
|
.then(if (theme.enableAnimations) Modifier.animateContentSize() else Modifier),
|
||||||
) {
|
) {
|
||||||
val minDateContainerWidth = remember(edited, isOut) {
|
val minDateContainerWidth = remember(edited, isOut) {
|
||||||
val mainPart = if (edited) 50.dp else 30.dp
|
val mainPart = if (edited) 50.dp else 30.dp
|
||||||
@@ -89,7 +90,7 @@ fun MessageBubble(
|
|||||||
.padding(end = 4.dp)
|
.padding(end = 4.dp)
|
||||||
.padding(end = dateContainerWidth)
|
.padding(end = dateContainerWidth)
|
||||||
.padding(end = 4.dp)
|
.padding(end = 4.dp)
|
||||||
.then(if (animate) Modifier.animateContentSize() else Modifier),
|
.then(if (theme.enableAnimations) Modifier.animateContentSize() else Modifier),
|
||||||
color = textColor
|
color = textColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -98,7 +99,7 @@ fun MessageBubble(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomEnd)
|
.align(Alignment.BottomEnd)
|
||||||
.defaultMinSize(minWidth = dateContainerWidth)
|
.defaultMinSize(minWidth = dateContainerWidth)
|
||||||
.then(if (animate) Modifier.animateContentSize() else Modifier),
|
.then(if (theme.enableAnimations) Modifier.animateContentSize() else Modifier),
|
||||||
) {
|
) {
|
||||||
if (pinned) {
|
if (pinned) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
+8
-13
@@ -49,20 +49,17 @@ fun MessagesList(
|
|||||||
onMessageClicked: (Int) -> Unit = {},
|
onMessageClicked: (Int) -> Unit = {},
|
||||||
onMessageLongClicked: (Int) -> Unit = {}
|
onMessageLongClicked: (Int) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val enableAnimations = remember {
|
|
||||||
AppSettings.Experimental.moreAnimations
|
|
||||||
}
|
|
||||||
val messages = remember(immutableMessages) {
|
val messages = remember(immutableMessages) {
|
||||||
immutableMessages.toList()
|
immutableMessages.toList()
|
||||||
}
|
}
|
||||||
val currentTheme = LocalThemeConfig.current
|
val theme = LocalThemeConfig.current
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.then(
|
.then(
|
||||||
if (currentTheme.enableBlur) {
|
if (theme.enableBlur) {
|
||||||
Modifier.hazeSource(state = hazeState)
|
Modifier.hazeSource(state = hazeState)
|
||||||
} else Modifier
|
} else Modifier
|
||||||
),
|
),
|
||||||
@@ -93,7 +90,7 @@ fun MessagesList(
|
|||||||
is UiItem.ActionMessage -> {
|
is UiItem.ActionMessage -> {
|
||||||
ActionMessageItem(
|
ActionMessageItem(
|
||||||
modifier = Modifier.then(
|
modifier = Modifier.then(
|
||||||
if (enableAnimations) Modifier.animateItem(
|
if (theme.enableAnimations) Modifier.animateItem(
|
||||||
fadeInSpec = null,
|
fadeInSpec = null,
|
||||||
fadeOutSpec = null
|
fadeOutSpec = null
|
||||||
) else Modifier
|
) else Modifier
|
||||||
@@ -119,7 +116,7 @@ fun MessagesList(
|
|||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(
|
.then(
|
||||||
if (enableAnimations) Modifier.animateItem(
|
if (theme.enableAnimations) Modifier.animateItem(
|
||||||
fadeInSpec = null,
|
fadeInSpec = null,
|
||||||
fadeOutSpec = null
|
fadeOutSpec = null
|
||||||
) else Modifier
|
) else Modifier
|
||||||
@@ -141,14 +138,13 @@ fun MessagesList(
|
|||||||
Modifier
|
Modifier
|
||||||
.padding(vertical = 4.dp)
|
.padding(vertical = 4.dp)
|
||||||
.then(
|
.then(
|
||||||
if (enableAnimations) Modifier.animateItem(
|
if (theme.enableAnimations) Modifier.animateItem(
|
||||||
fadeInSpec = null,
|
fadeInSpec = null,
|
||||||
fadeOutSpec = null
|
fadeOutSpec = null
|
||||||
)
|
)
|
||||||
else Modifier
|
else Modifier
|
||||||
),
|
),
|
||||||
message = item,
|
message = item
|
||||||
animate = enableAnimations
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
IncomingMessageBubble(
|
IncomingMessageBubble(
|
||||||
@@ -156,14 +152,13 @@ fun MessagesList(
|
|||||||
Modifier
|
Modifier
|
||||||
.padding(vertical = 4.dp)
|
.padding(vertical = 4.dp)
|
||||||
.then(
|
.then(
|
||||||
if (enableAnimations) Modifier.animateItem(
|
if (theme.enableAnimations) Modifier.animateItem(
|
||||||
fadeInSpec = null,
|
fadeInSpec = null,
|
||||||
fadeOutSpec = null
|
fadeOutSpec = null
|
||||||
)
|
)
|
||||||
else Modifier
|
else Modifier
|
||||||
),
|
),
|
||||||
message = item,
|
message = item
|
||||||
animate = enableAnimations
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-3
@@ -12,15 +12,20 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.meloda.fast.common.extensions.orDots
|
import dev.meloda.fast.common.extensions.orDots
|
||||||
import dev.meloda.fast.messageshistory.model.UiItem
|
import dev.meloda.fast.messageshistory.model.UiItem
|
||||||
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun OutgoingMessageBubble(
|
fun OutgoingMessageBubble(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
message: UiItem.Message,
|
message: UiItem.Message,
|
||||||
animate: Boolean
|
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth().then(if (animate) Modifier.animateContentSize() else Modifier),
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.then(
|
||||||
|
if (LocalThemeConfig.current.enableAnimations) Modifier.animateContentSize()
|
||||||
|
else Modifier
|
||||||
|
),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.End
|
horizontalArrangement = Arrangement.End
|
||||||
) {
|
) {
|
||||||
@@ -37,7 +42,6 @@ fun OutgoingMessageBubble(
|
|||||||
isOut = true,
|
isOut = true,
|
||||||
date = message.date,
|
date = message.date,
|
||||||
edited = message.isEdited,
|
edited = message.isEdited,
|
||||||
animate = animate,
|
|
||||||
isRead = message.isRead,
|
isRead = message.isRead,
|
||||||
sendingStatus = message.sendingStatus,
|
sendingStatus = message.sendingStatus,
|
||||||
pinned = message.isPinned
|
pinned = message.isPinned
|
||||||
|
|||||||
Reference in New Issue
Block a user