mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-09 12:48:42 +01:00
refactor: use filtered contents to render bookmarks (#840)
This commit is contained in:
parent
236590c0f9
commit
a10189d212
@ -70,7 +70,6 @@ kotlin {
|
||||
implementation(projects.unit.modlog)
|
||||
implementation(projects.unit.myaccount)
|
||||
implementation(projects.unit.reportlist)
|
||||
implementation(projects.unit.saveditems)
|
||||
implementation(projects.unit.web)
|
||||
implementation(projects.unit.zoomableimage)
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ import com.github.diegoberaldin.raccoonforlemmy.unit.managesubscriptions.ManageS
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.modlog.ModlogScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.myaccount.ProfileLoggedScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.reportlist.ReportListScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.saveditems.SavedItemsScreen
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
@ -125,7 +124,8 @@ internal object ProfileMainScreen : Tab {
|
||||
}
|
||||
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Bookmarks -> {
|
||||
navigationCoordinator.pushScreen(SavedItemsScreen())
|
||||
val screen = FilteredContentsScreen(type = FilteredContentsType.Bookmarks.toInt())
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
}
|
||||
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Drafts -> {
|
||||
|
@ -85,7 +85,6 @@ include(":unit:rawcontent")
|
||||
include(":unit:remove")
|
||||
include(":unit:replies")
|
||||
include(":unit:reportlist")
|
||||
include(":unit:saveditems")
|
||||
include(":unit:selectcommunity")
|
||||
include(":unit:selectinstance")
|
||||
include(":unit:userdetail")
|
||||
|
@ -87,7 +87,6 @@ kotlin {
|
||||
implementation(projects.unit.postdetail)
|
||||
implementation(projects.unit.remove)
|
||||
implementation(projects.unit.reportlist)
|
||||
implementation(projects.unit.saveditems)
|
||||
implementation(projects.unit.selectcommunity)
|
||||
implementation(projects.unit.selectinstance)
|
||||
implementation(projects.unit.userdetail)
|
||||
|
@ -50,7 +50,6 @@ import com.github.diegoberaldin.raccoonforlemmy.unit.modlog.di.modlogModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.postdetail.di.postDetailModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.remove.di.removeModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.reportlist.di.reportListModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.saveditems.di.savedItemsModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.selectcommunity.di.selectCommunityModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.selectinstance.di.selectInstanceModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.userdetail.di.userDetailModule
|
||||
@ -93,7 +92,6 @@ val sharedHelperModule = module {
|
||||
instanceInfoModule,
|
||||
removeModule,
|
||||
reportListModule,
|
||||
savedItemsModule,
|
||||
createReportModule,
|
||||
createPostModule,
|
||||
createCommentModule,
|
||||
|
@ -51,7 +51,6 @@ import com.github.diegoberaldin.raccoonforlemmy.unit.modlog.di.modlogModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.postdetail.di.postDetailModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.remove.di.removeModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.reportlist.di.reportListModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.saveditems.di.savedItemsModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.selectcommunity.di.selectCommunityModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.selectinstance.di.selectInstanceModule
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.userdetail.di.userDetailModule
|
||||
@ -96,7 +95,6 @@ fun initKoin() {
|
||||
instanceInfoModule,
|
||||
removeModule,
|
||||
reportListModule,
|
||||
savedItemsModule,
|
||||
createReportModule,
|
||||
createPostModule,
|
||||
createCommentModule,
|
||||
|
@ -11,14 +11,17 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||
sealed interface FilteredContentsType {
|
||||
data object Votes : FilteredContentsType
|
||||
data object Moderated : FilteredContentsType
|
||||
data object Bookmarks : FilteredContentsType
|
||||
}
|
||||
|
||||
fun FilteredContentsType.toInt(): Int = when (this) {
|
||||
FilteredContentsType.Moderated -> 0
|
||||
FilteredContentsType.Votes -> 1
|
||||
FilteredContentsType.Bookmarks -> 2
|
||||
}
|
||||
|
||||
fun Int.toFilteredContentsType(): FilteredContentsType = when (this) {
|
||||
2 -> FilteredContentsType.Bookmarks
|
||||
1 -> FilteredContentsType.Votes
|
||||
else -> FilteredContentsType.Moderated
|
||||
}
|
||||
@ -26,7 +29,6 @@ fun Int.toFilteredContentsType(): FilteredContentsType = when (this) {
|
||||
|
||||
sealed interface FilteredContentsSection {
|
||||
data object Posts : FilteredContentsSection
|
||||
|
||||
data object Comments : FilteredContentsSection
|
||||
}
|
||||
|
||||
@ -47,7 +49,7 @@ interface FilteredContentsMviModel :
|
||||
data class ModFeaturePost(val id: Long) : Intent
|
||||
data class ModLockPost(val id: Long) : Intent
|
||||
data class ModDistinguishComment(val commentId: Long) : Intent
|
||||
data object WillOpenDetail: Intent
|
||||
data object WillOpenDetail : Intent
|
||||
}
|
||||
|
||||
data class State(
|
||||
|
@ -175,6 +175,7 @@ class FilteredContentsScreen(
|
||||
text = when (uiState.contentsType) {
|
||||
FilteredContentsType.Moderated -> LocalXmlStrings.current.moderatorZoneActionContents
|
||||
FilteredContentsType.Votes -> LocalXmlStrings.current.profileUpvotesDownvotes
|
||||
FilteredContentsType.Bookmarks -> LocalXmlStrings.current.navigationDrawerTitleBookmarks
|
||||
},
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
|
@ -21,8 +21,6 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.imageUrl
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@ -185,6 +183,10 @@ class FilteredContentsViewModel(
|
||||
liked = currentState.liked,
|
||||
sortType = SortType.New,
|
||||
)
|
||||
|
||||
FilteredContentsType.Bookmarks -> PostPaginationSpecification.Saved(
|
||||
sortType = SortType.New,
|
||||
)
|
||||
}
|
||||
postPaginationManager.reset(postSpecification)
|
||||
val commentSpecification = when (currentState.contentsType) {
|
||||
@ -197,6 +199,10 @@ class FilteredContentsViewModel(
|
||||
liked = currentState.liked,
|
||||
sortType = SortType.New,
|
||||
)
|
||||
|
||||
FilteredContentsType.Bookmarks -> CommentPaginationSpecification.Saved(
|
||||
sortType = SortType.New,
|
||||
)
|
||||
}
|
||||
commentPaginationManager.reset(commentSpecification)
|
||||
updateState {
|
||||
@ -329,7 +335,7 @@ class FilteredContentsViewModel(
|
||||
saved = newValue,
|
||||
)
|
||||
handlePostUpdate(newPost)
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
screenModelScope.launch {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
postRepository.save(
|
||||
@ -348,7 +354,7 @@ class FilteredContentsViewModel(
|
||||
}
|
||||
|
||||
private fun feature(post: PostModel) {
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
screenModelScope.launch {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newPost = postRepository.featureInCommunity(
|
||||
postId = post.id, auth = auth, featured = !post.featuredCommunity
|
||||
@ -360,7 +366,7 @@ class FilteredContentsViewModel(
|
||||
}
|
||||
|
||||
private fun lock(post: PostModel) {
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
screenModelScope.launch {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newPost = postRepository.lock(
|
||||
postId = post.id,
|
||||
|
@ -1,81 +0,0 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.compose)
|
||||
alias(libs.plugins.detekt)
|
||||
}
|
||||
|
||||
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
|
||||
kotlin {
|
||||
applyDefaultHierarchyTemplate()
|
||||
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
listOf(
|
||||
iosX64(),
|
||||
iosArm64(),
|
||||
iosSimulatorArm64()
|
||||
).forEach {
|
||||
it.binaries.framework {
|
||||
baseName = "saveditems"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material)
|
||||
implementation(compose.material3)
|
||||
implementation(compose.materialIconsExtended)
|
||||
|
||||
implementation(libs.koin.core)
|
||||
implementation(libs.voyager.navigator)
|
||||
implementation(libs.voyager.screenmodel)
|
||||
implementation(libs.voyager.koin)
|
||||
|
||||
implementation(projects.core.appearance)
|
||||
implementation(projects.core.architecture)
|
||||
implementation(projects.core.commonui.components)
|
||||
implementation(projects.core.commonui.detailopenerApi)
|
||||
implementation(projects.core.commonui.lemmyui)
|
||||
implementation(projects.core.commonui.modals)
|
||||
implementation(projects.core.l10n)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.notifications)
|
||||
implementation(projects.core.persistence)
|
||||
implementation(projects.core.utils)
|
||||
|
||||
implementation(projects.domain.identity)
|
||||
implementation(projects.domain.lemmy.data)
|
||||
implementation(projects.domain.lemmy.pagination)
|
||||
implementation(projects.domain.lemmy.repository)
|
||||
|
||||
implementation(projects.unit.zoomableimage)
|
||||
implementation(projects.unit.web)
|
||||
implementation(projects.unit.createreport)
|
||||
implementation(projects.unit.createcomment)
|
||||
implementation(projects.unit.rawcontent)
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.github.diegoberaldin.raccoonforlemmy.unit.saveditems"
|
||||
compileSdk = libs.versions.android.targetSdk.get().toInt()
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
||||
<CurrentIssues>
|
||||
<ID>CyclomaticComplexMethod:SavedItemsScreen.kt$SavedItemsScreen$@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable override fun Content()</ID>
|
||||
<ID>CyclomaticComplexMethod:SavedItemsViewModel.kt$SavedItemsViewModel$override fun reduce(intent: SavedItemsMviModel.Intent)</ID>
|
||||
<ID>LongMethod:SavedItemsScreen.kt$SavedItemsScreen$@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable override fun Content()</ID>
|
||||
<ID>LongMethod:SavedItemsViewModel.kt$SavedItemsViewModel$private fun loadNextPage()</ID>
|
||||
<ID>LongParameterList:SavedItemsViewModel.kt$SavedItemsViewModel$( private val identityRepository: IdentityRepository, private val apiConfigurationRepository: ApiConfigurationRepository, private val siteRepository: SiteRepository, private val userRepository: UserRepository, private val postRepository: PostRepository, private val commentRepository: CommentRepository, private val themeRepository: ThemeRepository, private val settingsRepository: SettingsRepository, private val shareHelper: ShareHelper, private val notificationCenter: NotificationCenter, private val hapticFeedback: HapticFeedback, private val getSortTypesUseCase: GetSortTypesUseCase, )</ID>
|
||||
<ID>TooGenericExceptionCaught:SavedItemsViewModel.kt$SavedItemsViewModel$e: Throwable</ID>
|
||||
<ID>TooManyFunctions:SavedItemsViewModel.kt$SavedItemsViewModel : SavedItemsMviModelDefaultMviModel</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
@ -1,56 +0,0 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.unit.saveditems
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.VoteFormat
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||
|
||||
@Stable
|
||||
interface SavedItemsMviModel :
|
||||
MviModel<SavedItemsMviModel.Intent, SavedItemsMviModel.UiState, SavedItemsMviModel.Effect>,
|
||||
ScreenModel {
|
||||
|
||||
sealed interface Intent {
|
||||
data object Refresh : Intent
|
||||
data object LoadNextPage : Intent
|
||||
data class ChangeSection(val section: SavedItemsSection) : Intent
|
||||
data class UpVotePost(val id: Long, val feedback: Boolean = false) : Intent
|
||||
data class DownVotePost(val id: Long, val feedback: Boolean = false) : Intent
|
||||
data class SavePost(val id: Long, val feedback: Boolean = false) : Intent
|
||||
data class UpVoteComment(val id: Long, val feedback: Boolean = false) : Intent
|
||||
data class DownVoteComment(val id: Long, val feedback: Boolean = false) : Intent
|
||||
data class SaveComment(val id: Long, val feedback: Boolean = false) : Intent
|
||||
data class Share(val url: String) : Intent
|
||||
data object WillOpenSave : Intent
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
val section: SavedItemsSection = SavedItemsSection.Posts,
|
||||
val user: UserModel? = null,
|
||||
val instance: String = "",
|
||||
val refreshing: Boolean = false,
|
||||
val loading: Boolean = false,
|
||||
val canFetchMore: Boolean = true,
|
||||
val sortType: SortType = SortType.New,
|
||||
val blurNsfw: Boolean = true,
|
||||
val posts: List<PostModel> = emptyList(),
|
||||
val comments: List<CommentModel> = emptyList(),
|
||||
val postLayout: PostLayout = PostLayout.Card,
|
||||
val fullHeightImages: Boolean = true,
|
||||
val fullWidthImages: Boolean = false,
|
||||
val voteFormat: VoteFormat = VoteFormat.Aggregated,
|
||||
val autoLoadImages: Boolean = true,
|
||||
val preferNicknames: Boolean = true,
|
||||
val showScores: Boolean = true,
|
||||
val availableSortTypes: List<SortType> = emptyList(),
|
||||
)
|
||||
|
||||
sealed interface Effect {
|
||||
data object BackToTop : Effect
|
||||
}
|
||||
}
|
@ -1,565 +0,0 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.unit.saveditems
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Snackbar
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.koin.getScreenModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.FloatingActionButtonMenu
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.FloatingActionButtonMenuItem
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SectionSelector
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.detailopener.api.getDetailOpener
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.CommentCard
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.Option
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.OptionId
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.PostCard
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.di.getFabNestedScrollConnection
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.ShareBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.LocalXmlStrings
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.readableHandle
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.toIcon
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.toInt
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.createreport.CreateReportScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.rawcontent.RawContentDialog
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.web.WebViewScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.zoomableimage.ZoomableImageScreen
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SavedItemsScreen : Screen {
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val model = getScreenModel<SavedItemsMviModel>()
|
||||
val uiState by model.uiState.collectAsState()
|
||||
val navigatorCoordinator = remember { getNavigationCoordinator() }
|
||||
val topAppBarState = rememberTopAppBarState()
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
|
||||
val lazyListState = rememberLazyListState()
|
||||
val scope = rememberCoroutineScope()
|
||||
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
|
||||
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
|
||||
var rawContent by remember { mutableStateOf<Any?>(null) }
|
||||
val settingsRepository = remember { getSettingsRepository() }
|
||||
val settings by settingsRepository.currentSettings.collectAsState()
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val detailOpener = remember { getDetailOpener() }
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
LaunchedEffect(navigationCoordinator) {
|
||||
navigationCoordinator.globalMessage.onEach { message ->
|
||||
snackbarHostState.showSnackbar(
|
||||
message = message,
|
||||
)
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.background(MaterialTheme.colorScheme.background),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = Spacing.s),
|
||||
text = LocalXmlStrings.current.navigationDrawerTitleBookmarks,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.onClick(
|
||||
onClick = {
|
||||
val sheet = SortBottomSheet(
|
||||
values = uiState.availableSortTypes.map { it.toInt() },
|
||||
screenKey = "savedItems",
|
||||
)
|
||||
navigatorCoordinator.showBottomSheet(sheet)
|
||||
},
|
||||
),
|
||||
imageVector = uiState.sortType.toIcon(),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
Image(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = {
|
||||
navigatorCoordinator.popScreen()
|
||||
},
|
||||
),
|
||||
imageVector = Icons.AutoMirrored.Default.ArrowBack,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
AnimatedVisibility(
|
||||
visible = isFabVisible,
|
||||
enter = slideInVertically(
|
||||
initialOffsetY = { it * 2 },
|
||||
),
|
||||
exit = slideOutVertically(
|
||||
targetOffsetY = { it * 2 },
|
||||
),
|
||||
) {
|
||||
FloatingActionButtonMenu(
|
||||
items = buildList {
|
||||
this += FloatingActionButtonMenuItem(
|
||||
icon = Icons.Default.ExpandLess,
|
||||
text = LocalXmlStrings.current.actionBackToTop,
|
||||
onSelected = rememberCallback {
|
||||
scope.launch {
|
||||
runCatching {
|
||||
lazyListState.scrollToItem(0)
|
||||
topAppBarState.heightOffset = 0f
|
||||
topAppBarState.contentOffset = 0f
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
snackbarHost = {
|
||||
SnackbarHost(snackbarHostState) { data ->
|
||||
Snackbar(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
snackbarData = data,
|
||||
)
|
||||
}
|
||||
},
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.then(
|
||||
if (settings.hideNavigationBarWhileScrolling) {
|
||||
Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
).nestedScroll(fabNestedScrollConnection),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
SectionSelector(
|
||||
titles = listOf(
|
||||
LocalXmlStrings.current.profileSectionPosts,
|
||||
LocalXmlStrings.current.profileSectionComments,
|
||||
),
|
||||
currentSection = when (uiState.section) {
|
||||
SavedItemsSection.Comments -> 1
|
||||
else -> 0
|
||||
},
|
||||
onSectionSelected = {
|
||||
val section = when (it) {
|
||||
1 -> SavedItemsSection.Comments
|
||||
else -> SavedItemsSection.Posts
|
||||
}
|
||||
model.reduce(SavedItemsMviModel.Intent.ChangeSection(section))
|
||||
},
|
||||
)
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = uiState.refreshing,
|
||||
onRefresh = rememberCallback(model) {
|
||||
model.reduce(SavedItemsMviModel.Intent.Refresh)
|
||||
},
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().pullRefresh(pullRefreshState),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = lazyListState,
|
||||
) {
|
||||
if (uiState.section == SavedItemsSection.Posts) {
|
||||
items(uiState.posts) { post ->
|
||||
PostCard(
|
||||
post = post,
|
||||
postLayout = uiState.postLayout,
|
||||
limitBodyHeight = true,
|
||||
fullHeightImage = uiState.fullHeightImages,
|
||||
fullWidthImage = uiState.fullWidthImages,
|
||||
voteFormat = uiState.voteFormat,
|
||||
autoLoadImages = uiState.autoLoadImages,
|
||||
preferNicknames = uiState.preferNicknames,
|
||||
showScores = uiState.showScores,
|
||||
blurNsfw = uiState.blurNsfw,
|
||||
onClick = {
|
||||
model.reduce(SavedItemsMviModel.Intent.WillOpenSave)
|
||||
detailOpener.openPostDetail(post)
|
||||
},
|
||||
onOpenCommunity = rememberCallbackArgs { community, instance ->
|
||||
detailOpener.openCommunityDetail(community, instance)
|
||||
},
|
||||
onOpenCreator = rememberCallbackArgs { u, instance ->
|
||||
if (u.id != uiState.user?.id) {
|
||||
detailOpener.openUserDetail(u, instance)
|
||||
}
|
||||
},
|
||||
onOpenPost = rememberCallbackArgs { p, instance ->
|
||||
detailOpener.openPostDetail(p, instance)
|
||||
},
|
||||
onOpenWeb = rememberCallbackArgs { url ->
|
||||
navigationCoordinator.pushScreen(
|
||||
WebViewScreen(url)
|
||||
)
|
||||
},
|
||||
onUpVote = rememberCallback(model) {
|
||||
model.reduce(
|
||||
SavedItemsMviModel.Intent.UpVotePost(
|
||||
id = post.id,
|
||||
),
|
||||
)
|
||||
},
|
||||
onDownVote = rememberCallback(model) {
|
||||
model.reduce(
|
||||
SavedItemsMviModel.Intent.DownVotePost(
|
||||
id = post.id,
|
||||
),
|
||||
)
|
||||
},
|
||||
onSave = rememberCallback(model) {
|
||||
model.reduce(
|
||||
SavedItemsMviModel.Intent.SavePost(
|
||||
id = post.id,
|
||||
),
|
||||
)
|
||||
},
|
||||
onReply = rememberCallback {
|
||||
model.reduce(SavedItemsMviModel.Intent.WillOpenSave)
|
||||
detailOpener.openPostDetail(post)
|
||||
},
|
||||
onOpenImage = rememberCallbackArgs { url ->
|
||||
navigatorCoordinator.pushScreen(
|
||||
ZoomableImageScreen(
|
||||
url = url,
|
||||
source = post.community?.readableHandle.orEmpty(),
|
||||
),
|
||||
)
|
||||
},
|
||||
options = buildList {
|
||||
add(
|
||||
Option(
|
||||
OptionId.Share,
|
||||
LocalXmlStrings.current.postActionShare
|
||||
)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.SeeRaw,
|
||||
LocalXmlStrings.current.postActionSeeRaw
|
||||
)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.Report,
|
||||
LocalXmlStrings.current.postActionReport
|
||||
)
|
||||
)
|
||||
},
|
||||
onOptionSelected = { optionIndex ->
|
||||
when (optionIndex) {
|
||||
OptionId.Report -> {
|
||||
navigatorCoordinator.pushScreen(
|
||||
CreateReportScreen(postId = post.id),
|
||||
)
|
||||
}
|
||||
|
||||
OptionId.SeeRaw -> {
|
||||
rawContent = post
|
||||
}
|
||||
|
||||
OptionId.Share -> {
|
||||
val urls = listOfNotNull(
|
||||
post.originalUrl,
|
||||
"https://${uiState.instance}/post/${post.id}"
|
||||
).distinct()
|
||||
if (urls.size == 1) {
|
||||
model.reduce(
|
||||
SavedItemsMviModel.Intent.Share(
|
||||
urls.first()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val screen = ShareBottomSheet(urls = urls)
|
||||
navigationCoordinator.showBottomSheet(screen)
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
},
|
||||
)
|
||||
if (uiState.postLayout != PostLayout.Card) {
|
||||
HorizontalDivider(modifier = Modifier.padding(vertical = Spacing.interItem))
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(Spacing.interItem))
|
||||
}
|
||||
}
|
||||
|
||||
if (uiState.posts.isEmpty() && !uiState.loading) {
|
||||
item {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = Spacing.xs),
|
||||
textAlign = TextAlign.Center,
|
||||
text = LocalXmlStrings.current.messageEmptyList,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items(uiState.comments) { comment ->
|
||||
CommentCard(
|
||||
comment = comment,
|
||||
voteFormat = uiState.voteFormat,
|
||||
autoLoadImages = uiState.autoLoadImages,
|
||||
preferNicknames = uiState.preferNicknames,
|
||||
showScores = uiState.showScores,
|
||||
showBot = true,
|
||||
indentAmount = 0,
|
||||
onClick = {
|
||||
detailOpener.openPostDetail(
|
||||
post = PostModel(id = comment.postId),
|
||||
highlightCommentId = comment.id,
|
||||
)
|
||||
},
|
||||
onImageClick = rememberCallbackArgs { url ->
|
||||
navigationCoordinator.pushScreen(
|
||||
ZoomableImageScreen(
|
||||
url = url,
|
||||
source = comment.community?.readableHandle.orEmpty(),
|
||||
)
|
||||
)
|
||||
},
|
||||
onUpVote = {
|
||||
model.reduce(
|
||||
SavedItemsMviModel.Intent.UpVoteComment(
|
||||
id = comment.id,
|
||||
),
|
||||
)
|
||||
},
|
||||
onDownVote = {
|
||||
model.reduce(
|
||||
SavedItemsMviModel.Intent.DownVoteComment(
|
||||
id = comment.id,
|
||||
),
|
||||
)
|
||||
},
|
||||
onSave = {
|
||||
model.reduce(
|
||||
SavedItemsMviModel.Intent.SaveComment(
|
||||
id = comment.id,
|
||||
),
|
||||
)
|
||||
},
|
||||
onReply = {
|
||||
detailOpener.openReply(
|
||||
originalPost = PostModel(id = comment.postId),
|
||||
originalComment = comment,
|
||||
)
|
||||
|
||||
},
|
||||
options = buildList {
|
||||
add(
|
||||
Option(
|
||||
OptionId.SeeRaw,
|
||||
LocalXmlStrings.current.postActionSeeRaw
|
||||
)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.Report,
|
||||
LocalXmlStrings.current.postActionReport
|
||||
)
|
||||
)
|
||||
},
|
||||
onOptionSelected = { optionIndex ->
|
||||
when (optionIndex) {
|
||||
OptionId.Report -> {
|
||||
navigatorCoordinator.pushScreen(
|
||||
CreateReportScreen(commentId = comment.id),
|
||||
)
|
||||
}
|
||||
|
||||
OptionId.SeeRaw -> {
|
||||
rawContent = comment
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
},
|
||||
)
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(vertical = Spacing.xxxs),
|
||||
thickness = 0.25.dp
|
||||
)
|
||||
}
|
||||
|
||||
if (uiState.comments.isEmpty() && !uiState.loading) {
|
||||
item {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = Spacing.xs),
|
||||
textAlign = TextAlign.Center,
|
||||
text = LocalXmlStrings.current.messageEmptyList,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
||||
model.reduce(SavedItemsMviModel.Intent.LoadNextPage)
|
||||
}
|
||||
if (uiState.loading && !uiState.refreshing) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().padding(Spacing.xs),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(25.dp),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = uiState.refreshing,
|
||||
state = pullRefreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
backgroundColor = MaterialTheme.colorScheme.background,
|
||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rawContent != null) {
|
||||
when (val content = rawContent) {
|
||||
is PostModel -> {
|
||||
RawContentDialog(
|
||||
title = content.title,
|
||||
publishDate = content.publishDate,
|
||||
updateDate = content.updateDate,
|
||||
url = content.url,
|
||||
text = content.text,
|
||||
upVotes = content.upvotes,
|
||||
downVotes = content.downvotes,
|
||||
onDismiss = rememberCallback {
|
||||
rawContent = null
|
||||
},
|
||||
onQuote = rememberCallbackArgs { quotation ->
|
||||
rawContent = null
|
||||
if (quotation != null) {
|
||||
detailOpener.openReply(
|
||||
originalPost = content,
|
||||
initialText = buildString {
|
||||
append("> ")
|
||||
append(quotation)
|
||||
append("\n\n")
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
is CommentModel -> {
|
||||
RawContentDialog(
|
||||
text = content.text,
|
||||
publishDate = content.publishDate,
|
||||
updateDate = content.updateDate,
|
||||
upVotes = content.upvotes,
|
||||
downVotes = content.downvotes,
|
||||
onDismiss = {
|
||||
rawContent = null
|
||||
},
|
||||
onQuote = rememberCallbackArgs { quotation ->
|
||||
rawContent = null
|
||||
if (quotation != null) {
|
||||
detailOpener.openReply(
|
||||
originalComment = content,
|
||||
initialText = buildString {
|
||||
append("> ")
|
||||
append(quotation)
|
||||
append("\n\n")
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.unit.saveditems
|
||||
|
||||
sealed interface SavedItemsSection {
|
||||
data object Posts : SavedItemsSection
|
||||
|
||||
data object Comments : SavedItemsSection
|
||||
}
|
@ -1,406 +0,0 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.unit.saveditems
|
||||
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.CommentPaginationManager
|
||||
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.CommentPaginationSpecification
|
||||
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.PostNavigationManager
|
||||
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.PostPaginationManager
|
||||
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.PostPaginationSpecification
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.repository.ThemeRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.SettingsRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.share.ShareHelper
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.vibrate.HapticFeedback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.ApiConfigurationRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.GetSortTypesUseCase
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SavedItemsViewModel(
|
||||
private val identityRepository: IdentityRepository,
|
||||
private val apiConfigurationRepository: ApiConfigurationRepository,
|
||||
private val postPaginationManager: PostPaginationManager,
|
||||
private val commentPaginationManager: CommentPaginationManager,
|
||||
private val postRepository: PostRepository,
|
||||
private val commentRepository: CommentRepository,
|
||||
private val themeRepository: ThemeRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val shareHelper: ShareHelper,
|
||||
private val notificationCenter: NotificationCenter,
|
||||
private val hapticFeedback: HapticFeedback,
|
||||
private val getSortTypesUseCase: GetSortTypesUseCase,
|
||||
private val postNavigationManager: PostNavigationManager,
|
||||
) : SavedItemsMviModel,
|
||||
DefaultMviModel<SavedItemsMviModel.Intent, SavedItemsMviModel.UiState, SavedItemsMviModel.Effect>(
|
||||
initialState = SavedItemsMviModel.UiState(),
|
||||
) {
|
||||
|
||||
init {
|
||||
updateState { it.copy(instance = apiConfigurationRepository.instance.value) }
|
||||
screenModelScope.launch {
|
||||
themeRepository.postLayout.onEach { layout ->
|
||||
updateState { it.copy(postLayout = layout) }
|
||||
}.launchIn(this)
|
||||
settingsRepository.currentSettings.onEach { settings ->
|
||||
updateState {
|
||||
it.copy(
|
||||
voteFormat = settings.voteFormat,
|
||||
autoLoadImages = settings.autoLoadImages,
|
||||
preferNicknames = settings.preferUserNicknames,
|
||||
fullHeightImages = settings.fullHeightImages,
|
||||
fullWidthImages = settings.fullWidthImages,
|
||||
showScores = settings.showScores,
|
||||
)
|
||||
}
|
||||
}.launchIn(this)
|
||||
notificationCenter.subscribe(NotificationCenterEvent.PostUpdated::class).onEach { evt ->
|
||||
handlePostUpdate(evt.model)
|
||||
}.launchIn(this)
|
||||
notificationCenter.subscribe(NotificationCenterEvent.PostDeleted::class).onEach { evt ->
|
||||
handlePostDelete(evt.model.id)
|
||||
}.launchIn(this)
|
||||
notificationCenter.subscribe(NotificationCenterEvent.ChangeSortType::class)
|
||||
.onEach { evt ->
|
||||
if (evt.screenKey == "savedItems") {
|
||||
applySortType(evt.value)
|
||||
}
|
||||
}.launchIn(this)
|
||||
notificationCenter.subscribe(NotificationCenterEvent.Share::class).onEach { evt ->
|
||||
shareHelper.share(evt.url)
|
||||
}.launchIn(this)
|
||||
|
||||
if (uiState.value.posts.isEmpty()) {
|
||||
val sortTypes = getSortTypesUseCase.getTypesForSavedItems()
|
||||
updateState { it.copy(availableSortTypes = sortTypes) }
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun reduce(intent: SavedItemsMviModel.Intent) {
|
||||
when (intent) {
|
||||
SavedItemsMviModel.Intent.LoadNextPage -> screenModelScope.launch {
|
||||
loadNextPage()
|
||||
}
|
||||
|
||||
SavedItemsMviModel.Intent.Refresh -> screenModelScope.launch {
|
||||
refresh()
|
||||
}
|
||||
|
||||
is SavedItemsMviModel.Intent.ChangeSection -> changeSection(intent.section)
|
||||
is SavedItemsMviModel.Intent.DownVoteComment -> {
|
||||
if (intent.feedback) {
|
||||
hapticFeedback.vibrate()
|
||||
}
|
||||
toggleDownVoteComment(
|
||||
comment = uiState.value.comments.first { it.id == intent.id },
|
||||
)
|
||||
}
|
||||
|
||||
is SavedItemsMviModel.Intent.DownVotePost -> {
|
||||
if (intent.feedback) {
|
||||
hapticFeedback.vibrate()
|
||||
}
|
||||
toggleDownVotePost(
|
||||
post = uiState.value.posts.first { it.id == intent.id },
|
||||
)
|
||||
}
|
||||
|
||||
is SavedItemsMviModel.Intent.SaveComment -> {
|
||||
if (intent.feedback) {
|
||||
hapticFeedback.vibrate()
|
||||
}
|
||||
toggleSaveComment(
|
||||
comment = uiState.value.comments.first { it.id == intent.id },
|
||||
)
|
||||
}
|
||||
|
||||
is SavedItemsMviModel.Intent.SavePost -> {
|
||||
if (intent.feedback) {
|
||||
hapticFeedback.vibrate()
|
||||
}
|
||||
toggleSavePost(
|
||||
post = uiState.value.posts.first { it.id == intent.id },
|
||||
)
|
||||
}
|
||||
|
||||
is SavedItemsMviModel.Intent.Share -> {
|
||||
shareHelper.share(intent.url)
|
||||
}
|
||||
|
||||
is SavedItemsMviModel.Intent.UpVoteComment -> {
|
||||
if (intent.feedback) {
|
||||
hapticFeedback.vibrate()
|
||||
}
|
||||
toggleUpVoteComment(
|
||||
comment = uiState.value.comments.first { it.id == intent.id },
|
||||
)
|
||||
}
|
||||
|
||||
is SavedItemsMviModel.Intent.UpVotePost -> {
|
||||
if (intent.feedback) {
|
||||
hapticFeedback.vibrate()
|
||||
}
|
||||
toggleUpVotePost(
|
||||
post = uiState.value.posts.first { it.id == intent.id },
|
||||
)
|
||||
}
|
||||
|
||||
SavedItemsMviModel.Intent.WillOpenSave -> {
|
||||
val state = postPaginationManager.extractState()
|
||||
postNavigationManager.push(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refresh() {
|
||||
postPaginationManager.reset(
|
||||
PostPaginationSpecification.Saved(sortType = uiState.value.sortType)
|
||||
)
|
||||
commentPaginationManager.reset(
|
||||
CommentPaginationSpecification.Saved(sortType = uiState.value.sortType)
|
||||
)
|
||||
updateState {
|
||||
it.copy(
|
||||
canFetchMore = true,
|
||||
refreshing = true,
|
||||
loading = false,
|
||||
)
|
||||
}
|
||||
loadNextPage()
|
||||
}
|
||||
|
||||
private suspend fun loadNextPage() {
|
||||
val currentState = uiState.value
|
||||
if (!currentState.canFetchMore || currentState.loading) {
|
||||
updateState { it.copy(refreshing = false) }
|
||||
return
|
||||
}
|
||||
|
||||
updateState { it.copy(loading = true) }
|
||||
val section = currentState.section
|
||||
if (section == SavedItemsSection.Posts) {
|
||||
val posts = postPaginationManager.loadNextPage()
|
||||
updateState {
|
||||
it.copy(
|
||||
posts = posts,
|
||||
loading = false,
|
||||
canFetchMore = postPaginationManager.canFetchMore,
|
||||
refreshing = false,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val comments = commentPaginationManager.loadNextPage()
|
||||
updateState {
|
||||
it.copy(
|
||||
comments = comments,
|
||||
loading = false,
|
||||
canFetchMore = commentPaginationManager.canFetchMore,
|
||||
refreshing = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applySortType(value: SortType) {
|
||||
if (uiState.value.sortType == value) {
|
||||
return
|
||||
}
|
||||
updateState { it.copy(sortType = value) }
|
||||
screenModelScope.launch {
|
||||
emitEffect(SavedItemsMviModel.Effect.BackToTop)
|
||||
delay(50)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePostUpdate(post: PostModel) {
|
||||
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
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePostDelete(id: Long) {
|
||||
updateState { it.copy(posts = it.posts.filter { post -> post.id != id }) }
|
||||
}
|
||||
|
||||
private fun changeSection(section: SavedItemsSection) {
|
||||
updateState {
|
||||
it.copy(
|
||||
section = section,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleUpVotePost(post: PostModel) {
|
||||
val newValue = post.myVote <= 0
|
||||
val newPost = postRepository.asUpVoted(
|
||||
post = post,
|
||||
voted = newValue,
|
||||
)
|
||||
handlePostUpdate(newPost)
|
||||
screenModelScope.launch {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
postRepository.upVote(
|
||||
auth = auth,
|
||||
post = post,
|
||||
voted = newValue,
|
||||
)
|
||||
notificationCenter.send(
|
||||
event = NotificationCenterEvent.PostUpdated(newPost),
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
handlePostUpdate(post)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleDownVotePost(post: PostModel) {
|
||||
val newValue = post.myVote >= 0
|
||||
val newPost = postRepository.asDownVoted(
|
||||
post = post,
|
||||
downVoted = newValue,
|
||||
)
|
||||
handlePostUpdate(newPost)
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
postRepository.downVote(
|
||||
auth = auth,
|
||||
post = post,
|
||||
downVoted = newValue,
|
||||
)
|
||||
notificationCenter.send(
|
||||
event = NotificationCenterEvent.PostUpdated(newPost),
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
handlePostUpdate(post)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleSavePost(post: PostModel) {
|
||||
val newValue = !post.saved
|
||||
val newPost = postRepository.asSaved(
|
||||
post = post,
|
||||
saved = newValue,
|
||||
)
|
||||
handlePostUpdate(newPost)
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
postRepository.save(
|
||||
auth = auth,
|
||||
post = post,
|
||||
saved = newValue,
|
||||
)
|
||||
notificationCenter.send(
|
||||
event = NotificationCenterEvent.PostUpdated(newPost),
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
handlePostUpdate(post)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleUpVoteComment(comment: CommentModel) {
|
||||
val newValue = comment.myVote <= 0
|
||||
val newComment = commentRepository.asUpVoted(
|
||||
comment = comment,
|
||||
voted = newValue,
|
||||
)
|
||||
handleCommentUpdate(newComment)
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
commentRepository.upVote(
|
||||
auth = auth,
|
||||
comment = comment,
|
||||
voted = newValue,
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
handleCommentUpdate(comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleDownVoteComment(comment: CommentModel) {
|
||||
val newValue = comment.myVote >= 0
|
||||
val newComment = commentRepository.asDownVoted(comment, newValue)
|
||||
handleCommentUpdate(newComment)
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
commentRepository.downVote(
|
||||
auth = auth,
|
||||
comment = comment,
|
||||
downVoted = newValue,
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
handleCommentUpdate(comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleSaveComment(comment: CommentModel) {
|
||||
val newValue = !comment.saved
|
||||
val newComment = commentRepository.asSaved(
|
||||
comment = comment,
|
||||
saved = newValue,
|
||||
)
|
||||
handleCommentUpdate(newComment)
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
commentRepository.save(
|
||||
auth = auth,
|
||||
comment = comment,
|
||||
saved = newValue,
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
handleCommentUpdate(comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.unit.saveditems.di
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.saveditems.SavedItemsMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.saveditems.SavedItemsViewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val savedItemsModule = module {
|
||||
factory<SavedItemsMviModel> {
|
||||
SavedItemsViewModel(
|
||||
identityRepository = get(),
|
||||
apiConfigurationRepository = get(),
|
||||
postPaginationManager = get(),
|
||||
commentPaginationManager = get(),
|
||||
postRepository = get(),
|
||||
commentRepository = get(),
|
||||
themeRepository = get(),
|
||||
settingsRepository = get(),
|
||||
shareHelper = get(),
|
||||
hapticFeedback = get(),
|
||||
notificationCenter = get(),
|
||||
getSortTypesUseCase = get(),
|
||||
postNavigationManager = get(),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user