mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-10 07:10:39 +01:00
refactor(profile): move saved items to separate screen and restructure post and comments section (#23)
* feat(profile): move saved items to separate screen and introduce saved comments * feat(profile): unifies posts and comments
This commit is contained in:
parent
0eda8a341a
commit
2614e31b9b
@ -9,6 +9,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Bookmarks
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -18,6 +21,7 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.github.diegoberaldin.racconforlemmy.core.utils.onClick
|
||||||
import com.github.diegoberaldin.racconforlemmy.core.utils.toLocalPixel
|
import com.github.diegoberaldin.racconforlemmy.core.utils.toLocalPixel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||||
@ -27,6 +31,7 @@ import io.kamel.image.asyncPainterResource
|
|||||||
@Composable
|
@Composable
|
||||||
fun UserHeader(
|
fun UserHeader(
|
||||||
user: UserModel,
|
user: UserModel,
|
||||||
|
onOpenBookmarks: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val userAvatar = user.avatar.orEmpty()
|
val userAvatar = user.avatar.orEmpty()
|
||||||
val userDisplayName = user.name
|
val userDisplayName = user.name
|
||||||
@ -97,5 +102,17 @@ fun UserHeader(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (onOpenBookmarks != null) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.TopEnd)
|
||||||
|
.padding(Spacing.s)
|
||||||
|
.onClick {
|
||||||
|
onOpenBookmarks.invoke()
|
||||||
|
},
|
||||||
|
imageVector = Icons.Outlined.Bookmarks,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,6 @@ class UserRepository(
|
|||||||
page: Int,
|
page: Int,
|
||||||
limit: Int = PostsRepository.DEFAULT_PAGE_SIZE,
|
limit: Int = PostsRepository.DEFAULT_PAGE_SIZE,
|
||||||
sort: SortType = SortType.Active,
|
sort: SortType = SortType.Active,
|
||||||
savedOnly: Boolean = false,
|
|
||||||
): List<PostModel> = runCatching {
|
): List<PostModel> = runCatching {
|
||||||
val response = serviceProvider.user.getDetails(
|
val response = serviceProvider.user.getDetails(
|
||||||
auth = auth,
|
auth = auth,
|
||||||
@ -51,7 +50,25 @@ class UserRepository(
|
|||||||
page = page,
|
page = page,
|
||||||
limit = limit,
|
limit = limit,
|
||||||
sort = sort.toCommentDto(),
|
sort = sort.toCommentDto(),
|
||||||
savedOnly = savedOnly,
|
)
|
||||||
|
val dto = response.body() ?: return@runCatching emptyList()
|
||||||
|
dto.posts.map { it.toModel() }
|
||||||
|
}.getOrElse { emptyList() }
|
||||||
|
|
||||||
|
suspend fun getSavedPosts(
|
||||||
|
id: Int,
|
||||||
|
auth: String? = null,
|
||||||
|
page: Int,
|
||||||
|
limit: Int = PostsRepository.DEFAULT_PAGE_SIZE,
|
||||||
|
sort: SortType = SortType.Active,
|
||||||
|
): List<PostModel> = runCatching {
|
||||||
|
val response = serviceProvider.user.getDetails(
|
||||||
|
auth = auth,
|
||||||
|
personId = id,
|
||||||
|
page = page,
|
||||||
|
limit = limit,
|
||||||
|
sort = sort.toCommentDto(),
|
||||||
|
savedOnly = true,
|
||||||
)
|
)
|
||||||
val dto = response.body() ?: return@runCatching emptyList()
|
val dto = response.body() ?: return@runCatching emptyList()
|
||||||
dto.posts.map { it.toModel() }
|
dto.posts.map { it.toModel() }
|
||||||
@ -75,6 +92,25 @@ class UserRepository(
|
|||||||
dto.comments.map { it.toModel() }
|
dto.comments.map { it.toModel() }
|
||||||
}.getOrElse { emptyList() }
|
}.getOrElse { emptyList() }
|
||||||
|
|
||||||
|
suspend fun getSavedComments(
|
||||||
|
id: Int,
|
||||||
|
auth: String? = null,
|
||||||
|
page: Int,
|
||||||
|
limit: Int = PostsRepository.DEFAULT_PAGE_SIZE,
|
||||||
|
sort: SortType = SortType.Active,
|
||||||
|
): List<CommentModel> = runCatching {
|
||||||
|
val response = serviceProvider.user.getDetails(
|
||||||
|
auth = auth,
|
||||||
|
personId = id,
|
||||||
|
page = page,
|
||||||
|
limit = limit,
|
||||||
|
sort = sort.toCommentDto(),
|
||||||
|
savedOnly = true,
|
||||||
|
)
|
||||||
|
val dto = response.body() ?: return@runCatching emptyList()
|
||||||
|
dto.comments.map { it.toModel() }
|
||||||
|
}.getOrElse { emptyList() }
|
||||||
|
|
||||||
suspend fun getMentions(
|
suspend fun getMentions(
|
||||||
auth: String? = null,
|
auth: String? = null,
|
||||||
page: Int,
|
page: Int,
|
||||||
|
@ -3,8 +3,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.profile.di
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileContentViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileContentViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments.ProfileCommentsViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved.ProfileSavedViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts.ProfilePostsViewModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetViewModel
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
import org.koin.java.KoinJavaComponent.inject
|
import org.koin.java.KoinJavaComponent.inject
|
||||||
@ -24,20 +23,9 @@ actual fun getProfileLoggedViewModel(): ProfileLoggedViewModel {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun getProfilePostsViewModel(
|
actual fun getProfileSavedViewModel(user: UserModel): ProfileSavedViewModel {
|
||||||
user: UserModel,
|
val res: ProfileSavedViewModel by inject(
|
||||||
savedOnly: Boolean,
|
clazz = ProfileSavedViewModel::class.java,
|
||||||
): ProfilePostsViewModel {
|
|
||||||
val res: ProfilePostsViewModel by inject(
|
|
||||||
clazz = ProfilePostsViewModel::class.java,
|
|
||||||
parameters = { parametersOf(user, savedOnly) },
|
|
||||||
)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun getProfileCommentsViewModel(user: UserModel): ProfileCommentsViewModel {
|
|
||||||
val res: ProfileCommentsViewModel by inject(
|
|
||||||
clazz = ProfileCommentsViewModel::class.java,
|
|
||||||
parameters = { parametersOf(user) },
|
parameters = { parametersOf(user) },
|
||||||
)
|
)
|
||||||
return res
|
return res
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
@ -1,18 +1,29 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
||||||
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
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.UserModel
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||||
|
|
||||||
interface ProfileLoggedMviModel :
|
interface ProfileLoggedMviModel :
|
||||||
MviModel<ProfileLoggedMviModel.Intent, ProfileLoggedMviModel.UiState, ProfileLoggedMviModel.Effect> {
|
MviModel<ProfileLoggedMviModel.Intent, ProfileLoggedMviModel.UiState, ProfileLoggedMviModel.Effect> {
|
||||||
|
|
||||||
sealed interface Intent {
|
sealed interface Intent {
|
||||||
data class SelectTab(val value: ProfileLoggedSection) : Intent
|
data class ChangeSection(val section: ProfileLoggedSection) : Intent
|
||||||
|
object Refresh : Intent
|
||||||
|
object LoadNextPage : Intent
|
||||||
|
data class DeletePost(val id: Int) : Intent
|
||||||
|
data class DeleteComment(val id: Int) : Intent
|
||||||
}
|
}
|
||||||
|
|
||||||
data class UiState(
|
data class UiState(
|
||||||
val user: UserModel? = null,
|
val user: UserModel? = null,
|
||||||
val currentTab: ProfileLoggedSection = ProfileLoggedSection.POSTS,
|
val section: ProfileLoggedSection = ProfileLoggedSection.Posts,
|
||||||
|
val refreshing: Boolean = false,
|
||||||
|
val loading: Boolean = false,
|
||||||
|
val canFetchMore: Boolean = true,
|
||||||
|
val posts: List<PostModel> = emptyList(),
|
||||||
|
val comments: List<CommentModel> = emptyList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed interface Effect
|
sealed interface Effect
|
||||||
|
@ -1,34 +1,50 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
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.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.navigator.CurrentScreen
|
|
||||||
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
|
||||||
import cafe.adriel.voyager.navigator.tab.Tab
|
import cafe.adriel.voyager.navigator.tab.Tab
|
||||||
import cafe.adriel.voyager.navigator.tab.TabNavigator
|
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
|
import com.github.diegoberaldin.racconforlemmy.core.utils.onClick
|
||||||
|
import com.github.diegoberaldin.racconforlemmy.core.utils.toLocalPixel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SectionSelector
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserCounters
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserHeader
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImageScreen
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments.ProfileCommentsScreen
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved.ProfileSavedScreen
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts.ProfilePostsScreen
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.saved.ProfileSavedScreen
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.di.getProfileLoggedViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.di.getProfileLoggedViewModel
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||||
import kotlinx.coroutines.flow.map
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
|
|
||||||
internal object ProfileLoggedScreen : Tab {
|
internal object ProfileLoggedScreen : Tab {
|
||||||
|
|
||||||
@ -37,6 +53,7 @@ internal object ProfileLoggedScreen : Tab {
|
|||||||
return TabOptions(0u, "")
|
return TabOptions(0u, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
Column(
|
Column(
|
||||||
@ -49,6 +66,7 @@ internal object ProfileLoggedScreen : Tab {
|
|||||||
val uiState by model.uiState.collectAsState()
|
val uiState by model.uiState.collectAsState()
|
||||||
val user = uiState.user
|
val user = uiState.user
|
||||||
val notificationCenter = remember { getNotificationCenter() }
|
val notificationCenter = remember { getNotificationCenter() }
|
||||||
|
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
|
||||||
DisposableEffect(key) {
|
DisposableEffect(key) {
|
||||||
onDispose {
|
onDispose {
|
||||||
notificationCenter.removeObserver(key)
|
notificationCenter.removeObserver(key)
|
||||||
@ -56,47 +74,133 @@ internal object ProfileLoggedScreen : Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
val screens = remember {
|
val pullRefreshState = rememberPullRefreshState(uiState.refreshing, {
|
||||||
val postsScreen = ProfilePostsScreen(user)
|
model.reduce(ProfileLoggedMviModel.Intent.Refresh)
|
||||||
val commentsScreen = ProfileCommentsScreen(user)
|
})
|
||||||
val savedScreen = ProfileSavedScreen(user)
|
Box(
|
||||||
|
modifier = Modifier.pullRefresh(pullRefreshState),
|
||||||
notificationCenter.addObserver({
|
) {
|
||||||
(it as? ProfileLoggedSection)?.also { value ->
|
LazyColumn(
|
||||||
model.reduce(ProfileLoggedMviModel.Intent.SelectTab(value))
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||||
|
) {
|
||||||
|
UserHeader(
|
||||||
|
user = user,
|
||||||
|
onOpenBookmarks = {
|
||||||
|
navigator?.push(
|
||||||
|
ProfileSavedScreen(user = user),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
UserCounters(
|
||||||
|
modifier = Modifier.graphicsLayer(translationY = -Spacing.m.toLocalPixel()),
|
||||||
|
user = user,
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(Spacing.xs))
|
||||||
|
SectionSelector(
|
||||||
|
titles = listOf(
|
||||||
|
stringResource(MR.strings.profile_section_posts),
|
||||||
|
stringResource(MR.strings.profile_section_comments),
|
||||||
|
),
|
||||||
|
currentSection = when (uiState.section) {
|
||||||
|
ProfileLoggedSection.Comments -> 1
|
||||||
|
else -> 0
|
||||||
|
},
|
||||||
|
onSectionSelected = {
|
||||||
|
val section = when (it) {
|
||||||
|
1 -> ProfileLoggedSection.Comments
|
||||||
|
else -> ProfileLoggedSection.Posts
|
||||||
|
}
|
||||||
|
model.reduce(
|
||||||
|
ProfileLoggedMviModel.Intent.ChangeSection(
|
||||||
|
section
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(Spacing.m))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, key, NotificationCenterContractKeys.SectionChanged)
|
if (uiState.section == ProfileLoggedSection.Posts) {
|
||||||
notificationCenter.addObserver({
|
items(uiState.posts) { post ->
|
||||||
(it as? ProfileLoggedSection)?.also { value ->
|
ProfilePostCard(
|
||||||
model.reduce(ProfileLoggedMviModel.Intent.SelectTab(value))
|
modifier = Modifier.onClick {
|
||||||
|
navigator?.push(
|
||||||
|
PostDetailScreen(post),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
post = post,
|
||||||
|
options = listOf(stringResource(MR.strings.comment_action_delete)),
|
||||||
|
onOpenCommunity = { community ->
|
||||||
|
navigator?.push(
|
||||||
|
CommunityDetailScreen(community),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onImageClick = { url ->
|
||||||
|
navigator?.push(
|
||||||
|
ZoomableImageScreen(url),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onOptionSelected = { idx ->
|
||||||
|
when (idx) {
|
||||||
|
else -> model.reduce(
|
||||||
|
ProfileLoggedMviModel.Intent.DeletePost(post.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items(uiState.comments) { comment ->
|
||||||
|
ProfileCommentCard(
|
||||||
|
comment = comment,
|
||||||
|
options = listOf(stringResource(MR.strings.comment_action_delete)),
|
||||||
|
onOptionSelected = { idx ->
|
||||||
|
when (idx) {
|
||||||
|
else ->
|
||||||
|
model.reduce(
|
||||||
|
ProfileLoggedMviModel.Intent.DeleteComment(
|
||||||
|
comment.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, key, NotificationCenterContractKeys.SectionChanged)
|
item {
|
||||||
notificationCenter.addObserver({
|
Spacer(modifier = Modifier.height(Spacing.xxxl))
|
||||||
(it as? ProfileLoggedSection)?.also { value ->
|
|
||||||
model.reduce(ProfileLoggedMviModel.Intent.SelectTab(value))
|
|
||||||
}
|
}
|
||||||
}, key, NotificationCenterContractKeys.SectionChanged)
|
item {
|
||||||
|
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
||||||
listOf(
|
model.reduce(ProfileLoggedMviModel.Intent.LoadNextPage)
|
||||||
postsScreen,
|
}
|
||||||
commentsScreen,
|
if (uiState.loading && !uiState.refreshing) {
|
||||||
savedScreen,
|
Box(
|
||||||
)
|
modifier = Modifier.fillMaxWidth().padding(Spacing.xs),
|
||||||
}
|
contentAlignment = Alignment.Center,
|
||||||
TabNavigator(screens.first()) {
|
) {
|
||||||
CurrentScreen()
|
CircularProgressIndicator(
|
||||||
val navigator = LocalTabNavigator.current
|
modifier = Modifier.size(25.dp),
|
||||||
LaunchedEffect(model) {
|
color = MaterialTheme.colorScheme.primary,
|
||||||
model.uiState.map { it.currentTab }
|
)
|
||||||
.onEach { section ->
|
|
||||||
val index = when (section) {
|
|
||||||
ProfileLoggedSection.POSTS -> 0
|
|
||||||
ProfileLoggedSection.COMMENTS -> 1
|
|
||||||
ProfileLoggedSection.SAVED -> 2
|
|
||||||
}
|
}
|
||||||
navigator.current = screens[index]
|
}
|
||||||
}.launchIn(this)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PullRefreshIndicator(
|
||||||
|
refreshing = uiState.refreshing,
|
||||||
|
state = pullRefreshState,
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter),
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
||||||
|
|
||||||
enum class ProfileLoggedSection {
|
sealed interface ProfileLoggedSection {
|
||||||
POSTS,
|
object Posts : ProfileLoggedSection
|
||||||
COMMENTS,
|
object Comments : ProfileLoggedSection
|
||||||
SAVED,
|
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,15 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
|||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
import cafe.adriel.voyager.core.model.ScreenModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
||||||
|
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.PostsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.SiteRepository
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.SiteRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.UserRepository
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.IO
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -13,8 +20,32 @@ class ProfileLoggedViewModel(
|
|||||||
private val mvi: DefaultMviModel<ProfileLoggedMviModel.Intent, ProfileLoggedMviModel.UiState, ProfileLoggedMviModel.Effect>,
|
private val mvi: DefaultMviModel<ProfileLoggedMviModel.Intent, ProfileLoggedMviModel.UiState, ProfileLoggedMviModel.Effect>,
|
||||||
private val identityRepository: IdentityRepository,
|
private val identityRepository: IdentityRepository,
|
||||||
private val siteRepository: SiteRepository,
|
private val siteRepository: SiteRepository,
|
||||||
|
private val postsRepository: PostsRepository,
|
||||||
|
private val commentRepository: CommentRepository,
|
||||||
|
private val userRepository: UserRepository,
|
||||||
|
private val notificationCenter: NotificationCenter,
|
||||||
) : ScreenModel,
|
) : ScreenModel,
|
||||||
MviModel<ProfileLoggedMviModel.Intent, ProfileLoggedMviModel.UiState, ProfileLoggedMviModel.Effect> by mvi {
|
MviModel<ProfileLoggedMviModel.Intent, ProfileLoggedMviModel.UiState, ProfileLoggedMviModel.Effect> by mvi {
|
||||||
|
|
||||||
|
private var currentPage = 1
|
||||||
|
|
||||||
|
init {
|
||||||
|
notificationCenter.addObserver({
|
||||||
|
(it as? PostModel)?.also { post ->
|
||||||
|
handlePostUpdate(post)
|
||||||
|
}
|
||||||
|
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdated)
|
||||||
|
notificationCenter.addObserver({
|
||||||
|
(it as? PostModel)?.also { post ->
|
||||||
|
handlePostDelete(post.id)
|
||||||
|
}
|
||||||
|
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finalize() {
|
||||||
|
notificationCenter.removeObserver(this::class.simpleName.orEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStarted() {
|
override fun onStarted() {
|
||||||
mvi.onStarted()
|
mvi.onStarted()
|
||||||
val auth = identityRepository.authToken.value.orEmpty()
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
@ -26,9 +57,123 @@ class ProfileLoggedViewModel(
|
|||||||
|
|
||||||
override fun reduce(intent: ProfileLoggedMviModel.Intent) {
|
override fun reduce(intent: ProfileLoggedMviModel.Intent) {
|
||||||
when (intent) {
|
when (intent) {
|
||||||
is ProfileLoggedMviModel.Intent.SelectTab -> mvi.updateState {
|
is ProfileLoggedMviModel.Intent.ChangeSection -> changeSection(intent.section)
|
||||||
it.copy(currentTab = intent.value)
|
is ProfileLoggedMviModel.Intent.DeleteComment -> deleteComment(intent.id)
|
||||||
|
is ProfileLoggedMviModel.Intent.DeletePost -> deletePost(intent.id)
|
||||||
|
ProfileLoggedMviModel.Intent.LoadNextPage -> loadNextPage()
|
||||||
|
ProfileLoggedMviModel.Intent.Refresh -> refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refresh() {
|
||||||
|
currentPage = 1
|
||||||
|
mvi.updateState { it.copy(canFetchMore = true, refreshing = true) }
|
||||||
|
loadNextPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeSection(section: ProfileLoggedSection) {
|
||||||
|
currentPage = 1
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
section = section,
|
||||||
|
canFetchMore = true,
|
||||||
|
refreshing = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
loadNextPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadNextPage() {
|
||||||
|
val currentState = mvi.uiState.value
|
||||||
|
if (!currentState.canFetchMore || currentState.loading) {
|
||||||
|
mvi.updateState { it.copy(refreshing = false) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
mvi.updateState { it.copy(loading = true) }
|
||||||
|
val auth = identityRepository.authToken.value
|
||||||
|
val refreshing = currentState.refreshing
|
||||||
|
val userId = currentState.user?.id ?: 0
|
||||||
|
val section = currentState.section
|
||||||
|
if (section == ProfileLoggedSection.Posts) {
|
||||||
|
val postList = userRepository.getPosts(
|
||||||
|
auth = auth,
|
||||||
|
id = userId,
|
||||||
|
page = currentPage,
|
||||||
|
sort = SortType.New,
|
||||||
|
)
|
||||||
|
val canFetchMore = postList.size >= PostsRepository.DEFAULT_PAGE_SIZE
|
||||||
|
mvi.updateState {
|
||||||
|
val newPosts = if (refreshing) {
|
||||||
|
postList
|
||||||
|
} else {
|
||||||
|
it.posts + postList
|
||||||
|
}
|
||||||
|
it.copy(
|
||||||
|
posts = newPosts,
|
||||||
|
loading = false,
|
||||||
|
canFetchMore = canFetchMore,
|
||||||
|
refreshing = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val commentList = userRepository.getComments(
|
||||||
|
auth = auth,
|
||||||
|
id = userId,
|
||||||
|
page = currentPage,
|
||||||
|
sort = SortType.New,
|
||||||
|
)
|
||||||
|
val canFetchMore = commentList.size >= PostsRepository.DEFAULT_PAGE_SIZE
|
||||||
|
mvi.updateState {
|
||||||
|
val newcomments = if (refreshing) {
|
||||||
|
commentList
|
||||||
|
} else {
|
||||||
|
it.comments + commentList
|
||||||
|
}
|
||||||
|
it.copy(
|
||||||
|
comments = newcomments,
|
||||||
|
loading = false,
|
||||||
|
canFetchMore = canFetchMore,
|
||||||
|
refreshing = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
currentPage++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePostUpdate(post: PostModel) {
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
posts = it.posts.map { p ->
|
||||||
|
if (p.id == post.id) {
|
||||||
|
post
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePostDelete(id: Int) {
|
||||||
|
mvi.updateState { it.copy(posts = it.posts.filter { post -> post.id != id }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deletePost(id: Int) {
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
postsRepository.delete(id = id, auth = auth)
|
||||||
|
handlePostDelete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteComment(id: Int) {
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
commentRepository.delete(id, auth)
|
||||||
|
refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
@ -1,23 +0,0 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments
|
|
||||||
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel
|
|
||||||
|
|
||||||
interface ProfileCommentsMviModel :
|
|
||||||
MviModel<ProfileCommentsMviModel.Intent, ProfileCommentsMviModel.UiState, ProfileCommentsMviModel.Effect> {
|
|
||||||
|
|
||||||
sealed interface Intent {
|
|
||||||
object Refresh : Intent
|
|
||||||
object LoadNextPage : Intent
|
|
||||||
data class DeleteComment(val id: Int) : Intent
|
|
||||||
}
|
|
||||||
|
|
||||||
data class UiState(
|
|
||||||
val refreshing: Boolean = false,
|
|
||||||
val loading: Boolean = false,
|
|
||||||
val canFetchMore: Boolean = true,
|
|
||||||
val comments: List<CommentModel> = emptyList(),
|
|
||||||
)
|
|
||||||
|
|
||||||
sealed interface Effect
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments
|
|
||||||
|
|
||||||
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.material.ExperimentalMaterialApi
|
|
||||||
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.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
|
||||||
import cafe.adriel.voyager.navigator.tab.Tab
|
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
|
||||||
import com.github.diegoberaldin.racconforlemmy.core.utils.toLocalPixel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SectionSelector
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserCounters
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserHeader
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedSection
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.di.getProfileCommentsViewModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
|
||||||
|
|
||||||
internal class ProfileCommentsScreen(
|
|
||||||
private val user: UserModel,
|
|
||||||
) : Tab {
|
|
||||||
|
|
||||||
override val options: TabOptions
|
|
||||||
@Composable get() {
|
|
||||||
return TabOptions(1u, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
val model = rememberScreenModel { getProfileCommentsViewModel(user) }
|
|
||||||
model.bindToLifecycle(key)
|
|
||||||
val uiState by model.uiState.collectAsState()
|
|
||||||
val notificationCenter = remember { getNotificationCenter() }
|
|
||||||
val pullRefreshState = rememberPullRefreshState(uiState.refreshing, {
|
|
||||||
model.reduce(ProfileCommentsMviModel.Intent.Refresh)
|
|
||||||
})
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.pullRefresh(pullRefreshState),
|
|
||||||
) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
|
||||||
) {
|
|
||||||
item {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
|
||||||
) {
|
|
||||||
UserHeader(user = user)
|
|
||||||
UserCounters(
|
|
||||||
modifier = Modifier.graphicsLayer(translationY = -Spacing.m.toLocalPixel()),
|
|
||||||
user = user,
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.s))
|
|
||||||
SectionSelector(
|
|
||||||
titles = listOf(
|
|
||||||
stringResource(MR.strings.profile_section_posts),
|
|
||||||
stringResource(MR.strings.profile_section_comments),
|
|
||||||
stringResource(MR.strings.profile_section_saved),
|
|
||||||
),
|
|
||||||
currentSection = 1,
|
|
||||||
onSectionSelected = {
|
|
||||||
val section = when (it) {
|
|
||||||
0 -> ProfileLoggedSection.POSTS
|
|
||||||
1 -> ProfileLoggedSection.COMMENTS
|
|
||||||
else -> ProfileLoggedSection.SAVED
|
|
||||||
}
|
|
||||||
notificationCenter.getObserver(NotificationCenterContractKeys.SectionChanged)?.also { observer ->
|
|
||||||
observer.invoke(section)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.m))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items(uiState.comments) { comment ->
|
|
||||||
ProfileCommentCard(
|
|
||||||
comment = comment,
|
|
||||||
options = listOf(stringResource(MR.strings.comment_action_delete)),
|
|
||||||
onOptionSelected = { idx ->
|
|
||||||
when (idx) {
|
|
||||||
else ->
|
|
||||||
model.reduce(
|
|
||||||
ProfileCommentsMviModel.Intent.DeleteComment(
|
|
||||||
comment.id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.xxxl))
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
|
||||||
model.reduce(ProfileCommentsMviModel.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.surface,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments
|
|
||||||
|
|
||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostsRepository
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.UserRepository
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.IO
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class ProfileCommentsViewModel(
|
|
||||||
private val mvi: DefaultMviModel<ProfileCommentsMviModel.Intent, ProfileCommentsMviModel.UiState, ProfileCommentsMviModel.Effect>,
|
|
||||||
private val user: UserModel,
|
|
||||||
private val identityRepository: IdentityRepository,
|
|
||||||
private val userRepository: UserRepository,
|
|
||||||
private val commentRepository: CommentRepository,
|
|
||||||
) : ScreenModel,
|
|
||||||
MviModel<ProfileCommentsMviModel.Intent, ProfileCommentsMviModel.UiState, ProfileCommentsMviModel.Effect> by mvi {
|
|
||||||
|
|
||||||
private var currentPage: Int = 1
|
|
||||||
override fun onStarted() {
|
|
||||||
mvi.onStarted()
|
|
||||||
|
|
||||||
if (mvi.uiState.value.comments.isEmpty()) {
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun reduce(intent: ProfileCommentsMviModel.Intent) {
|
|
||||||
when (intent) {
|
|
||||||
ProfileCommentsMviModel.Intent.LoadNextPage -> loadNextPage()
|
|
||||||
ProfileCommentsMviModel.Intent.Refresh -> refresh()
|
|
||||||
is ProfileCommentsMviModel.Intent.DeleteComment -> deleteComment(intent.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun refresh() {
|
|
||||||
currentPage = 1
|
|
||||||
mvi.updateState { it.copy(canFetchMore = true, refreshing = true) }
|
|
||||||
loadNextPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadNextPage() {
|
|
||||||
val currentState = mvi.uiState.value
|
|
||||||
if (!currentState.canFetchMore || currentState.loading) {
|
|
||||||
mvi.updateState { it.copy(refreshing = false) }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mvi.scope?.launch(Dispatchers.IO) {
|
|
||||||
mvi.updateState { it.copy(loading = true) }
|
|
||||||
val auth = identityRepository.authToken.value
|
|
||||||
val refreshing = currentState.refreshing
|
|
||||||
val commentList = userRepository.getComments(
|
|
||||||
auth = auth,
|
|
||||||
id = user.id,
|
|
||||||
page = currentPage,
|
|
||||||
sort = SortType.New,
|
|
||||||
)
|
|
||||||
currentPage++
|
|
||||||
val canFetchMore = commentList.size >= PostsRepository.DEFAULT_PAGE_SIZE
|
|
||||||
mvi.updateState {
|
|
||||||
val newcomments = if (refreshing) {
|
|
||||||
commentList
|
|
||||||
} else {
|
|
||||||
it.comments + commentList
|
|
||||||
}
|
|
||||||
it.copy(
|
|
||||||
comments = newcomments,
|
|
||||||
loading = false,
|
|
||||||
canFetchMore = canFetchMore,
|
|
||||||
refreshing = false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun deleteComment(id: Int) {
|
|
||||||
mvi.scope?.launch(Dispatchers.IO) {
|
|
||||||
val auth = identityRepository.authToken.value.orEmpty()
|
|
||||||
commentRepository.delete(id, auth)
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts
|
|
||||||
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
|
||||||
|
|
||||||
interface ProfilePostsMviModel :
|
|
||||||
MviModel<ProfilePostsMviModel.Intent, ProfilePostsMviModel.UiState, ProfilePostsMviModel.Effect> {
|
|
||||||
|
|
||||||
sealed interface Intent {
|
|
||||||
object Refresh : Intent
|
|
||||||
object LoadNextPage : Intent
|
|
||||||
data class DeletePost(val id: Int) : Intent
|
|
||||||
}
|
|
||||||
|
|
||||||
data class UiState(
|
|
||||||
val refreshing: Boolean = false,
|
|
||||||
val loading: Boolean = false,
|
|
||||||
val canFetchMore: Boolean = true,
|
|
||||||
val posts: List<PostModel> = emptyList(),
|
|
||||||
)
|
|
||||||
|
|
||||||
sealed interface Effect
|
|
||||||
}
|
|
@ -1,171 +0,0 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts
|
|
||||||
|
|
||||||
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.material.ExperimentalMaterialApi
|
|
||||||
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.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
|
||||||
import cafe.adriel.voyager.navigator.tab.Tab
|
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
|
||||||
import com.github.diegoberaldin.racconforlemmy.core.utils.onClick
|
|
||||||
import com.github.diegoberaldin.racconforlemmy.core.utils.toLocalPixel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SectionSelector
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserCounters
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserHeader
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImageScreen
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedSection
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.di.getProfilePostsViewModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
|
||||||
|
|
||||||
internal class ProfilePostsScreen(
|
|
||||||
private val user: UserModel,
|
|
||||||
) : Tab {
|
|
||||||
override val options: TabOptions
|
|
||||||
@Composable get() {
|
|
||||||
return TabOptions(0u, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
val model = rememberScreenModel {
|
|
||||||
getProfilePostsViewModel(
|
|
||||||
user = user,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
model.bindToLifecycle(key)
|
|
||||||
val uiState by model.uiState.collectAsState()
|
|
||||||
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
|
|
||||||
val notificationCenter = remember { getNotificationCenter() }
|
|
||||||
val pullRefreshState = rememberPullRefreshState(uiState.refreshing, {
|
|
||||||
model.reduce(ProfilePostsMviModel.Intent.Refresh)
|
|
||||||
})
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.pullRefresh(pullRefreshState),
|
|
||||||
) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
|
||||||
) {
|
|
||||||
item {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
|
||||||
) {
|
|
||||||
UserHeader(user = user)
|
|
||||||
UserCounters(
|
|
||||||
modifier = Modifier.graphicsLayer(translationY = -Spacing.m.toLocalPixel()),
|
|
||||||
user = user,
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.s))
|
|
||||||
SectionSelector(
|
|
||||||
titles = listOf(
|
|
||||||
stringResource(MR.strings.profile_section_posts),
|
|
||||||
stringResource(MR.strings.profile_section_comments),
|
|
||||||
stringResource(MR.strings.profile_section_saved),
|
|
||||||
),
|
|
||||||
currentSection = 0,
|
|
||||||
onSectionSelected = {
|
|
||||||
val section = when (it) {
|
|
||||||
0 -> ProfileLoggedSection.POSTS
|
|
||||||
1 -> ProfileLoggedSection.COMMENTS
|
|
||||||
else -> ProfileLoggedSection.SAVED
|
|
||||||
}
|
|
||||||
notificationCenter.getObserver(NotificationCenterContractKeys.SectionChanged)
|
|
||||||
?.also { observer ->
|
|
||||||
observer.invoke(section)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.m))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items(uiState.posts) { post ->
|
|
||||||
ProfilePostCard(
|
|
||||||
modifier = Modifier.onClick {
|
|
||||||
navigator?.push(
|
|
||||||
PostDetailScreen(post),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
post = post,
|
|
||||||
options = listOf(stringResource(MR.strings.comment_action_delete)),
|
|
||||||
onOpenCommunity = { community ->
|
|
||||||
navigator?.push(
|
|
||||||
CommunityDetailScreen(community),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onImageClick = { url ->
|
|
||||||
navigator?.push(
|
|
||||||
ZoomableImageScreen(url),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onOptionSelected = { idx ->
|
|
||||||
when (idx) {
|
|
||||||
else -> model.reduce(
|
|
||||||
ProfilePostsMviModel.Intent.DeletePost(post.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.xxxl))
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
|
||||||
model.reduce(ProfilePostsMviModel.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.surface,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts
|
|
||||||
|
|
||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
|
||||||
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
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostsRepository
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.UserRepository
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.IO
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class ProfilePostsViewModel(
|
|
||||||
private val mvi: DefaultMviModel<ProfilePostsMviModel.Intent, ProfilePostsMviModel.UiState, ProfilePostsMviModel.Effect>,
|
|
||||||
private val user: UserModel,
|
|
||||||
private val savedOnly: Boolean = false,
|
|
||||||
private val identityRepository: IdentityRepository,
|
|
||||||
private val userRepository: UserRepository,
|
|
||||||
private val postsRepository: PostsRepository,
|
|
||||||
private val notificationCenter: NotificationCenter,
|
|
||||||
) : ScreenModel,
|
|
||||||
MviModel<ProfilePostsMviModel.Intent, ProfilePostsMviModel.UiState, ProfilePostsMviModel.Effect> by mvi {
|
|
||||||
|
|
||||||
private var currentPage: Int = 1
|
|
||||||
|
|
||||||
init {
|
|
||||||
notificationCenter.addObserver({
|
|
||||||
(it as? PostModel)?.also { post ->
|
|
||||||
handlePostUpdate(post)
|
|
||||||
}
|
|
||||||
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdated)
|
|
||||||
notificationCenter.addObserver({
|
|
||||||
(it as? PostModel)?.also { post ->
|
|
||||||
handlePostDelete(post.id)
|
|
||||||
}
|
|
||||||
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostDeleted)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun finalize() {
|
|
||||||
notificationCenter.removeObserver(this::class.simpleName.orEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStarted() {
|
|
||||||
mvi.onStarted()
|
|
||||||
|
|
||||||
if (mvi.uiState.value.posts.isEmpty()) {
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun reduce(intent: ProfilePostsMviModel.Intent) {
|
|
||||||
when (intent) {
|
|
||||||
ProfilePostsMviModel.Intent.LoadNextPage -> loadNextPage()
|
|
||||||
ProfilePostsMviModel.Intent.Refresh -> refresh()
|
|
||||||
is ProfilePostsMviModel.Intent.DeletePost -> deletePost(intent.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun refresh() {
|
|
||||||
currentPage = 1
|
|
||||||
mvi.updateState { it.copy(canFetchMore = true, refreshing = true) }
|
|
||||||
loadNextPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadNextPage() {
|
|
||||||
val currentState = mvi.uiState.value
|
|
||||||
if (!currentState.canFetchMore || currentState.loading) {
|
|
||||||
mvi.updateState { it.copy(refreshing = false) }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mvi.scope?.launch(Dispatchers.IO) {
|
|
||||||
mvi.updateState { it.copy(loading = true) }
|
|
||||||
val auth = identityRepository.authToken.value
|
|
||||||
val refreshing = currentState.refreshing
|
|
||||||
val postList = userRepository.getPosts(
|
|
||||||
auth = auth,
|
|
||||||
id = user.id,
|
|
||||||
savedOnly = savedOnly,
|
|
||||||
page = currentPage,
|
|
||||||
sort = SortType.New,
|
|
||||||
)
|
|
||||||
currentPage++
|
|
||||||
val canFetchMore = postList.size >= PostsRepository.DEFAULT_PAGE_SIZE
|
|
||||||
mvi.updateState {
|
|
||||||
val newPosts = if (refreshing) {
|
|
||||||
postList
|
|
||||||
} else {
|
|
||||||
it.posts + postList
|
|
||||||
}
|
|
||||||
it.copy(
|
|
||||||
posts = newPosts,
|
|
||||||
loading = false,
|
|
||||||
canFetchMore = canFetchMore,
|
|
||||||
refreshing = false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handlePostUpdate(post: PostModel) {
|
|
||||||
mvi.updateState {
|
|
||||||
it.copy(
|
|
||||||
posts = it.posts.map { p ->
|
|
||||||
if (p.id == post.id) {
|
|
||||||
post
|
|
||||||
} else {
|
|
||||||
p
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handlePostDelete(id: Int) {
|
|
||||||
mvi.updateState { it.copy(posts = it.posts.filter { post -> post.id != id }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun deletePost(id: Int) {
|
|
||||||
mvi.scope?.launch(Dispatchers.IO) {
|
|
||||||
val auth = identityRepository.authToken.value.orEmpty()
|
|
||||||
postsRepository.delete(id = id, auth = auth)
|
|
||||||
handlePostDelete(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,160 +0,0 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.saved
|
|
||||||
|
|
||||||
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.material.ExperimentalMaterialApi
|
|
||||||
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.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
|
||||||
import cafe.adriel.voyager.navigator.tab.Tab
|
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
|
||||||
import com.github.diegoberaldin.racconforlemmy.core.utils.toLocalPixel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SectionSelector
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserCounters
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserHeader
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImageScreen
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedSection
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts.ProfilePostCard
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts.ProfilePostsMviModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.di.getProfilePostsViewModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
|
||||||
|
|
||||||
internal class ProfileSavedScreen(
|
|
||||||
private val user: UserModel,
|
|
||||||
) : Tab {
|
|
||||||
|
|
||||||
override val options: TabOptions
|
|
||||||
@Composable get() {
|
|
||||||
return TabOptions(0u, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
val model = rememberScreenModel {
|
|
||||||
getProfilePostsViewModel(
|
|
||||||
user = user,
|
|
||||||
savedOnly = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
model.bindToLifecycle(key)
|
|
||||||
val uiState by model.uiState.collectAsState()
|
|
||||||
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
|
|
||||||
val notificationCenter = remember { getNotificationCenter() }
|
|
||||||
val pullRefreshState = rememberPullRefreshState(uiState.refreshing, {
|
|
||||||
model.reduce(ProfilePostsMviModel.Intent.Refresh)
|
|
||||||
})
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.pullRefresh(pullRefreshState),
|
|
||||||
) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
|
||||||
) {
|
|
||||||
item {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
|
||||||
) {
|
|
||||||
UserHeader(user = user)
|
|
||||||
UserCounters(
|
|
||||||
modifier = Modifier.graphicsLayer(translationY = -Spacing.m.toLocalPixel()),
|
|
||||||
user = user,
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.s))
|
|
||||||
SectionSelector(
|
|
||||||
titles = listOf(
|
|
||||||
stringResource(MR.strings.profile_section_posts),
|
|
||||||
stringResource(MR.strings.profile_section_comments),
|
|
||||||
stringResource(MR.strings.profile_section_saved),
|
|
||||||
),
|
|
||||||
currentSection = 2,
|
|
||||||
onSectionSelected = {
|
|
||||||
val section = when (it) {
|
|
||||||
0 -> ProfileLoggedSection.POSTS
|
|
||||||
1 -> ProfileLoggedSection.COMMENTS
|
|
||||||
else -> ProfileLoggedSection.SAVED
|
|
||||||
}
|
|
||||||
notificationCenter.getObserver(NotificationCenterContractKeys.SectionChanged)
|
|
||||||
?.also { observer ->
|
|
||||||
observer.invoke(section)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.m))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items(uiState.posts) { post ->
|
|
||||||
ProfilePostCard(
|
|
||||||
post = post,
|
|
||||||
onOpenCommunity = { community ->
|
|
||||||
navigator?.push(
|
|
||||||
CommunityDetailScreen(community),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onImageClick = { url ->
|
|
||||||
navigator?.push(
|
|
||||||
ZoomableImageScreen(url),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
Spacer(modifier = Modifier.height(Spacing.xxxl))
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
|
||||||
model.reduce(ProfilePostsMviModel.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.surface,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
interface ProfileSavedMviModel :
|
||||||
|
MviModel<ProfileSavedMviModel.Intent, ProfileSavedMviModel.UiState, ProfileSavedMviModel.Effect> {
|
||||||
|
|
||||||
|
sealed interface Intent {
|
||||||
|
object Refresh : Intent
|
||||||
|
object LoadNextPage : Intent
|
||||||
|
data class ChangeSection(val section: ProfileSavedSection) : Intent
|
||||||
|
data class UpVotePost(val index: Int, val feedback: Boolean = false) : Intent
|
||||||
|
data class DownVotePost(val index: Int, val feedback: Boolean = false) : Intent
|
||||||
|
data class SavePost(val index: Int, val feedback: Boolean = false) : Intent
|
||||||
|
data class UpVoteComment(val index: Int, val feedback: Boolean = false) : Intent
|
||||||
|
data class DownVoteComment(val index: Int, val feedback: Boolean = false) : Intent
|
||||||
|
data class SaveComment(val index: Int, val feedback: Boolean = false) : Intent
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UiState(
|
||||||
|
val section: ProfileSavedSection = ProfileSavedSection.Posts,
|
||||||
|
val refreshing: Boolean = false,
|
||||||
|
val loading: Boolean = false,
|
||||||
|
val canFetchMore: Boolean = true,
|
||||||
|
val blurNsfw: Boolean = true,
|
||||||
|
val posts: List<PostModel> = emptyList(),
|
||||||
|
val comments: List<CommentModel> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed interface Effect
|
||||||
|
}
|
@ -0,0 +1,256 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
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.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
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.unit.dp
|
||||||
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
|
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.tab.Tab
|
||||||
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
|
import com.github.diegoberaldin.racconforlemmy.core.utils.onClick
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CommentCard
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCard
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SectionSelector
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.createcomment.CreateCommentScreen
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImageScreen
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.di.getProfileSavedViewModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||||
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
|
|
||||||
|
internal class ProfileSavedScreen(
|
||||||
|
private val user: UserModel,
|
||||||
|
) : Tab {
|
||||||
|
|
||||||
|
override val options: TabOptions
|
||||||
|
@Composable get() {
|
||||||
|
return TabOptions(0u, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val model = rememberScreenModel {
|
||||||
|
getProfileSavedViewModel(user = user)
|
||||||
|
}
|
||||||
|
model.bindToLifecycle(key)
|
||||||
|
val uiState by model.uiState.collectAsState()
|
||||||
|
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
|
||||||
|
val bottomSheetNavigator = LocalBottomSheetNavigator.current
|
||||||
|
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
title = {
|
||||||
|
Text(stringResource(MR.strings.profile_section_saved))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
Image(
|
||||||
|
modifier = Modifier.onClick {
|
||||||
|
navigator?.pop()
|
||||||
|
},
|
||||||
|
imageVector = Icons.Default.ArrowBack,
|
||||||
|
contentDescription = null,
|
||||||
|
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||||
|
) {
|
||||||
|
SectionSelector(
|
||||||
|
modifier = Modifier.padding(vertical = Spacing.s),
|
||||||
|
titles = listOf(
|
||||||
|
stringResource(MR.strings.profile_section_posts),
|
||||||
|
stringResource(MR.strings.profile_section_comments),
|
||||||
|
),
|
||||||
|
currentSection = when (uiState.section) {
|
||||||
|
ProfileSavedSection.Comments -> 1
|
||||||
|
else -> 0
|
||||||
|
},
|
||||||
|
onSectionSelected = {
|
||||||
|
val section = when (it) {
|
||||||
|
1 -> ProfileSavedSection.Comments
|
||||||
|
else -> ProfileSavedSection.Posts
|
||||||
|
}
|
||||||
|
model.reduce(ProfileSavedMviModel.Intent.ChangeSection(section))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
val pullRefreshState = rememberPullRefreshState(uiState.refreshing, {
|
||||||
|
model.reduce(ProfileSavedMviModel.Intent.Refresh)
|
||||||
|
})
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.pullRefresh(pullRefreshState),
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||||
|
) {
|
||||||
|
if (uiState.section == ProfileSavedSection.Posts) {
|
||||||
|
itemsIndexed(uiState.posts) { idx, post ->
|
||||||
|
PostCard(
|
||||||
|
modifier = Modifier.onClick {
|
||||||
|
navigator?.push(
|
||||||
|
PostDetailScreen(post),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
post = post,
|
||||||
|
blurNsfw = uiState.blurNsfw,
|
||||||
|
onOpenCommunity = { community ->
|
||||||
|
navigator?.push(
|
||||||
|
CommunityDetailScreen(community),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onOpenCreator = { u ->
|
||||||
|
if (u.id != user.id) {
|
||||||
|
navigator?.push(UserDetailScreen(u))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onUpVote = {
|
||||||
|
model.reduce(
|
||||||
|
ProfileSavedMviModel.Intent.UpVotePost(
|
||||||
|
index = idx,
|
||||||
|
feedback = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onDownVote = {
|
||||||
|
model.reduce(
|
||||||
|
ProfileSavedMviModel.Intent.DownVotePost(
|
||||||
|
index = idx,
|
||||||
|
feedback = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onSave = {
|
||||||
|
model.reduce(
|
||||||
|
ProfileSavedMviModel.Intent.SavePost(
|
||||||
|
index = idx,
|
||||||
|
feedback = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onReply = {
|
||||||
|
val screen = CreateCommentScreen(
|
||||||
|
originalPost = post,
|
||||||
|
)
|
||||||
|
bottomSheetNavigator.show(screen)
|
||||||
|
},
|
||||||
|
onImageClick = { url ->
|
||||||
|
navigator?.push(
|
||||||
|
ZoomableImageScreen(url),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itemsIndexed(uiState.comments) { idx, comment ->
|
||||||
|
CommentCard(
|
||||||
|
comment = comment,
|
||||||
|
onUpVote = {
|
||||||
|
model.reduce(
|
||||||
|
ProfileSavedMviModel.Intent.UpVoteComment(
|
||||||
|
index = idx,
|
||||||
|
feedback = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onDownVote = {
|
||||||
|
model.reduce(
|
||||||
|
ProfileSavedMviModel.Intent.DownVoteComment(
|
||||||
|
index = idx,
|
||||||
|
feedback = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onSave = {
|
||||||
|
model.reduce(
|
||||||
|
ProfileSavedMviModel.Intent.SaveComment(
|
||||||
|
index = idx,
|
||||||
|
feedback = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onReply = {
|
||||||
|
val screen = CreateCommentScreen(
|
||||||
|
originalPost = PostModel(id = comment.postId),
|
||||||
|
originalComment = comment,
|
||||||
|
)
|
||||||
|
bottomSheetNavigator.show(screen)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
||||||
|
model.reduce(ProfileSavedMviModel.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.surface,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved
|
||||||
|
|
||||||
|
sealed interface ProfileSavedSection {
|
||||||
|
object Posts : ProfileSavedSection
|
||||||
|
|
||||||
|
object Comments : ProfileSavedSection
|
||||||
|
}
|
@ -0,0 +1,492 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved
|
||||||
|
|
||||||
|
import cafe.adriel.voyager.core.model.ScreenModel
|
||||||
|
import com.github.diegoberaldin.racconforlemmy.core.utils.HapticFeedback
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||||
|
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.data.UserModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostsRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.UserRepository
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class ProfileSavedViewModel(
|
||||||
|
private val mvi: DefaultMviModel<ProfileSavedMviModel.Intent, ProfileSavedMviModel.UiState, ProfileSavedMviModel.Effect>,
|
||||||
|
private val user: UserModel,
|
||||||
|
private val identityRepository: IdentityRepository,
|
||||||
|
private val userRepository: UserRepository,
|
||||||
|
private val postsRepository: PostsRepository,
|
||||||
|
private val commentRepository: CommentRepository,
|
||||||
|
private val notificationCenter: NotificationCenter,
|
||||||
|
private val hapticFeedback: HapticFeedback,
|
||||||
|
) : ScreenModel,
|
||||||
|
MviModel<ProfileSavedMviModel.Intent, ProfileSavedMviModel.UiState, ProfileSavedMviModel.Effect> by mvi {
|
||||||
|
|
||||||
|
private var currentPage: Int = 1
|
||||||
|
|
||||||
|
init {
|
||||||
|
notificationCenter.addObserver({
|
||||||
|
(it as? PostModel)?.also { post ->
|
||||||
|
handlePostUpdate(post)
|
||||||
|
}
|
||||||
|
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdated)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finalize() {
|
||||||
|
notificationCenter.removeObserver(this::class.simpleName.orEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStarted() {
|
||||||
|
mvi.onStarted()
|
||||||
|
|
||||||
|
if (mvi.uiState.value.posts.isEmpty()) {
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reduce(intent: ProfileSavedMviModel.Intent) {
|
||||||
|
when (intent) {
|
||||||
|
ProfileSavedMviModel.Intent.LoadNextPage -> loadNextPage()
|
||||||
|
ProfileSavedMviModel.Intent.Refresh -> refresh()
|
||||||
|
is ProfileSavedMviModel.Intent.ChangeSection -> changeSection(intent.section)
|
||||||
|
is ProfileSavedMviModel.Intent.DownVoteComment -> toggleDownVoteComment(
|
||||||
|
comment = uiState.value.comments[intent.index],
|
||||||
|
feedback = intent.feedback,
|
||||||
|
)
|
||||||
|
|
||||||
|
is ProfileSavedMviModel.Intent.DownVotePost -> toggleDownVotePost(
|
||||||
|
post = uiState.value.posts[intent.index],
|
||||||
|
feedback = intent.feedback,
|
||||||
|
)
|
||||||
|
|
||||||
|
is ProfileSavedMviModel.Intent.SaveComment -> toggleSaveComment(
|
||||||
|
comment = uiState.value.comments[intent.index],
|
||||||
|
feedback = intent.feedback,
|
||||||
|
)
|
||||||
|
|
||||||
|
is ProfileSavedMviModel.Intent.SavePost -> toggleSavePost(
|
||||||
|
post = uiState.value.posts[intent.index],
|
||||||
|
feedback = intent.feedback,
|
||||||
|
)
|
||||||
|
|
||||||
|
is ProfileSavedMviModel.Intent.UpVoteComment -> toggleUpVoteComment(
|
||||||
|
comment = uiState.value.comments[intent.index],
|
||||||
|
feedback = intent.feedback,
|
||||||
|
)
|
||||||
|
|
||||||
|
is ProfileSavedMviModel.Intent.UpVotePost -> toggleUpVotePost(
|
||||||
|
post = uiState.value.posts[intent.index],
|
||||||
|
feedback = intent.feedback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refresh() {
|
||||||
|
currentPage = 1
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
canFetchMore = true, refreshing = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
loadNextPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadNextPage() {
|
||||||
|
val currentState = mvi.uiState.value
|
||||||
|
if (!currentState.canFetchMore || currentState.loading) {
|
||||||
|
mvi.updateState { it.copy(refreshing = false) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
mvi.updateState { it.copy(loading = true) }
|
||||||
|
val auth = identityRepository.authToken.value
|
||||||
|
val refreshing = currentState.refreshing
|
||||||
|
val section = currentState.section
|
||||||
|
if (section == ProfileSavedSection.Posts) {
|
||||||
|
val postList = userRepository.getSavedPosts(
|
||||||
|
auth = auth,
|
||||||
|
id = user.id,
|
||||||
|
page = currentPage,
|
||||||
|
sort = SortType.New,
|
||||||
|
)
|
||||||
|
val canFetchMore = postList.size >= PostsRepository.DEFAULT_PAGE_SIZE
|
||||||
|
mvi.updateState {
|
||||||
|
val newPosts = if (refreshing) {
|
||||||
|
postList
|
||||||
|
} else {
|
||||||
|
it.posts + postList
|
||||||
|
}
|
||||||
|
it.copy(
|
||||||
|
posts = newPosts,
|
||||||
|
loading = false,
|
||||||
|
canFetchMore = canFetchMore,
|
||||||
|
refreshing = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val commentList = userRepository.getSavedComments(
|
||||||
|
auth = auth,
|
||||||
|
id = user.id,
|
||||||
|
page = currentPage,
|
||||||
|
sort = SortType.New,
|
||||||
|
)
|
||||||
|
val canFetchMore = commentList.size >= PostsRepository.DEFAULT_PAGE_SIZE
|
||||||
|
mvi.updateState {
|
||||||
|
val newComments = if (refreshing) {
|
||||||
|
commentList
|
||||||
|
} else {
|
||||||
|
it.comments + commentList
|
||||||
|
}
|
||||||
|
it.copy(
|
||||||
|
comments = newComments,
|
||||||
|
loading = false,
|
||||||
|
canFetchMore = canFetchMore,
|
||||||
|
refreshing = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentPage++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePostUpdate(post: PostModel) {
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
posts = it.posts.map { p ->
|
||||||
|
if (p.id == post.id) {
|
||||||
|
post
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePostDelete(id: Int) {
|
||||||
|
mvi.updateState { it.copy(posts = it.posts.filter { post -> post.id != id }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deletePost(id: Int) {
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
postsRepository.delete(id = id, auth = auth)
|
||||||
|
handlePostDelete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeSection(section: ProfileSavedSection) {
|
||||||
|
currentPage = 1
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
section = section,
|
||||||
|
canFetchMore = true,
|
||||||
|
refreshing = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
loadNextPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleUpVotePost(
|
||||||
|
post: PostModel,
|
||||||
|
feedback: Boolean,
|
||||||
|
) {
|
||||||
|
val newValue = post.myVote <= 0
|
||||||
|
if (feedback) {
|
||||||
|
hapticFeedback.vibrate()
|
||||||
|
}
|
||||||
|
val newPost = postsRepository.asUpVoted(
|
||||||
|
post = post,
|
||||||
|
voted = newValue,
|
||||||
|
)
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
posts = it.posts.map { p ->
|
||||||
|
if (p.id == post.id) {
|
||||||
|
newPost
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
postsRepository.upVote(
|
||||||
|
auth = auth,
|
||||||
|
post = post,
|
||||||
|
voted = newValue,
|
||||||
|
)
|
||||||
|
notificationCenter.getObserver(NotificationCenterContractKeys.PostUpdated)?.also {
|
||||||
|
it.invoke(newPost)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
posts = it.posts.map { p ->
|
||||||
|
if (p.id == post.id) {
|
||||||
|
newPost
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleDownVotePost(
|
||||||
|
post: PostModel,
|
||||||
|
feedback: Boolean,
|
||||||
|
) {
|
||||||
|
val newValue = post.myVote >= 0
|
||||||
|
if (feedback) {
|
||||||
|
hapticFeedback.vibrate()
|
||||||
|
}
|
||||||
|
val newPost = postsRepository.asDownVoted(
|
||||||
|
post = post,
|
||||||
|
downVoted = newValue,
|
||||||
|
)
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
posts = it.posts.map { p ->
|
||||||
|
if (p.id == post.id) {
|
||||||
|
newPost
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
postsRepository.downVote(
|
||||||
|
auth = auth,
|
||||||
|
post = post,
|
||||||
|
downVoted = newValue,
|
||||||
|
)
|
||||||
|
notificationCenter.getObserver(NotificationCenterContractKeys.PostUpdated)?.also {
|
||||||
|
it.invoke(newPost)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
posts = it.posts.map { p ->
|
||||||
|
if (p.id == post.id) {
|
||||||
|
newPost
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleSavePost(
|
||||||
|
post: PostModel,
|
||||||
|
feedback: Boolean,
|
||||||
|
) {
|
||||||
|
val newValue = !post.saved
|
||||||
|
if (feedback) {
|
||||||
|
hapticFeedback.vibrate()
|
||||||
|
}
|
||||||
|
val newPost = postsRepository.asSaved(
|
||||||
|
post = post,
|
||||||
|
saved = newValue,
|
||||||
|
)
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
posts = it.posts.map { p ->
|
||||||
|
if (p.id == post.id) {
|
||||||
|
newPost
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
postsRepository.save(
|
||||||
|
auth = auth,
|
||||||
|
post = post,
|
||||||
|
saved = newValue,
|
||||||
|
)
|
||||||
|
notificationCenter.getObserver(NotificationCenterContractKeys.PostUpdated)?.also {
|
||||||
|
it.invoke(newPost)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
posts = it.posts.map { p ->
|
||||||
|
if (p.id == post.id) {
|
||||||
|
newPost
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleUpVoteComment(
|
||||||
|
comment: CommentModel,
|
||||||
|
feedback: Boolean,
|
||||||
|
) {
|
||||||
|
val newValue = comment.myVote <= 0
|
||||||
|
if (feedback) {
|
||||||
|
hapticFeedback.vibrate()
|
||||||
|
}
|
||||||
|
val newComment = commentRepository.asUpVoted(
|
||||||
|
comment = comment,
|
||||||
|
voted = newValue,
|
||||||
|
)
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
comments = it.comments.map { c ->
|
||||||
|
if (c.id == comment.id) {
|
||||||
|
newComment
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
commentRepository.upVote(
|
||||||
|
auth = auth,
|
||||||
|
comment = comment,
|
||||||
|
voted = newValue,
|
||||||
|
)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
comments = it.comments.map { c ->
|
||||||
|
if (c.id == comment.id) {
|
||||||
|
comment
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleDownVoteComment(
|
||||||
|
comment: CommentModel,
|
||||||
|
feedback: Boolean,
|
||||||
|
) {
|
||||||
|
val newValue = comment.myVote >= 0
|
||||||
|
if (feedback) {
|
||||||
|
hapticFeedback.vibrate()
|
||||||
|
}
|
||||||
|
val newComment = commentRepository.asDownVoted(comment, newValue)
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
comments = it.comments.map { c ->
|
||||||
|
if (c.id == comment.id) {
|
||||||
|
newComment
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
commentRepository.downVote(
|
||||||
|
auth = auth,
|
||||||
|
comment = comment,
|
||||||
|
downVoted = newValue,
|
||||||
|
)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
comments = it.comments.map { c ->
|
||||||
|
if (c.id == comment.id) {
|
||||||
|
comment
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleSaveComment(
|
||||||
|
comment: CommentModel,
|
||||||
|
feedback: Boolean,
|
||||||
|
) {
|
||||||
|
val newValue = !comment.saved
|
||||||
|
if (feedback) {
|
||||||
|
hapticFeedback.vibrate()
|
||||||
|
}
|
||||||
|
val newComment = commentRepository.asSaved(
|
||||||
|
comment = comment,
|
||||||
|
saved = newValue,
|
||||||
|
)
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
comments = it.comments.map { c ->
|
||||||
|
if (c.id == comment.id) {
|
||||||
|
newComment
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mvi.scope?.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val auth = identityRepository.authToken.value.orEmpty()
|
||||||
|
commentRepository.save(
|
||||||
|
auth = auth,
|
||||||
|
comment = comment,
|
||||||
|
saved = newValue,
|
||||||
|
)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
mvi.updateState {
|
||||||
|
it.copy(
|
||||||
|
comments = it.comments.map { c ->
|
||||||
|
if (c.id == comment.id) {
|
||||||
|
comment
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,10 +5,8 @@ import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileC
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileContentViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileContentViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedMviModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedMviModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments.ProfileCommentsMviModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved.ProfileSavedMviModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments.ProfileCommentsViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved.ProfileSavedViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts.ProfilePostsMviModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts.ProfilePostsViewModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetMviModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetMviModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetViewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
@ -33,26 +31,22 @@ val profileTabModule = module {
|
|||||||
mvi = DefaultMviModel(ProfileLoggedMviModel.UiState()),
|
mvi = DefaultMviModel(ProfileLoggedMviModel.UiState()),
|
||||||
identityRepository = get(),
|
identityRepository = get(),
|
||||||
siteRepository = get(),
|
siteRepository = get(),
|
||||||
)
|
|
||||||
}
|
|
||||||
factory { params ->
|
|
||||||
ProfilePostsViewModel(
|
|
||||||
mvi = DefaultMviModel(ProfilePostsMviModel.UiState()),
|
|
||||||
user = params[0],
|
|
||||||
savedOnly = params[1],
|
|
||||||
identityRepository = get(),
|
|
||||||
userRepository = get(),
|
userRepository = get(),
|
||||||
postsRepository = get(),
|
postsRepository = get(),
|
||||||
|
commentRepository = get(),
|
||||||
notificationCenter = get(),
|
notificationCenter = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
factory { params ->
|
factory { params ->
|
||||||
ProfileCommentsViewModel(
|
ProfileSavedViewModel(
|
||||||
mvi = DefaultMviModel(ProfileCommentsMviModel.UiState()),
|
mvi = DefaultMviModel(ProfileSavedMviModel.UiState()),
|
||||||
user = params[0],
|
user = params[0],
|
||||||
identityRepository = get(),
|
identityRepository = get(),
|
||||||
userRepository = get(),
|
userRepository = get(),
|
||||||
|
postsRepository = get(),
|
||||||
commentRepository = get(),
|
commentRepository = get(),
|
||||||
|
hapticFeedback = get(),
|
||||||
|
notificationCenter = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.profile.di
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileContentViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileContentViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments.ProfileCommentsViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved.ProfileSavedViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts.ProfilePostsViewModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetViewModel
|
||||||
|
|
||||||
expect fun getProfileScreenModel(): ProfileContentViewModel
|
expect fun getProfileScreenModel(): ProfileContentViewModel
|
||||||
@ -13,9 +12,4 @@ expect fun getLoginBottomSheetViewModel(): LoginBottomSheetViewModel
|
|||||||
|
|
||||||
expect fun getProfileLoggedViewModel(): ProfileLoggedViewModel
|
expect fun getProfileLoggedViewModel(): ProfileLoggedViewModel
|
||||||
|
|
||||||
expect fun getProfilePostsViewModel(
|
expect fun getProfileSavedViewModel(user: UserModel): ProfileSavedViewModel
|
||||||
user: UserModel,
|
|
||||||
savedOnly: Boolean = false,
|
|
||||||
): ProfilePostsViewModel
|
|
||||||
|
|
||||||
expect fun getProfileCommentsViewModel(user: UserModel): ProfileCommentsViewModel
|
|
||||||
|
@ -3,8 +3,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.profile.di
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileContentViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.ProfileContentViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.ProfileLoggedViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.comments.ProfileCommentsViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.saved.ProfileSavedViewModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.posts.ProfilePostsViewModel
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetViewModel
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheetViewModel
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
@ -17,29 +16,19 @@ actual fun getLoginBottomSheetViewModel() = ProfileScreenModelHelper.loginModel
|
|||||||
actual fun getProfileLoggedViewModel(): ProfileLoggedViewModel =
|
actual fun getProfileLoggedViewModel(): ProfileLoggedViewModel =
|
||||||
ProfileScreenModelHelper.loggedModel
|
ProfileScreenModelHelper.loggedModel
|
||||||
|
|
||||||
actual fun getProfilePostsViewModel(
|
actual fun getProfileSavedViewModel(
|
||||||
user: UserModel,
|
user: UserModel,
|
||||||
savedOnly: Boolean,
|
): ProfileSavedViewModel =
|
||||||
): ProfilePostsViewModel =
|
ProfileScreenModelHelper.getSavedModel(user = user)
|
||||||
ProfileScreenModelHelper.getPostsModel(user = user, savedOnly = savedOnly)
|
|
||||||
|
|
||||||
actual fun getProfileCommentsViewModel(user: UserModel): ProfileCommentsViewModel =
|
|
||||||
ProfileScreenModelHelper.getCommentsModel(user)
|
|
||||||
|
|
||||||
object ProfileScreenModelHelper : KoinComponent {
|
object ProfileScreenModelHelper : KoinComponent {
|
||||||
val profileModel: ProfileContentViewModel by inject()
|
val profileModel: ProfileContentViewModel by inject()
|
||||||
val loginModel: LoginBottomSheetViewModel by inject()
|
val loginModel: LoginBottomSheetViewModel by inject()
|
||||||
val loggedModel: ProfileLoggedViewModel by inject()
|
val loggedModel: ProfileLoggedViewModel by inject()
|
||||||
|
|
||||||
fun getPostsModel(user: UserModel, savedOnly: Boolean): ProfilePostsViewModel {
|
|
||||||
val res: ProfilePostsViewModel by inject(
|
|
||||||
parameters = { parametersOf(user, savedOnly) },
|
|
||||||
)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCommentsModel(user: UserModel): ProfileCommentsViewModel {
|
fun getSavedModel(user: UserModel): ProfileSavedViewModel {
|
||||||
val res: ProfileCommentsViewModel by inject(
|
val res: ProfileSavedViewModel by inject(
|
||||||
parameters = { parametersOf(user) },
|
parameters = { parametersOf(user) },
|
||||||
)
|
)
|
||||||
return res
|
return res
|
||||||
|
Loading…
x
Reference in New Issue
Block a user