fix: MVI model state update (#879)

This commit is contained in:
Diego Beraldin 2024-05-20 22:54:34 +02:00 committed by GitHub
parent aeaff6be61
commit 58e33fccd8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 1040 additions and 881 deletions

View File

@ -3,6 +3,8 @@ package com.github.diegoberaldin.raccoonforlemmy.core.architecture
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/**
* Basic implementation of the MVI model.
@ -20,13 +22,14 @@ abstract class DefaultMviModel<Intent, State, Effect>(
) : MviModel<Intent, State, Effect> {
override val uiState = MutableStateFlow(initialState)
override val effects = MutableSharedFlow<Effect>()
private val mutex = Mutex()
/**
* Emit an effect (event).
*
* @param value Value
*/
suspend fun emitEffect(value: Effect) {
protected suspend fun emitEffect(value: Effect) {
effects.emit(value)
}
@ -35,8 +38,10 @@ abstract class DefaultMviModel<Intent, State, Effect>(
*
* @param block Block
*/
inline fun updateState(block: (State) -> State) {
uiState.update { block(uiState.value) }
protected suspend fun updateState(block: (State) -> State) {
mutex.withLock {
uiState.update { block(uiState.value) }
}
}
override fun reduce(intent: Intent) {

View File

@ -30,40 +30,4 @@ class DefaultNotificationCenterTest {
expectNoEvents()
}
}
@Test
fun givenMultipleSubscriptions_whenSendReplayableEvent_thenEventIsReceivedAndReplayed() =
runTest {
launch {
sut.send(NotificationCenterEvent.PostCreated)
}
sut.subscribe(NotificationCenterEvent.PostCreated::class).test {
val evt = awaitItem()
assertEquals(NotificationCenterEvent.PostCreated, evt)
}
sut.subscribe(NotificationCenterEvent.PostCreated::class).test {
val evt = awaitItem()
assertEquals(NotificationCenterEvent.PostCreated, evt)
}
}
@Test
fun givenMultipleSubscriptions_whenResetCache_thenEventIsNotReplayed() =
runTest {
launch {
sut.send(NotificationCenterEvent.PostCreated)
}
sut.subscribe(NotificationCenterEvent.PostCreated::class).test {
val evt = awaitItem()
assertEquals(NotificationCenterEvent.PostCreated, evt)
}
sut.resetCache()
sut.subscribe(NotificationCenterEvent.PostCreated::class).test {
expectNoEvents()
}
}
}

View File

@ -2,7 +2,6 @@ package com.github.diegoberaldin.raccoonforlemmy.core.notifications
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@ -11,49 +10,17 @@ import kotlinx.coroutines.launch
import kotlin.reflect.KClass
import kotlin.reflect.safeCast
private const val REPLAY_EVENT_COUNT = 5
object DefaultNotificationCenter : NotificationCenter {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val events = MutableSharedFlow<NotificationCenterEvent>()
private val replayedEvents =
MutableSharedFlow<NotificationCenterEvent>(
replay = REPLAY_EVENT_COUNT,
)
override fun send(event: NotificationCenterEvent) {
scope.launch(Dispatchers.Main) {
if (isReplayable(event::class)) {
replayedEvents.emit(event)
} else {
events.emit(event)
}
events.emit(event)
}
}
override fun <T : NotificationCenterEvent> subscribe(clazz: KClass<T>): Flow<T> {
return if (isReplayable(clazz)) {
replayedEvents.mapNotNull { clazz.safeCast(it) }
} else {
events.mapNotNull { clazz.safeCast(it) }
}
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun resetCache() {
replayedEvents.resetReplayCache()
}
}
private fun <T : NotificationCenterEvent> isReplayable(clazz: KClass<T>): Boolean {
return when (clazz) {
NotificationCenterEvent.MultiCommunityCreated::class -> true
NotificationCenterEvent.PostUpdated::class -> true
NotificationCenterEvent.PostCreated::class -> true
NotificationCenterEvent.PostDeleted::class -> true
NotificationCenterEvent.CommentCreated::class -> true
NotificationCenterEvent.UserBannedPost::class -> true
NotificationCenterEvent.UserBannedComment::class -> true
else -> false
return events.mapNotNull { clazz.safeCast(it) }
}
}

View File

@ -12,6 +12,4 @@ interface NotificationCenter {
fun send(event: NotificationCenterEvent)
fun <T : NotificationCenterEvent> subscribe(clazz: KClass<T>): Flow<T>
fun resetCache()
}

View File

@ -60,8 +60,10 @@ class InboxViewModel(
override fun reduce(intent: InboxMviModel.Intent) {
when (intent) {
is InboxMviModel.Intent.ChangeSection ->
updateState {
it.copy(section = intent.value)
screenModelScope.launch {
updateState {
it.copy(section = intent.value)
}
}
InboxMviModel.Intent.ReadAll -> markAllRead()
@ -69,10 +71,12 @@ class InboxViewModel(
}
private fun changeUnreadOnly(value: Boolean) {
updateState {
it.copy(unreadOnly = value)
screenModelScope.launch {
updateState {
it.copy(unreadOnly = value)
}
coordinator.setUnreadOnly(value)
}
coordinator.setUnreadOnly(value)
}
private fun markAllRead() {

View File

@ -80,33 +80,33 @@ class AdvancedSettingsViewModel(
}.launchIn(this)
updateAvailableLanguages()
}
val settings = settingsRepository.currentSettings.value
updateState {
it.copy(
defaultExploreType = settings.defaultExploreType.toListingType(),
defaultInboxUnreadOnly = settings.defaultInboxType.toInboxUnreadOnly(),
enableDoubleTapAction = settings.enableDoubleTapAction,
autoLoadImages = settings.autoLoadImages,
autoExpandComments = settings.autoExpandComments,
hideNavigationBarWhileScrolling = settings.hideNavigationBarWhileScrolling,
zombieModeInterval = settings.zombieModeInterval,
zombieModeScrollAmount = settings.zombieModeScrollAmount,
markAsReadWhileScrolling = settings.markAsReadWhileScrolling,
searchPostTitleOnly = settings.searchPostTitleOnly,
edgeToEdge = settings.edgeToEdge,
infiniteScrollDisabled = !settings.infiniteScrollEnabled,
opaqueSystemBars = settings.opaqueSystemBars,
imageSourceSupported = galleryHelper.supportsCustomPath,
imageSourcePath = settings.imageSourcePath,
defaultLanguageId = settings.defaultLanguageId,
appIconChangeSupported = appIconManager.supportsMultipleIcons,
fadeReadPosts = settings.fadeReadPosts,
showUnreadComments = settings.showUnreadComments,
supportSettingsImportExport = fileSystemManager.isSupported,
enableButtonsToScrollBetweenComments = settings.enableButtonsToScrollBetweenComments,
)
val settings = settingsRepository.currentSettings.value
updateState {
it.copy(
defaultExploreType = settings.defaultExploreType.toListingType(),
defaultInboxUnreadOnly = settings.defaultInboxType.toInboxUnreadOnly(),
enableDoubleTapAction = settings.enableDoubleTapAction,
autoLoadImages = settings.autoLoadImages,
autoExpandComments = settings.autoExpandComments,
hideNavigationBarWhileScrolling = settings.hideNavigationBarWhileScrolling,
zombieModeInterval = settings.zombieModeInterval,
zombieModeScrollAmount = settings.zombieModeScrollAmount,
markAsReadWhileScrolling = settings.markAsReadWhileScrolling,
searchPostTitleOnly = settings.searchPostTitleOnly,
edgeToEdge = settings.edgeToEdge,
infiniteScrollDisabled = !settings.infiniteScrollEnabled,
opaqueSystemBars = settings.opaqueSystemBars,
imageSourceSupported = galleryHelper.supportsCustomPath,
imageSourcePath = settings.imageSourcePath,
defaultLanguageId = settings.defaultLanguageId,
appIconChangeSupported = appIconManager.supportsMultipleIcons,
fadeReadPosts = settings.fadeReadPosts,
showUnreadComments = settings.showUnreadComments,
supportSettingsImportExport = fileSystemManager.isSupported,
enableButtonsToScrollBetweenComments = settings.enableButtonsToScrollBetweenComments,
)
}
}
}
@ -153,64 +153,64 @@ class AdvancedSettingsViewModel(
}
private fun changeEnableDoubleTapAction(value: Boolean) {
updateState { it.copy(enableDoubleTapAction = value) }
screenModelScope.launch {
updateState { it.copy(enableDoubleTapAction = value) }
val settings = settingsRepository.currentSettings.value.copy(enableDoubleTapAction = value)
saveSettings(settings)
}
}
private fun changeAutoLoadImages(value: Boolean) {
updateState { it.copy(autoLoadImages = value) }
screenModelScope.launch {
updateState { it.copy(autoLoadImages = value) }
val settings = settingsRepository.currentSettings.value.copy(autoLoadImages = value)
saveSettings(settings)
}
}
private fun changeAutoExpandComments(value: Boolean) {
updateState { it.copy(autoExpandComments = value) }
screenModelScope.launch {
updateState { it.copy(autoExpandComments = value) }
val settings = settingsRepository.currentSettings.value.copy(autoExpandComments = value)
saveSettings(settings)
}
}
private fun changeHideNavigationBarWhileScrolling(value: Boolean) {
updateState { it.copy(hideNavigationBarWhileScrolling = value) }
screenModelScope.launch {
updateState { it.copy(hideNavigationBarWhileScrolling = value) }
val settings = settingsRepository.currentSettings.value.copy(hideNavigationBarWhileScrolling = value)
saveSettings(settings)
}
}
private fun changeMarkAsReadWhileScrolling(value: Boolean) {
updateState { it.copy(markAsReadWhileScrolling = value) }
screenModelScope.launch {
updateState { it.copy(markAsReadWhileScrolling = value) }
val settings = settingsRepository.currentSettings.value.copy(markAsReadWhileScrolling = value)
saveSettings(settings)
}
}
private fun changeZombieModeInterval(value: Duration) {
updateState { it.copy(zombieModeInterval = value) }
screenModelScope.launch {
updateState { it.copy(zombieModeInterval = value) }
val settings = settingsRepository.currentSettings.value.copy(zombieModeInterval = value)
saveSettings(settings)
}
}
private fun changeZombieModeScrollAmount(value: Float) {
updateState { it.copy(zombieModeScrollAmount = value) }
screenModelScope.launch {
updateState { it.copy(zombieModeScrollAmount = value) }
val settings = settingsRepository.currentSettings.value.copy(zombieModeScrollAmount = value)
saveSettings(settings)
}
}
private fun changeDefaultInboxUnreadOnly(value: Boolean) {
updateState { it.copy(defaultInboxUnreadOnly = value) }
screenModelScope.launch {
updateState { it.copy(defaultInboxUnreadOnly = value) }
val settings = settingsRepository.currentSettings.value.copy(defaultInboxType = value.toInboxDefaultType())
saveSettings(settings)
notificationCenter.send(NotificationCenterEvent.ResetInbox)
@ -218,32 +218,32 @@ class AdvancedSettingsViewModel(
}
private fun changeSearchPostTitleOnly(value: Boolean) {
updateState { it.copy(searchPostTitleOnly = value) }
screenModelScope.launch {
updateState { it.copy(searchPostTitleOnly = value) }
val settings = settingsRepository.currentSettings.value.copy(searchPostTitleOnly = value)
saveSettings(settings)
}
}
private fun changeExploreType(value: ListingType) {
updateState { it.copy(defaultExploreType = value) }
screenModelScope.launch {
updateState { it.copy(defaultExploreType = value) }
val settings = settingsRepository.currentSettings.value.copy(defaultExploreType = value.toInt())
saveSettings(settings)
}
}
private fun changeEdgeToEdge(value: Boolean) {
updateState { it.copy(edgeToEdge = value) }
screenModelScope.launch {
updateState { it.copy(edgeToEdge = value) }
val settings = settingsRepository.currentSettings.value.copy(edgeToEdge = value)
saveSettings(settings)
}
}
private fun changeInfiniteScrollDisabled(value: Boolean) {
updateState { it.copy(infiniteScrollDisabled = value) }
screenModelScope.launch {
updateState { it.copy(infiniteScrollDisabled = value) }
val settings = settingsRepository.currentSettings.value.copy(infiniteScrollEnabled = !value)
saveSettings(settings)
}
@ -255,16 +255,16 @@ class AdvancedSettingsViewModel(
UiBarTheme.Opaque -> true
else -> false
}
updateState { it.copy(opaqueSystemBars = opaque) }
screenModelScope.launch {
updateState { it.copy(opaqueSystemBars = opaque) }
val settings = settingsRepository.currentSettings.value.copy(opaqueSystemBars = opaque)
saveSettings(settings)
}
}
private fun changeImageSourcePath(value: Boolean) {
updateState { it.copy(imageSourcePath = value) }
screenModelScope.launch {
updateState { it.copy(imageSourcePath = value) }
val settings = settingsRepository.currentSettings.value.copy(imageSourcePath = value)
saveSettings(settings)
}
@ -279,40 +279,40 @@ class AdvancedSettingsViewModel(
}
private fun changeDefaultLanguageId(value: Long?) {
updateState { it.copy(defaultLanguageId = value) }
screenModelScope.launch {
updateState { it.copy(defaultLanguageId = value) }
val settings = settingsRepository.currentSettings.value.copy(defaultLanguageId = value)
saveSettings(settings)
}
}
private fun changeInboxBackgroundCheckPeriod(value: Duration) {
updateState { it.copy(inboxBackgroundCheckPeriod = value) }
screenModelScope.launch {
updateState { it.copy(inboxBackgroundCheckPeriod = value) }
val settings = settingsRepository.currentSettings.value.copy(inboxBackgroundCheckPeriod = value)
saveSettings(settings)
}
}
private fun changeFadeReadPosts(value: Boolean) {
updateState { it.copy(fadeReadPosts = value) }
screenModelScope.launch {
updateState { it.copy(fadeReadPosts = value) }
val settings = settingsRepository.currentSettings.value.copy(fadeReadPosts = value)
saveSettings(settings)
}
}
private fun changeShowUnreadPosts(value: Boolean) {
updateState { it.copy(showUnreadComments = value) }
screenModelScope.launch {
updateState { it.copy(showUnreadComments = value) }
val settings = settingsRepository.currentSettings.value.copy(showUnreadComments = value)
saveSettings(settings)
}
}
private fun changeEnableButtonsToScrollBetweenComments(value: Boolean) {
updateState { it.copy(enableButtonsToScrollBetweenComments = value) }
screenModelScope.launch {
updateState { it.copy(enableButtonsToScrollBetweenComments = value) }
val settings = settingsRepository.currentSettings.value.copy(enableButtonsToScrollBetweenComments = value)
saveSettings(settings)
}

View File

@ -96,21 +96,21 @@ class SettingsViewModel(
availableSortTypesForComments = availableSortTypesForComments,
)
}
}
val settings = settingsRepository.currentSettings.value
updateState {
it.copy(
defaultListingType = settings.defaultListingType.toListingType(),
defaultPostSortType = settings.defaultPostSortType.toSortType(),
defaultCommentSortType = settings.defaultCommentSortType.toSortType(),
includeNsfw = settings.includeNsfw,
blurNsfw = settings.blurNsfw,
urlOpeningMode = settings.urlOpeningMode.toUrlOpeningMode(),
enableSwipeActions = settings.enableSwipeActions,
crashReportEnabled = crashReportConfiguration.isEnabled(),
customTabsEnabled = customTabsHelper.isSupported,
)
val settings = settingsRepository.currentSettings.value
updateState {
it.copy(
defaultListingType = settings.defaultListingType.toListingType(),
defaultPostSortType = settings.defaultPostSortType.toSortType(),
defaultCommentSortType = settings.defaultCommentSortType.toSortType(),
includeNsfw = settings.includeNsfw,
blurNsfw = settings.blurNsfw,
urlOpeningMode = settings.urlOpeningMode.toUrlOpeningMode(),
enableSwipeActions = settings.enableSwipeActions,
crashReportEnabled = crashReportConfiguration.isEnabled(),
customTabsEnabled = customTabsHelper.isSupported,
)
}
}
}
@ -148,8 +148,8 @@ class SettingsViewModel(
}
private fun changeDefaultListingType(value: ListingType) {
updateState { it.copy(defaultListingType = value) }
screenModelScope.launch {
updateState { it.copy(defaultListingType = value) }
val settings =
settingsRepository.currentSettings.value.copy(
defaultListingType = value.toInt(),
@ -160,8 +160,8 @@ class SettingsViewModel(
}
private fun changeDefaultPostSortType(value: SortType) {
updateState { it.copy(defaultPostSortType = value) }
screenModelScope.launch {
updateState { it.copy(defaultPostSortType = value) }
val settings =
settingsRepository.currentSettings.value.copy(
defaultPostSortType = value.toInt(),
@ -172,8 +172,8 @@ class SettingsViewModel(
}
private fun changeDefaultCommentSortType(value: SortType) {
updateState { it.copy(defaultCommentSortType = value) }
screenModelScope.launch {
updateState { it.copy(defaultCommentSortType = value) }
val settings =
settingsRepository.currentSettings.value.copy(
defaultCommentSortType = value.toInt(),
@ -183,8 +183,8 @@ class SettingsViewModel(
}
private fun changeIncludeNsfw(value: Boolean) {
updateState { it.copy(includeNsfw = value) }
screenModelScope.launch {
updateState { it.copy(includeNsfw = value) }
val settings =
settingsRepository.currentSettings.value.copy(
includeNsfw = value,
@ -196,8 +196,8 @@ class SettingsViewModel(
}
private fun changeBlurNsfw(value: Boolean) {
updateState { it.copy(blurNsfw = value) }
screenModelScope.launch {
updateState { it.copy(blurNsfw = value) }
val settings =
settingsRepository.currentSettings.value.copy(
blurNsfw = value,
@ -207,8 +207,8 @@ class SettingsViewModel(
}
private fun changeUrlOpeningMode(value: UrlOpeningMode) {
updateState { it.copy(urlOpeningMode = value) }
screenModelScope.launch {
updateState { it.copy(urlOpeningMode = value) }
val settings =
settingsRepository.currentSettings.value.copy(
urlOpeningMode = value.toInt(),
@ -218,8 +218,8 @@ class SettingsViewModel(
}
private fun changeEnableSwipeActions(value: Boolean) {
updateState { it.copy(enableSwipeActions = value) }
screenModelScope.launch {
updateState { it.copy(enableSwipeActions = value) }
val settings =
settingsRepository.currentSettings.value.copy(
enableSwipeActions = value,
@ -229,8 +229,10 @@ class SettingsViewModel(
}
private fun changeCrashReportEnabled(value: Boolean) {
crashReportConfiguration.setEnabled(value)
updateState { it.copy(crashReportEnabled = value) }
screenModelScope.launch {
crashReportConfiguration.setEnabled(value)
updateState { it.copy(crashReportEnabled = value) }
}
}
private suspend fun saveSettings(settings: SettingsModel) {

View File

@ -46,7 +46,9 @@ class MainViewModel(
override fun reduce(intent: MainScreenMviModel.Intent) {
when (intent) {
is MainScreenMviModel.Intent.SetBottomBarOffsetHeightPx -> {
updateState { it.copy(bottomBarOffsetHeightPx = intent.value) }
screenModelScope.launch {
updateState { it.copy(bottomBarOffsetHeightPx = intent.value) }
}
}
}
}

View File

@ -1,17 +1,21 @@
package com.github.diegoberaldin.raccoonforlemmy.unit.about
import cafe.adriel.voyager.core.model.screenModelScope
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
import com.github.diegoberaldin.raccoonforlemmy.core.utils.debug.AppInfo
import kotlinx.coroutines.launch
class AboutDialogViewModel : AboutDialogMviModel,
DefaultMviModel<AboutDialogMviModel.Intent, AboutDialogMviModel.UiState, AboutDialogMviModel.Effect>(
initialState = AboutDialogMviModel.UiState(),
) {
init {
updateState {
it.copy(
version = AppInfo.versionCode,
)
screenModelScope.launch {
updateState {
it.copy(
version = AppInfo.versionCode,
)
}
}
}
}

View File

@ -55,92 +55,112 @@ class AccountSettingsViewModel(
override fun reduce(intent: AccountSettingsMviModel.Intent) {
when (intent) {
is AccountSettingsMviModel.Intent.ChangeDisplayName -> {
updateState {
it.copy(
displayName = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
displayName = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeEmail -> {
updateState {
it.copy(
email = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
email = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeMatrixUserId -> {
updateState {
it.copy(
matrixUserId = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
matrixUserId = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeBio -> {
updateState {
it.copy(
bio = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
bio = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeBot -> {
updateState {
it.copy(
bot = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
bot = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeSendNotificationsToEmail -> {
updateState {
it.copy(
sendNotificationsToEmail = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
sendNotificationsToEmail = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeShowBotAccounts -> {
updateState {
it.copy(
showBotAccounts = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
showBotAccounts = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeShowNsfw -> {
updateState {
it.copy(
showNsfw = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
showNsfw = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeShowScores -> {
updateState {
it.copy(
showScores = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
showScores = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is AccountSettingsMviModel.Intent.ChangeShowReadPosts -> {
updateState {
it.copy(
showReadPosts = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
showReadPosts = intent.value,
hasUnsavedChanges = true,
)
}
}
}
@ -240,8 +260,8 @@ class AccountSettingsViewModel(
showScores = currentState.showScores,
showReadPosts = currentState.showReadPosts,
) ?: return
updateState { it.copy(loading = true) }
screenModelScope.launch(Dispatchers.IO) {
updateState { it.copy(loading = true) }
try {
val auth = identityRepository.authToken.value.orEmpty()
siteRepository.updateAccountSettings(

View File

@ -22,8 +22,10 @@ class BanUserViewModel(
initialState = BanUserMviModel.UiState(),
) {
init {
updateState {
it.copy(targetBanValue = newValue)
screenModelScope.launch {
updateState {
it.copy(targetBanValue = newValue)
}
}
}
@ -31,21 +33,36 @@ class BanUserViewModel(
when (intent) {
BanUserMviModel.Intent.IncrementDays -> incrementDays()
BanUserMviModel.Intent.DecrementDays -> decrementDays()
is BanUserMviModel.Intent.ChangePermanent -> updateState { it.copy(permanent = intent.value) }
is BanUserMviModel.Intent.ChangeRemoveData -> updateState { it.copy(removeData = intent.value) }
is BanUserMviModel.Intent.SetText -> updateState { it.copy(text = intent.value) }
is BanUserMviModel.Intent.ChangePermanent ->
screenModelScope.launch {
updateState { it.copy(permanent = intent.value) }
}
is BanUserMviModel.Intent.ChangeRemoveData ->
screenModelScope.launch {
updateState { it.copy(removeData = intent.value) }
}
is BanUserMviModel.Intent.SetText ->
screenModelScope.launch {
updateState { it.copy(text = intent.value) }
}
BanUserMviModel.Intent.Submit -> submit()
}
}
private fun incrementDays() {
val newValue = uiState.value.days + 1
updateState { it.copy(days = newValue) }
screenModelScope.launch {
val newValue = uiState.value.days + 1
updateState { it.copy(days = newValue) }
}
}
private fun decrementDays() {
val newValue = (uiState.value.days - 1).coerceAtLeast(1)
updateState { it.copy(days = newValue) }
screenModelScope.launch {
val newValue = (uiState.value.days - 1).coerceAtLeast(1)
updateState { it.copy(days = newValue) }
}
}
private fun submit() {
@ -57,8 +74,8 @@ class BanUserViewModel(
val removeData = currentState.removeData.takeIf { newValue } ?: false
val days = currentState.days.toLong().takeIf { newValue }
updateState { it.copy(loading = true) }
screenModelScope.launch {
updateState { it.copy(loading = true) }
try {
val auth = identityRepository.authToken.value.orEmpty()
val newUser =

View File

@ -181,17 +181,19 @@ class InboxChatViewModel(
}
private fun handleMessageUpdate(newMessage: PrivateMessageModel) {
updateState {
it.copy(
messages =
it.messages.map { msg ->
if (msg.id == newMessage.id) {
newMessage
} else {
msg
}
},
)
screenModelScope.launch {
updateState {
it.copy(
messages =
it.messages.map { msg ->
if (msg.id == newMessage.id) {
newMessage
} else {
msg
}
},
)
}
}
}
@ -215,10 +217,12 @@ class InboxChatViewModel(
}
private fun startEditingMessage(message: PrivateMessageModel) {
updateState {
it.copy(
editedMessageId = message.id,
)
screenModelScope.launch {
updateState {
it.copy(
editedMessageId = message.id,
)
}
}
}
@ -268,7 +272,9 @@ class InboxChatViewModel(
}
private fun handleLogout() {
updateState { it.copy(messages = emptyList()) }
screenModelScope.launch {
updateState { it.copy(messages = emptyList()) }
}
}
private fun deleteMessage(message: PrivateMessageModel) {

View File

@ -114,7 +114,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomS
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.getScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.ActionOnSwipe
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
@ -176,7 +175,6 @@ class CommunityDetailScreen(
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val notificationCenter = remember { getNotificationCenter() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val themeRepository = remember { getThemeRepository() }
val upVoteColor by themeRepository.upVoteColor.collectAsState()
@ -212,9 +210,6 @@ class CommunityDetailScreen(
WindowInsets.statusBars.getTop(this)
}
LaunchedEffect(notificationCenter) {
notificationCenter.resetCache()
}
LaunchedEffect(model) {
model.effects.onEach { effect ->
when (effect) {

View File

@ -265,16 +265,20 @@ class CommunityDetailViewModel(
}
CommunityDetailMviModel.Intent.PauseZombieMode -> {
updateState { it.copy(zombieModeActive = false) }
zombieModeHelper.pause()
screenModelScope.launch {
updateState { it.copy(zombieModeActive = false) }
zombieModeHelper.pause()
}
}
is CommunityDetailMviModel.Intent.StartZombieMode -> {
updateState { it.copy(zombieModeActive = true) }
zombieModeHelper.start(
initialValue = intent.index,
interval = settingsRepository.currentSettings.value.zombieModeInterval,
)
screenModelScope.launch {
updateState { it.copy(zombieModeActive = true) }
zombieModeHelper.start(
initialValue = intent.index,
interval = settingsRepository.currentSettings.value.zombieModeInterval,
)
}
}
is CommunityDetailMviModel.Intent.ModFeaturePost ->
@ -298,9 +302,11 @@ class CommunityDetailViewModel(
}
is CommunityDetailMviModel.Intent.ChangeSearching -> {
updateState { it.copy(searching = intent.value) }
if (!intent.value) {
updateSearchText("")
screenModelScope.launch {
updateState { it.copy(searching = intent.value) }
if (!intent.value) {
updateSearchText("")
}
}
}
@ -375,8 +381,8 @@ class CommunityDetailViewModel(
if (uiState.value.sortType == value) {
return
}
updateState { it.copy(sortType = value) }
screenModelScope.launch {
updateState { it.copy(sortType = value) }
emitEffect(CommunityDetailMviModel.Effect.BackToTop)
delay(50)
refresh()
@ -562,27 +568,31 @@ class CommunityDetailViewModel(
}
private fun handlePostUpdate(post: PostModel) {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
screenModelScope.launch {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}
private fun handlePostDelete(id: Long) {
updateState { it.copy(posts = it.posts.filter { post -> post.id != id }) }
screenModelScope.launch {
updateState { it.copy(posts = it.posts.filter { post -> post.id != id }) }
}
}
private fun blockCommunity() {
updateState { it.copy(asyncInProgress = true) }
screenModelScope.launch {
updateState { it.copy(asyncInProgress = true) }
try {
val auth = identityRepository.authToken.value
communityRepository.block(communityId, true, auth).getOrThrow()
@ -596,8 +606,8 @@ class CommunityDetailViewModel(
}
private fun blockInstance() {
updateState { it.copy(asyncInProgress = true) }
screenModelScope.launch {
updateState { it.copy(asyncInProgress = true) }
try {
val community = uiState.value.community
val instanceId = community.instanceId
@ -613,23 +623,27 @@ class CommunityDetailViewModel(
}
private fun clearRead() {
hideReadPosts = true
updateState {
val newPosts = it.posts.filter { e -> !e.read }
it.copy(
posts = newPosts,
)
screenModelScope.launch {
hideReadPosts = true
updateState {
val newPosts = it.posts.filter { e -> !e.read }
it.copy(
posts = newPosts,
)
}
}
}
private fun hide(post: PostModel) {
updateState {
val newPosts = it.posts.filter { e -> e.id != post.id }
it.copy(
posts = newPosts,
)
screenModelScope.launch {
updateState {
val newPosts = it.posts.filter { e -> e.id != post.id }
it.copy(
posts = newPosts,
)
}
markAsRead(post)
}
markAsRead(post)
}
private fun feature(post: PostModel) {
@ -698,8 +712,8 @@ class CommunityDetailViewModel(
}
private fun updateSearchText(value: String) {
updateState { it.copy(searchText = value) }
screenModelScope.launch {
updateState { it.copy(searchText = value) }
searchEventChannel.send(Unit)
}
}

View File

@ -137,8 +137,8 @@ class ConfigureContentViewViewModel(
}
private fun changeVoteFormat(value: VoteFormat) {
updateState { it.copy(voteFormat = value) }
screenModelScope.launch {
updateState { it.copy(voteFormat = value) }
val settings =
settingsRepository.currentSettings.value.let {
if (value == VoteFormat.Hidden) {
@ -155,8 +155,8 @@ class ConfigureContentViewViewModel(
}
private fun changeFullHeightImages(value: Boolean) {
updateState { it.copy(fullHeightImages = value) }
screenModelScope.launch {
updateState { it.copy(fullHeightImages = value) }
val settings =
settingsRepository.currentSettings.value.copy(
fullHeightImages = value,
@ -166,8 +166,8 @@ class ConfigureContentViewViewModel(
}
private fun changeFullWidthImages(value: Boolean) {
updateState { it.copy(fullWidthImages = value) }
screenModelScope.launch {
updateState { it.copy(fullWidthImages = value) }
val settings =
settingsRepository.currentSettings.value.copy(
fullWidthImages = value,
@ -177,8 +177,8 @@ class ConfigureContentViewViewModel(
}
private fun changePreferUserNicknames(value: Boolean) {
updateState { it.copy(preferUserNicknames = value) }
screenModelScope.launch {
updateState { it.copy(preferUserNicknames = value) }
val settings =
settingsRepository.currentSettings.value.copy(
preferUserNicknames = value,
@ -188,8 +188,8 @@ class ConfigureContentViewViewModel(
}
private fun changePostBodyMaxLines(value: Int?) {
updateState { it.copy(postBodyMaxLines = value) }
screenModelScope.launch {
updateState { it.copy(postBodyMaxLines = value) }
val settings =
settingsRepository.currentSettings.value.copy(
postBodyMaxLines = value,
@ -232,8 +232,8 @@ class ConfigureContentViewViewModel(
}
private fun changeCommentBarThickness(value: Int) {
updateState { it.copy(commentBarThickness = value) }
screenModelScope.launch {
updateState { it.copy(commentBarThickness = value) }
val settings =
settingsRepository.currentSettings.value.copy(
commentBarThickness = value,
@ -243,8 +243,8 @@ class ConfigureContentViewViewModel(
}
private fun changeCommentIndentAmount(value: Int) {
updateState { it.copy(commentIndentAmount = value) }
screenModelScope.launch {
updateState { it.copy(commentIndentAmount = value) }
val settings =
settingsRepository.currentSettings.value.copy(
commentIndentAmount = value,

View File

@ -51,8 +51,9 @@ class ConfigureSwipeActionsViewModel(
)
}
}.launchIn(this)
refresh()
}
refresh()
}
override fun reduce(intent: ConfigureSwipeActionsMviModel.Intent) {
@ -81,7 +82,7 @@ class ConfigureSwipeActionsViewModel(
}
}
private fun refresh() {
private suspend fun refresh() {
val settings = settingsRepository.currentSettings.value
updateState {
it.copy(
@ -515,12 +516,14 @@ class ConfigureSwipeActionsViewModel(
this -= currentState.actionsOnSwipeToEndInbox.toSet()
}
}
updateState {
it.copy(
availableOptionsPosts = actionsPosts.toList(),
availableOptionsComments = actionsComments.toList(),
availableOptionsInbox = actionsInbox.toList(),
)
screenModelScope.launch {
updateState {
it.copy(
availableOptionsPosts = actionsPosts.toList(),
availableOptionsComments = actionsComments.toList(),
availableOptionsInbox = actionsInbox.toList(),
)
}
}
}
}

View File

@ -107,7 +107,9 @@ class CreateCommentViewModel(
override fun reduce(intent: CreateCommentMviModel.Intent) {
when (intent) {
is CreateCommentMviModel.Intent.ChangeSection -> {
updateState { it.copy(section = intent.value) }
screenModelScope.launch {
updateState { it.copy(section = intent.value) }
}
}
is CreateCommentMviModel.Intent.ImageSelected -> {
@ -115,11 +117,15 @@ class CreateCommentViewModel(
}
is CreateCommentMviModel.Intent.ChangeLanguage -> {
updateState { it.copy(currentLanguageId = intent.value) }
screenModelScope.launch {
updateState { it.copy(currentLanguageId = intent.value) }
}
}
is CreateCommentMviModel.Intent.ChangeTextValue -> {
updateState { it.copy(textValue = intent.value) }
screenModelScope.launch {
updateState { it.copy(textValue = intent.value) }
}
}
is CreateCommentMviModel.Intent.Send -> submit()
@ -133,20 +139,24 @@ class CreateCommentViewModel(
return
}
updateState {
it.copy(
textError = null,
)
screenModelScope.launch {
updateState {
it.copy(
textError = null,
)
}
}
val text = currentState.textValue.text.trim()
val languageId = currentState.currentLanguageId
var valid = true
if (text.isEmpty()) {
updateState {
it.copy(
textError = ValidationError.MissingField,
)
screenModelScope.launch {
updateState {
it.copy(
textError = ValidationError.MissingField,
)
}
}
valid = false
}
@ -154,8 +164,8 @@ class CreateCommentViewModel(
return
}
updateState { it.copy(loading = true) }
screenModelScope.launch {
updateState { it.copy(loading = true) }
try {
val auth = identityRepository.authToken.value.orEmpty()
if (postId != null) {

View File

@ -97,20 +97,26 @@ class CreatePostViewModel(
}
is CreatePostMviModel.Intent.SetTitle -> {
updateState {
it.copy(title = intent.value)
screenModelScope.launch {
updateState {
it.copy(title = intent.value)
}
}
}
is CreatePostMviModel.Intent.ChangeNsfw -> {
updateState {
it.copy(nsfw = intent.value)
screenModelScope.launch {
updateState {
it.copy(nsfw = intent.value)
}
}
}
is CreatePostMviModel.Intent.SetUrl -> {
updateState {
it.copy(url = intent.value)
screenModelScope.launch {
updateState {
it.copy(url = intent.value)
}
}
}
@ -123,18 +129,23 @@ class CreatePostViewModel(
}
is CreatePostMviModel.Intent.ChangeSection ->
updateState {
it.copy(section = intent.value)
screenModelScope.launch {
updateState {
it.copy(section = intent.value)
}
}
is CreatePostMviModel.Intent.ChangeLanguage ->
updateState {
it.copy(currentLanguageId = intent.value)
screenModelScope.launch {
updateState {
it.copy(currentLanguageId = intent.value)
}
}
is CreatePostMviModel.Intent.ChangeBodyValue ->
updateState {
it.copy(bodyValue = intent.value)
screenModelScope.launch {
updateState {
it.copy(bodyValue = intent.value)
}
}
CreatePostMviModel.Intent.Send -> submit()
@ -214,12 +225,14 @@ class CreatePostViewModel(
return
}
updateState {
it.copy(
titleError = null,
urlError = null,
bodyError = null,
)
screenModelScope.launch {
updateState {
it.copy(
titleError = null,
urlError = null,
bodyError = null,
)
}
}
val communityId = currentState.communityId
@ -230,20 +243,26 @@ class CreatePostViewModel(
val languageId = currentState.currentLanguageId
var valid = true
if (title.isEmpty()) {
updateState {
it.copy(titleError = ValidationError.MissingField)
screenModelScope.launch {
updateState {
it.copy(titleError = ValidationError.MissingField)
}
}
valid = false
}
if (!url.isNullOrEmpty() && !url.isValidUrl()) {
updateState {
it.copy(urlError = ValidationError.InvalidField)
screenModelScope.launch {
updateState {
it.copy(urlError = ValidationError.InvalidField)
}
}
valid = false
}
if (communityId == null) {
updateState {
it.copy(communityError = ValidationError.MissingField)
screenModelScope.launch {
updateState {
it.copy(communityError = ValidationError.MissingField)
}
}
valid = false
}
@ -252,8 +271,8 @@ class CreatePostViewModel(
return
}
updateState { it.copy(loading = true) }
screenModelScope.launch {
updateState { it.copy(loading = true) }
try {
val auth = identityRepository.authToken.value.orEmpty()
when {

View File

@ -44,8 +44,10 @@ class DraftsViewModel(
override fun reduce(intent: DraftsMviModel.Intent) {
when (intent) {
is DraftsMviModel.Intent.ChangeSection ->
updateState {
it.copy(section = intent.section)
screenModelScope.launch {
updateState {
it.copy(section = intent.section)
}
}
is DraftsMviModel.Intent.Delete -> deleteDraft(intent.model)
@ -54,14 +56,14 @@ class DraftsViewModel(
}
private fun refresh(initial: Boolean = false) {
updateState {
it.copy(
refreshing = !initial,
initial = initial,
loading = false,
)
}
screenModelScope.launch {
updateState {
it.copy(
refreshing = !initial,
initial = initial,
loading = false,
)
}
val currentState = uiState.value
updateState { it.copy(loading = true) }
val refreshing = currentState.refreshing

View File

@ -123,8 +123,8 @@ class ModalDrawerViewModel(
}
is ModalDrawerMviModel.Intent.SetSearch -> {
updateState { it.copy(searchText = intent.value) }
screenModelScope.launch {
updateState { it.copy(searchText = intent.value) }
searchEventChannel.send(Unit)
}
}

View File

@ -32,38 +32,46 @@ class EditCommunityViewModel(
is EditCommunityMviModel.Intent.BannerSelected -> loadImageBanner(intent.value)
is EditCommunityMviModel.Intent.ChangeDescription -> {
updateState {
it.copy(
description = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
description = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is EditCommunityMviModel.Intent.ChangeTitle -> {
updateState {
it.copy(
title = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
title = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is EditCommunityMviModel.Intent.ChangeNsfw -> {
updateState {
it.copy(
nsfw = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
nsfw = intent.value,
hasUnsavedChanges = true,
)
}
}
}
is EditCommunityMviModel.Intent.ChangePostingRestrictedToMods -> {
updateState {
it.copy(
postingRestrictedToMods = intent.value,
hasUnsavedChanges = true,
)
screenModelScope.launch {
updateState {
it.copy(
postingRestrictedToMods = intent.value,
hasUnsavedChanges = true,
)
}
}
}
@ -138,8 +146,8 @@ class EditCommunityViewModel(
private fun submit() {
val community = originalCommunity?.copy() ?: return
val currentState = uiState.value
updateState { it.copy(loading = true) }
screenModelScope.launch(Dispatchers.IO) {
updateState { it.copy(loading = true) }
try {
val auth = identityRepository.authToken.value.orEmpty()
val newValue =

View File

@ -65,12 +65,12 @@ class ExploreViewModel(
}
init {
updateState {
it.copy(
instance = apiConfigRepository.instance.value,
)
}
screenModelScope.launch {
updateState {
it.copy(
instance = apiConfigRepository.instance.value,
)
}
identityRepository.isLogged.onEach { isLogged ->
updateState {
it.copy(isLogged = isLogged ?: false)
@ -143,16 +143,16 @@ class ExploreViewModel(
}
private fun onFirstLoad() {
val settings = settingsRepository.currentSettings.value
val listingType = if (isOnOtherInstance) ListingType.Local else settings.defaultExploreType.toListingType()
val sortType = settings.defaultPostSortType.toSortType()
updateState {
it.copy(
listingType = listingType,
sortType = sortType,
)
}
screenModelScope.launch {
val settings = settingsRepository.currentSettings.value
val listingType = if (isOnOtherInstance) ListingType.Local else settings.defaultExploreType.toListingType()
val sortType = settings.defaultPostSortType.toSortType()
updateState {
it.copy(
listingType = listingType,
sortType = sortType,
)
}
val auth = identityRepository.authToken.value
val downVoteEnabled = siteRepository.isDownVoteEnabled(auth)
updateState { it.copy(downVoteEnabled = downVoteEnabled) }
@ -265,31 +265,31 @@ class ExploreViewModel(
}
private fun setSearch(value: String) {
updateState { it.copy(searchText = value) }
screenModelScope.launch {
updateState { it.copy(searchText = value) }
searchEventChannel.send(Unit)
}
}
private fun changeListingType(value: ListingType) {
updateState { it.copy(listingType = value) }
screenModelScope.launch {
updateState { it.copy(listingType = value) }
emitEffect(ExploreMviModel.Effect.BackToTop)
refresh()
}
}
private fun changeSortType(value: SortType) {
updateState { it.copy(sortType = value) }
screenModelScope.launch {
updateState { it.copy(sortType = value) }
emitEffect(ExploreMviModel.Effect.BackToTop)
refresh()
}
}
private fun changeResultType(value: SearchResultType) {
updateState { it.copy(resultType = value) }
screenModelScope.launch {
updateState { it.copy(resultType = value) }
emitEffect(ExploreMviModel.Effect.BackToTop)
refresh()
}
@ -434,67 +434,73 @@ class ExploreViewModel(
}
private fun handleLogout() {
currentPage = 1
updateState {
it.copy(
listingType = ListingType.Local,
results = emptyList(),
)
screenModelScope.launch {
currentPage = 1
updateState {
it.copy(
listingType = ListingType.Local,
results = emptyList(),
)
}
onFirstLoad()
}
onFirstLoad()
}
private fun handlePostUpdate(post: PostModel) {
updateState {
it.copy(
results =
it.results.map { r ->
if (r is SearchResult.Post && r.model.id == post.id) {
r.copy(model = post)
} else {
r
}
},
)
screenModelScope.launch {
updateState {
it.copy(
results =
it.results.map { r ->
if (r is SearchResult.Post && r.model.id == post.id) {
r.copy(model = post)
} else {
r
}
},
)
}
}
}
private fun handleCommentUpdate(comment: CommentModel) {
updateState {
it.copy(
results =
it.results.map { r ->
if (r is SearchResult.Comment && r.model.id == comment.id) {
r.copy(model = comment)
} else {
r
}
},
)
screenModelScope.launch {
updateState {
it.copy(
results =
it.results.map { r ->
if (r is SearchResult.Comment && r.model.id == comment.id) {
r.copy(model = comment)
} else {
r
}
},
)
}
}
}
private fun toggleUpVote(post: PostModel) {
val newVote = post.myVote <= 0
val newPost =
postRepository.asUpVoted(
post = post,
voted = newVote,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Post) return@map res
if (res.model.id == post.id) {
res.copy(model = newPost)
} else {
res
}
},
)
}
screenModelScope.launch {
val newVote = post.myVote <= 0
val newPost =
postRepository.asUpVoted(
post = post,
voted = newVote,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Post) return@map res
if (res.model.id == post.id) {
res.copy(model = newPost)
} else {
res
}
},
)
}
try {
val auth = identityRepository.authToken.value.orEmpty()
postRepository.upVote(
@ -522,26 +528,26 @@ class ExploreViewModel(
}
private fun toggleDownVote(post: PostModel) {
val newValue = post.myVote >= 0
val newPost =
postRepository.asDownVoted(
post = post,
downVoted = newValue,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Post) return@map res
if (res.model.id == post.id) {
res.copy(model = newPost)
} else {
res
}
},
)
}
screenModelScope.launch {
val newValue = post.myVote >= 0
val newPost =
postRepository.asDownVoted(
post = post,
downVoted = newValue,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Post) return@map res
if (res.model.id == post.id) {
res.copy(model = newPost)
} else {
res
}
},
)
}
try {
val auth = identityRepository.authToken.value.orEmpty()
postRepository.downVote(
@ -569,26 +575,26 @@ class ExploreViewModel(
}
private fun toggleSave(post: PostModel) {
val newValue = !post.saved
val newPost =
postRepository.asSaved(
post = post,
saved = newValue,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Post) return@map res
if (res.model.id == post.id) {
res.copy(model = newPost)
} else {
res
}
},
)
}
screenModelScope.launch {
val newValue = !post.saved
val newPost =
postRepository.asSaved(
post = post,
saved = newValue,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Post) return@map res
if (res.model.id == post.id) {
res.copy(model = newPost)
} else {
res
}
},
)
}
try {
val auth = identityRepository.authToken.value.orEmpty()
postRepository.save(
@ -616,26 +622,26 @@ class ExploreViewModel(
}
private fun toggleUpVoteComment(comment: CommentModel) {
val newValue = comment.myVote <= 0
val newComment =
commentRepository.asUpVoted(
comment = comment,
voted = newValue,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Comment) return@map res
if (res.model.id == comment.id) {
res.copy(model = newComment)
} else {
res
}
},
)
}
screenModelScope.launch {
val newValue = comment.myVote <= 0
val newComment =
commentRepository.asUpVoted(
comment = comment,
voted = newValue,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Comment) return@map res
if (res.model.id == comment.id) {
res.copy(model = newComment)
} else {
res
}
},
)
}
try {
val auth = identityRepository.authToken.value.orEmpty()
commentRepository.upVote(
@ -663,22 +669,22 @@ class ExploreViewModel(
}
private fun toggleDownVoteComment(comment: CommentModel) {
val newValue = comment.myVote >= 0
val newComment = commentRepository.asDownVoted(comment, newValue)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Comment) return@map res
if (res.model.id == comment.id) {
res.copy(model = newComment)
} else {
res
}
},
)
}
screenModelScope.launch {
val newValue = comment.myVote >= 0
val newComment = commentRepository.asDownVoted(comment, newValue)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Comment) return@map res
if (res.model.id == comment.id) {
res.copy(model = newComment)
} else {
res
}
},
)
}
try {
val auth = identityRepository.authToken.value.orEmpty()
commentRepository.downVote(
@ -706,26 +712,26 @@ class ExploreViewModel(
}
private fun toggleSaveComment(comment: CommentModel) {
val newValue = !comment.saved
val newComment =
commentRepository.asSaved(
comment = comment,
saved = newValue,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Comment) return@map res
if (res.model.id == comment.id) {
res.copy(model = newComment)
} else {
res
}
},
)
}
screenModelScope.launch {
val newValue = !comment.saved
val newComment =
commentRepository.asSaved(
comment = comment,
saved = newValue,
)
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Comment) return@map res
if (res.model.id == comment.id) {
res.copy(model = newComment)
} else {
res
}
},
)
}
try {
val auth = identityRepository.authToken.value.orEmpty()
commentRepository.save(
@ -787,18 +793,20 @@ class ExploreViewModel(
}
private fun handleCommunityUpdate(community: CommunityModel) {
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Community) return@map res
if (res.model.id == community.id) {
res.copy(model = community)
} else {
res
}
},
)
screenModelScope.launch {
updateState {
it.copy(
results =
it.results.map { res ->
if (res !is SearchResult.Community) return@map res
if (res.model.id == community.id) {
res.copy(model = community)
} else {
res
}
},
)
}
}
}
}

View File

@ -47,10 +47,10 @@ class FilteredContentsViewModel(
initialState = FilteredContentsMviModel.State(),
) {
init {
updateState {
it.copy(contentsType = contentsType.toFilteredContentsType())
}
screenModelScope.launch {
updateState {
it.copy(contentsType = contentsType.toFilteredContentsType())
}
updateState {
it.copy(isAdmin = identityRepository.cachedUser?.admin == true)
}
@ -238,10 +238,10 @@ class FilteredContentsViewModel(
}
private fun changeSection(section: FilteredContentsSection) {
updateState {
it.copy(
section = section,
)
screenModelScope.launch {
updateState {
it.copy(section = section)
}
}
}
@ -408,17 +408,19 @@ class FilteredContentsViewModel(
}
private fun handlePostUpdate(post: PostModel) {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
screenModelScope.launch {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}
@ -503,23 +505,25 @@ class FilteredContentsViewModel(
}
private fun handleCommentUpdate(comment: CommentModel) {
updateState {
it.copy(
comments =
it.comments.map { c ->
if (c.id == comment.id) {
comment
} else {
c
}
},
)
screenModelScope.launch {
updateState {
it.copy(
comments =
it.comments.map { c ->
if (c.id == comment.id) {
comment
} else {
c
}
},
)
}
}
}
private fun changeLiked(value: Boolean) {
updateState { it.copy(liked = value) }
screenModelScope.launch {
updateState { it.copy(liked = value) }
refresh(initial = true)
emitEffect(FilteredContentsMviModel.Effect.BackToTop)
}

View File

@ -55,21 +55,23 @@ class InstanceInfoViewModel(
)
}
}
}
if (uiState.value.initial) {
refresh(initial = true)
if (uiState.value.initial) {
refresh(initial = true)
}
}
}
override fun reduce(intent: InstanceInfoMviModel.Intent) {
when (intent) {
InstanceInfoMviModel.Intent.LoadNextPage -> loadNextPage()
InstanceInfoMviModel.Intent.Refresh -> refresh()
InstanceInfoMviModel.Intent.Refresh ->
screenModelScope.launch {
refresh()
}
}
}
private fun refresh(initial: Boolean = false) {
private suspend fun refresh(initial: Boolean = false) {
currentPage = 1
updateState {
it.copy(
@ -85,7 +87,9 @@ class InstanceInfoViewModel(
private fun loadNextPage() {
val currentState = uiState.value
if (!currentState.canFetchMore || currentState.loading) {
updateState { it.copy(refreshing = false) }
screenModelScope.launch {
updateState { it.copy(refreshing = false) }
}
return
}
@ -133,8 +137,8 @@ class InstanceInfoViewModel(
}
private fun changeSortType(value: SortType) {
updateState { it.copy(sortType = value) }
screenModelScope.launch {
updateState { it.copy(sortType = value) }
emitEffect(InstanceInfoMviModel.Effect.BackToTop)
refresh()
}

View File

@ -1,18 +1,22 @@
package com.github.diegoberaldin.raccoonforlemmy.unit.licences
import cafe.adriel.voyager.core.model.screenModelScope
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
import com.github.diegoberaldin.raccoonforlemmy.unit.licences.models.LicenceItem
import com.github.diegoberaldin.raccoonforlemmy.unit.licences.models.LicenceItemType
import kotlinx.coroutines.launch
class LicencesViewModel : LicencesMviModel,
DefaultMviModel<LicencesMviModel.Intent, LicencesMviModel.State, LicencesMviModel.Effect>(
initialState = LicencesMviModel.State(),
) {
init {
populate()
screenModelScope.launch {
populate()
}
}
private fun populate() {
private suspend fun populate() {
updateState {
it.copy(
items =

View File

@ -29,8 +29,10 @@ class LoginViewModel(
) {
init {
val instance = apiConfigurationRepository.instance.value
updateState {
it.copy(instanceName = instance)
screenModelScope.launch {
updateState {
it.copy(instanceName = instance)
}
}
}
@ -45,19 +47,27 @@ class LoginViewModel(
}
private fun setInstanceName(value: String) {
updateState { it.copy(instanceName = value) }
screenModelScope.launch {
updateState { it.copy(instanceName = value) }
}
}
private fun setUsername(value: String) {
updateState { it.copy(username = value.trim()) }
screenModelScope.launch {
updateState { it.copy(username = value.trim()) }
}
}
private fun setPassword(value: String) {
updateState { it.copy(password = value) }
screenModelScope.launch {
updateState { it.copy(password = value) }
}
}
private fun setTotp2faToken(value: String) {
updateState { it.copy(totp2faToken = value) }
screenModelScope.launch {
updateState { it.copy(totp2faToken = value) }
}
}
private fun submit() {
@ -70,33 +80,41 @@ class LoginViewModel(
val username = currentState.username
val password = currentState.password
val totp2faToken = currentState.totp2faToken
updateState {
it.copy(
instanceNameError = null,
usernameError = null,
passwordError = null,
)
screenModelScope.launch {
updateState {
it.copy(
instanceNameError = null,
usernameError = null,
passwordError = null,
)
}
}
val valid =
when {
instance.isEmpty() -> {
updateState {
it.copy(instanceNameError = ValidationError.MissingField)
screenModelScope.launch {
updateState {
it.copy(instanceNameError = ValidationError.MissingField)
}
}
false
}
username.isEmpty() -> {
updateState {
it.copy(usernameError = ValidationError.MissingField)
screenModelScope.launch {
updateState {
it.copy(usernameError = ValidationError.MissingField)
}
}
false
}
password.isEmpty() -> {
updateState {
it.copy(passwordError = ValidationError.MissingField)
screenModelScope.launch {
updateState {
it.copy(passwordError = ValidationError.MissingField)
}
}
false
}

View File

@ -41,7 +41,9 @@ class ManageBanViewModel(
override fun reduce(intent: ManageBanMviModel.Intent) {
when (intent) {
is ManageBanMviModel.Intent.ChangeSection -> {
updateState { it.copy(section = intent.section) }
screenModelScope.launch {
updateState { it.copy(section = intent.section) }
}
}
ManageBanMviModel.Intent.Refresh -> {

View File

@ -99,8 +99,8 @@ class ManageSubscriptionsViewModel(
if (uiState.value.refreshing) {
return
}
updateState { it.copy(refreshing = true) }
screenModelScope.launch {
updateState { it.copy(refreshing = true) }
val auth = identityRepository.authToken.value
val accountId = accountRepository.getActive()?.id ?: 0L
val favoriteCommunityIds =
@ -169,20 +169,22 @@ class ManageSubscriptionsViewModel(
}
private fun handleMultiCommunityCreated(community: MultiCommunityModel) {
val oldCommunities = uiState.value.multiCommunities
val newCommunities =
if (oldCommunities.any { it.id == community.id }) {
oldCommunities.map {
if (it.id == community.id) {
community
} else {
it
screenModelScope.launch {
val oldCommunities = uiState.value.multiCommunities
val newCommunities =
if (oldCommunities.any { it.id == community.id }) {
oldCommunities.map {
if (it.id == community.id) {
community
} else {
it
}
}
}
} else {
oldCommunities + community
}.sortedBy { it.name }
updateState { it.copy(multiCommunities = newCommunities) }
} else {
oldCommunities + community
}.sortedBy { it.name }
updateState { it.copy(multiCommunities = newCommunities) }
}
}
private fun toggleFavorite(community: CommunityModel) {
@ -205,23 +207,25 @@ class ManageSubscriptionsViewModel(
}
private fun handleCommunityUpdate(community: CommunityModel) {
updateState {
it.copy(
communities =
it.communities.map { c ->
if (c.id == community.id) {
community
} else {
c
}
},
)
screenModelScope.launch {
updateState {
it.copy(
communities =
it.communities.map { c ->
if (c.id == community.id) {
community
} else {
c
}
},
)
}
}
}
private fun updateSearchText(value: String) {
updateState { it.copy(searchText = value) }
screenModelScope.launch {
updateState { it.copy(searchText = value) }
searchEventChannel.send(Unit)
}
}

View File

@ -126,8 +126,8 @@ class InboxMentionsViewModel(
}
private fun changeUnreadOnly(value: Boolean) {
updateState { it.copy(unreadOnly = value) }
screenModelScope.launch {
updateState { it.copy(unreadOnly = value) }
refresh(initial = true)
emitEffect(InboxMentionsMviModel.Effect.BackToTop)
}
@ -174,17 +174,19 @@ class InboxMentionsViewModel(
}
private fun handleItemUpdate(item: PersonMentionModel) {
updateState {
it.copy(
mentions =
it.mentions.map { i ->
if (i.id == item.id) {
item
} else {
i
}
},
)
screenModelScope.launch {
updateState {
it.copy(
mentions =
it.mentions.map { i ->
if (i.id == item.id) {
item
} else {
i
}
},
)
}
}
}
@ -287,8 +289,8 @@ class InboxMentionsViewModel(
}
private fun handleLogout() {
updateState { it.copy(mentions = emptyList()) }
screenModelScope.launch {
updateState { it.copy(mentions = emptyList()) }
refresh(initial = true)
}
}

View File

@ -100,8 +100,8 @@ class InboxMessagesViewModel(
if (uiState.value.currentUserId == 0L) {
return
}
updateState { it.copy(unreadOnly = value) }
screenModelScope.launch {
updateState { it.copy(unreadOnly = value) }
refresh(initial = true)
emitEffect(InboxMessagesMviModel.Effect.BackToTop)
}
@ -168,8 +168,8 @@ class InboxMessagesViewModel(
}
private fun handleLogout() {
updateState { it.copy(chats = emptyList()) }
screenModelScope.launch {
updateState { it.copy(chats = emptyList()) }
refresh(initial = true)
}
}

View File

@ -22,14 +22,18 @@ class ModerateWithReasonViewModel(
initialState = ModerateWithReasonMviModel.UiState(),
) {
init {
updateState { it.copy(action = actionId.toModerateWithReasonAction()) }
screenModelScope.launch {
updateState { it.copy(action = actionId.toModerateWithReasonAction()) }
}
}
override fun reduce(intent: ModerateWithReasonMviModel.Intent) {
when (intent) {
is ModerateWithReasonMviModel.Intent.SetText -> {
updateState {
it.copy(text = intent.value)
screenModelScope.launch {
updateState {
it.copy(text = intent.value)
}
}
}
@ -43,8 +47,8 @@ class ModerateWithReasonViewModel(
}
val text = uiState.value.text
updateState { it.copy(loading = true) }
screenModelScope.launch {
updateState { it.copy(loading = true) }
val auth = identityRepository.authToken.value.orEmpty()
try {
when (uiState.value.action) {

View File

@ -53,16 +53,16 @@ class ModlogViewModel(
}
private fun refresh(initial: Boolean = false) {
currentPage = 1
updateState {
it.copy(
canFetchMore = true,
refreshing = !initial,
initial = initial,
loading = false,
)
}
screenModelScope.launch {
currentPage = 1
updateState {
it.copy(
canFetchMore = true,
refreshing = !initial,
initial = initial,
loading = false,
)
}
loadNextPage()
}
}
@ -70,7 +70,9 @@ class ModlogViewModel(
private fun loadNextPage() {
val currentState = uiState.value
if (!currentState.canFetchMore || currentState.loading) {
updateState { it.copy(refreshing = false) }
screenModelScope.launch {
updateState { it.copy(refreshing = false) }
}
return
}

View File

@ -247,8 +247,8 @@ class MultiCommunityViewModel(
if (uiState.value.sortType == value) {
return
}
updateState { it.copy(sortType = value) }
screenModelScope.launch {
updateState { it.copy(sortType = value) }
emitEffect(MultiCommunityMviModel.Effect.BackToTop)
delay(50)
refresh()
@ -349,35 +349,41 @@ class MultiCommunityViewModel(
}
private fun handlePostUpdate(post: PostModel) {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
screenModelScope.launch {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}
private fun clearRead() {
hideReadPosts = true
updateState {
val newPosts = it.posts.filter { e -> !e.read }
it.copy(posts = newPosts)
screenModelScope.launch {
hideReadPosts = true
updateState {
val newPosts = it.posts.filter { e -> !e.read }
it.copy(posts = newPosts)
}
}
}
private fun hide(post: PostModel) {
updateState {
val newPosts = it.posts.filter { e -> e.id != post.id }
it.copy(
posts = newPosts,
)
screenModelScope.launch {
updateState {
val newPosts = it.posts.filter { e -> e.id != post.id }
it.copy(
posts = newPosts,
)
}
markAsRead(post)
}
markAsRead(post)
}
}

View File

@ -62,7 +62,10 @@ class MultiCommunityEditorViewModel(
override fun reduce(intent: MultiCommunityEditorMviModel.Intent) {
when (intent) {
is MultiCommunityEditorMviModel.Intent.SelectImage -> selectImage(intent.index)
is MultiCommunityEditorMviModel.Intent.SetName -> updateState { it.copy(name = intent.value) }
is MultiCommunityEditorMviModel.Intent.SetName ->
screenModelScope.launch {
updateState { it.copy(name = intent.value) }
}
is MultiCommunityEditorMviModel.Intent.ToggleCommunity -> toggleCommunity(intent.id)
is MultiCommunityEditorMviModel.Intent.SetSearch -> setSearch(intent.value)
MultiCommunityEditorMviModel.Intent.Submit -> submit()
@ -94,8 +97,8 @@ class MultiCommunityEditorViewModel(
}
private fun setSearch(value: String) {
updateState { it.copy(searchText = value) }
screenModelScope.launch {
updateState { it.copy(searchText = value) }
searchEventChannel.send(Unit)
}
}
@ -112,13 +115,15 @@ class MultiCommunityEditorViewModel(
}
private fun selectImage(index: Int?) {
val image =
if (index == null) {
null
} else {
uiState.value.availableIcons[index]
}
updateState { it.copy(icon = image) }
screenModelScope.launch {
val image =
if (index == null) {
null
} else {
uiState.value.availableIcons[index]
}
updateState { it.copy(icon = image) }
}
}
private fun toggleCommunity(communityId: Long) {
@ -138,21 +143,27 @@ class MultiCommunityEditorViewModel(
}
communities = newCommunities
val filtered = filterCommunities()
updateState { state ->
state.copy(
communities = filtered,
availableIcons = availableIcons,
)
screenModelScope.launch {
updateState { state ->
state.copy(
communities = filtered,
availableIcons = availableIcons,
)
}
}
}
private fun submit() {
updateState { it.copy(nameError = null) }
screenModelScope.launch {
updateState { it.copy(nameError = null) }
}
val currentState = uiState.value
var valid = true
val name = currentState.name
if (name.isEmpty()) {
updateState { it.copy(nameError = ValidationError.MissingField) }
screenModelScope.launch {
updateState { it.copy(nameError = ValidationError.MissingField) }
}
valid = false
}
if (!valid) {

View File

@ -53,8 +53,9 @@ class ProfileLoggedViewModel(
initialState = ProfileLoggedMviModel.UiState(),
) {
init {
updateState { it.copy(instance = apiConfigurationRepository.instance.value) }
screenModelScope.launch {
updateState { it.copy(instance = apiConfigurationRepository.instance.value) }
themeRepository.postLayout.onEach { layout ->
updateState { it.copy(postLayout = layout) }
}.launchIn(this)
@ -253,8 +254,10 @@ class ProfileLoggedViewModel(
}
private fun changeSection(section: ProfileLoggedSection) {
updateState {
it.copy(section = section)
screenModelScope.launch {
updateState {
it.copy(section = section)
}
}
}
@ -444,37 +447,47 @@ class ProfileLoggedViewModel(
}
private fun handlePostUpdate(post: PostModel) {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
screenModelScope.launch {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}
private fun handleCommentUpdate(comment: CommentModel) {
updateState {
it.copy(
comments =
it.comments.map { c ->
if (c.id == comment.id) {
comment
} else {
c
}
},
)
screenModelScope.launch {
updateState {
it.copy(
comments =
it.comments.map { c ->
if (c.id == comment.id) {
comment
} else {
c
}
},
)
}
}
}
private fun handlePostDelete(id: Long) {
updateState { it.copy(posts = it.posts.filter { post -> post.id != id }) }
screenModelScope.launch {
updateState {
it.copy(
posts = it.posts.filter { post -> post.id != id },
)
}
}
}
private fun deletePost(id: Long) {

View File

@ -126,7 +126,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomS
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.getScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.ActionOnSwipe
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
@ -191,7 +190,6 @@ class PostDetailScreen(
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val notificationCenter = remember { getNotificationCenter() }
val themeRepository = remember { getThemeRepository() }
val upVoteColor by themeRepository.upVoteColor.collectAsState()
val downVoteColor by themeRepository.downVoteColor.collectAsState()
@ -257,9 +255,6 @@ class PostDetailScreen(
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.resetCache()
}
LaunchedEffect(model) {
model.effects.onEach { effect ->
when (effect) {

View File

@ -74,14 +74,14 @@ class PostDetailViewModel(
}
init {
updateState {
it.copy(
instance =
otherInstance.takeIf { n -> n.isNotEmpty() }
?: apiConfigurationRepository.instance.value,
)
}
screenModelScope.launch {
updateState {
it.copy(
instance =
otherInstance.takeIf { n -> n.isNotEmpty() }
?: apiConfigurationRepository.instance.value,
)
}
if (uiState.value.post.id == 0L) {
val post = itemCache.getPost(postId) ?: PostModel()
val downVoteEnabled = siteRepository.isDownVoteEnabled(identityRepository.authToken.value)
@ -371,9 +371,11 @@ class PostDetailViewModel(
}
is PostDetailMviModel.Intent.ChangeSearching -> {
updateState { it.copy(searching = intent.value) }
if (!intent.value) {
updateSearchText("")
screenModelScope.launch {
updateState { it.copy(searching = intent.value) }
if (!intent.value) {
updateSearchText("")
}
}
}
@ -472,8 +474,8 @@ class PostDetailViewModel(
if (uiState.value.sortType == value) {
return
}
updateState { it.copy(sortType = value) }
screenModelScope.launch {
updateState { it.copy(sortType = value) }
emitEffect(PostDetailMviModel.Effect.BackToTop)
delay(50)
refresh()
@ -481,8 +483,10 @@ class PostDetailViewModel(
}
private fun handlePostUpdate(post: PostModel) {
updateState {
it.copy(post = post)
screenModelScope.launch {
updateState {
it.copy(post = post)
}
}
}
@ -546,8 +550,8 @@ class PostDetailViewModel(
post = post,
voted = newValue,
)
updateState { it.copy(post = newPost) }
screenModelScope.launch {
updateState { it.copy(post = newPost) }
try {
val auth = identityRepository.authToken.value.orEmpty()
postRepository.upVote(
@ -572,10 +576,10 @@ class PostDetailViewModel(
post = post,
downVoted = newValue,
)
updateState {
it.copy(post = newPost)
}
screenModelScope.launch {
updateState {
it.copy(post = newPost)
}
try {
val auth = identityRepository.authToken.value.orEmpty()
postRepository.downVote(
@ -600,8 +604,8 @@ class PostDetailViewModel(
post = post,
saved = newValue,
)
updateState { it.copy(post = newPost) }
screenModelScope.launch {
updateState { it.copy(post = newPost) }
try {
val auth = identityRepository.authToken.value.orEmpty()
postRepository.save(
@ -620,17 +624,19 @@ class PostDetailViewModel(
}
private fun handleCommentUpdate(comment: CommentModel) {
updateState {
it.copy(
comments =
it.comments.map { c ->
if (c.id == comment.id) {
comment
} else {
c
}
},
)
screenModelScope.launch {
updateState {
it.copy(
comments =
it.comments.map { c ->
if (c.id == comment.id) {
comment
} else {
c
}
},
)
}
}
}
@ -718,7 +724,9 @@ class PostDetailViewModel(
}
private fun handleCommentDelete(id: Long) {
updateState { it.copy(comments = it.comments.filter { comment -> comment.id != id }) }
screenModelScope.launch {
updateState { it.copy(comments = it.comments.filter { comment -> comment.id != id }) }
}
}
private fun deletePost() {
@ -833,16 +841,16 @@ class PostDetailViewModel(
}
private fun updateSearchText(value: String) {
updateState { it.copy(searchText = value) }
screenModelScope.launch {
updateState { it.copy(searchText = value) }
searchEventChannel.send(Unit)
}
}
private fun navigateToPreviousPost() {
val currentId = uiState.value.post.id
updateState { it.copy(loading = true, initial = true) }
screenModelScope.launch {
updateState { it.copy(loading = true, initial = true) }
postNavigationManager.getPrevious(currentId)?.also { newPost ->
loadNewPost(newPost)
}
@ -851,8 +859,8 @@ class PostDetailViewModel(
private fun navigateToNextPost() {
val currentId = uiState.value.post.id
updateState { it.copy(loading = true, initial = true) }
screenModelScope.launch {
updateState { it.copy(loading = true, initial = true) }
postNavigationManager.getNext(currentId)?.also { newPost ->
loadNewPost(newPost)
}

View File

@ -156,14 +156,14 @@ class PostListViewModel(
}
private fun onFirstLoad() {
val settings = settingsRepository.currentSettings.value
updateState {
it.copy(
listingType = settings.defaultListingType.toListingType(),
sortType = settings.defaultPostSortType.toSortType(),
)
}
screenModelScope.launch {
val settings = settingsRepository.currentSettings.value
updateState {
it.copy(
listingType = settings.defaultListingType.toListingType(),
sortType = settings.defaultPostSortType.toSortType(),
)
}
refreshUser()
refresh(initial = true)
emitEffect(PostListMviModel.Effect.BackToTop)
@ -251,16 +251,20 @@ class PostListViewModel(
}
PostListMviModel.Intent.PauseZombieMode -> {
updateState { it.copy(zombieModeActive = false) }
zombieModeHelper.pause()
screenModelScope.launch {
updateState { it.copy(zombieModeActive = false) }
zombieModeHelper.pause()
}
}
is PostListMviModel.Intent.StartZombieMode -> {
updateState { it.copy(zombieModeActive = true) }
zombieModeHelper.start(
initialValue = intent.index,
interval = settingsRepository.currentSettings.value.zombieModeInterval,
)
screenModelScope.launch {
updateState { it.copy(zombieModeActive = true) }
zombieModeHelper.start(
initialValue = intent.index,
interval = settingsRepository.currentSettings.value.zombieModeInterval,
)
}
}
is PostListMviModel.Intent.Copy ->
@ -339,8 +343,8 @@ class PostListViewModel(
if (uiState.value.sortType == value) {
return
}
updateState { it.copy(sortType = value) }
screenModelScope.launch {
updateState { it.copy(sortType = value) }
emitEffect(PostListMviModel.Effect.BackToTop)
delay(50)
refresh()
@ -351,8 +355,8 @@ class PostListViewModel(
if (uiState.value.listingType == value) {
return
}
updateState { it.copy(listingType = value) }
screenModelScope.launch {
updateState { it.copy(listingType = value) }
emitEffect(PostListMviModel.Effect.BackToTop)
delay(50)
refresh()
@ -453,28 +457,32 @@ class PostListViewModel(
}
private fun handlePostUpdate(post: PostModel) {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
screenModelScope.launch {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}
private fun handleLogout() {
updateState {
it.copy(
posts = emptyList(),
isLogged = false,
)
screenModelScope.launch {
updateState {
it.copy(
posts = emptyList(),
isLogged = false,
)
}
onFirstLoad()
}
onFirstLoad()
}
private fun handlePostDelete(id: Long) {
@ -486,21 +494,25 @@ class PostListViewModel(
}
private fun clearRead() {
hideReadPosts = true
updateState {
val newPosts = it.posts.filter { e -> !e.read }
it.copy(posts = newPosts)
screenModelScope.launch {
hideReadPosts = true
updateState {
val newPosts = it.posts.filter { e -> !e.read }
it.copy(posts = newPosts)
}
}
}
private fun hide(post: PostModel) {
updateState {
val newPosts = it.posts.filter { e -> e.id != post.id }
it.copy(
posts = newPosts,
)
screenModelScope.launch {
updateState {
val newPosts = it.posts.filter { e -> e.id != post.id }
it.copy(
posts = newPosts,
)
}
markAsRead(post)
}
markAsRead(post)
}
private fun blockUser(userId: Long) {

View File

@ -123,8 +123,8 @@ class InboxRepliesViewModel(
}
private fun changeUnreadOnly(value: Boolean) {
updateState { it.copy(unreadOnly = value) }
screenModelScope.launch {
updateState { it.copy(unreadOnly = value) }
refresh(initial = true)
emitEffect(InboxRepliesMviModel.Effect.BackToTop)
}
@ -171,17 +171,19 @@ class InboxRepliesViewModel(
}
private fun handleItemUpdate(item: PersonMentionModel) {
updateState {
it.copy(
replies =
it.replies.map { i ->
if (i.id == item.id) {
item
} else {
i
}
},
)
screenModelScope.launch {
updateState {
it.copy(
replies =
it.replies.map { i ->
if (i.id == item.id) {
item
} else {
i
}
},
)
}
}
}
@ -282,8 +284,8 @@ class InboxRepliesViewModel(
}
private fun handleLogout() {
updateState { it.copy(replies = emptyList()) }
screenModelScope.launch {
updateState { it.copy(replies = emptyList()) }
refresh(initial = true)
}
}

View File

@ -90,18 +90,20 @@ class ReportListViewModel(
}
private fun changeSection(section: ReportListSection) {
updateState {
it.copy(
section = section,
)
screenModelScope.launch {
updateState {
it.copy(
section = section,
)
}
}
}
private fun changeUnresolvedOnly(value: Boolean) {
updateState {
it.copy(unresolvedOnly = value)
}
screenModelScope.launch {
updateState {
it.copy(unresolvedOnly = value)
}
emitEffect(ReportListMviModel.Effect.BackToTop)
delay(50)
refresh(initial = true)
@ -255,48 +257,56 @@ class ReportListViewModel(
}
private fun handleReportUpdate(report: PostReportModel) {
updateState {
it.copy(
postReports =
it.postReports.map { r ->
if (r.id == report.id) {
report
} else {
r
}
},
)
screenModelScope.launch {
updateState {
it.copy(
postReports =
it.postReports.map { r ->
if (r.id == report.id) {
report
} else {
r
}
},
)
}
}
}
private fun handleReportUpdate(report: CommentReportModel) {
updateState {
it.copy(
commentReports =
it.commentReports.map { r ->
if (r.id == report.id) {
report
} else {
r
}
},
)
screenModelScope.launch {
updateState {
it.copy(
commentReports =
it.commentReports.map { r ->
if (r.id == report.id) {
report
} else {
r
}
},
)
}
}
}
private fun handleReporDelete(report: PostReportModel) {
updateState {
it.copy(
postReports = it.postReports.filter { r -> r.id != report.id },
)
screenModelScope.launch {
updateState {
it.copy(
postReports = it.postReports.filter { r -> r.id != report.id },
)
}
}
}
private fun handleReporDelete(report: CommentReportModel) {
updateState {
it.copy(
commentReports = it.commentReports.filter { r -> r.id != report.id },
)
screenModelScope.launch {
updateState {
it.copy(
commentReports = it.commentReports.filter { r -> r.id != report.id },
)
}
}
}
}

View File

@ -58,8 +58,8 @@ class SelectCommunityViewModel(
}
private fun setSearch(value: String) {
updateState { it.copy(searchText = value) }
screenModelScope.launch {
updateState { it.copy(searchText = value) }
searchEventChannel.send(Unit)
}
}

View File

@ -51,7 +51,9 @@ class SelectInstanceViewModel(
}
is SelectInstanceMviModel.Intent.ChangeInstanceName -> {
updateState { it.copy(changeInstanceName = intent.value) }
screenModelScope.launch {
updateState { it.copy(changeInstanceName = intent.value) }
}
}
is SelectInstanceMviModel.Intent.SubmitChangeInstanceDialog -> submitChangeInstance()
@ -69,11 +71,15 @@ class SelectInstanceViewModel(
}
private fun submitChangeInstance() {
updateState { it.copy(changeInstanceNameError = null) }
screenModelScope.launch {
updateState { it.copy(changeInstanceNameError = null) }
}
var valid = true
val instanceName = uiState.value.changeInstanceName
if (instanceName.isEmpty()) {
updateState { it.copy(changeInstanceNameError = ValidationError.MissingField) }
screenModelScope.launch {
updateState { it.copy(changeInstanceNameError = ValidationError.MissingField) }
}
valid = false
}
if (!valid) {

View File

@ -98,7 +98,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomS
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.getScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.ActionOnSwipe
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
@ -152,7 +151,6 @@ class UserDetailScreen(
val otherInstanceName = remember { otherInstance }
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val notificationCenter = remember { getNotificationCenter() }
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val themeRepository = remember { getThemeRepository() }
@ -171,9 +169,6 @@ class UserDetailScreen(
val detailOpener = remember { getDetailOpener() }
val clipboardManager = LocalClipboardManager.current
LaunchedEffect(notificationCenter) {
notificationCenter.resetCache()
}
LaunchedEffect(model) {
model.effects.onEach { effect ->
when (effect) {

View File

@ -63,14 +63,15 @@ class UserDetailViewModel(
initialState = UserDetailMviModel.UiState(),
) {
init {
updateState {
it.copy(
instance =
otherInstance.takeIf { n -> n.isNotEmpty() }
?: apiConfigurationRepository.instance.value,
)
}
screenModelScope.launch {
updateState {
it.copy(
instance =
otherInstance.takeIf { n -> n.isNotEmpty() }
?: apiConfigurationRepository.instance.value,
)
}
if (uiState.value.user.id == 0L) {
val user = itemCache.getUser(userId) ?: UserModel()
updateState {
@ -238,8 +239,8 @@ class UserDetailViewModel(
if (uiState.value.sortType == value) {
return
}
updateState { it.copy(sortType = value) }
screenModelScope.launch(Dispatchers.Main) {
updateState { it.copy(sortType = value) }
emitEffect(UserDetailMviModel.Effect.BackToTop)
delay(50)
refresh()
@ -247,8 +248,10 @@ class UserDetailViewModel(
}
private fun changeSection(section: UserDetailSection) {
updateState {
it.copy(section = section)
screenModelScope.launch {
updateState {
it.copy(section = section)
}
}
}
@ -495,38 +498,42 @@ class UserDetailViewModel(
}
private fun handlePostUpdate(post: PostModel) {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
screenModelScope.launch {
updateState {
it.copy(
posts =
it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}
private fun handleCommentUpdate(comment: CommentModel) {
updateState {
it.copy(
comments =
it.comments.map { c ->
if (c.id == comment.id) {
comment
} else {
c
}
},
)
screenModelScope.launch {
updateState {
it.copy(
comments =
it.comments.map { c ->
if (c.id == comment.id) {
comment
} else {
c
}
},
)
}
}
}
private fun blockUser() {
updateState { it.copy(asyncInProgress = true) }
screenModelScope.launch {
updateState { it.copy(asyncInProgress = true) }
try {
val auth = identityRepository.authToken.value
userRepository.block(userId, true, auth).getOrThrow()
@ -540,8 +547,8 @@ class UserDetailViewModel(
}
private fun blockInstance() {
updateState { it.copy(asyncInProgress = true) }
screenModelScope.launch {
updateState { it.copy(asyncInProgress = true) }
try {
val user = uiState.value.user
val instanceId = user.instanceId

View File

@ -125,8 +125,10 @@ class ZoomableImageViewModel(
private fun changeContentScale(contentScale: ContentScale) {
imagePreloadManager.remove(url)
updateState {
it.copy(contentScale = contentScale)
screenModelScope.launch {
updateState {
it.copy(contentScale = contentScale)
}
}
}
}