chore: screen model and navigator optimizations (#103)

This commit is contained in:
Diego Beraldin 2023-11-05 13:58:46 +01:00 committed by GitHub
parent a4a8722b02
commit e007426b37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 1227 additions and 951 deletions

View File

@ -1,11 +1,13 @@
package com.github.diegoberaldin.raccoonforlemmy.core.appearance.repository
import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.Color
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiFontFamily
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
import kotlinx.coroutines.flow.StateFlow
@Stable
interface ThemeRepository {
val uiTheme: StateFlow<UiTheme>

View File

@ -1,9 +1,11 @@
package com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.Color
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
@Stable
interface ColorSchemeProvider {
val supportsDynamicColors: Boolean

View File

@ -1,9 +1,11 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.chat
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PrivateMessageModel
@Stable
interface InboxChatMviModel :
MviModel<InboxChatMviModel.Intent, InboxChatMviModel.UiState, InboxChatMviModel.SideEffect>,
ScreenModel {

View File

@ -67,7 +67,7 @@ class InboxChatScreen(
val model = rememberScreenModel { getInboxChatViewModel(otherUserId) }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val focusManager = LocalFocusManager.current
val drawerCoordinator = remember { getDrawerCoordinator() }
DisposableEffect(key) {
@ -112,7 +112,7 @@ class InboxChatScreen(
Image(
modifier = Modifier.onClick(
rememberCallback {
navigator?.pop()
navigationCoordinator.getRootNavigator()?.pop()
},
),
imageVector = Icons.Default.ArrowBack,

View File

@ -31,9 +31,11 @@ class InboxChatViewModel(
private var currentPage: Int = 1
init {
notificationCenter.addObserver({
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout)
notificationCenter.addObserver(
{
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout
)
}
fun finalize() {

View File

@ -1,9 +1,11 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.communityInfo
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
@Stable
interface CommunityInfoMviModel :
MviModel<CommunityInfoMviModel.Intent, CommunityInfoMviModel.UiState, CommunityInfoMviModel.Effect>,
ScreenModel {

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
@ -7,6 +8,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
@Stable
interface CommunityDetailMviModel :
MviModel<CommunityDetailMviModel.Intent, CommunityDetailMviModel.UiState, CommunityDetailMviModel.Effect>,
ScreenModel {
@ -15,16 +17,16 @@ interface CommunityDetailMviModel :
data object Refresh : Intent
data object LoadNextPage : Intent
data class ChangeSort(val value: SortType) : 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 UpVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class DownVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class SavePost(val id: Int, val feedback: Boolean = false) : Intent
data object HapticIndication : Intent
data object Subscribe : Intent
data object Unsubscribe : Intent
data class DeletePost(val id: Int) : Intent
data class SharePost(val index: Int) : Intent
data class MarkAsRead(val index: Int) : Intent
data class Hide(val index: Int) : Intent
data class SharePost(val id: Int) : Intent
data class MarkAsRead(val id: Int) : Intent
data class Hide(val id: Int) : Intent
data object Block : Intent
data object BlockInstance : Intent
data object ClearRead : Intent

View File

@ -13,7 +13,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
@ -60,7 +60,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
@ -88,6 +87,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateRepor
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallbackArgs
@ -123,14 +123,13 @@ class CommunityDetailScreen(
val snackbarHostState = remember { SnackbarHostState() }
val genericError = stringResource(MR.strings.message_generic_error)
val successMessage = stringResource(MR.strings.message_operation_successful)
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val isOnOtherInstance = otherInstance.isNotEmpty()
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val notificationCenter = remember { getNotificationCenter() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
val downvoteColor by themeRepository.downvoteColor.collectAsState()
@ -138,6 +137,8 @@ class CommunityDetailScreen(
val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary
val drawerCoordinator = remember { getDrawerCoordinator() }
var rawContent by remember { mutableStateOf<Any?>(null) }
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
DisposableEffect(key) {
drawerCoordinator.setGesturesEnabled(false)
@ -146,7 +147,38 @@ class CommunityDetailScreen(
drawerCoordinator.setGesturesEnabled(true)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
CommunityDetailMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
notificationCenter.addObserver(
{
model.reduce(CommunityDetailMviModel.Intent.Refresh)
}, key, NotificationCenterContractKeys.PostCreated
)
notificationCenter.addObserver(
{
model.reduce(CommunityDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.PostCreated
)
notificationCenter.addObserver(
{
model.reduce(CommunityDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
}
LaunchedEffect(model) {
model.effects.onEach {
when (it) {
@ -170,8 +202,6 @@ class CommunityDetailScreen(
val stateCommunity = uiState.community
Scaffold(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(fabNestedScrollConnection)
.background(MaterialTheme.colorScheme.background)
.padding(Spacing.xs),
topBar = {
@ -216,16 +246,7 @@ class CommunityDetailScreen(
val sheet = SortBottomSheet(
expandTop = true,
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
CommunityDetailMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
),
imageVector = uiState.sortType.toIcon(),
@ -234,6 +255,7 @@ class CommunityDetailScreen(
)
},
navigationIcon = {
val navigator = navigationCoordinator.getRootNavigator()
if (navigator?.canPop == true) {
Image(
modifier = Modifier.onClick(
@ -264,7 +286,7 @@ class CommunityDetailScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ExpandLess,
text = stringResource(MR.strings.action_back_to_top),
onSelected = {
onSelected = rememberCallback {
scope.launch {
lazyListState.scrollToItem(0)
topAppBarState.heightOffset = 0f
@ -275,7 +297,7 @@ class CommunityDetailScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ClearAll,
text = stringResource(MR.strings.action_clear_read),
onSelected = {
onSelected = rememberCallback {
model.reduce(CommunityDetailMviModel.Intent.ClearRead)
scope.launch {
lazyListState.scrollToItem(0)
@ -286,14 +308,11 @@ class CommunityDetailScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.Create,
text = stringResource(MR.strings.action_create_post),
onSelected = {
onSelected = rememberCallback {
val screen = CreatePostScreen(
communityId = stateCommunity.id,
)
notificationCenter.addObserver({
model.reduce(CommunityDetailMviModel.Intent.Refresh)
}, key, NotificationCenterContractKeys.PostCreated)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()?.show(screen)
},
)
}
@ -312,6 +331,14 @@ class CommunityDetailScreen(
Box(
modifier = Modifier
.padding(padding)
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
}
.nestedScroll(fabNestedScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
@ -328,7 +355,8 @@ class CommunityDetailScreen(
stringResource(MR.strings.community_detail_block_instance),
),
onOpenImage = rememberCallbackArgs { url ->
navigator?.push(ZoomableImageScreen(url))
navigationCoordinator.getRootNavigator()
?.push(ZoomableImageScreen(url))
},
onOptionSelected = rememberCallbackArgs { optionIdx ->
when (optionIdx) {
@ -336,7 +364,7 @@ class CommunityDetailScreen(
2 -> model.reduce(CommunityDetailMviModel.Intent.Block)
1 -> {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
InstanceInfoScreen(
url = stateCommunity.instanceUrl,
),
@ -344,7 +372,7 @@ class CommunityDetailScreen(
}
else -> {
bottomSheetNavigator.show(
navigationCoordinator.getBottomNavigator()?.show(
CommunityInfoScreen(stateCommunity),
)
}
@ -367,7 +395,7 @@ class CommunityDetailScreen(
}
}
}
itemsIndexed(uiState.posts) { idx, post ->
items(uiState.posts) { post ->
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled && !isOnOtherInstance,
@ -398,12 +426,12 @@ class CommunityDetailScreen(
},
onDismissToStart = rememberCallback(model) {
model.reduce(
CommunityDetailMviModel.Intent.UpVotePost(idx),
CommunityDetailMviModel.Intent.UpVotePost(post.id),
)
},
onDismissToEnd = rememberCallback(model) {
model.reduce(
CommunityDetailMviModel.Intent.DownVotePost(idx),
CommunityDetailMviModel.Intent.DownVotePost(post.id),
)
},
content = {
@ -420,10 +448,10 @@ class CommunityDetailScreen(
onClick = rememberCallback(model) {
model.reduce(
CommunityDetailMviModel.Intent.MarkAsRead(
idx
post.id
)
)
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(
post = post,
otherInstance = otherInstance,
@ -431,7 +459,7 @@ class CommunityDetailScreen(
)
},
onOpenCreator = rememberCallbackArgs { user ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(
user = user,
otherInstance = otherInstance,
@ -442,7 +470,7 @@ class CommunityDetailScreen(
if (!isOnOtherInstance) {
model.reduce(
CommunityDetailMviModel.Intent.UpVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -452,7 +480,7 @@ class CommunityDetailScreen(
if (!isOnOtherInstance) {
model.reduce(
CommunityDetailMviModel.Intent.DownVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -462,7 +490,7 @@ class CommunityDetailScreen(
if (!isOnOtherInstance) {
model.reduce(
CommunityDetailMviModel.Intent.SavePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -473,23 +501,17 @@ class CommunityDetailScreen(
val screen = CreateCommentScreen(
originalPost = post,
)
notificationCenter.addObserver(
{
model.reduce(CommunityDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()
?.show(screen)
}
},
onImageClick = rememberCallbackArgs(model) { url ->
model.reduce(
CommunityDetailMviModel.Intent.MarkAsRead(
idx
post.id
)
)
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
ZoomableImageScreen(url),
)
},
@ -512,26 +534,21 @@ class CommunityDetailScreen(
)
4 -> {
notificationCenter.addObserver(
{
model.reduce(CommunityDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.PostCreated
)
bottomSheetNavigator.show(
CreatePostScreen(
editedPost = post,
navigationCoordinator.getBottomNavigator()
?.show(
CreatePostScreen(
editedPost = post,
)
)
)
}
3 -> {
bottomSheetNavigator.show(
CreateReportScreen(
postId = post.id
navigationCoordinator.getBottomNavigator()
?.show(
CreateReportScreen(
postId = post.id
)
)
)
}
2 -> {
@ -540,12 +557,12 @@ class CommunityDetailScreen(
1 -> model.reduce(
CommunityDetailMviModel.Intent.Hide(
idx
post.id
)
)
else -> model.reduce(
CommunityDetailMviModel.Intent.SharePost(idx)
CommunityDetailMviModel.Intent.SharePost(post.id)
)
}
})

View File

@ -33,8 +33,8 @@ class CommunityDetailViewModel(
private val settingsRepository: SettingsRepository,
private val shareHelper: ShareHelper,
private val hapticFeedback: HapticFeedback,
) : MviModel<CommunityDetailMviModel.Intent, CommunityDetailMviModel.UiState, CommunityDetailMviModel.Effect> by mvi,
CommunityDetailMviModel {
) : CommunityDetailMviModel,
MviModel<CommunityDetailMviModel.Intent, CommunityDetailMviModel.UiState, CommunityDetailMviModel.Effect> by mvi {
private var currentPage: Int = 1
private var pageCursor: String? = null
@ -85,17 +85,17 @@ class CommunityDetailViewModel(
CommunityDetailMviModel.Intent.Refresh -> refresh()
is CommunityDetailMviModel.Intent.DownVotePost -> toggleDownVotePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is CommunityDetailMviModel.Intent.SavePost -> toggleSavePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is CommunityDetailMviModel.Intent.UpVotePost -> toggleUpVotePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
@ -105,17 +105,17 @@ class CommunityDetailViewModel(
CommunityDetailMviModel.Intent.Unsubscribe -> unsubscribe()
is CommunityDetailMviModel.Intent.DeletePost -> handlePostDelete(intent.id)
is CommunityDetailMviModel.Intent.SharePost -> share(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
)
CommunityDetailMviModel.Intent.Block -> blockCommunity()
CommunityDetailMviModel.Intent.BlockInstance -> blockInstance()
is CommunityDetailMviModel.Intent.MarkAsRead -> {
markAsRead(uiState.value.posts[intent.index])
markAsRead(uiState.value.posts.first { it.id == intent.id })
}
CommunityDetailMviModel.Intent.ClearRead -> clearRead()
is CommunityDetailMviModel.Intent.Hide -> hide(post = uiState.value.posts[intent.index])
is CommunityDetailMviModel.Intent.Hide -> hide(post = uiState.value.posts.first { it.id == intent.id })
}
}

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.components
import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@ -13,6 +14,7 @@ import kotlinx.coroutines.flow.stateIn
private const val THRESHOLD = 5f
@Stable
interface FabNestedScrollConnection : NestedScrollConnection {
val isFabVisible: StateFlow<Boolean>
}

View File

@ -17,7 +17,7 @@ fun PostCardBody(
onClick: (() -> Unit)? = null,
) {
val uriHandler = LocalUriHandler.current
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val settingsRepository = remember { getSettingsRepository() }
if (text.isNotEmpty()) {
@ -31,11 +31,11 @@ fun PostCardBody(
url = url,
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
uriHandler = uriHandler,
navigator = navigator
navigator = navigationCoordinator.getRootNavigator()
)
},
onOpenImage = { url ->
navigator?.push(ZoomableImageScreen(url))
navigationCoordinator.getRootNavigator()?.push(ZoomableImageScreen(url))
},
onClick = onClick,
)

View File

@ -17,7 +17,7 @@ fun PostCardTitle(
onClick: (() -> Unit)? = null,
) {
val uriHandler = LocalUriHandler.current
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val settingsRepository = remember { getSettingsRepository() }
CustomMarkdown(
@ -29,11 +29,11 @@ fun PostCardTitle(
url = url,
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
uriHandler = uriHandler,
navigator = navigator
navigator = navigationCoordinator.getRootNavigator(),
)
},
onOpenImage = { url ->
navigator?.push(ZoomableImageScreen(url))
navigationCoordinator.getRootNavigator()?.push(ZoomableImageScreen(url))
},
onClick = onClick,
)

View File

@ -30,7 +30,7 @@ fun PostLinkBanner(
url: String,
) {
val uriHandler = LocalUriHandler.current
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val settingsRepository = remember { getSettingsRepository() }
if (url.isNotEmpty()) {
@ -44,7 +44,7 @@ fun PostLinkBanner(
if (settingsRepository.currentSettings.value.openUrlsInExternalBrowser) {
uriHandler.openUri(url)
} else {
navigator?.push(WebViewScreen(url))
navigationCoordinator.getRootNavigator()?.push(WebViewScreen(url))
}
},
).padding(

View File

@ -1,11 +1,13 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.createcomment
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.createpost.CreatePostSection
import dev.icerock.moko.resources.desc.StringDesc
@Stable
interface CreateCommentMviModel :
MviModel<CreateCommentMviModel.Intent, CreateCommentMviModel.UiState, CreateCommentMviModel.Effect>,
ScreenModel {

View File

@ -45,7 +45,6 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
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.BottomSheetHandle
@ -57,6 +56,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Section
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.TextFormattingBar
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.createpost.CreatePostSection
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getCreateCommentViewModel
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.RawContentDialog
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
@ -88,7 +88,7 @@ class CreateCommentScreen(
val uiState by model.uiState.collectAsState()
val snackbarHostState = remember { SnackbarHostState() }
val genericError = stringResource(MR.strings.message_generic_error)
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
val galleryHelper = remember { getGalleryHelper() }
var openImagePicker by remember { mutableStateOf(false) }
@ -109,7 +109,7 @@ class CreateCommentScreen(
CreateCommentMviModel.Effect.Success -> {
notificationCenter.getObserver(NotificationCenterContractKeys.CommentCreated)
?.also { o -> o.invoke(Unit) }
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
}
is CreateCommentMviModel.Effect.AddImageToText -> {

View File

@ -1,10 +1,12 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.createpost
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import dev.icerock.moko.resources.desc.StringDesc
@Stable
interface CreatePostMviModel :
MviModel<CreatePostMviModel.Intent, CreatePostMviModel.UiState, CreatePostMviModel.Effect>,
ScreenModel {

View File

@ -51,7 +51,6 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
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.BottomSheetHandle
@ -60,6 +59,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Progres
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SectionSelector
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.TextFormattingBar
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getCreatePostViewModel
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.getGalleryHelper
@ -89,7 +89,6 @@ class CreatePostScreen(
val uiState by model.uiState.collectAsState()
val snackbarHostState = remember { SnackbarHostState() }
val genericError = stringResource(MR.strings.message_generic_error)
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val notificationCenter = remember { getNotificationCenter() }
val galleryHelper = remember { getGalleryHelper() }
var bodyTextFieldValue by remember {
@ -98,6 +97,7 @@ class CreatePostScreen(
val bodyFocusRequester = remember { FocusRequester() }
val urlFocusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
val navigationCoordinator = remember { getNavigationCoordinator() }
LaunchedEffect(model) {
model.reduce(CreatePostMviModel.Intent.SetTitle(editedPost?.title.orEmpty()))
@ -112,7 +112,7 @@ class CreatePostScreen(
CreatePostMviModel.Effect.Success -> {
notificationCenter.getObserver(NotificationCenterContractKeys.PostCreated)
?.also { o -> o.invoke(Unit) }
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
}
is CreatePostMviModel.Effect.AddImageToBody -> {

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.drawer
import androidx.compose.runtime.Stable
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.MultiCommunityModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
import kotlinx.coroutines.flow.SharedFlow
@ -13,6 +14,7 @@ sealed interface DrawerEvent {
data object OpenBookmarks : DrawerEvent
}
@Stable
interface DrawerCoordinator {
val gesturesEnabled: StateFlow<Boolean>

View File

@ -11,7 +11,7 @@ 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.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ExperimentalMaterialApi
@ -179,7 +179,7 @@ object ModalDrawerContent : Tab {
}
}
itemsIndexed(uiState.multiCommunities) { _, community ->
items(uiState.multiCommunities) { community ->
MultiCommunityItem(
modifier = Modifier.fillMaxWidth().onClick(
rememberCallback {
@ -196,7 +196,7 @@ object ModalDrawerContent : Tab {
autoLoadImages = uiState.autoLoadImages,
)
}
itemsIndexed(uiState.communities) { _, community ->
items(uiState.communities) { community ->
CommunityItem(
modifier = Modifier.fillMaxWidth().onClick(
rememberCallback {

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.drawer
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.MultiCommunityModel
@ -7,6 +8,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
import dev.icerock.moko.resources.desc.StringDesc
@Stable
interface ModalDrawerMviModel :
MviModel<ModalDrawerMviModel.Intent, ModalDrawerMviModel.UiState, ModalDrawerMviModel.Effect>,
ScreenModel {

View File

@ -1,8 +1,10 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.image
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
@Stable
interface ZoomableImageMviModel :
MviModel<ZoomableImageMviModel.Intent, ZoomableImageMviModel.UiState, ZoomableImageMviModel.Effect>,
ScreenModel {

View File

@ -55,7 +55,7 @@ class ZoomableImageScreen(
val uiState by model.uiState.collectAsState()
val snackbarHostState = remember { SnackbarHostState() }
val successMessage = stringResource(MR.strings.message_operation_successful)
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val drawerCoordinator = remember { getDrawerCoordinator() }
LaunchedEffect(model) {
@ -82,7 +82,7 @@ class ZoomableImageScreen(
Icon(
modifier = Modifier.onClick(
rememberCallback {
navigator?.pop()
navigationCoordinator.getRootNavigator()?.pop()
},
),
imageVector = Icons.Default.ArrowBack,

View File

@ -1,9 +1,11 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.instanceinfo
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
@Stable
interface InstanceInfoMviModel :
MviModel<InstanceInfoMviModel.Intent, InstanceInfoMviModel.UiState, InstanceInfoMviModel.Effect>,
ScreenModel {

View File

@ -43,6 +43,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Communi
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.ScaledContent
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getInstanceInfoViewModel
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
@ -61,9 +62,11 @@ class InstanceInfoScreen(
val model = rememberScreenModel { getInstanceInfoViewModel(url) }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val instanceName = url.replace("https://", "")
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
Scaffold(
modifier = Modifier.background(MaterialTheme.colorScheme.background)
@ -75,7 +78,7 @@ class InstanceInfoScreen(
Image(
modifier = Modifier.onClick(
rememberCallback {
navigator?.pop()
navigationCoordinator.getRootNavigator()?.pop()
},
),
imageVector = Icons.Default.ArrowBack,
@ -101,7 +104,13 @@ class InstanceInfoScreen(
)
Box(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
}
.padding(paddingValues)
.pullRefresh(pullRefreshState),
) {
@ -143,7 +152,7 @@ class InstanceInfoScreen(
CommunityItem(
modifier = Modifier.onClick(
rememberCallback {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(
community = it,
otherInstance = instanceName,

View File

@ -28,9 +28,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
@ -43,7 +43,7 @@ class ColorBottomSheet : Screen {
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
var customPickerDialogOpened by remember { mutableStateOf(false) }
val settingsRepository = remember { getSettingsRepository() }
@ -101,7 +101,7 @@ class ColorBottomSheet : Screen {
?.also {
it.invoke(value.first ?: Unit)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
} else {
customPickerDialogOpened = true
}
@ -149,7 +149,7 @@ class ColorBottomSheet : Screen {
?.also {
it.invoke(color)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
}
)
}

View File

@ -14,11 +14,11 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiFontFamily
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toReadableName
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
@ -39,7 +39,7 @@ class FontFamilyBottomSheet(
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column(
modifier = Modifier
@ -80,7 +80,7 @@ class FontFamilyBottomSheet(
?.also {
it.invoke(value)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {

View File

@ -14,12 +14,12 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.FontScale
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.scaleFactor
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toReadableName
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
@ -41,7 +41,7 @@ class FontScaleBottomSheet(
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column(
modifier = Modifier
@ -82,7 +82,7 @@ class FontScaleBottomSheet(
?.also {
it.invoke(value.scaleFactor)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {

View File

@ -14,9 +14,9 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
@ -27,7 +27,7 @@ import dev.icerock.moko.resources.compose.stringResource
class InboxTypeSheet : Screen {
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column(
@ -66,7 +66,7 @@ class InboxTypeSheet : Screen {
?.also {
it.invoke(true)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {
@ -88,7 +88,7 @@ class InboxTypeSheet : Screen {
?.also {
it.invoke(false)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {

View File

@ -14,9 +14,9 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
@ -29,7 +29,7 @@ class LanguageBottomSheet : Screen {
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column(
modifier = Modifier
@ -80,7 +80,7 @@ class LanguageBottomSheet : Screen {
?.also {
it.invoke(value)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {

View File

@ -17,9 +17,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
@ -35,8 +35,9 @@ class ListingTypeBottomSheet(
) : Screen {
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column(
modifier = Modifier
.padding(
@ -81,7 +82,7 @@ class ListingTypeBottomSheet(
NotificationCenterContractKeys.ChangeFeedType
)
?.invoke(value)
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {

View File

@ -14,11 +14,11 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toReadableName
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
@ -30,8 +30,9 @@ class PostLayoutBottomSheet : Screen {
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column(
modifier = Modifier.padding(
top = Spacing.s,
@ -71,7 +72,7 @@ class PostLayoutBottomSheet : Screen {
?.also {
it.invoke(value)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {

View File

@ -43,7 +43,7 @@ fun RawContentDialog(
onDismiss: (() -> Unit)? = null,
) {
val uriHandler = LocalUriHandler.current
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val settingsRepository = remember { getSettingsRepository() }
val clipboardManager = LocalClipboardManager.current
val onSearchLambda = {
@ -53,7 +53,7 @@ fun RawContentDialog(
url = url,
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
uriHandler = uriHandler,
navigator = navigator
navigator = navigationCoordinator.getRootNavigator()
)
}

View File

@ -24,10 +24,10 @@ import androidx.compose.ui.graphics.ColorFilter
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
@ -83,7 +83,7 @@ internal class SortBottomSheetMain(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column(
horizontalAlignment = Alignment.CenterHorizontally
@ -118,7 +118,7 @@ internal class SortBottomSheetMain(
?.also {
it.invoke(value)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
}
},
),
@ -163,7 +163,7 @@ internal class SortBottomSheetTop(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column {
@ -205,7 +205,7 @@ internal class SortBottomSheetTop(
?.also {
it.invoke(value)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {

View File

@ -17,12 +17,12 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toIcon
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toReadableName
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
@ -34,7 +34,7 @@ class ThemeBottomSheet : Screen {
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
Column(
modifier = Modifier
@ -80,7 +80,7 @@ class ThemeBottomSheet : Screen {
?.also {
it.invoke(value)
}
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
},
),
) {

View File

@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator
import cafe.adriel.voyager.navigator.tab.Tab
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
@ -17,6 +18,7 @@ internal class DefaultNavigationCoordinator : NavigationCoordinator {
private var connection: NestedScrollConnection? = null
private var navigator: Navigator? = null
private var bottomNavigator: BottomSheetNavigator? = null
private var currentTab: Tab? = null
private val scope = CoroutineScope(SupervisorJob())
private var canGoBackCallback: (() -> Boolean)? = null
@ -60,4 +62,12 @@ internal class DefaultNavigationCoordinator : NavigationCoordinator {
override fun setInboxUnread(count: Int) {
inboxUnread.value = count
}
override fun setBottomNavigator(value: BottomSheetNavigator?) {
bottomNavigator = value
}
override fun getBottomNavigator(): BottomSheetNavigator? {
return bottomNavigator
}
}

View File

@ -1,11 +1,14 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation
import androidx.compose.runtime.Stable
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator
import cafe.adriel.voyager.navigator.tab.Tab
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@Stable
interface NavigationCoordinator {
val onDoubleTabSelection: Flow<Tab>
@ -29,4 +32,8 @@ interface NavigationCoordinator {
fun getBottomBarScrollConnection(): NestedScrollConnection?
fun setInboxUnread(count: Int)
fun setBottomNavigator(value: BottomSheetNavigator?)
fun getBottomNavigator(): BottomSheetNavigator?
}

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
@ -7,6 +8,7 @@ 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
@Stable
interface PostDetailMviModel :
MviModel<PostDetailMviModel.Intent, PostDetailMviModel.UiState, PostDetailMviModel.Effect>,
ScreenModel {

View File

@ -22,7 +22,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
@ -71,7 +71,6 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
@ -97,6 +96,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateRepor
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallbackArgs
@ -136,21 +136,12 @@ class PostDetailScreen(
val uiState by model.uiState.collectAsState()
val isOnOtherInstance = otherInstance.isNotEmpty()
val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val notificationCenter = remember { getNotificationCenter() }
val drawerCoordinator = remember { getDrawerCoordinator() }
DisposableEffect(key) {
drawerCoordinator.setGesturesEnabled(false)
onDispose {
notificationCenter.removeObserver(key)
drawerCoordinator.setGesturesEnabled(true)
}
}
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
val downvoteColor by themeRepository.downvoteColor.collectAsState()
@ -159,12 +150,91 @@ class PostDetailScreen(
val lazyListState = rememberLazyListState()
val scope = rememberCoroutineScope()
var rawContent by remember { mutableStateOf<Any?>(null) }
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
DisposableEffect(key) {
drawerCoordinator.setGesturesEnabled(false)
onDispose {
notificationCenter.removeObserver(key)
drawerCoordinator.setGesturesEnabled(true)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
PostDetailMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
model.reduce(PostDetailMviModel.Intent.RefreshPost)
}, key, NotificationCenterContractKeys.CommentCreated
)
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
model.reduce(PostDetailMviModel.Intent.RefreshPost)
}, key, NotificationCenterContractKeys.CommentCreated
)
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.RefreshPost)
}, key, NotificationCenterContractKeys.PostCreated
)
notificationCenter.addObserver(
{
model.reduce(
PostDetailMviModel.Intent.Refresh
)
model.reduce(
PostDetailMviModel.Intent.RefreshPost
)
},
key,
NotificationCenterContractKeys.CommentCreated
)
notificationCenter.addObserver(
{
model.reduce(
PostDetailMviModel.Intent.Refresh
)
model.reduce(
PostDetailMviModel.Intent.RefreshPost
)
},
key,
NotificationCenterContractKeys.CommentCreated
)
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
model.reduce(PostDetailMviModel.Intent.RefreshPost)
},
key,
NotificationCenterContractKeys.CommentCreated
)
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
model.reduce(PostDetailMviModel.Intent.RefreshPost)
},
key,
NotificationCenterContractKeys.CommentCreated
)
}
LaunchedEffect(model) {
model.effects.onEach { evt ->
when (evt) {
PostDetailMviModel.Effect.Close -> {
navigator?.pop()
navigationCoordinator.getRootNavigator()?.pop()
}
is PostDetailMviModel.Effect.ScrollToComment -> {
@ -183,8 +253,6 @@ class PostDetailScreen(
val statePost = uiState.post
Scaffold(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(fabNestedScrollConnection)
.background(MaterialTheme.colorScheme.background)
.padding(Spacing.xs),
topBar = {
@ -211,16 +279,7 @@ class PostDetailScreen(
SortType.Controversial,
),
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
PostDetailMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
),
imageVector = uiState.sortType.toIcon(),
@ -229,6 +288,7 @@ class PostDetailScreen(
)
},
navigationIcon = {
val navigator = navigationCoordinator.getRootNavigator()
if (navigator?.canPop == true) {
Image(
modifier = Modifier.onClick(
@ -258,7 +318,7 @@ class PostDetailScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ExpandLess,
text = stringResource(MR.strings.action_back_to_top),
onSelected = {
onSelected = rememberCallback {
scope.launch {
lazyListState.scrollToItem(0)
topAppBarState.heightOffset = 0f
@ -270,15 +330,11 @@ class PostDetailScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.Reply,
text = stringResource(MR.strings.action_reply),
onSelected = {
onSelected = rememberCallback {
val screen = CreateCommentScreen(
originalPost = statePost,
)
notificationCenter.addObserver({
model.reduce(PostDetailMviModel.Intent.Refresh)
model.reduce(PostDetailMviModel.Intent.RefreshPost)
}, key, NotificationCenterContractKeys.CommentCreated)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()?.show(screen)
},
)
}
@ -295,6 +351,14 @@ class PostDetailScreen(
)
Box(
modifier = Modifier.padding(padding)
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
}
.nestedScroll(fabNestedScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
@ -310,12 +374,12 @@ class PostDetailScreen(
autoLoadImages = uiState.autoLoadImages,
blurNsfw = false,
onOpenCommunity = rememberCallbackArgs { community ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community = community)
)
},
onOpenCreator = rememberCallbackArgs { user ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(user = user)
)
},
@ -350,11 +414,7 @@ class PostDetailScreen(
val screen = CreateCommentScreen(
originalPost = statePost,
)
notificationCenter.addObserver({
model.reduce(PostDetailMviModel.Intent.Refresh)
model.reduce(PostDetailMviModel.Intent.RefreshPost)
}, key, NotificationCenterContractKeys.CommentCreated)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()?.show(screen)
}
},
options = buildList {
@ -371,12 +431,7 @@ class PostDetailScreen(
4 -> model.reduce(PostDetailMviModel.Intent.DeletePost)
3 -> {
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.RefreshPost)
}, key, NotificationCenterContractKeys.PostCreated
)
bottomSheetNavigator.show(
navigationCoordinator.getBottomNavigator()?.show(
CreatePostScreen(
editedPost = statePost,
)
@ -384,7 +439,9 @@ class PostDetailScreen(
}
2 -> {
bottomSheetNavigator.show(CreateReportScreen(postId = statePost.id))
navigationCoordinator.getBottomNavigator()?.show(
CreateReportScreen(postId = statePost.id)
)
}
1 -> {
@ -395,7 +452,7 @@ class PostDetailScreen(
}
},
onImageClick = rememberCallbackArgs { url ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
ZoomableImageScreen(url),
)
},
@ -441,11 +498,12 @@ class PostDetailScreen(
id = crossPost.id,
community = community,
)
navigator?.push(
PostDetailScreen(
post = post,
navigationCoordinator.getRootNavigator()
?.push(
PostDetailScreen(
post = post,
)
)
)
},
),
text = string,
@ -465,7 +523,7 @@ class PostDetailScreen(
)
}
}
itemsIndexed(uiState.comments, key = { _, c -> c.id }) { _, comment ->
items(uiState.comments, key = { c -> c.id }) { comment ->
val commentId = comment.id
AnimatedVisibility(
visible = comment.visible,
@ -586,41 +644,34 @@ class PostDetailScreen(
originalPost = statePost,
originalComment = comment,
)
notificationCenter.addObserver(
{
model.reduce(
PostDetailMviModel.Intent.Refresh
)
model.reduce(
PostDetailMviModel.Intent.RefreshPost
)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()
?.show(
screen
)
}
},
onOpenCreator = rememberCallbackArgs {
val user = comment.creator
if (user != null) {
navigator?.push(
UserDetailScreen(
user = user,
otherInstance = otherInstance,
),
)
navigationCoordinator.getRootNavigator()
?.push(
UserDetailScreen(
user = user,
otherInstance = otherInstance,
),
)
}
},
onOpenCommunity = rememberCallbackArgs {
val community = comment.community
if (community != null) {
navigator?.push(
CommunityDetailScreen(
community = community,
otherInstance = otherInstance,
),
)
navigationCoordinator.getRootNavigator()
?.push(
CommunityDetailScreen(
community = community,
otherInstance = otherInstance,
),
)
}
},
options = buildList {
@ -642,31 +693,21 @@ class PostDetailScreen(
)
2 -> {
notificationCenter.addObserver(
{
model.reduce(
PostDetailMviModel.Intent.Refresh
navigationCoordinator.getBottomNavigator()
?.show(
CreateCommentScreen(
editedComment = comment,
)
model.reduce(
PostDetailMviModel.Intent.RefreshPost
)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(
CreateCommentScreen(
editedComment = comment,
)
)
}
1 -> {
bottomSheetNavigator.show(
CreateReportScreen(
commentId = comment.id
navigationCoordinator.getBottomNavigator()
?.show(
CreateReportScreen(
commentId = comment.id
)
)
)
}
else -> {
@ -723,26 +764,20 @@ class PostDetailScreen(
originalPost = statePost,
originalComment = comment,
)
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
model.reduce(PostDetailMviModel.Intent.RefreshPost)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()
?.show(screen)
}
},
onOpenCreator = rememberCallbackArgs {
val user = comment.creator
if (user != null) {
navigator?.push(
UserDetailScreen(
user = user,
otherInstance = otherInstance,
),
)
navigationCoordinator.getRootNavigator()
?.push(
UserDetailScreen(
user = user,
otherInstance = otherInstance,
),
)
}
},
options = buildList {
@ -762,27 +797,21 @@ class PostDetailScreen(
)
2 -> {
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
model.reduce(PostDetailMviModel.Intent.RefreshPost)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(
CreateCommentScreen(
editedComment = comment,
navigationCoordinator.getBottomNavigator()
?.show(
CreateCommentScreen(
editedComment = comment,
)
)
)
}
1 -> {
bottomSheetNavigator.show(
CreateReportScreen(
commentId = comment.id
navigationCoordinator.getBottomNavigator()
?.show(
CreateReportScreen(
commentId = comment.id
)
)
)
}
else -> {

View File

@ -37,19 +37,21 @@ class PostDetailViewModel(
private val shareHelper: ShareHelper,
private val notificationCenter: NotificationCenter,
private val hapticFeedback: HapticFeedback,
) : MviModel<PostDetailMviModel.Intent, PostDetailMviModel.UiState, PostDetailMviModel.Effect> by mvi,
PostDetailMviModel {
) : PostDetailMviModel,
MviModel<PostDetailMviModel.Intent, PostDetailMviModel.UiState, PostDetailMviModel.Effect> by mvi {
private var currentPage: Int = 1
private var highlightCommentPath: String? = null
private var commentWasHighlighted = false
init {
notificationCenter.addObserver({
(it as? PostModel)?.also { post ->
handlePostUpdate(post)
}
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdated)
notificationCenter.addObserver(
{
(it as? PostModel)?.also { post ->
handlePostUpdate(post)
}
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdated
)
}
fun finalize() {

View File

@ -1,9 +1,11 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.report
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import dev.icerock.moko.resources.desc.StringDesc
@Stable
interface CreateReportMviModel :
MviModel<CreateReportMviModel.Intent, CreateReportMviModel.UiState, CreateReportMviModel.Effect>,
ScreenModel {

View File

@ -34,13 +34,12 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
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.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.ProgressHud
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getCreateReportViewModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.localized
import dev.icerock.moko.resources.compose.stringResource
@ -64,8 +63,7 @@ class CreateReportScreen(
val uiState by model.uiState.collectAsState()
val snackbarHostState = remember { SnackbarHostState() }
val genericError = stringResource(MR.strings.message_generic_error)
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val notificationCenter = remember { getNotificationCenter() }
val navigationCoordinator = remember { getNavigationCoordinator() }
LaunchedEffect(model) {
model.effects.onEach {
@ -75,7 +73,7 @@ class CreateReportScreen(
}
CreateReportMviModel.Effect.Success -> {
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
}
}
}.launchIn(this)

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
@ -8,6 +9,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
@Stable
interface SavedItemsMviModel :
MviModel<SavedItemsMviModel.Intent, SavedItemsMviModel.UiState, SavedItemsMviModel.Effect>,
ScreenModel {
@ -17,13 +19,13 @@ interface SavedItemsMviModel :
data object LoadNextPage : Intent
data class ChangeSort(val value: SortType) : Intent
data class ChangeSection(val section: SavedItemsSection) : 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 SharePost(val index: Int) : 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 UpVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class DownVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class SavePost(val id: Int, val feedback: Boolean = false) : Intent
data class SharePost(val id: Int) : Intent
data class UpVoteComment(val id: Int, val feedback: Boolean = false) : Intent
data class DownVoteComment(val id: Int, val feedback: Boolean = false) : Intent
data class SaveComment(val id: Int, val feedback: Boolean = false) : Intent
}
data class UiState(

View File

@ -13,7 +13,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
@ -33,6 +33,7 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -47,7 +48,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
@ -70,6 +70,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateRepor
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel
@ -88,8 +89,7 @@ class SavedItemsScreen : Screen {
val model = rememberScreenModel { getSavedItemsViewModel() }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigatorCoordinator = remember { getNavigationCoordinator() }
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val notificationCenter = remember { getNotificationCenter() }
@ -99,18 +99,31 @@ class SavedItemsScreen : Screen {
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val drawerCoordinator = remember { getDrawerCoordinator() }
var rawContent by remember { mutableStateOf<Any?>(null) }
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
DisposableEffect(key) {
notificationCenter.removeObserver(key)
drawerCoordinator.setGesturesEnabled(false)
onDispose {
drawerCoordinator.setGesturesEnabled(true)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
SavedItemsMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
}
Scaffold(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(fabNestedScrollConnection),
topBar = {
TopAppBar(
scrollBehavior = scrollBehavior,
@ -131,16 +144,7 @@ class SavedItemsScreen : Screen {
SortType.Old,
),
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
SavedItemsMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigatorCoordinator.getBottomNavigator()?.show(sheet)
},
),
imageVector = uiState.sortType.toIcon(),
@ -152,7 +156,7 @@ class SavedItemsScreen : Screen {
Image(
modifier = Modifier.onClick(
rememberCallback {
navigator?.pop()
navigatorCoordinator.getRootNavigator()?.pop()
},
),
imageVector = Icons.Default.ArrowBack,
@ -177,7 +181,7 @@ class SavedItemsScreen : Screen {
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ExpandLess,
text = stringResource(MR.strings.action_back_to_top),
onSelected = {
onSelected = rememberCallback {
scope.launch {
lazyListState.scrollToItem(0)
topAppBarState.heightOffset = 0f
@ -191,7 +195,15 @@ class SavedItemsScreen : Screen {
},
) { paddingValues ->
Column(
modifier = Modifier.padding(paddingValues),
modifier = Modifier.padding(paddingValues)
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
}
.nestedScroll(fabNestedScrollConnection),
verticalArrangement = Arrangement.spacedBy(Spacing.s),
) {
SectionSelector(
@ -227,7 +239,7 @@ class SavedItemsScreen : Screen {
modifier = Modifier.padding(horizontal = Spacing.xxxs),
) {
if (uiState.section == SavedItemsSection.Posts) {
itemsIndexed(uiState.posts) { idx, post ->
items(uiState.posts) { post ->
PostCard(
post = post,
postLayout = uiState.postLayout,
@ -236,24 +248,25 @@ class SavedItemsScreen : Screen {
autoLoadImages = uiState.autoLoadImages,
blurNsfw = uiState.blurNsfw,
onClick = {
navigator?.push(
navigatorCoordinator.getRootNavigator()?.push(
PostDetailScreen(post),
)
},
onOpenCommunity = { community ->
navigator?.push(
navigatorCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community),
)
},
onOpenCreator = { u ->
if (u.id != uiState.user?.id) {
navigator?.push(UserDetailScreen(u))
navigatorCoordinator.getRootNavigator()
?.push(UserDetailScreen(u))
}
},
onUpVote = {
model.reduce(
SavedItemsMviModel.Intent.UpVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -261,7 +274,7 @@ class SavedItemsScreen : Screen {
onDownVote = {
model.reduce(
SavedItemsMviModel.Intent.DownVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -269,7 +282,7 @@ class SavedItemsScreen : Screen {
onSave = {
model.reduce(
SavedItemsMviModel.Intent.SavePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -278,10 +291,10 @@ class SavedItemsScreen : Screen {
val screen = CreateCommentScreen(
originalPost = post,
)
bottomSheetNavigator.show(screen)
navigatorCoordinator.getBottomNavigator()?.show(screen)
},
onImageClick = { url ->
navigator?.push(
navigatorCoordinator.getRootNavigator()?.push(
ZoomableImageScreen(url),
)
},
@ -293,7 +306,7 @@ class SavedItemsScreen : Screen {
onOptionSelected = { optionIndex ->
when (optionIndex) {
2 -> {
bottomSheetNavigator.show(
navigatorCoordinator.getBottomNavigator()?.show(
CreateReportScreen(
postId = post.id
)
@ -305,7 +318,11 @@ class SavedItemsScreen : Screen {
}
else -> {
model.reduce(SavedItemsMviModel.Intent.SharePost(idx))
model.reduce(
SavedItemsMviModel.Intent.SharePost(
post.id
)
)
}
}
},
@ -330,14 +347,14 @@ class SavedItemsScreen : Screen {
}
}
} else {
itemsIndexed(uiState.comments) { idx, comment ->
items(uiState.comments) { comment ->
CommentCard(
comment = comment,
separateUpAndDownVotes = uiState.separateUpAndDownVotes,
autoLoadImages = uiState.autoLoadImages,
hideIndent = true,
onClick = {
navigator?.push(
navigatorCoordinator.getRootNavigator()?.push(
PostDetailScreen(
post = PostModel(id = comment.postId),
highlightCommentId = comment.id,
@ -347,7 +364,7 @@ class SavedItemsScreen : Screen {
onUpVote = {
model.reduce(
SavedItemsMviModel.Intent.UpVoteComment(
index = idx,
id = comment.id,
feedback = true,
),
)
@ -355,7 +372,7 @@ class SavedItemsScreen : Screen {
onDownVote = {
model.reduce(
SavedItemsMviModel.Intent.DownVoteComment(
index = idx,
id = comment.id,
feedback = true,
),
)
@ -363,7 +380,7 @@ class SavedItemsScreen : Screen {
onSave = {
model.reduce(
SavedItemsMviModel.Intent.SaveComment(
index = idx,
id = comment.id,
feedback = true,
),
)
@ -373,7 +390,7 @@ class SavedItemsScreen : Screen {
originalPost = PostModel(id = comment.postId),
originalComment = comment,
)
bottomSheetNavigator.show(screen)
navigatorCoordinator.getBottomNavigator()?.show(screen)
},
options = buildList {
add(stringResource(MR.strings.post_action_see_raw))
@ -382,7 +399,7 @@ class SavedItemsScreen : Screen {
onOptionSelected = { optionIndex ->
when (optionIndex) {
1 -> {
bottomSheetNavigator.show(
navigatorCoordinator.getBottomNavigator()?.show(
CreateReportScreen(
commentId = comment.id
)

View File

@ -41,11 +41,13 @@ class SavedItemsViewModel(
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 ->
handlePostUpdate(post)
}
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdated
)
}
fun finalize() {
@ -80,37 +82,39 @@ class SavedItemsViewModel(
SavedItemsMviModel.Intent.Refresh -> refresh()
is SavedItemsMviModel.Intent.ChangeSection -> changeSection(intent.section)
is SavedItemsMviModel.Intent.DownVoteComment -> toggleDownVoteComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is SavedItemsMviModel.Intent.DownVotePost -> toggleDownVotePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is SavedItemsMviModel.Intent.SaveComment -> toggleSaveComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is SavedItemsMviModel.Intent.SavePost -> toggleSavePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is SavedItemsMviModel.Intent.UpVoteComment -> toggleUpVoteComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is SavedItemsMviModel.Intent.UpVotePost -> toggleUpVotePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is SavedItemsMviModel.Intent.ChangeSort -> applySortType(intent.value)
is SavedItemsMviModel.Intent.SharePost -> share(post = uiState.value.posts[intent.index])
is SavedItemsMviModel.Intent.SharePost -> share(
post = uiState.value.posts.first { it.id == intent.id }
)
}
}

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
@ -8,6 +9,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
@Stable
interface UserDetailMviModel :
MviModel<UserDetailMviModel.Intent, UserDetailMviModel.UiState, UserDetailMviModel.Effect>,
ScreenModel {
@ -17,14 +19,14 @@ interface UserDetailMviModel :
data object Refresh : Intent
data object LoadNextPage : Intent
data class ChangeSection(val section: UserDetailSection) : 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 UpVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class DownVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class SavePost(val id: Int, val feedback: Boolean = false) : Intent
data class UpVoteComment(val id: Int, val feedback: Boolean = false) : Intent
data class DownVoteComment(val id: Int, val feedback: Boolean = false) : Intent
data class SaveComment(val id: Int, val feedback: Boolean = false) : Intent
data object HapticIndication : Intent
data class SharePost(val index: Int) : Intent
data class SharePost(val id: Int) : Intent
data object Block : Intent
data object BlockInstance : Intent
}

View File

@ -14,7 +14,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
@ -59,7 +59,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
@ -88,6 +87,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDet
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportScreen
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallbackArgs
@ -121,8 +121,6 @@ class UserDetailScreen(
val genericError = stringResource(MR.strings.message_generic_error)
val successMessage = stringResource(MR.strings.message_operation_successful)
val isOnOtherInstance = otherInstance.isNotEmpty()
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val notificationCenter = remember { getNotificationCenter() }
@ -133,8 +131,11 @@ class UserDetailScreen(
val downvoteColor by themeRepository.downvoteColor.collectAsState()
val defaultUpvoteColor = MaterialTheme.colorScheme.primary
val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary
val navigationCoordinator = remember { getNavigationCoordinator() }
val drawerCoordinator = remember { getDrawerCoordinator() }
var rawContent by remember { mutableStateOf<Any?>(null) }
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
DisposableEffect(key) {
drawerCoordinator.setGesturesEnabled(false)
@ -143,6 +144,33 @@ class UserDetailScreen(
drawerCoordinator.setGesturesEnabled(true)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
UserDetailMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
notificationCenter.addObserver(
{
model.reduce(UserDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
notificationCenter.addObserver(
{
model.reduce(UserDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
}
LaunchedEffect(model) {
model.effects.onEach {
when (it) {
@ -166,8 +194,6 @@ class UserDetailScreen(
Scaffold(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(fabNestedScrollConnection)
.padding(Spacing.xs),
topBar = {
val userName = user.name
@ -194,16 +220,7 @@ class UserDetailScreen(
val sheet = SortBottomSheet(
expandTop = true,
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
UserDetailMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
),
imageVector = uiState.sortType.toIcon(),
@ -212,6 +229,7 @@ class UserDetailScreen(
)
},
navigationIcon = {
val navigator = navigationCoordinator.getRootNavigator()
if (navigator?.canPop == true) {
Image(
modifier = Modifier.onClick(
@ -242,7 +260,7 @@ class UserDetailScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ExpandLess,
text = stringResource(MR.strings.action_back_to_top),
onSelected = {
onSelected = rememberCallback {
scope.launch {
lazyListState.scrollToItem(0)
topAppBarState.heightOffset = 0f
@ -254,9 +272,9 @@ class UserDetailScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.Chat,
text = stringResource(MR.strings.action_chat),
onSelected = {
onSelected = rememberCallback {
val screen = InboxChatScreen(otherUserId = user.id)
navigator?.push(screen)
navigationCoordinator.getRootNavigator()?.push(screen)
},
)
}
@ -277,6 +295,14 @@ class UserDetailScreen(
Box(
modifier = Modifier
.padding(padding)
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
}
.nestedScroll(fabNestedScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
@ -295,7 +321,8 @@ class UserDetailScreen(
stringResource(MR.strings.community_detail_block_instance),
),
onOpenImage = rememberCallbackArgs { url ->
navigator?.push(ZoomableImageScreen(url))
navigationCoordinator.getRootNavigator()
?.push(ZoomableImageScreen(url))
},
onOptionSelected = rememberCallbackArgs { optionIdx ->
when (optionIdx) {
@ -337,7 +364,7 @@ class UserDetailScreen(
}
}
}
itemsIndexed(uiState.posts) { idx, post ->
items(uiState.posts) { post ->
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled,
@ -377,12 +404,12 @@ class UserDetailScreen(
},
onDismissToStart = rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.UpVotePost(idx),
UserDetailMviModel.Intent.UpVotePost(post.id),
)
},
onDismissToEnd = rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.DownVotePost(idx),
UserDetailMviModel.Intent.DownVotePost(post.id),
)
},
content = {
@ -395,7 +422,8 @@ class UserDetailScreen(
separateUpAndDownVotes = uiState.separateUpAndDownVotes,
autoLoadImages = uiState.autoLoadImages,
onClick = rememberCallback {
navigator?.push(PostDetailScreen(post = post))
navigationCoordinator.getRootNavigator()
?.push(PostDetailScreen(post = post))
},
onUpVote = if (isOnOtherInstance) {
null
@ -403,7 +431,7 @@ class UserDetailScreen(
rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.UpVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -415,7 +443,7 @@ class UserDetailScreen(
rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.DownVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -427,14 +455,15 @@ class UserDetailScreen(
rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.SavePost(
index = idx,
id = post.id,
feedback = true,
),
)
}
},
onOpenCommunity = rememberCallbackArgs { community ->
navigator?.push(CommunityDetailScreen(community))
navigationCoordinator.getRootNavigator()
?.push(CommunityDetailScreen(community))
},
onReply = if (isOnOtherInstance) {
null
@ -443,18 +472,12 @@ class UserDetailScreen(
val screen = CreateCommentScreen(
originalPost = post,
)
notificationCenter.addObserver(
{
model.reduce(UserDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()
?.show(screen)
}
},
onImageClick = rememberCallbackArgs { url ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
ZoomableImageScreen(url),
)
},
@ -466,11 +489,12 @@ class UserDetailScreen(
onOptionSelected = rememberCallbackArgs { optionIdx ->
when (optionIdx) {
2 -> {
bottomSheetNavigator.show(
CreateReportScreen(
postId = post.id
navigationCoordinator.getBottomNavigator()
?.show(
CreateReportScreen(
postId = post.id
)
)
)
}
1 -> {
@ -478,7 +502,7 @@ class UserDetailScreen(
}
else -> model.reduce(
UserDetailMviModel.Intent.SharePost(idx)
UserDetailMviModel.Intent.SharePost(post.id)
)
}
})
@ -512,7 +536,7 @@ class UserDetailScreen(
)
}
}
itemsIndexed(uiState.comments) { idx, comment ->
items(uiState.comments) { comment ->
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled,
@ -551,12 +575,12 @@ class UserDetailScreen(
},
onDismissToStart = rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.UpVoteComment(idx),
UserDetailMviModel.Intent.UpVoteComment(comment.id),
)
},
onDismissToEnd = rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.DownVoteComment(idx),
UserDetailMviModel.Intent.DownVoteComment(comment.id),
)
},
content = {
@ -569,7 +593,7 @@ class UserDetailScreen(
hideAuthor = true,
hideIndent = true,
onClick = rememberCallback {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(
post = PostModel(id = comment.postId),
highlightCommentId = comment.id,
@ -582,7 +606,7 @@ class UserDetailScreen(
rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.SaveComment(
index = idx,
id = comment.id,
feedback = true,
),
)
@ -594,7 +618,7 @@ class UserDetailScreen(
rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.UpVoteComment(
index = idx,
id = comment.id,
feedback = true,
),
)
@ -606,7 +630,7 @@ class UserDetailScreen(
rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.DownVoteComment(
index = idx,
id = comment.id,
feedback = true,
),
)
@ -620,18 +644,13 @@ class UserDetailScreen(
originalPost = PostModel(id = comment.postId),
originalComment = comment,
)
notificationCenter.addObserver(
{
model.reduce(UserDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()
?.show(screen)
}
},
onOpenCommunity = rememberCallbackArgs { community ->
navigator?.push(CommunityDetailScreen(community))
navigationCoordinator.getRootNavigator()
?.push(CommunityDetailScreen(community))
},
options = buildList {
add(stringResource(MR.strings.post_action_see_raw))
@ -640,11 +659,12 @@ class UserDetailScreen(
onOptionSelected = rememberCallbackArgs { optionId ->
when (optionId) {
1 -> {
bottomSheetNavigator.show(
CreateReportScreen(
commentId = comment.id
navigationCoordinator.getBottomNavigator()
?.show(
CreateReportScreen(
commentId = comment.id
)
)
)
}
else -> {

View File

@ -45,11 +45,13 @@ class UserDetailViewModel(
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 ->
handlePostUpdate(post)
}
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdated
)
}
fun finalize() {
@ -92,13 +94,13 @@ class UserDetailViewModel(
is UserDetailMviModel.Intent.ChangeSort -> applySortType(intent.value)
is UserDetailMviModel.Intent.ChangeSection -> changeSection(intent.section)
is UserDetailMviModel.Intent.DownVoteComment -> toggleDownVoteComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is UserDetailMviModel.Intent.DownVotePost -> {
toggleDownVote(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
}
@ -107,27 +109,27 @@ class UserDetailViewModel(
UserDetailMviModel.Intent.LoadNextPage -> loadNextPage()
UserDetailMviModel.Intent.Refresh -> refresh()
is UserDetailMviModel.Intent.SaveComment -> toggleSaveComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is UserDetailMviModel.Intent.SavePost -> toggleSave(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is UserDetailMviModel.Intent.UpVoteComment -> toggleUpVoteComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is UserDetailMviModel.Intent.UpVotePost -> toggleUpVote(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is UserDetailMviModel.Intent.SharePost -> share(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
)
UserDetailMviModel.Intent.Block -> blockUser()

View File

@ -33,7 +33,7 @@ class WebViewScreen(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun Content() {
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
var shareHelper = remember { getShareHelper() }
val drawerCoordinator = remember { getDrawerCoordinator() }
@ -54,7 +54,7 @@ class WebViewScreen(
Image(
modifier = Modifier.onClick(
rememberCallback {
navigator?.pop()
navigationCoordinator.getRootNavigator()?.pop()
},
),
imageVector = Icons.Default.ArrowBack,

View File

@ -1,10 +1,12 @@
package com.github.diegoberaldin.raccoonforlemmy.core.notifications
import androidx.compose.runtime.Stable
import kotlinx.coroutines.flow.SharedFlow
/**
* Utility to publish and subscribe for broadcast notifications.
*/
@Stable
interface NotificationCenter {
/**

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.feature.home.postlist
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
@ -7,6 +8,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
@Stable
interface PostListMviModel :
MviModel<PostListMviModel.Intent, PostListMviModel.UiState, PostListMviModel.Effect>,
ScreenModel {
@ -16,15 +18,15 @@ interface PostListMviModel :
data object LoadNextPage : Intent
data class ChangeSort(val value: SortType) : Intent
data class ChangeListing(val value: ListingType) : 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 UpVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class DownVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class SavePost(val id: Int, val feedback: Boolean = false) : Intent
data class HandlePostUpdate(val post: PostModel) : Intent
data object HapticIndication : Intent
data class DeletePost(val id: Int) : Intent
data class SharePost(val index: Int) : Intent
data class MarkAsRead(val index: Int) : Intent
data class Hide(val index: Int) : Intent
data class SharePost(val id: Int) : Intent
data class MarkAsRead(val id: Int) : Intent
data class Hide(val id: Int) : Intent
data object ClearRead : Intent
}

View File

@ -10,7 +10,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
@ -49,7 +49,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Dimensions
@ -61,7 +60,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Floatin
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCardPlaceholder
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SwipeableCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.createcomment.CreateCommentScreen
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.createpost.CreatePostScreen
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getDrawerCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getFabNestedScrollConnection
@ -97,13 +95,11 @@ class PostListScreen : Screen {
val model = rememberScreenModel { getHomeScreenModel() }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val notificationCenter = remember { getNotificationCenter() }
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
@ -122,7 +118,7 @@ class PostListScreen : Screen {
notificationCenter.removeObserver(key)
}
}
LaunchedEffect(navigator) {
LaunchedEffect(Unit) {
navigationCoordinator.onDoubleTabSelection.onEach { tab ->
if (tab == HomeTab) {
lazyListState.scrollToItem(0)
@ -140,20 +136,41 @@ class PostListScreen : Screen {
}
}.launchIn(this)
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{ result ->
(result as? ListingType)?.also {
model.reduce(PostListMviModel.Intent.ChangeListing(it))
}
}, key, NotificationCenterContractKeys.ChangeFeedType
)
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
PostListMviModel.Intent.ChangeSort(sortType)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
notificationCenter.addObserver(
{
model.reduce(PostListMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
notificationCenter.addObserver(
{
model.reduce(PostListMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.PostCreated
)
}
Scaffold(
modifier = Modifier
.padding(Spacing.xxs)
.let {
val connection = navigationCoordinator.getBottomBarScrollConnection()
if (connection != null && settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(connection)
} else {
it
}
}
.nestedScroll(fabNestedScrollConnection)
.nestedScroll(scrollBehavior.nestedScrollConnection),
modifier = Modifier.padding(Spacing.xxs),
topBar = {
PostsTopBar(
currentInstance = uiState.instance,
@ -169,25 +186,13 @@ class PostListScreen : Screen {
val sheet = ListingTypeBottomSheet(
isLogged = uiState.isLogged,
)
notificationCenter.addObserver({ result ->
(result as? ListingType)?.also {
model.reduce(PostListMviModel.Intent.ChangeListing(it))
}
}, key, NotificationCenterContractKeys.ChangeFeedType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
onSelectSortType = rememberCallback {
val sheet = SortBottomSheet(
expandTop = true,
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
PostListMviModel.Intent.ChangeSort(sortType)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
},
@ -207,7 +212,7 @@ class PostListScreen : Screen {
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ExpandLess,
text = stringResource(MR.strings.action_back_to_top),
onSelected = {
onSelected = rememberCallback {
scope.launch {
lazyListState.scrollToItem(0)
topAppBarState.heightOffset = 0f
@ -218,7 +223,7 @@ class PostListScreen : Screen {
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ClearAll,
text = stringResource(MR.strings.action_clear_read),
onSelected = {
onSelected = rememberCallback {
model.reduce(PostListMviModel.Intent.ClearRead)
scope.launch {
lazyListState.scrollToItem(0)
@ -241,6 +246,22 @@ class PostListScreen : Screen {
modifier = Modifier
.padding(padding)
.fillMaxWidth()
.let {
val connection = navigationCoordinator.getBottomBarScrollConnection()
if (connection != null && settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(connection)
} else {
it
}
}
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
}
.nestedScroll(fabNestedScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
@ -258,7 +279,7 @@ class PostListScreen : Screen {
}
}
}
itemsIndexed(uiState.posts) { idx, post ->
items(uiState.posts) { post ->
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled,
@ -273,14 +294,14 @@ class PostListScreen : Screen {
DismissValue.Default -> Color.Transparent
}
},
onGestureBegin = rememberCallback(model) {
onGestureBegin = {
model.reduce(PostListMviModel.Intent.HapticIndication)
},
onDismissToStart = rememberCallback(model) {
model.reduce(PostListMviModel.Intent.UpVotePost(idx))
model.reduce(PostListMviModel.Intent.UpVotePost(post.id))
},
onDismissToEnd = rememberCallback(model) {
model.reduce(PostListMviModel.Intent.DownVotePost(idx))
model.reduce(PostListMviModel.Intent.DownVotePost(post.id))
},
swipeContent = { direction ->
val icon = when (direction) {
@ -302,25 +323,25 @@ class PostListScreen : Screen {
autoLoadImages = uiState.autoLoadImages,
blurNsfw = uiState.blurNsfw,
onClick = rememberCallback(model) {
model.reduce(PostListMviModel.Intent.MarkAsRead(idx))
navigator?.push(
model.reduce(PostListMviModel.Intent.MarkAsRead(post.id))
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(post),
)
},
onOpenCommunity = rememberCallbackArgs { community ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community),
)
},
onOpenCreator = rememberCallbackArgs { user ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(user),
)
},
onUpVote = rememberCallback(model) {
model.reduce(
PostListMviModel.Intent.UpVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -328,7 +349,7 @@ class PostListScreen : Screen {
onDownVote = rememberCallback(model) {
model.reduce(
PostListMviModel.Intent.DownVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -336,27 +357,22 @@ class PostListScreen : Screen {
onSave = rememberCallback(model) {
model.reduce(
PostListMviModel.Intent.SavePost(
index = idx,
id = post.id,
feedback = true,
),
)
},
onReply = rememberCallback(model) {
val screen = CreateCommentScreen(
originalPost = post,
)
notificationCenter.addObserver(
{
model.reduce(PostListMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
},
// onReply = rememberCallback(model) {
// val screen = CreateCommentScreen(
// originalPost = post,
// )
// bottomSheetNavigator.show(screen)
// },
onImageClick = rememberCallbackArgs(model) { url ->
model.reduce(PostListMviModel.Intent.MarkAsRead(idx))
navigator?.push(ZoomableImageScreen(url))
model.reduce(PostListMviModel.Intent.MarkAsRead(post.id))
navigationCoordinator.getRootNavigator()?.push(
ZoomableImageScreen(url)
)
},
options = buildList {
add(stringResource(MR.strings.post_action_share))
@ -375,32 +391,27 @@ class PostListScreen : Screen {
)
4 -> {
notificationCenter.addObserver(
{
model.reduce(PostListMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.PostCreated
)
bottomSheetNavigator.show(
CreatePostScreen(editedPost = post)
)
navigationCoordinator.getBottomNavigator()
?.show(
CreatePostScreen(editedPost = post)
)
}
3 -> {
bottomSheetNavigator.show(
CreateReportScreen(postId = post.id)
)
navigationCoordinator.getBottomNavigator()
?.show(
CreateReportScreen(postId = post.id)
)
}
2 -> {
rawContent = post
}
1 -> model.reduce(PostListMviModel.Intent.Hide(idx))
1 -> model.reduce(PostListMviModel.Intent.Hide(post.id))
else -> model.reduce(
PostListMviModel.Intent.SharePost(idx)
PostListMviModel.Intent.SharePost(post.id)
)
}
}

View File

@ -45,23 +45,31 @@ class PostListViewModel(
private var hideReadPosts = false
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)
notificationCenter.addObserver({
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout)
notificationCenter.addObserver({
// apply new feed and sort type
firstLoad = true
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.ResetContents)
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
)
notificationCenter.addObserver(
{
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout
)
notificationCenter.addObserver(
{
// apply new feed and sort type
firstLoad = true
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.ResetContents
)
}
fun finalize() {
@ -134,17 +142,17 @@ class PostListViewModel(
is PostListMviModel.Intent.ChangeSort -> applySortType(intent.value)
is PostListMviModel.Intent.ChangeListing -> applyListingType(intent.value)
is PostListMviModel.Intent.DownVotePost -> toggleDownVote(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is PostListMviModel.Intent.SavePost -> toggleSave(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is PostListMviModel.Intent.UpVotePost -> toggleUpVote(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
@ -152,15 +160,15 @@ class PostListViewModel(
is PostListMviModel.Intent.HandlePostUpdate -> handlePostUpdate(intent.post)
is PostListMviModel.Intent.DeletePost -> handlePostDelete(intent.id)
is PostListMviModel.Intent.SharePost -> {
share(post = uiState.value.posts[intent.index])
share(post = uiState.value.posts.first { it.id == intent.id })
}
is PostListMviModel.Intent.MarkAsRead -> {
markAsRead(post = uiState.value.posts[intent.index])
markAsRead(post = uiState.value.posts.first { it.id == intent.id })
}
PostListMviModel.Intent.ClearRead -> clearRead()
is PostListMviModel.Intent.Hide -> hide(post = uiState.value.posts[intent.index])
is PostListMviModel.Intent.Hide -> hide(post = uiState.value.posts.first { it.id == intent.id })
}
}

View File

@ -26,7 +26,6 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.CurrentScreen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.Tab
import cafe.adriel.voyager.navigator.tab.TabNavigator
@ -35,9 +34,11 @@ 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.di.getDrawerCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.InboxTypeSheet
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.di.getInboxViewModel
@ -66,16 +67,30 @@ object InboxScreen : Tab {
val model = rememberScreenModel { getInboxViewModel() }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val notificationCenter = remember { getNotificationCenter() }
val drawerCoordinator = remember { getDrawerCoordinator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val scope = rememberCoroutineScope()
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
DisposableEffect(key) {
onDispose {
notificationCenter.removeObserver(key)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{
(it as? Boolean)?.also { value ->
model.reduce(
InboxMviModel.Intent.ChangeUnreadOnly(value)
)
}
}, key, NotificationCenterContractKeys.ChangeInboxType
)
}
Scaffold(
modifier = Modifier.padding(Spacing.xxs),
@ -115,14 +130,7 @@ object InboxScreen : Tab {
modifier = Modifier.onClick(
rememberCallback {
val sheet = InboxTypeSheet()
notificationCenter.addObserver({
(it as? Boolean)?.also { value ->
model.reduce(
InboxMviModel.Intent.ChangeUnreadOnly(value)
)
}
}, key, NotificationCenterContractKeys.ChangeInboxType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
),
text = text,
@ -162,7 +170,13 @@ object InboxScreen : Tab {
Column(
modifier = Modifier
.padding(paddingValues)
.nestedScroll(scrollBehavior.nestedScrollConnection),
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
},
verticalArrangement = Arrangement.spacedBy(Spacing.s),
) {
SectionSelector(

View File

@ -1,10 +1,12 @@
package com.github.diegoberaldin.raccoonforlemmy.feature.inbox.mentions
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel
@Stable
interface InboxMentionsMviModel :
MviModel<InboxMentionsMviModel.Intent, InboxMentionsMviModel.UiState, InboxMentionsMviModel.Effect>,
ScreenModel {
@ -12,10 +14,10 @@ interface InboxMentionsMviModel :
sealed interface Intent {
data object Refresh : Intent
data object LoadNextPage : Intent
data class MarkAsRead(val read: Boolean, val index: Int) : Intent
data class MarkAsRead(val read: Boolean, val id: Int) : Intent
data object HapticIndication : Intent
data class UpVoteComment(val index: Int) : Intent
data class DownVoteComment(val index: Int) : Intent
data class UpVoteComment(val id: Int) : Intent
data class DownVoteComment(val id: Int) : Intent
}
data class UiState(

View File

@ -9,7 +9,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
@ -73,8 +73,8 @@ class InboxMentionsScreen : Tab {
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val lazyListState = rememberLazyListState()
LaunchedEffect(navigationCoordinator) {
navigationCoordinator.onDoubleTabSelection.onEach {
if (it == InboxTab) {
@ -129,7 +129,7 @@ class InboxMentionsScreen : Tab {
)
}
}
itemsIndexed(uiState.mentions) { idx, mention ->
items(uiState.mentions) { mention ->
val endColor = MaterialTheme.colorScheme.secondary
val startColor = MaterialTheme.colorScheme.tertiary
SwipeableCard(
@ -149,7 +149,7 @@ class InboxMentionsScreen : Tab {
model.reduce(
InboxMentionsMviModel.Intent.MarkAsRead(
read = true,
index = idx,
id = mention.id,
),
)
},
@ -157,7 +157,7 @@ class InboxMentionsScreen : Tab {
model.reduce(
InboxMentionsMviModel.Intent.MarkAsRead(
read = false,
index = idx,
id = mention.id,
),
)
},
@ -181,7 +181,7 @@ class InboxMentionsScreen : Tab {
autoLoadImages = uiState.autoLoadImages,
separateUpAndDownVotes = uiState.separateUpAndDownVotes,
onOpenPost = rememberCallbackArgs { post ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(
post = post,
highlightCommentId = mention.comment.id,
@ -189,20 +189,24 @@ class InboxMentionsScreen : Tab {
)
},
onOpenCreator = rememberCallbackArgs { user ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(user),
)
},
onOpenCommunity = rememberCallbackArgs { community ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community),
)
},
onUpVote = rememberCallbackArgs(model) {
model.reduce(InboxMentionsMviModel.Intent.UpVoteComment(idx))
model.reduce(InboxMentionsMviModel.Intent.UpVoteComment(mention.id))
},
onDownVote = rememberCallbackArgs(model) {
model.reduce(InboxMentionsMviModel.Intent.DownVoteComment(idx))
model.reduce(
InboxMentionsMviModel.Intent.DownVoteComment(
mention.id
)
)
},
)
},

View File

@ -36,9 +36,11 @@ class InboxMentionsViewModel(
private var currentPage: Int = 1
init {
notificationCenter.addObserver({
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout)
notificationCenter.addObserver(
{
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout
)
}
fun finalize() {
@ -81,18 +83,18 @@ class InboxMentionsViewModel(
is InboxMentionsMviModel.Intent.MarkAsRead -> {
markAsRead(
read = intent.read,
mention = mvi.uiState.value.mentions[intent.index],
mention = uiState.value.mentions.first { it.id == intent.id },
)
}
InboxMentionsMviModel.Intent.HapticIndication -> hapticFeedback.vibrate()
is InboxMentionsMviModel.Intent.DownVoteComment -> toggleDownVoteComment(
mention = mvi.uiState.value.mentions[intent.index],
mention = uiState.value.mentions.first { it.id == intent.id },
feedback = true,
)
is InboxMentionsMviModel.Intent.UpVoteComment -> toggleUpVoteComment(
mention = mvi.uiState.value.mentions[intent.index],
mention = uiState.value.mentions.first { it.id == intent.id },
feedback = true,
)
}

View File

@ -1,9 +1,11 @@
package com.github.diegoberaldin.raccoonforlemmy.feature.inbox.messages
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PrivateMessageModel
@Stable
interface InboxMessagesMviModel :
MviModel<InboxMessagesMviModel.Intent, InboxMessagesMviModel.UiState, InboxMessagesMviModel.Effect>,
ScreenModel {

View File

@ -36,6 +36,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.chat.InboxChatScre
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallbackArgs
import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.di.getInboxMessagesViewModel
import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.ui.InboxTab
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
@ -57,7 +58,6 @@ class InboxMessagesScreen : Tab {
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val lazyListState = rememberLazyListState()
LaunchedEffect(navigationCoordinator) {
navigationCoordinator.onDoubleTabSelection.onEach {
@ -117,15 +117,15 @@ class InboxMessagesScreen : Tab {
autoLoadImages = uiState.autoLoadImages,
lastMessage = chat.content.orEmpty(),
lastMessageDate = chat.publishDate,
onOpenUser = { user ->
navigator?.push(
onOpenUser = rememberCallbackArgs { user ->
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(user)
)
},
onOpen = {
onOpen = rememberCallback {
val userId = otherUser?.id
if (userId != null) {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
InboxChatScreen(userId)
)
}

View File

@ -32,9 +32,11 @@ class InboxMessagesViewModel(
private var currentPage: Int = 1
init {
notificationCenter.addObserver({
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout)
notificationCenter.addObserver(
{
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout
)
}
fun finalize() {

View File

@ -1,10 +1,12 @@
package com.github.diegoberaldin.raccoonforlemmy.feature.inbox.replies
import androidx.compose.runtime.Stable
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel
@Stable
interface InboxRepliesMviModel :
MviModel<InboxRepliesMviModel.Intent, InboxRepliesMviModel.UiState, InboxRepliesMviModel.Effect>,
ScreenModel {
@ -12,10 +14,10 @@ interface InboxRepliesMviModel :
sealed interface Intent {
data object Refresh : Intent
data object LoadNextPage : Intent
data class MarkAsRead(val read: Boolean, val index: Int) : Intent
data class MarkAsRead(val read: Boolean, val id: Int) : Intent
data object HapticIndication : Intent
data class UpVoteComment(val index: Int) : Intent
data class DownVoteComment(val index: Int) : Intent
data class UpVoteComment(val id: Int) : Intent
data class DownVoteComment(val id: Int) : Intent
}
data class UiState(

View File

@ -9,7 +9,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
@ -72,8 +72,8 @@ class InboxRepliesScreen : Tab {
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val lazyListState = rememberLazyListState()
LaunchedEffect(navigationCoordinator) {
navigationCoordinator.onDoubleTabSelection.onEach {
if (it == InboxTab) {
@ -128,7 +128,7 @@ class InboxRepliesScreen : Tab {
)
}
}
itemsIndexed(uiState.replies) { idx, mention ->
items(uiState.replies) { reply ->
val endColor = MaterialTheme.colorScheme.secondary
val startColor = MaterialTheme.colorScheme.tertiary
SwipeableCard(
@ -148,7 +148,7 @@ class InboxRepliesScreen : Tab {
model.reduce(
InboxRepliesMviModel.Intent.MarkAsRead(
read = true,
index = idx,
id = reply.id,
),
)
},
@ -156,7 +156,7 @@ class InboxRepliesScreen : Tab {
model.reduce(
InboxRepliesMviModel.Intent.MarkAsRead(
read = false,
index = idx,
id = reply.id,
),
)
},
@ -174,34 +174,34 @@ class InboxRepliesScreen : Tab {
},
content = {
InboxCard(
mention = mention,
mention = reply,
postLayout = uiState.postLayout,
type = InboxCardType.Reply,
autoLoadImages = uiState.autoLoadImages,
separateUpAndDownVotes = uiState.separateUpAndDownVotes,
onOpenPost = rememberCallbackArgs { post ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(
post = post,
highlightCommentId = mention.comment.id,
highlightCommentId = reply.comment.id,
),
)
},
onOpenCreator = rememberCallbackArgs { user ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(user),
)
},
onOpenCommunity = rememberCallbackArgs { community ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community),
)
},
onUpVote = rememberCallbackArgs(model) {
model.reduce(InboxRepliesMviModel.Intent.UpVoteComment(idx))
model.reduce(InboxRepliesMviModel.Intent.UpVoteComment(reply.id))
},
onDownVote = rememberCallbackArgs(model) {
model.reduce(InboxRepliesMviModel.Intent.DownVoteComment(idx))
model.reduce(InboxRepliesMviModel.Intent.DownVoteComment(reply.id))
},
)
},

View File

@ -39,9 +39,11 @@ class InboxRepliesViewModel(
private var currentUserId: Int? = null
init {
notificationCenter.addObserver({
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout)
notificationCenter.addObserver(
{
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout
)
}
fun finalize() {
@ -85,19 +87,19 @@ class InboxRepliesViewModel(
is InboxRepliesMviModel.Intent.MarkAsRead -> {
markAsRead(
read = intent.read,
mention = mvi.uiState.value.replies[intent.index]
mention = uiState.value.replies.first { it.id == intent.id },
)
}
InboxRepliesMviModel.Intent.HapticIndication -> hapticFeedback.vibrate()
is InboxRepliesMviModel.Intent.DownVoteComment -> toggleDownVoteComment(
mention = mvi.uiState.value.replies[intent.index],
mention = uiState.value.replies.first { it.id == intent.id },
feedback = true,
)
is InboxRepliesMviModel.Intent.UpVoteComment -> toggleUpVoteComment(
mention = mvi.uiState.value.replies[intent.index],
mention = uiState.value.replies.first { it.id == intent.id },
feedback = true,
)
}

View File

@ -17,14 +17,14 @@ interface ProfileLoggedMviModel :
data object LoadNextPage : Intent
data class DeletePost(val id: Int) : Intent
data class DeleteComment(val id: Int) : Intent
data class SharePost(val index: Int) : 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 SharePost(val id: Int) : Intent
data class UpVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class DownVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class SavePost(val id: Int, val feedback: Boolean = false) : Intent
data class UpVoteComment(val id: Int, val feedback: Boolean = false) : Intent
data class DownVoteComment(val id: Int, val feedback: Boolean = false) : Intent
data class SaveComment(val index: Int, val feedback: Boolean = false) : Intent
data class SaveComment(val id: Int, val feedback: Boolean = false) : Intent
}
data class UiState(

View File

@ -11,7 +11,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Text
@ -34,7 +34,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
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.raccoonforlemmy.core.appearance.data.PostLayout
@ -82,12 +81,10 @@ internal object ProfileLoggedScreen : Tab {
val user = uiState.user
val notificationCenter = remember { getNotificationCenter() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val lazyListState = rememberLazyListState()
var rawContent by remember { mutableStateOf<Any?>(null) }
LaunchedEffect(navigator) {
LaunchedEffect(Unit) {
navigationCoordinator.onDoubleTabSelection.onEach { tab ->
if (tab == ProfileTab) {
lazyListState.scrollToItem(0)
@ -99,6 +96,22 @@ internal object ProfileLoggedScreen : Tab {
notificationCenter.removeObserver(key)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{
model.reduce(ProfileLoggedMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.PostCreated
)
notificationCenter.addObserver(
{
model.reduce(ProfileLoggedMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
}
if (user != null) {
Column(
@ -127,7 +140,8 @@ internal object ProfileLoggedScreen : Tab {
user = user,
autoLoadImages = uiState.autoLoadImages,
onOpenImage = rememberCallbackArgs { url ->
navigator?.push(ZoomableImageScreen(url))
navigationCoordinator.getRootNavigator()
?.push(ZoomableImageScreen(url))
},
)
SectionSelector(
@ -167,7 +181,7 @@ internal object ProfileLoggedScreen : Tab {
}
}
}
itemsIndexed(uiState.posts) { idx, post ->
items(uiState.posts) { post ->
PostCard(
post = post,
postLayout = uiState.postLayout,
@ -177,41 +191,41 @@ internal object ProfileLoggedScreen : Tab {
hideAuthor = true,
blurNsfw = false,
onClick = rememberCallback {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(post),
)
},
onOpenCommunity = rememberCallbackArgs { community ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community),
)
},
onImageClick = rememberCallbackArgs { url ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
ZoomableImageScreen(url),
)
},
onUpVote = rememberCallback(model) {
model.reduce(
ProfileLoggedMviModel.Intent.UpVotePost(
idx,
true
id = post.id,
feedback = true
)
)
},
onDownVote = rememberCallback(model) {
model.reduce(
ProfileLoggedMviModel.Intent.DownVotePost(
idx,
true
id = post.id,
feedback = true
)
)
},
onSave = rememberCallback(model) {
model.reduce(
ProfileLoggedMviModel.Intent.SavePost(
idx,
true
id = post.id,
feedback = true
)
)
},
@ -228,14 +242,7 @@ internal object ProfileLoggedScreen : Tab {
)
2 -> {
notificationCenter.addObserver(
{
model.reduce(ProfileLoggedMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.PostCreated
)
bottomSheetNavigator.show(
navigationCoordinator.getBottomNavigator()?.show(
CreatePostScreen(
editedPost = post,
)
@ -247,7 +254,7 @@ internal object ProfileLoggedScreen : Tab {
}
else -> model.reduce(
ProfileLoggedMviModel.Intent.SharePost(idx)
ProfileLoggedMviModel.Intent.SharePost(post.id)
)
}
},
@ -281,7 +288,7 @@ internal object ProfileLoggedScreen : Tab {
)
}
}
itemsIndexed(uiState.comments) { idx, comment ->
items(uiState.comments) { comment ->
CommentCard(
modifier = Modifier.background(MaterialTheme.colorScheme.background),
comment = comment,
@ -291,7 +298,7 @@ internal object ProfileLoggedScreen : Tab {
hideAuthor = true,
hideIndent = true,
onClick = rememberCallback {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(
post = PostModel(id = comment.postId),
highlightCommentId = comment.id,
@ -301,24 +308,24 @@ internal object ProfileLoggedScreen : Tab {
onUpVote = rememberCallback(model) {
model.reduce(
ProfileLoggedMviModel.Intent.UpVoteComment(
idx,
true
id = comment.id,
feedback = true
)
)
},
onDownVote = rememberCallback(model) {
model.reduce(
ProfileLoggedMviModel.Intent.DownVoteComment(
idx,
true
id = comment.id,
feedback = true
)
)
},
onSave = rememberCallback(model) {
model.reduce(
ProfileLoggedMviModel.Intent.SaveComment(
idx,
true
id = comment.id,
feedback = true
)
)
},
@ -338,14 +345,7 @@ internal object ProfileLoggedScreen : Tab {
}
1 -> {
notificationCenter.addObserver(
{
model.reduce(ProfileLoggedMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(
navigationCoordinator.getBottomNavigator()?.show(
CreateCommentScreen(
editedComment = comment,
)

View File

@ -46,16 +46,20 @@ class ProfileLoggedViewModel(
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)
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() {
@ -112,36 +116,36 @@ class ProfileLoggedViewModel(
}
is ProfileLoggedMviModel.Intent.SharePost -> share(
post = uiState.value.posts[intent.index]
post = uiState.value.posts.first { it.id == intent.id },
)
is ProfileLoggedMviModel.Intent.DownVoteComment -> toggleDownVoteComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is ProfileLoggedMviModel.Intent.DownVotePost -> toggleDownVotePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is ProfileLoggedMviModel.Intent.SaveComment -> toggleSaveComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is ProfileLoggedMviModel.Intent.SavePost -> toggleSavePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is ProfileLoggedMviModel.Intent.UpVoteComment -> toggleUpVoteComment(
comment = uiState.value.comments[intent.index],
comment = uiState.value.comments.first { it.id == intent.id },
feedback = intent.feedback,
)
is ProfileLoggedMviModel.Intent.UpVotePost -> toggleUpVotePost(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
}

View File

@ -45,7 +45,6 @@ import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
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.BottomSheetHandle
@ -74,7 +73,7 @@ class LoginBottomSheet : Screen {
val uiState by model.uiState.collectAsState()
val snackbarHostState = remember { SnackbarHostState() }
val genericError = stringResource(MR.strings.message_generic_error)
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
LaunchedEffect(model) {
model.effects.onEach {
@ -86,14 +85,13 @@ class LoginBottomSheet : Screen {
}
LoginBottomSheetMviModel.Effect.LoginSuccess -> {
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
}
}
}.launchIn(this)
}
val uriHandler = LocalUriHandler.current
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val settingsRepository = remember { getSettingsRepository() }
Box(
@ -125,12 +123,12 @@ class LoginBottomSheet : Screen {
IconButton(
modifier = Modifier.align(Alignment.TopEnd),
onClick = {
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
handleUrl(
url = HELP_URL,
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
uriHandler = uriHandler,
navigator = navigator
navigator = navigationCoordinator.getRootNavigator(),
)
},
) {

View File

@ -28,7 +28,6 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.CurrentScreen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.Tab
import cafe.adriel.voyager.navigator.tab.TabNavigator
@ -36,6 +35,8 @@ import cafe.adriel.voyager.navigator.tab.TabOptions
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getDrawerCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.di.getProfileScreenModel
@ -66,9 +67,11 @@ internal object ProfileMainScreen : Tab {
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val drawerCoordinator = remember { getDrawerCoordinator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val scope = rememberCoroutineScope()
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
Scaffold(
modifier = Modifier.padding(Spacing.xxs),
@ -106,7 +109,8 @@ internal object ProfileMainScreen : Tab {
Image(
modifier = Modifier.onClick(
rememberCallback {
bottomSheetNavigator.show(ManageAccountsScreen())
navigationCoordinator.getBottomNavigator()
?.show(ManageAccountsScreen())
},
),
imageVector = Icons.Default.ManageAccounts,
@ -130,11 +134,17 @@ internal object ProfileMainScreen : Tab {
},
)
},
) {
) { paddinValues ->
Box(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
.padding(it),
.padding(paddinValues)
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
},
contentAlignment = Alignment.Center,
) {
// wait until logging status is determined

View File

@ -28,6 +28,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.draw.clip
@ -36,11 +37,11 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
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.BottomSheetHandle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomImage
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.di.getManageAccountsViewModel
@ -57,13 +58,13 @@ class ManageAccountsScreen : Screen {
val model = rememberScreenModel { getManageAccountsViewModel() }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
LaunchedEffect(model) {
model.effects.onEach { effect ->
when (effect) {
ManageAccountsMviModel.Effect.Close -> {
bottomSheetNavigator.hide()
navigationCoordinator.getBottomNavigator()?.hide()
}
}
}.launchIn(this)
@ -151,7 +152,7 @@ class ManageAccountsScreen : Screen {
Spacer(modifier = Modifier.height(Spacing.m))
Button(
onClick = {
bottomSheetNavigator.show(LoginBottomSheet())
navigationCoordinator.getBottomNavigator()?.show(LoginBottomSheet())
},
) {
Row(

View File

@ -9,12 +9,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.login.LoginBottomSheet
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.stringResource
@ -28,7 +29,8 @@ internal object ProfileNotLoggedScreen : Tab {
@Composable
override fun Content() {
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() }
Column(
modifier = Modifier.fillMaxSize().padding(horizontal = Spacing.m),
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
@ -40,7 +42,7 @@ internal object ProfileNotLoggedScreen : Tab {
Button(
modifier = Modifier.align(Alignment.CenterHorizontally),
onClick = {
bottomSheetNavigator.show(
navigationCoordinator.getBottomNavigator()?.show(
LoginBottomSheet(),
)
},

View File

@ -16,12 +16,12 @@ interface ExploreMviModel :
data class SetListingType(val value: ListingType) : Intent
data class SetSortType(val value: SortType) : Intent
data class SetResultType(val value: SearchResultType) : 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 UpVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class DownVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class SavePost(val id: Int, val feedback: Boolean = false) : Intent
data class UpVoteComment(val id: Int, val feedback: Boolean = false) : Intent
data class DownVoteComment(val id: Int, val feedback: Boolean = false) : Intent
data class SaveComment(val id: Int, val feedback: Boolean = false) : Intent
}
data class UiState(

View File

@ -10,7 +10,7 @@ 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.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ExperimentalMaterialApi
@ -51,7 +51,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
@ -71,6 +70,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDet
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallbackArgs
@ -98,8 +98,6 @@ class ExploreScreen : Screen {
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val notificationCenter = remember { getNotificationCenter() }
@ -114,13 +112,40 @@ class ExploreScreen : Screen {
}
}
}
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
DisposableEffect(key) {
onDispose {
notificationCenter.removeObserver(key)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{ result ->
(result as? ListingType)?.also {
model.reduce(ExploreMviModel.Intent.SetListingType(it))
}
}, key, NotificationCenterContractKeys.ChangeFeedType
)
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
ExploreMviModel.Intent.SetSortType(sortType)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
notificationCenter.addObserver(
{
model.reduce(ExploreMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
}
val lazyListState = rememberLazyListState()
LaunchedEffect(navigator) {
LaunchedEffect(Unit) {
navigationCoordinator.onDoubleTabSelection.onEach { tab ->
if (tab == ExploreTab) {
lazyListState.scrollToItem(0)
@ -140,10 +165,7 @@ class ExploreScreen : Screen {
}
Scaffold(
modifier = Modifier
.padding(Spacing.xxs)
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(keyboardScrollConnection),
modifier = Modifier.padding(Spacing.xxs),
topBar = {
ExploreTopBar(
scrollBehavior = scrollBehavior,
@ -154,26 +176,14 @@ class ExploreScreen : Screen {
val sheet = ListingTypeBottomSheet(
isLogged = uiState.isLogged,
)
notificationCenter.addObserver({ result ->
(result as? ListingType)?.also {
model.reduce(ExploreMviModel.Intent.SetListingType(it))
}
}, key, NotificationCenterContractKeys.ChangeFeedType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
onSelectSortType = rememberCallback {
focusManager.clearFocus()
val sheet = SortBottomSheet(
expandTop = true,
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
ExploreMviModel.Intent.SetSortType(sortType)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
onHamburgerTapped = rememberCallback {
scope.launch {
@ -265,7 +275,16 @@ class ExploreScreen : Screen {
{ model.reduce(ExploreMviModel.Intent.Refresh) },
)
Box(
modifier = Modifier.padding(Spacing.xxs).pullRefresh(pullRefreshState),
modifier = Modifier.padding(Spacing.xxs)
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
}
.nestedScroll(keyboardScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
state = lazyListState,
@ -282,7 +301,7 @@ class ExploreScreen : Screen {
}
}
}
itemsIndexed(uiState.results) { idx, result ->
items(uiState.results) { result ->
when (result) {
is CommunityModel -> {
CommunityItem(
@ -290,7 +309,7 @@ class ExploreScreen : Screen {
.fillMaxWidth()
.onClick(
rememberCallback {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(result),
)
},
@ -309,24 +328,24 @@ class ExploreScreen : Screen {
autoLoadImages = uiState.autoLoadImages,
blurNsfw = uiState.blurNsfw,
onClick = rememberCallback {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(result),
)
},
onOpenCommunity = rememberCallbackArgs { community ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community),
)
},
onOpenCreator = rememberCallbackArgs { user ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(user),
)
},
onUpVote = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.UpVotePost(
index = idx,
id = result.id,
feedback = true,
),
)
@ -334,7 +353,7 @@ class ExploreScreen : Screen {
onDownVote = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.DownVotePost(
index = idx,
id = result.id,
feedback = true,
),
)
@ -342,7 +361,7 @@ class ExploreScreen : Screen {
onSave = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.SavePost(
index = idx,
id = result.id,
feedback = true,
),
)
@ -351,17 +370,10 @@ class ExploreScreen : Screen {
val screen = CreateCommentScreen(
originalPost = result,
)
notificationCenter.addObserver(
{
model.reduce(ExploreMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()?.show(screen)
},
onImageClick = rememberCallbackArgs { url ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
ZoomableImageScreen(url),
)
},
@ -381,7 +393,7 @@ class ExploreScreen : Screen {
autoLoadImages = uiState.autoLoadImages,
hideIndent = true,
onClick = rememberCallback {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(
post = PostModel(id = result.postId),
highlightCommentId = result.id,
@ -391,7 +403,7 @@ class ExploreScreen : Screen {
onUpVote = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.UpVoteComment(
index = idx,
id = result.id,
feedback = true,
),
)
@ -399,7 +411,7 @@ class ExploreScreen : Screen {
onDownVote = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.DownVoteComment(
index = idx,
id = result.id,
feedback = true,
),
)
@ -407,7 +419,7 @@ class ExploreScreen : Screen {
onSave = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.SaveComment(
index = idx,
id = result.id,
feedback = true,
),
)
@ -417,22 +429,15 @@ class ExploreScreen : Screen {
originalPost = PostModel(id = result.postId),
originalComment = result,
)
notificationCenter.addObserver(
{
model.reduce(ExploreMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()?.show(screen)
},
onOpenCommunity = rememberCallbackArgs {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(it)
)
},
onOpenCreator = rememberCallbackArgs {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(it)
)
},
@ -449,7 +454,7 @@ class ExploreScreen : Screen {
.fillMaxWidth()
.onClick(
rememberCallback {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(result),
)
},

View File

@ -49,13 +49,17 @@ class ExploreViewModel(
private var firstLoad = true
init {
notificationCenter.addObserver({
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout)
notificationCenter.addObserver({
// apply new feed and sort type
firstLoad = true
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.ResetContents)
notificationCenter.addObserver(
{
handleLogout()
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.Logout
)
notificationCenter.addObserver(
{
// apply new feed and sort type
firstLoad = true
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.ResetContents
)
}
fun finalize() {
@ -127,32 +131,32 @@ class ExploreViewModel(
is ExploreMviModel.Intent.SetSortType -> changeSortType(intent.value)
is ExploreMviModel.Intent.SetResultType -> changeResultType(intent.value)
is ExploreMviModel.Intent.DownVotePost -> toggleDownVote(
post = uiState.value.results[intent.index] as PostModel,
post = uiState.value.results.first { (it as? PostModel)?.id == intent.id } as PostModel,
feedback = intent.feedback,
)
is ExploreMviModel.Intent.SavePost -> toggleSave(
post = uiState.value.results[intent.index] as PostModel,
post = uiState.value.results.first { (it as? PostModel)?.id == intent.id } as PostModel,
feedback = intent.feedback,
)
is ExploreMviModel.Intent.UpVotePost -> toggleUpVote(
post = uiState.value.results[intent.index] as PostModel,
post = uiState.value.results.first { (it as? PostModel)?.id == intent.id } as PostModel,
feedback = intent.feedback,
)
is ExploreMviModel.Intent.DownVoteComment -> toggleDownVoteComment(
comment = uiState.value.results[intent.index] as CommentModel,
comment = uiState.value.results.first { (it as? CommentModel)?.id == intent.id } as CommentModel,
feedback = intent.feedback,
)
is ExploreMviModel.Intent.SaveComment -> toggleSaveComment(
comment = uiState.value.results[intent.index] as CommentModel,
comment = uiState.value.results.first { (it as? CommentModel)?.id == intent.id } as CommentModel,
feedback = intent.feedback,
)
is ExploreMviModel.Intent.UpVoteComment -> toggleUpVoteComment(
comment = uiState.value.results[intent.index] as CommentModel,
comment = uiState.value.results.first { (it as? CommentModel)?.id == intent.id } as CommentModel,
feedback = intent.feedback,
)
}

View File

@ -11,8 +11,8 @@ interface ManageSubscriptionsMviModel :
sealed interface Intent {
data object Refresh : Intent
data object HapticIndication : Intent
data class Unsubscribe(val index: Int) : Intent
data class DeleteMultiCommunity(val index: Int) : Intent
data class Unsubscribe(val id: Int) : Intent
data class DeleteMultiCommunity(val id: Int) : Intent
}
data class UiState(

View File

@ -13,7 +13,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
@ -78,7 +78,7 @@ class ManageSubscriptionsScreen : Screen {
val model = rememberScreenModel { getManageSubscriptionsViewModel() }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigatorCoordinator = remember { getNavigationCoordinator() }
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val lazyListState = rememberLazyListState()
@ -94,9 +94,6 @@ class ManageSubscriptionsScreen : Screen {
}
Scaffold(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(fabNestedScrollConnection),
topBar = {
TopAppBar(
title = {
@ -111,7 +108,7 @@ class ManageSubscriptionsScreen : Screen {
Image(
modifier = Modifier.onClick(
rememberCallback {
navigator?.pop()
navigatorCoordinator.getRootNavigator()?.pop()
},
),
imageVector = Icons.Default.ArrowBack,
@ -136,7 +133,7 @@ class ManageSubscriptionsScreen : Screen {
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ExpandLess,
text = stringResource(MR.strings.action_back_to_top),
onSelected = {
onSelected = rememberCallback {
scope.launch {
lazyListState.scrollToItem(0)
topAppBarState.heightOffset = 0f
@ -156,7 +153,10 @@ class ManageSubscriptionsScreen : Screen {
},
)
Box(
modifier = Modifier.padding(paddingValues)
modifier = Modifier
.padding(paddingValues)
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(fabNestedScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
@ -178,7 +178,9 @@ class ManageSubscriptionsScreen : Screen {
Icon(
modifier = Modifier.onClick(
rememberCallback {
navigator?.push(MultiCommunityEditorScreen())
navigatorCoordinator.getRootNavigator()?.push(
MultiCommunityEditorScreen()
)
},
),
imageVector = Icons.Default.AddCircle,
@ -187,7 +189,7 @@ class ManageSubscriptionsScreen : Screen {
)
}
}
itemsIndexed(uiState.multiCommunities) { idx, community ->
items(uiState.multiCommunities) { community ->
val endColor = MaterialTheme.colorScheme.secondary
val startColor = MaterialTheme.colorScheme.tertiary
SwipeableCard(
@ -203,13 +205,15 @@ class ManageSubscriptionsScreen : Screen {
model.reduce(ManageSubscriptionsMviModel.Intent.HapticIndication)
},
onDismissToStart = rememberCallback {
navigator?.push(
navigatorCoordinator.getRootNavigator()?.push(
MultiCommunityEditorScreen(community),
)
},
onDismissToEnd = rememberCallback(model) {
model.reduce(
ManageSubscriptionsMviModel.Intent.DeleteMultiCommunity(idx),
ManageSubscriptionsMviModel.Intent.DeleteMultiCommunity(
(community.id ?: 0).toInt()
),
)
},
swipeContent = { direction ->
@ -229,7 +233,7 @@ class ManageSubscriptionsScreen : Screen {
modifier = Modifier.fillMaxWidth()
.background(MaterialTheme.colorScheme.background).onClick(
rememberCallback {
navigator?.push(
navigatorCoordinator.getRootNavigator()?.push(
MultiCommunityScreen(community),
)
},
@ -252,7 +256,7 @@ class ManageSubscriptionsScreen : Screen {
)
}
}
itemsIndexed(uiState.communities) { idx, community ->
items(uiState.communities) { community ->
val endColor = MaterialTheme.colorScheme.secondary
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
@ -268,7 +272,7 @@ class ManageSubscriptionsScreen : Screen {
},
onDismissToStart = rememberCallback(model) {
model.reduce(
ManageSubscriptionsMviModel.Intent.Unsubscribe(idx),
ManageSubscriptionsMviModel.Intent.Unsubscribe(community.id),
)
},
swipeContent = { _ ->
@ -285,7 +289,7 @@ class ManageSubscriptionsScreen : Screen {
.background(MaterialTheme.colorScheme.background)
.onClick(
rememberCallback {
navigator?.push(
navigatorCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community),
)
},

View File

@ -63,11 +63,13 @@ class ManageSubscriptionsViewModel(
ManageSubscriptionsMviModel.Intent.HapticIndication -> hapticFeedback.vibrate()
ManageSubscriptionsMviModel.Intent.Refresh -> refresh()
is ManageSubscriptionsMviModel.Intent.Unsubscribe -> handleUnsubscription(
community = uiState.value.communities[intent.index],
community = uiState.value.communities.first { it.id == intent.id },
)
is ManageSubscriptionsMviModel.Intent.DeleteMultiCommunity -> deleteMultiCommunity(
community = uiState.value.multiCommunities[intent.index],
community = uiState.value.multiCommunities.first {
(it.id ?: 0).toInt() == intent.id
},
)
}
}

View File

@ -14,12 +14,12 @@ interface MultiCommunityMviModel :
data object LoadNextPage : Intent
data class ChangeSort(val value: SortType) : Intent
data object HapticIndication : 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 SharePost(val index: Int) : Intent
data class MarkAsRead(val index: Int) : Intent
data class Hide(val index: Int) : Intent
data class UpVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class DownVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class SavePost(val id: Int, val feedback: Boolean = false) : Intent
data class SharePost(val id: Int) : Intent
data class MarkAsRead(val id: Int) : Intent
data class Hide(val id: Int) : Intent
data object ClearRead : Intent
}

View File

@ -13,7 +13,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
@ -39,6 +39,7 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@ -53,7 +54,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
@ -76,6 +76,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDet
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.MultiCommunityModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
@ -95,11 +96,9 @@ class MultiCommunityScreen(
val model = rememberScreenModel { getMultiCommunityViewModel(community) }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val bottomNavCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { bottomNavCoordinator.getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val notificationCenter = remember { getNotificationCenter() }
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
@ -111,6 +110,9 @@ class MultiCommunityScreen(
val fabNestedScrollConnection = remember { getFabNestedScrollConnection() }
val isFabVisible by fabNestedScrollConnection.isFabVisible.collectAsState()
val drawerCoordinator = remember { getDrawerCoordinator() }
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
DisposableEffect(key) {
drawerCoordinator.setGesturesEnabled(false)
onDispose {
@ -118,12 +120,28 @@ class MultiCommunityScreen(
drawerCoordinator.setGesturesEnabled(true)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
MultiCommunityMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
notificationCenter.addObserver(
{
model.reduce(MultiCommunityMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
}
Scaffold(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(fabNestedScrollConnection),
topBar = {
val sortType = uiState.sortType
TopAppBar(
@ -139,7 +157,7 @@ class MultiCommunityScreen(
Image(
modifier = Modifier.onClick(
rememberCallback {
navigator?.pop()
navigationCoordinator.getRootNavigator()?.pop()
},
),
imageVector = Icons.Default.ArrowBack,
@ -176,16 +194,7 @@ class MultiCommunityScreen(
val sheet = SortBottomSheet(
expandTop = true,
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
MultiCommunityMviModel.Intent.ChangeSort(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
),
imageVector = sortType.toIcon(),
@ -212,7 +221,7 @@ class MultiCommunityScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ExpandLess,
text = stringResource(MR.strings.action_back_to_top),
onSelected = {
onSelected = rememberCallback {
scope.launch {
lazyListState.scrollToItem(0)
topAppBarState.heightOffset = 0f
@ -223,7 +232,7 @@ class MultiCommunityScreen(
this += FloatingActionButtonMenuItem(
icon = Icons.Default.ClearAll,
text = stringResource(MR.strings.action_clear_read),
onSelected = {
onSelected = rememberCallback {
model.reduce(MultiCommunityMviModel.Intent.ClearRead)
scope.launch {
lazyListState.scrollToItem(0)
@ -245,6 +254,14 @@ class MultiCommunityScreen(
modifier = Modifier
.padding(padding)
.fillMaxWidth()
.let {
if (settings.hideNavigationBarWhileScrolling) {
it.nestedScroll(scrollBehavior.nestedScrollConnection)
} else {
it
}
}
.nestedScroll(fabNestedScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
@ -262,7 +279,7 @@ class MultiCommunityScreen(
}
}
}
itemsIndexed(uiState.posts) { idx, post ->
items(uiState.posts) { post ->
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled,
@ -281,10 +298,10 @@ class MultiCommunityScreen(
model.reduce(MultiCommunityMviModel.Intent.HapticIndication)
},
onDismissToStart = {
model.reduce(MultiCommunityMviModel.Intent.UpVotePost(idx))
model.reduce(MultiCommunityMviModel.Intent.UpVotePost(post.id))
},
onDismissToEnd = {
model.reduce(MultiCommunityMviModel.Intent.DownVotePost(idx))
model.reduce(MultiCommunityMviModel.Intent.DownVotePost(post.id))
},
swipeContent = { direction ->
val icon = when (direction) {
@ -306,25 +323,25 @@ class MultiCommunityScreen(
autoLoadImages = uiState.autoLoadImages,
blurNsfw = uiState.blurNsfw,
onClick = {
model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(idx))
navigator?.push(
model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(post.id))
navigationCoordinator.getRootNavigator()?.push(
PostDetailScreen(post),
)
},
onOpenCommunity = { community ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(community),
)
},
onOpenCreator = { user ->
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
UserDetailScreen(user),
)
},
onUpVote = {
model.reduce(
MultiCommunityMviModel.Intent.UpVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -332,7 +349,7 @@ class MultiCommunityScreen(
onDownVote = {
model.reduce(
MultiCommunityMviModel.Intent.DownVotePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -340,7 +357,7 @@ class MultiCommunityScreen(
onSave = {
model.reduce(
MultiCommunityMviModel.Intent.SavePost(
index = idx,
id = post.id,
feedback = true,
),
)
@ -349,18 +366,11 @@ class MultiCommunityScreen(
val screen = CreateCommentScreen(
originalPost = post,
)
notificationCenter.addObserver(
{
model.reduce(MultiCommunityMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
navigationCoordinator.getBottomNavigator()?.show(screen)
},
onImageClick = { url ->
model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(idx))
navigator?.push(
model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(post.id))
navigationCoordinator.getRootNavigator()?.push(
ZoomableImageScreen(url),
)
},
@ -372,16 +382,21 @@ class MultiCommunityScreen(
onOptionSelected = { optionIdx ->
when (optionIdx) {
2 -> {
bottomSheetNavigator.show(
navigationCoordinator.getBottomNavigator()?.show(
CreateReportScreen(
postId = post.id
)
)
}
1 -> model.reduce(MultiCommunityMviModel.Intent.Hide(idx))
1 -> model.reduce(
MultiCommunityMviModel.Intent.Hide(
post.id
)
)
else -> model.reduce(
MultiCommunityMviModel.Intent.SharePost(idx)
MultiCommunityMviModel.Intent.SharePost(post.id)
)
}
}

View File

@ -89,7 +89,7 @@ class MultiCommunityViewModel(
when (intent) {
is MultiCommunityMviModel.Intent.ChangeSort -> applySortType(intent.value)
is MultiCommunityMviModel.Intent.DownVotePost -> toggleDownVote(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
@ -97,19 +97,25 @@ class MultiCommunityViewModel(
MultiCommunityMviModel.Intent.LoadNextPage -> loadNextPage()
MultiCommunityMviModel.Intent.Refresh -> refresh()
is MultiCommunityMviModel.Intent.SavePost -> toggleSave(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
is MultiCommunityMviModel.Intent.SharePost -> share(post = uiState.value.posts[intent.index])
is MultiCommunityMviModel.Intent.SharePost -> share(
post = uiState.value.posts.first { it.id == intent.id }
)
is MultiCommunityMviModel.Intent.UpVotePost -> toggleUpVote(
post = uiState.value.posts[intent.index],
post = uiState.value.posts.first { it.id == intent.id },
feedback = intent.feedback,
)
MultiCommunityMviModel.Intent.ClearRead -> clearRead()
is MultiCommunityMviModel.Intent.MarkAsRead -> markAsRead(post = uiState.value.posts[intent.index])
is MultiCommunityMviModel.Intent.Hide -> hide(post = uiState.value.posts[intent.index])
is MultiCommunityMviModel.Intent.MarkAsRead -> markAsRead(
post = uiState.value.posts.first { it.id == intent.id })
is MultiCommunityMviModel.Intent.Hide -> hide(
post = uiState.value.posts.first { it.id == intent.id })
}
}
@ -140,7 +146,7 @@ class MultiCommunityViewModel(
currentIds = if (refreshing) emptyList() else currentState.posts.map { it.id }
)
val canFetchMore = paginator.canFetchMore
val itemsToAdd = itemList.orEmpty().filter { post ->
val itemsToAdd = itemList.filter { post ->
if (includeNsfw) {
true
} else {

View File

@ -12,7 +12,7 @@ interface MultiCommunityEditorMviModel :
data class SetName(val value: String) : Intent
data class SetSearch(val value: String) : Intent
data class SelectImage(val index: Int?) : Intent
data class ToggleCommunity(val index: Int) : Intent
data class ToggleCommunity(val id: Int) : Intent
data object Submit : Intent
}

View File

@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
@ -81,13 +82,13 @@ class MultiCommunityEditorScreen(
val model = rememberScreenModel { getMultiCommunityEditorViewModel(editedCommunity) }
model.bindToLifecycle(key)
val uiState by model.uiState.collectAsState()
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
LaunchedEffect(model) {
model.effects.onEach {
when (it) {
MultiCommunityEditorMviModel.Effect.Close -> {
navigator?.pop()
navigationCoordinator.getRootNavigator()?.pop()
}
}
}.launchIn(this)
@ -114,7 +115,7 @@ class MultiCommunityEditorScreen(
Image(
modifier = Modifier.onClick(
rememberCallback {
navigator?.pop()
navigationCoordinator.getRootNavigator()?.pop()
},
),
imageVector = Icons.Default.ArrowBack,
@ -326,7 +327,7 @@ class MultiCommunityEditorScreen(
.weight(1f),
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
) {
itemsIndexed(uiState.communities) { idx, communityItem ->
items(uiState.communities) { communityItem ->
val community = communityItem.first
val selected = communityItem.second
Row(
@ -342,7 +343,7 @@ class MultiCommunityEditorScreen(
onCheckedChange = {
model.reduce(
MultiCommunityEditorMviModel.Intent.ToggleCommunity(
idx
community.id
)
)
},

View File

@ -52,7 +52,7 @@ class MultiCommunityEditorViewModel(
when (intent) {
is MultiCommunityEditorMviModel.Intent.SelectImage -> selectImage(intent.index)
is MultiCommunityEditorMviModel.Intent.SetName -> mvi.updateState { it.copy(name = intent.value) }
is MultiCommunityEditorMviModel.Intent.ToggleCommunity -> toggleCommunity(intent.index)
is MultiCommunityEditorMviModel.Intent.ToggleCommunity -> toggleCommunity(intent.id)
is MultiCommunityEditorMviModel.Intent.SetSearch -> setSearch(intent.value)
MultiCommunityEditorMviModel.Intent.Submit -> submit()
}
@ -109,10 +109,9 @@ class MultiCommunityEditorViewModel(
mvi.updateState { it.copy(icon = image) }
}
private fun toggleCommunity(index: Int) {
val toggledCommunity = uiState.value.communities[index]
private fun toggleCommunity(communityId: Int) {
val newCommunities = communities.map { item ->
if (item.first.id == toggledCommunity.first.id) {
if (item.first.id == communityId) {
item.first to !item.second
} else {
item

View File

@ -65,7 +65,7 @@ class AboutDialog : Screen {
viewModel.bindToLifecycle(key)
val uriHandler = LocalUriHandler.current
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
val navigationCoordinator = remember { getNavigationCoordinator() }
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
val uiState by viewModel.uiState.collectAsState()
@ -75,7 +75,7 @@ class AboutDialog : Screen {
viewModel.effects.onEach { effect ->
when (effect) {
is AboutDialogMviModel.Effect.OpenCommunity -> {
navigator?.push(
navigationCoordinator.getRootNavigator()?.push(
CommunityDetailScreen(
community = effect.community,
otherInstance = effect.instance,
@ -126,7 +126,7 @@ class AboutDialog : Screen {
url = CHANGELOG_URL,
openExternal = settings.openUrlsInExternalBrowser,
uriHandler = uriHandler,
navigator = navigator,
navigator = navigationCoordinator.getRootNavigator(),
)
}
)
@ -138,7 +138,7 @@ class AboutDialog : Screen {
url = REPORT_URL,
openExternal = settings.openUrlsInExternalBrowser,
uriHandler = uriHandler,
navigator = navigator,
navigator = navigationCoordinator.getRootNavigator(),
)
},
) {
@ -170,7 +170,7 @@ class AboutDialog : Screen {
url = WEBSITE_URL,
openExternal = settings.openUrlsInExternalBrowser,
uriHandler = uriHandler,
navigator = navigator,
navigator = navigationCoordinator.getRootNavigator(),
)
},
)

View File

@ -39,7 +39,6 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.FontScale
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiFontFamily
@ -95,36 +94,20 @@ class SettingsScreen : Screen {
val model = rememberScreenModel { getSettingsViewModel() }
model.bindToLifecycle(SettingsTab.key)
val uiState by model.uiState.collectAsState()
val bottomSheetNavigator = LocalBottomSheetNavigator.current
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
val notificationCenter = remember { getNotificationCenter() }
val drawerCoordinator = remember { getDrawerCoordinator() }
val scope = rememberCoroutineScope()
val navigationCoordinator = remember { getNavigationCoordinator() }
val navigator = remember { navigationCoordinator.getRootNavigator() }
val scrollState = rememberScrollState()
LaunchedEffect(navigator) {
navigationCoordinator.onDoubleTabSelection.onEach { tab ->
if (tab == SettingsTab) {
scrollState.scrollTo(0)
topAppBarState.heightOffset = 0f
topAppBarState.contentOffset = 0f
}
}.launchIn(this)
}
DisposableEffect(key) {
onDispose {
notificationCenter.removeObserver(key)
}
}
val languageRepository = remember { getLanguageRepository() }
val lang by languageRepository.currentLanguage.collectAsState()
var uiFontSizeWorkaround by remember { mutableStateOf(true) }
val themeRepository = remember { getThemeRepository() }
var upvoteColorDialogOpened by remember { mutableStateOf(false) }
var downvoteColorDialogOpened by remember { mutableStateOf(false) }
var infoDialogOpened by remember { mutableStateOf(false) }
LaunchedEffect(themeRepository) {
themeRepository.uiFontScale.drop(1).onEach {
@ -133,12 +116,131 @@ class SettingsScreen : Screen {
uiFontSizeWorkaround = true
}.launchIn(this)
}
LaunchedEffect(Unit) {
navigationCoordinator.onDoubleTabSelection.onEach { tab ->
if (tab == SettingsTab) {
scrollState.scrollTo(0)
topAppBarState.heightOffset = 0f
topAppBarState.contentOffset = 0f
}
}.launchIn(this)
}
DisposableEffect(key) {
onDispose {
notificationCenter.removeObserver(key)
}
}
LaunchedEffect(notificationCenter) {
notificationCenter.addObserver(
{ result ->
(result as? String)?.also { lang ->
model.reduce(SettingsMviModel.Intent.ChangeLanguage(lang))
}
}, key, NotificationCenterContractKeys.ChangeLanguage
)
notificationCenter.addObserver(
{ result ->
(result as? UiTheme)?.also { value ->
model.reduce(SettingsMviModel.Intent.ChangeUiTheme(value))
}
}, key, NotificationCenterContractKeys.ChangeTheme
)
notificationCenter.addObserver(
{ result ->
model.reduce(
SettingsMviModel.Intent.ChangeCustomSeedColor(
result as? Color?
)
)
}, key, NotificationCenterContractKeys.ChangeColor
)
notificationCenter.addObserver(
{ result ->
(result as? UiFontFamily)?.also { value ->
model.reduce(
SettingsMviModel.Intent.ChangeUiFontFamily(
value
)
)
}
}, key, NotificationCenterContractKeys.ChangeFontFamily
)
notificationCenter.addObserver(
{ result ->
(result as? Float)?.also { value ->
model.reduce(
SettingsMviModel.Intent.ChangeUiFontSize(
value
)
)
}
}, key, NotificationCenterContractKeys.ChangeFontSize
)
notificationCenter.addObserver(
{ result ->
(result as? Float)?.also { value ->
model.reduce(
SettingsMviModel.Intent.ChangeContentFontSize(
value
)
)
}
}, key, NotificationCenterContractKeys.ChangeFontSize
)
notificationCenter.addObserver(
{ result ->
(result as? PostLayout)?.also { value ->
model.reduce(
SettingsMviModel.Intent.ChangePostLayout(
value
)
)
}
}, key, NotificationCenterContractKeys.ChangePostLayout
)
notificationCenter.addObserver(
{ result ->
(result as? ListingType)?.also {
model.reduce(
SettingsMviModel.Intent.ChangeDefaultListingType(
it
)
)
}
}, key, NotificationCenterContractKeys.ChangeFeedType
)
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
SettingsMviModel.Intent.ChangeDefaultPostSortType(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
notificationCenter.addObserver(
{
(it as? SortType)?.also { sortType ->
model.reduce(
SettingsMviModel.Intent.ChangeDefaultCommentSortType(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType
)
notificationCenter.addObserver(
{
infoDialogOpened = false
}, key, NotificationCenterContractKeys.CloseDialog
)
}
if (!uiFontSizeWorkaround) {
return
}
var upvoteColorDialogOpened by remember { mutableStateOf(false) }
var downvoteColorDialogOpened by remember { mutableStateOf(false) }
var infoDialogOpened by remember { mutableStateOf(false) }
Scaffold(
modifier = Modifier.padding(Spacing.xxs),
@ -194,12 +296,7 @@ class SettingsScreen : Screen {
value = uiState.lang.toLanguageName(),
onTap = rememberCallback {
val sheet = LanguageBottomSheet()
notificationCenter.addObserver({ result ->
(result as? String)?.also { lang ->
model.reduce(SettingsMviModel.Intent.ChangeLanguage(lang))
}
}, key, NotificationCenterContractKeys.ChangeLanguage)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
@ -209,12 +306,7 @@ class SettingsScreen : Screen {
value = uiState.uiTheme.toReadableName(),
onTap = rememberCallback {
val sheet = ThemeBottomSheet()
notificationCenter.addObserver({ result ->
(result as? UiTheme)?.also { value ->
model.reduce(SettingsMviModel.Intent.ChangeUiTheme(value))
}
}, key, NotificationCenterContractKeys.ChangeTheme)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
@ -243,14 +335,7 @@ class SettingsScreen : Screen {
).primary,
onTap = rememberCallback {
val sheet = ColorBottomSheet()
notificationCenter.addObserver({ result ->
model.reduce(
SettingsMviModel.Intent.ChangeCustomSeedColor(
result as? Color?
)
)
}, key, NotificationCenterContractKeys.ChangeColor)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
// upvote and downvote colors
@ -275,16 +360,7 @@ class SettingsScreen : Screen {
value = uiState.uiFontFamily.toReadableName(),
onTap = rememberCallback {
val sheet = FontFamilyBottomSheet()
notificationCenter.addObserver({ result ->
(result as? UiFontFamily)?.also { value ->
model.reduce(
SettingsMviModel.Intent.ChangeUiFontFamily(
value
)
)
}
}, key, NotificationCenterContractKeys.ChangeFontFamily)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
// font scale
@ -299,16 +375,7 @@ class SettingsScreen : Screen {
FontScale.Small,
),
)
notificationCenter.addObserver({ result ->
(result as? Float)?.also { value ->
model.reduce(
SettingsMviModel.Intent.ChangeUiFontSize(
value
)
)
}
}, key, NotificationCenterContractKeys.ChangeFontSize)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
SettingsRow(
@ -316,16 +383,7 @@ class SettingsScreen : Screen {
value = uiState.contentFontScale.toReadableName(),
onTap = rememberCallback {
val sheet = FontScaleBottomSheet()
notificationCenter.addObserver({ result ->
(result as? Float)?.also { value ->
model.reduce(
SettingsMviModel.Intent.ChangeContentFontSize(
value
)
)
}
}, key, NotificationCenterContractKeys.ChangeFontSize)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
@ -335,16 +393,7 @@ class SettingsScreen : Screen {
value = uiState.postLayout.toReadableName(),
onTap = rememberCallback {
val sheet = PostLayoutBottomSheet()
notificationCenter.addObserver({ result ->
(result as? PostLayout)?.also { value ->
model.reduce(
SettingsMviModel.Intent.ChangePostLayout(
value
)
)
}
}, key, NotificationCenterContractKeys.ChangePostLayout)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
@ -400,16 +449,7 @@ class SettingsScreen : Screen {
val sheet = ListingTypeBottomSheet(
isLogged = uiState.isLogged,
)
notificationCenter.addObserver({ result ->
(result as? ListingType)?.also {
model.reduce(
SettingsMviModel.Intent.ChangeDefaultListingType(
it
)
)
}
}, key, NotificationCenterContractKeys.ChangeFeedType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
@ -421,16 +461,7 @@ class SettingsScreen : Screen {
val sheet = SortBottomSheet(
expandTop = true,
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
SettingsMviModel.Intent.ChangeDefaultPostSortType(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
@ -448,16 +479,7 @@ class SettingsScreen : Screen {
SortType.Controversial,
),
)
notificationCenter.addObserver({
(it as? SortType)?.also { sortType ->
model.reduce(
SettingsMviModel.Intent.ChangeDefaultCommentSortType(
sortType
)
)
}
}, key, NotificationCenterContractKeys.ChangeSortType)
bottomSheetNavigator.show(sheet)
navigationCoordinator.getBottomNavigator()?.show(sheet)
},
)
@ -623,9 +645,6 @@ class SettingsScreen : Screen {
}
if (infoDialogOpened) {
notificationCenter.addObserver({
infoDialogOpened = false
}, key, NotificationCenterContractKeys.CloseDialog)
AboutDialog().Content()
}
}

View File

@ -208,6 +208,6 @@
<string name="settings_ui_font_scale">Dimensione testo UI</string>
<string name="settings_ui_theme">Tema interfaccia</string>
<string name="settings_upvote_color">Colore voti positivi</string>
<string name="settings_hide_navigation_bar">Nascondi barra di navigation durante lo scroll
<string name="settings_hide_navigation_bar">Nascondi barra di navigazione durante lo scroll
</string>
</resources>

View File

@ -227,7 +227,9 @@ fun App() {
topEnd = CornerSize.xl
),
sheetBackgroundColor = MaterialTheme.colorScheme.background,
) {
) { bottomNavigator ->
navigationCoordinator.setBottomNavigator(bottomNavigator)
ModalNavigationDrawer(
drawerState = drawerState,
gesturesEnabled = drawerGestureEnabled,

View File

@ -84,8 +84,8 @@ internal object MainScreen : Screen {
}
}
TabNavigator(HomeTab) {
LaunchedEffect(it.current) {
TabNavigator(HomeTab) { tabNavigator ->
LaunchedEffect(tabNavigator.current) {
// when the current tab chanes, reset the bottom bar offset to the default value
model.reduce(MainScreenMviModel.Intent.SetBottomBarOffsetHeightPx(0f))
}