forked from melod1n/fast-messenger
profile avatar in bottom bar;
ability to open app settings from system's about app screen
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
<application
|
<application
|
||||||
android:name="dev.meloda.fast.common.AppGlobal"
|
android:name="dev.meloda.fast.common.AppGlobal"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:appCategory="social"
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:fullBackupOnly="true"
|
android:fullBackupOnly="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
@@ -16,7 +18,8 @@
|
|||||||
android:localeConfig="@xml/locales_config"
|
android:localeConfig="@xml/locales_config"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
tools:targetApi="tiramisu">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".presentation.MainActivity"
|
android:name=".presentation.MainActivity"
|
||||||
@@ -27,6 +30,10 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<category android:name="android.intent.category.NOTIFICATION_PREFERENCES" />
|
||||||
|
|
||||||
|
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package dev.meloda.fast
|
package dev.meloda.fast
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.core.os.LocaleListCompat
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.conena.nanokt.android.os.isMinSdk
|
||||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||||
import com.google.accompanist.permissions.PermissionStatus
|
import com.google.accompanist.permissions.PermissionStatus
|
||||||
import dev.meloda.fast.auth.AuthGraph
|
import dev.meloda.fast.auth.AuthGraph
|
||||||
@@ -15,11 +18,14 @@ import dev.meloda.fast.common.extensions.listenValue
|
|||||||
import dev.meloda.fast.common.extensions.setValue
|
import dev.meloda.fast.common.extensions.setValue
|
||||||
import dev.meloda.fast.common.model.LongPollState
|
import dev.meloda.fast.common.model.LongPollState
|
||||||
import dev.meloda.fast.data.UserConfig
|
import dev.meloda.fast.data.UserConfig
|
||||||
import dev.meloda.fast.domain.GetCurrentAccountUseCase
|
import dev.meloda.fast.data.processState
|
||||||
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.domain.GetCurrentAccountUseCase
|
||||||
|
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
||||||
import dev.meloda.fast.model.BaseError
|
import dev.meloda.fast.model.BaseError
|
||||||
import dev.meloda.fast.navigation.Main
|
import dev.meloda.fast.navigation.Main
|
||||||
|
import dev.meloda.fast.settings.navigation.Settings
|
||||||
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
|
||||||
@@ -36,11 +42,13 @@ interface MainViewModel {
|
|||||||
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()
|
||||||
|
|
||||||
fun onAppResumed()
|
fun onAppResumed(intent: Intent)
|
||||||
|
|
||||||
@OptIn(ExperimentalPermissionsApi::class)
|
@OptIn(ExperimentalPermissionsApi::class)
|
||||||
fun onPermissionCheckStatus(status: PermissionStatus)
|
fun onPermissionCheckStatus(status: PermissionStatus)
|
||||||
@@ -55,14 +63,11 @@ interface MainViewModel {
|
|||||||
|
|
||||||
class MainViewModelImpl(
|
class MainViewModelImpl(
|
||||||
private val getCurrentAccountUseCase: GetCurrentAccountUseCase,
|
private val getCurrentAccountUseCase: GetCurrentAccountUseCase,
|
||||||
|
private val loadUserByIdUseCase: LoadUserByIdUseCase,
|
||||||
private val userSettings: UserSettings,
|
private val userSettings: UserSettings,
|
||||||
private val longPollController: LongPollController
|
private val longPollController: LongPollController
|
||||||
) : MainViewModel, ViewModel() {
|
) : MainViewModel, ViewModel() {
|
||||||
|
|
||||||
init {
|
|
||||||
loadAccounts()
|
|
||||||
}
|
|
||||||
|
|
||||||
override val startDestination = MutableStateFlow<Any?>(null)
|
override val startDestination = MutableStateFlow<Any?>(null)
|
||||||
override val isNeedToReplaceWithAuth = MutableStateFlow(false)
|
override val isNeedToReplaceWithAuth = MutableStateFlow(false)
|
||||||
|
|
||||||
@@ -71,6 +76,11 @@ class MainViewModelImpl(
|
|||||||
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 openAppSettings = false
|
||||||
|
|
||||||
override fun onError(error: BaseError) {
|
override fun onError(error: BaseError) {
|
||||||
when (error) {
|
when (error) {
|
||||||
BaseError.SessionExpired -> {
|
BaseError.SessionExpired -> {
|
||||||
@@ -83,7 +93,12 @@ class MainViewModelImpl(
|
|||||||
isNeedToReplaceWithAuth.update { false }
|
isNeedToReplaceWithAuth.update { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAppResumed() {
|
override fun onAppResumed(intent: Intent) {
|
||||||
|
openNotificationsSettings =
|
||||||
|
intent.hasCategory(NotificationCompat.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
|
||||||
|
openAppSettings =
|
||||||
|
isMinSdk(Build.VERSION_CODES.N) && intent.action == Intent.ACTION_APPLICATION_PREFERENCES
|
||||||
|
|
||||||
if (isNeedToShowNotificationsRationaleDialog.value) {
|
if (isNeedToShowNotificationsRationaleDialog.value) {
|
||||||
isNeedToShowNotificationsRationaleDialog.update { false }
|
isNeedToShowNotificationsRationaleDialog.update { false }
|
||||||
isNeedToCheckNotificationsPermission.update { true }
|
isNeedToCheckNotificationsPermission.update { true }
|
||||||
@@ -100,6 +115,8 @@ class MainViewModelImpl(
|
|||||||
.take(5)
|
.take(5)
|
||||||
|
|
||||||
userSettings.onAppLanguageChanged(newLanguage)
|
userSettings.onAppLanguageChanged(newLanguage)
|
||||||
|
|
||||||
|
loadAccounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalPermissionsApi
|
@ExperimentalPermissionsApi
|
||||||
@@ -151,6 +168,22 @@ class MainViewModelImpl(
|
|||||||
disableBackgroundLongPoll()
|
disableBackgroundLongPoll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadProfile() {
|
||||||
|
loadUserByIdUseCase(userId = null)
|
||||||
|
.listenValue(viewModelScope) { state ->
|
||||||
|
state.processState(
|
||||||
|
error = { error ->
|
||||||
|
profileImageUrl.emit(null)
|
||||||
|
},
|
||||||
|
success = { response ->
|
||||||
|
val user = response ?: return@listenValue
|
||||||
|
|
||||||
|
profileImageUrl.emit(user.photo100)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun listenLongPollState() {
|
private fun listenLongPollState() {
|
||||||
longPollController.stateToApply.listenValue(viewModelScope) { newState ->
|
longPollController.stateToApply.listenValue(viewModelScope) { newState ->
|
||||||
if (newState == LongPollState.Background) {
|
if (newState == LongPollState.Background) {
|
||||||
@@ -184,9 +217,17 @@ class MainViewModelImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentAccount != null) {
|
||||||
|
loadProfile()
|
||||||
|
}
|
||||||
|
|
||||||
startDestination.setValue {
|
startDestination.setValue {
|
||||||
if (currentAccount == null) AuthGraph
|
when {
|
||||||
else Main
|
openAppSettings -> Settings
|
||||||
|
openNotificationsSettings -> Settings
|
||||||
|
currentAccount == null -> AuthGraph
|
||||||
|
else -> Main
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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
|
||||||
@@ -22,7 +23,8 @@ fun NavGraphBuilder.mainScreen(
|
|||||||
onError: (BaseError) -> Unit,
|
onError: (BaseError) -> Unit,
|
||||||
onSettingsButtonClicked: () -> Unit,
|
onSettingsButtonClicked: () -> Unit,
|
||||||
onConversationClicked: (conversationId: Int) -> Unit,
|
onConversationClicked: (conversationId: Int) -> Unit,
|
||||||
onPhotoClicked: (url: String) -> Unit
|
onPhotoClicked: (url: String) -> Unit,
|
||||||
|
viewModel: MainViewModel
|
||||||
) {
|
) {
|
||||||
val navigationItems = ImmutableList.of(
|
val navigationItems = ImmutableList.of(
|
||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
@@ -51,7 +53,8 @@ fun NavGraphBuilder.mainScreen(
|
|||||||
onError = onError,
|
onError = onError,
|
||||||
onSettingsButtonClicked = onSettingsButtonClicked,
|
onSettingsButtonClicked = onSettingsButtonClicked,
|
||||||
onConversationItemClicked = onConversationClicked,
|
onConversationItemClicked = onConversationClicked,
|
||||||
onPhotoClicked = onPhotoClicked
|
onPhotoClicked = onPhotoClicked,
|
||||||
|
viewModel = viewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
||||||
|
|
||||||
LifecycleResumeEffect(true) {
|
LifecycleResumeEffect(true) {
|
||||||
viewModel.onAppResumed()
|
viewModel.onAppResumed(intent)
|
||||||
onPauseOrDispose {}
|
onPauseOrDispose {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
import androidx.compose.material3.NavigationBarDefaults
|
import androidx.compose.material3.NavigationBarDefaults
|
||||||
@@ -16,19 +18,25 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
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.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
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.draw.alpha
|
||||||
|
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
|
||||||
|
import coil.compose.SubcomposeAsyncImage
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import dev.chrisbanes.haze.hazeChild
|
import dev.chrisbanes.haze.hazeChild
|
||||||
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
|
||||||
@@ -47,12 +55,15 @@ fun MainScreen(
|
|||||||
onError: (BaseError) -> Unit = {},
|
onError: (BaseError) -> Unit = {},
|
||||||
onSettingsButtonClicked: () -> Unit = {},
|
onSettingsButtonClicked: () -> Unit = {},
|
||||||
onConversationItemClicked: (conversationId: Int) -> Unit = {},
|
onConversationItemClicked: (conversationId: Int) -> Unit = {},
|
||||||
onPhotoClicked: (url: String) -> Unit = {}
|
onPhotoClicked: (url: String) -> 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)
|
||||||
}
|
}
|
||||||
@@ -90,13 +101,39 @@ fun MainScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon = {
|
icon = {
|
||||||
Icon(
|
if (index == navigationItems.size - 1) {
|
||||||
painter = painterResource(
|
var isLoading by remember {
|
||||||
id = if (selectedItemIndex == index) item.selectedIconResId
|
mutableStateOf(true)
|
||||||
else item.unselectedIconResId
|
}
|
||||||
),
|
if (isLoading) {
|
||||||
contentDescription = null
|
Icon(
|
||||||
)
|
painter = painterResource(
|
||||||
|
id = if (selectedItemIndex == index) item.selectedIconResId
|
||||||
|
else item.unselectedIconResId
|
||||||
|
),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SubcomposeAsyncImage(
|
||||||
|
model = profileImageUrl,
|
||||||
|
contentDescription = "Profile Image",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.alpha(if (isLoading) 0f else 1f),
|
||||||
|
onSuccess = {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(
|
||||||
|
id = if (selectedItemIndex == index) item.selectedIconResId
|
||||||
|
else item.unselectedIconResId
|
||||||
|
),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
alwaysShowLabel = false
|
alwaysShowLabel = false
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ fun RootScreen(
|
|||||||
onError = viewModel::onError,
|
onError = viewModel::onError,
|
||||||
onSettingsButtonClicked = navController::navigateToSettings,
|
onSettingsButtonClicked = navController::navigateToSettings,
|
||||||
onConversationClicked = navController::navigateToMessagesHistory,
|
onConversationClicked = navController::navigateToMessagesHistory,
|
||||||
onPhotoClicked = { url -> navController.navigateToPhotoView(listOf(url)) }
|
onPhotoClicked = { url -> navController.navigateToPhotoView(listOf(url)) },
|
||||||
|
viewModel = viewModel
|
||||||
)
|
)
|
||||||
|
|
||||||
messagesHistoryScreen(
|
messagesHistoryScreen(
|
||||||
|
|||||||
-22
@@ -18,7 +18,6 @@ import dev.meloda.fast.data.State
|
|||||||
import dev.meloda.fast.data.processState
|
import dev.meloda.fast.data.processState
|
||||||
import dev.meloda.fast.datastore.UserSettings
|
import dev.meloda.fast.datastore.UserSettings
|
||||||
import dev.meloda.fast.domain.ConversationsUseCase
|
import dev.meloda.fast.domain.ConversationsUseCase
|
||||||
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
|
||||||
import dev.meloda.fast.domain.LongPollUpdatesParser
|
import dev.meloda.fast.domain.LongPollUpdatesParser
|
||||||
import dev.meloda.fast.domain.MessagesUseCase
|
import dev.meloda.fast.domain.MessagesUseCase
|
||||||
import dev.meloda.fast.model.BaseError
|
import dev.meloda.fast.model.BaseError
|
||||||
@@ -66,7 +65,6 @@ interface ConversationsViewModel {
|
|||||||
class ConversationsViewModelImpl(
|
class ConversationsViewModelImpl(
|
||||||
updatesParser: LongPollUpdatesParser,
|
updatesParser: LongPollUpdatesParser,
|
||||||
private val conversationsUseCase: ConversationsUseCase,
|
private val conversationsUseCase: ConversationsUseCase,
|
||||||
private val loadUserByIdUseCase: LoadUserByIdUseCase,
|
|
||||||
private val messagesUseCase: MessagesUseCase,
|
private val messagesUseCase: MessagesUseCase,
|
||||||
private val resources: Resources,
|
private val resources: Resources,
|
||||||
private val userSettings: UserSettings
|
private val userSettings: UserSettings
|
||||||
@@ -99,8 +97,6 @@ class ConversationsViewModelImpl(
|
|||||||
updatesParser.onConversationPinStateChanged(::handlePinStateChanged)
|
updatesParser.onConversationPinStateChanged(::handlePinStateChanged)
|
||||||
updatesParser.onInteractions(::handleInteraction)
|
updatesParser.onInteractions(::handleInteraction)
|
||||||
|
|
||||||
loadProfile()
|
|
||||||
|
|
||||||
loadConversations()
|
loadConversations()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,24 +223,6 @@ class ConversationsViewModelImpl(
|
|||||||
screenState.setValue { old -> old.copy(showOptions = newShowOptions) }
|
screenState.setValue { old -> old.copy(showOptions = newShowOptions) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadProfile() {
|
|
||||||
loadUserByIdUseCase(userId = null)
|
|
||||||
.listenValue(viewModelScope) { state ->
|
|
||||||
state.processState(
|
|
||||||
error = { error ->
|
|
||||||
|
|
||||||
},
|
|
||||||
success = { response ->
|
|
||||||
val user = response ?: return@listenValue
|
|
||||||
|
|
||||||
screenState.setValue { old ->
|
|
||||||
old.copy(profileImageUrl = user.photo100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadConversations(
|
private fun loadConversations(
|
||||||
offset: Int = currentOffset.value
|
offset: Int = currentOffset.value
|
||||||
) {
|
) {
|
||||||
|
|||||||
+12
-15
@@ -10,7 +10,6 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideIn
|
import androidx.compose.animation.slideIn
|
||||||
import androidx.compose.animation.slideOut
|
import androidx.compose.animation.slideOut
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
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
|
||||||
@@ -21,17 +20,17 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.layout.statusBars
|
import androidx.compose.foundation.layout.statusBars
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.rounded.Refresh
|
import androidx.compose.material.icons.rounded.Refresh
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -54,7 +53,6 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
@@ -68,7 +66,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.view.HapticFeedbackConstantsCompat
|
import androidx.core.view.HapticFeedbackConstantsCompat
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import coil.imageLoader
|
import coil.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.haze
|
||||||
@@ -251,16 +248,16 @@ fun ConversationsScreen(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
AsyncImage(
|
IconButton(
|
||||||
model = screenState.profileImageUrl,
|
onClick = {
|
||||||
contentDescription = "Profile Image",
|
dropDownMenuExpanded = true
|
||||||
modifier = Modifier
|
}
|
||||||
.padding(end = 12.dp)
|
) {
|
||||||
.size(32.dp)
|
Icon(
|
||||||
.clip(CircleShape)
|
imageVector = Icons.Default.MoreVert,
|
||||||
.clickable { dropDownMenuExpanded = true },
|
contentDescription = null
|
||||||
placeholder = painterResource(id = UiR.drawable.ic_account_circle_cut)
|
)
|
||||||
)
|
}
|
||||||
|
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
|
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
|
||||||
|
|||||||
Reference in New Issue
Block a user