feat: double click management (#130)

This commit is contained in:
Diego Beraldin 2023-11-13 22:17:59 +01:00 committed by GitHub
parent aecf2549fb
commit e474c9227d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 1389 additions and 725 deletions

View File

@ -26,6 +26,7 @@ object IconSize {
val s = 20.dp val s = 20.dp
val m = 26.dp val m = 26.dp
val l = 30.dp val l = 30.dp
val xl = 46.dp
val xxl = 60.dp val xxl = 60.dp
} }

View File

@ -97,7 +97,7 @@ fun FloatingActionButtonMenu(
) { ) {
Row( Row(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
fabExpanded = false fabExpanded = false
item.onSelected?.invoke() item.onSelected?.invoke()
}, },

View File

@ -111,8 +111,8 @@ class InboxChatScreen(
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,

View File

@ -46,6 +46,7 @@ interface CommunityDetailMviModel :
val blurNsfw: Boolean = true, val blurNsfw: Boolean = true,
val currentUserId: Int? = null, val currentUserId: Int? = null,
val swipeActionsEnabled: Boolean = true, val swipeActionsEnabled: Boolean = true,
val doubleTapActionEnabled: Boolean = false,
val postLayout: PostLayout = PostLayout.Card, val postLayout: PostLayout = PostLayout.Card,
val fullHeightImages: Boolean = true, val fullHeightImages: Boolean = true,
val separateUpAndDownVotes: Boolean = false, val separateUpAndDownVotes: Boolean = false,

View File

@ -237,7 +237,7 @@ class CommunityDetailScreen(
if (!isOnOtherInstance && uiState.isLogged) { if (!isOnOtherInstance && uiState.isLogged) {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
when (uiState.community.subscribed) { when (uiState.community.subscribed) {
true -> model.reduce(CommunityDetailMviModel.Intent.Unsubscribe) true -> model.reduce(CommunityDetailMviModel.Intent.Unsubscribe)
false -> model.reduce(CommunityDetailMviModel.Intent.Subscribe) false -> model.reduce(CommunityDetailMviModel.Intent.Subscribe)
@ -259,11 +259,11 @@ class CommunityDetailScreen(
// sort button // sort button
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
val sheet = SortBottomSheet( val sheet = SortBottomSheet(
expandTop = true, expandTop = true,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
), ),
imageVector = uiState.sortType.toIcon(), imageVector = uiState.sortType.toIcon(),
@ -297,7 +297,7 @@ class CommunityDetailScreen(
modifier = Modifier.onGloballyPositioned { modifier = Modifier.onGloballyPositioned {
optionsOffset = it.positionInParent() optionsOffset = it.positionInParent()
}.padding(start = Spacing.s).onClick( }.padding(start = Spacing.s).onClick(
rememberCallback { onClick = rememberCallback {
optionsExpanded = true optionsExpanded = true
}, },
), ),
@ -321,7 +321,7 @@ class CommunityDetailScreen(
horizontal = Spacing.m, horizontal = Spacing.m,
vertical = Spacing.s, vertical = Spacing.s,
).onClick( ).onClick(
rememberCallback { onClick = rememberCallback {
optionsExpanded = false optionsExpanded = false
when (option.id) { when (option.id) {
OptionId.BlockInstance -> model.reduce( OptionId.BlockInstance -> model.reduce(
@ -333,19 +333,17 @@ class CommunityDetailScreen(
) )
OptionId.InfoInstance -> { OptionId.InfoInstance -> {
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(
?.push( InstanceInfoScreen(
InstanceInfoScreen( url = uiState.community.instanceUrl,
url = uiState.community.instanceUrl, ),
), )
)
} }
OptionId.Info -> { OptionId.Info -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CommunityInfoScreen(uiState.community),
CommunityInfoScreen(uiState.community), )
)
} }
else -> Unit else -> Unit
@ -359,12 +357,11 @@ class CommunityDetailScreen(
} }
}, },
navigationIcon = { navigationIcon = {
val navigator = navigationCoordinator.getRootNavigator() if (navigationCoordinator.canPop) {
if (navigator?.canPop == true) {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigator.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -437,7 +434,7 @@ class CommunityDetailScreen(
val screen = CreatePostScreen( val screen = CreatePostScreen(
communityId = uiState.community.id, communityId = uiState.community.id,
) )
navigationCoordinator.getBottomNavigator()?.show(screen) navigationCoordinator.showBottomSheet(screen)
}, },
) )
} }
@ -476,8 +473,7 @@ class CommunityDetailScreen(
community = uiState.community, community = uiState.community,
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
onOpenImage = rememberCallbackArgs { url -> onOpenImage = rememberCallbackArgs { url ->
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(ZoomableImageScreen(url))
?.push(ZoomableImageScreen(url))
}, },
) )
Spacer(modifier = Modifier.height(Spacing.m)) Spacer(modifier = Modifier.height(Spacing.m))
@ -559,15 +555,27 @@ class CommunityDetailScreen(
post.id post.id
) )
) )
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
PostDetailScreen( PostDetailScreen(
post = post, post = post,
otherInstance = otherInstanceName, otherInstance = otherInstanceName,
), ),
) )
}, },
onDoubleClick = if (!uiState.doubleTapActionEnabled || !uiState.isLogged || isOnOtherInstance) {
null
} else {
rememberCallback(model) {
model.reduce(
CommunityDetailMviModel.Intent.UpVotePost(
id = post.id,
feedback = true,
),
)
}
},
onOpenCreator = rememberCallbackArgs { user -> onOpenCreator = rememberCallbackArgs { user ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen( UserDetailScreen(
user = user, user = user,
otherInstance = otherInstanceName, otherInstance = otherInstanceName,
@ -609,8 +617,7 @@ class CommunityDetailScreen(
val screen = CreateCommentScreen( val screen = CreateCommentScreen(
originalPost = post, originalPost = post,
) )
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(screen)
?.show(screen)
} }
}, },
onImageClick = rememberCallbackArgs(model) { url -> onImageClick = rememberCallbackArgs(model) { url ->
@ -619,7 +626,7 @@ class CommunityDetailScreen(
post.id post.id
) )
) )
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
ZoomableImageScreen(url), ZoomableImageScreen(url),
) )
}, },
@ -680,24 +687,21 @@ class CommunityDetailScreen(
) )
OptionId.Edit -> { OptionId.Edit -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreatePostScreen(editedPost = post)
CreatePostScreen(editedPost = post) )
)
} }
OptionId.Report -> { OptionId.Report -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreateReportScreen(postId = post.id)
CreateReportScreen(postId = post.id) )
)
} }
OptionId.CrossPost -> { OptionId.CrossPost -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreatePostScreen(crossPost = post)
CreatePostScreen(crossPost = post) )
)
} }
OptionId.SeeRaw -> { OptionId.SeeRaw -> {

View File

@ -66,6 +66,7 @@ class CommunityDetailViewModel(
it.copy( it.copy(
blurNsfw = settings.blurNsfw, blurNsfw = settings.blurNsfw,
swipeActionsEnabled = settings.enableSwipeActions, swipeActionsEnabled = settings.enableSwipeActions,
doubleTapActionEnabled = settings.enableDoubleTapAction,
sortType = settings.defaultPostSortType.toSortType(), sortType = settings.defaultPostSortType.toSortType(),
fullHeightImages = settings.fullHeightImages, fullHeightImages = settings.fullHeightImages,
separateUpAndDownVotes = settings.separateUpAndDownVotes, separateUpAndDownVotes = settings.separateUpAndDownVotes,

View File

@ -21,7 +21,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepos
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
@ -42,6 +41,7 @@ fun CommentCard(
autoLoadImages: Boolean = true, autoLoadImages: Boolean = true,
options: List<Option> = emptyList(), options: List<Option> = emptyList(),
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
onUpVote: (() -> Unit)? = null, onUpVote: (() -> Unit)? = null,
onDownVote: (() -> Unit)? = null, onDownVote: (() -> Unit)? = null,
onSave: (() -> Unit)? = null, onSave: (() -> Unit)? = null,
@ -63,9 +63,10 @@ fun CommentCard(
endColor = MaterialTheme.colorScheme.background, endColor = MaterialTheme.colorScheme.background,
) )
Box( Box(
modifier = Modifier.onClick(rememberCallback { modifier = Modifier.onClick(
onClick?.invoke() onClick = onClick ?: {},
}).padding( onDoubleClick = onDoubleClick ?: {}
).padding(
start = if (hideIndent) 0.dp else (INDENT_AMOUNT * comment.depth).dp start = if (hideIndent) 0.dp else (INDENT_AMOUNT * comment.depth).dp
), ),
) { ) {
@ -96,6 +97,7 @@ fun CommentCard(
text = comment.text, text = comment.text,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
onClick = onClick, onClick = onClick,
onDoubleClick = onDoubleClick,
) )
} }
PostCardFooter( PostCardFooter(

View File

@ -38,6 +38,7 @@ fun CommunityAndCreatorInfo(
onOpenCommunity: ((CommunityModel) -> Unit)? = null, onOpenCommunity: ((CommunityModel) -> Unit)? = null,
onOpenCreator: ((UserModel) -> Unit)? = null, onOpenCreator: ((UserModel) -> Unit)? = null,
onToggleExpanded: (() -> Unit)? = null, onToggleExpanded: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
) { ) {
val communityName = community?.name.orEmpty() val communityName = community?.name.orEmpty()
val communityIcon = community?.icon.orEmpty() val communityIcon = community?.icon.orEmpty()
@ -56,11 +57,12 @@ fun CommunityAndCreatorInfo(
CustomImage( CustomImage(
modifier = Modifier modifier = Modifier
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
if (community != null) { if (community != null) {
onOpenCommunity?.invoke(community) onOpenCommunity?.invoke(community)
} }
}, },
onDoubleClick = onDoubleClick ?: {},
) )
.padding(Spacing.xxxs) .padding(Spacing.xxxs)
.size(iconSize) .size(iconSize)
@ -73,11 +75,12 @@ fun CommunityAndCreatorInfo(
} else { } else {
PlaceholderImage( PlaceholderImage(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
if (community != null) { if (community != null) {
onOpenCommunity?.invoke(community) onOpenCommunity?.invoke(community)
} }
}, },
onDoubleClick = onDoubleClick ?: {},
), ),
size = IconSize.l, size = IconSize.l,
title = communityName, title = communityName,
@ -88,11 +91,12 @@ fun CommunityAndCreatorInfo(
CustomImage( CustomImage(
modifier = Modifier modifier = Modifier
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
if (creator != null) { if (creator != null) {
onOpenCreator?.invoke(creator) onOpenCreator?.invoke(creator)
} }
}, },
onDoubleClick = onDoubleClick ?: {},
) )
.padding(Spacing.xxxs) .padding(Spacing.xxxs)
.size(iconSize) .size(iconSize)
@ -105,11 +109,12 @@ fun CommunityAndCreatorInfo(
} else { } else {
PlaceholderImage( PlaceholderImage(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
if (creator != null) { if (creator != null) {
onOpenCreator?.invoke(creator) onOpenCreator?.invoke(creator)
} }
}, },
onDoubleClick = onDoubleClick ?: {},
), ),
size = iconSize, size = iconSize,
title = creatorName, title = creatorName,
@ -123,9 +128,10 @@ fun CommunityAndCreatorInfo(
Text( Text(
modifier = Modifier modifier = Modifier
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onOpenCommunity?.invoke(community) onOpenCommunity?.invoke(community)
}, },
onDoubleClick = onDoubleClick ?: {},
), ),
text = buildString { text = buildString {
append(communityName) append(communityName)
@ -141,9 +147,10 @@ fun CommunityAndCreatorInfo(
Text( Text(
modifier = Modifier modifier = Modifier
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onOpenCreator?.invoke(creator) onOpenCreator?.invoke(creator)
}, },
onDoubleClick = onDoubleClick ?: {},
), ),
text = buildString { text = buildString {
append(creatorName) append(creatorName)
@ -161,7 +168,7 @@ fun CommunityAndCreatorInfo(
val expandedModifier = Modifier val expandedModifier = Modifier
.padding(end = Spacing.xs) .padding(end = Spacing.xs)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onToggleExpanded?.invoke() onToggleExpanded?.invoke()
}, },
) )

View File

@ -92,7 +92,7 @@ fun CommunityHeader(
.size(IconSize.xxl) .size(IconSize.xxl)
.clip(RoundedCornerShape(IconSize.xxl / 2)) .clip(RoundedCornerShape(IconSize.xxl / 2))
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onOpenImage?.invoke(communityIcon) onOpenImage?.invoke(communityIcon)
}, },
), ),

View File

@ -34,11 +34,13 @@ fun InboxCard(
autoLoadImages: Boolean = true, autoLoadImages: Boolean = true,
separateUpAndDownVotes: Boolean = true, separateUpAndDownVotes: Boolean = true,
postLayout: PostLayout = PostLayout.Card, postLayout: PostLayout = PostLayout.Card,
options: List<Option> = emptyList(),
onOpenPost: (PostModel) -> Unit, onOpenPost: (PostModel) -> Unit,
onOpenCreator: (UserModel) -> Unit, onOpenCreator: (UserModel) -> Unit,
onOpenCommunity: (CommunityModel) -> Unit, onOpenCommunity: (CommunityModel) -> Unit,
onUpVote: ((CommentModel) -> Unit)? = null, onUpVote: ((CommentModel) -> Unit)? = null,
onDownVote: ((CommentModel) -> Unit)? = null, onDownVote: ((CommentModel) -> Unit)? = null,
onOptionSelected: ((OptionId) -> Unit)? = null,
) { ) {
Box( Box(
modifier = Modifier.let { modifier = Modifier.let {
@ -52,7 +54,7 @@ fun InboxCard(
it.background(MaterialTheme.colorScheme.background) it.background(MaterialTheme.colorScheme.background)
} }
}.onClick( }.onClick(
rememberCallback { onClick = rememberCallback {
onOpenPost(mention.post) onOpenPost(mention.post)
}, },
), ),
@ -92,6 +94,7 @@ fun InboxCard(
separateUpAndDownVotes = separateUpAndDownVotes, separateUpAndDownVotes = separateUpAndDownVotes,
upVoted = mention.myVote > 0, upVoted = mention.myVote > 0,
downVoted = mention.myVote < 0, downVoted = mention.myVote < 0,
options = options,
onOpenCommunity = onOpenCommunity, onOpenCommunity = onOpenCommunity,
onOpenCreator = { user -> onOpenCreator = { user ->
onOpenCreator(user) onOpenCreator(user)
@ -102,6 +105,7 @@ fun InboxCard(
onDownVote = { onDownVote = {
onDownVote?.invoke(mention.comment) onDownVote?.invoke(mention.comment)
}, },
onOptionSelected = onOptionSelected,
) )
} }
} }

View File

@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.components
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -11,6 +12,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowCircleDown import androidx.compose.material.icons.filled.ArrowCircleDown
import androidx.compose.material.icons.filled.ArrowCircleUp import androidx.compose.material.icons.filled.ArrowCircleUp
import androidx.compose.material.icons.filled.MoreHoriz
import androidx.compose.material.icons.filled.Schedule import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -18,17 +20,23 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
@ -36,6 +44,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.prettifyDate import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.prettifyDate
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
@ -50,6 +59,7 @@ fun InboxReplySubtitle(
score: Int = 0, score: Int = 0,
upvotes: Int = 0, upvotes: Int = 0,
downvotes: Int = 0, downvotes: Int = 0,
options: List<Option> = emptyList(),
separateUpAndDownVotes: Boolean = false, separateUpAndDownVotes: Boolean = false,
upVoted: Boolean = false, upVoted: Boolean = false,
downVoted: Boolean = false, downVoted: Boolean = false,
@ -57,13 +67,17 @@ fun InboxReplySubtitle(
onOpenCreator: ((UserModel) -> Unit)? = null, onOpenCreator: ((UserModel) -> Unit)? = null,
onUpVote: (() -> Unit)? = null, onUpVote: (() -> Unit)? = null,
onDownVote: (() -> Unit)? = null, onDownVote: (() -> Unit)? = null,
onOptionSelected: ((OptionId) -> Unit)? = null,
) { ) {
val buttonModifier = Modifier.size(IconSize.m).padding(4.dp) val buttonModifier = Modifier.size(IconSize.m).padding(3.5.dp)
val themeRepository = remember { getThemeRepository() } val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState() val upvoteColor by themeRepository.upvoteColor.collectAsState()
val downvoteColor by themeRepository.downvoteColor.collectAsState() val downvoteColor by themeRepository.downvoteColor.collectAsState()
val defaultUpvoteColor = MaterialTheme.colorScheme.primary val defaultUpvoteColor = MaterialTheme.colorScheme.primary
val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary
var optionsExpanded by remember { mutableStateOf(false) }
var optionsOffset by remember { mutableStateOf(Offset.Zero) }
Column( Column(
modifier = modifier, modifier = modifier,
) { ) {
@ -83,7 +97,7 @@ fun InboxReplySubtitle(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
if (creator != null) { if (creator != null) {
onOpenCreator?.invoke(creator) onOpenCreator?.invoke(creator)
} }
@ -124,7 +138,7 @@ fun InboxReplySubtitle(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
if (community != null) { if (community != null) {
onOpenCommunity?.invoke(community) onOpenCommunity?.invoke(community)
} }
@ -162,96 +176,143 @@ fun InboxReplySubtitle(
} }
} }
Row( Box {
verticalAlignment = Alignment.CenterVertically, Row(
) { verticalAlignment = Alignment.CenterVertically,
Icon( ) {
modifier = buttonModifier, Icon(
imageVector = Icons.Default.Schedule, modifier = buttonModifier,
contentDescription = null, imageVector = Icons.Default.Schedule,
tint = MaterialTheme.colorScheme.onBackground, contentDescription = null,
) tint = MaterialTheme.colorScheme.onBackground,
Text( )
text = date?.prettifyDate() ?: "", Text(
) text = date?.prettifyDate() ?: "",
Spacer(modifier = Modifier.weight(1f)) )
Image( if (options.isNotEmpty()) {
modifier = buttonModifier Icon(
.onClick( modifier = buttonModifier
rememberCallback { .padding(top = Spacing.xxs)
onUpVote?.invoke() .onGloballyPositioned {
optionsOffset = it.positionInParent()
}
.onClick(
onClick = rememberCallback {
optionsExpanded = true
},
),
imageVector = Icons.Default.MoreHoriz,
contentDescription = null,
)
}
Spacer(modifier = Modifier.weight(1f))
Image(
modifier = buttonModifier
.onClick(
onClick = rememberCallback {
onUpVote?.invoke()
},
),
imageVector = Icons.Default.ArrowCircleUp,
contentDescription = null,
colorFilter = ColorFilter.tint(
color = if (upVoted) {
upvoteColor ?: defaultUpvoteColor
} else {
MaterialTheme.colorScheme.onSurface
}, },
), ),
imageVector = Icons.Default.ArrowCircleUp, )
contentDescription = null, Text(
colorFilter = ColorFilter.tint( text = buildAnnotatedString {
color = if (upVoted) { if (separateUpAndDownVotes) {
upvoteColor ?: defaultUpvoteColor val upvoteText = upvotes.toString()
} else { append(upvoteText)
MaterialTheme.colorScheme.onSurface if (upVoted) {
addStyle(
style = SpanStyle(color = upvoteColor ?: defaultUpvoteColor),
start = 0,
end = upvoteText.length
)
}
append(" / ")
val downvoteText = downvotes.toString()
append(downvoteText)
if (downVoted) {
addStyle(
style = SpanStyle(
color = downvoteColor ?: defaultDownVoteColor
),
start = upvoteText.length + 3,
end = upvoteText.length + 3 + downvoteText.length
)
}
} else {
val text = score.toString()
append(text)
if (upVoted) {
addStyle(
style = SpanStyle(color = upvoteColor ?: defaultUpvoteColor),
start = 0,
end = text.length
)
} else if (downVoted) {
addStyle(
style = SpanStyle(
color = downvoteColor ?: defaultDownVoteColor
),
start = 0,
end = length
)
}
}
}, },
), style = MaterialTheme.typography.labelLarge,
) color = MaterialTheme.colorScheme.onBackground,
Text( )
text = buildAnnotatedString { Image(
if (separateUpAndDownVotes) { modifier = buttonModifier
val upvoteText = upvotes.toString() .onClick(
append(upvoteText) onClick = rememberCallback {
if (upVoted) { onDownVote?.invoke()
addStyle( },
style = SpanStyle(color = upvoteColor ?: defaultUpvoteColor), ),
start = 0, imageVector = Icons.Default.ArrowCircleDown,
end = upvoteText.length contentDescription = null,
) colorFilter = ColorFilter.tint(
} color = if (downVoted) {
append(" / ") downvoteColor ?: defaultDownVoteColor
val downvoteText = downvotes.toString() } else {
append(downvoteText) MaterialTheme.colorScheme.onSurface
if (downVoted) { },
addStyle( ),
style = SpanStyle(color = downvoteColor ?: defaultDownVoteColor), )
start = upvoteText.length + 3, }
end = upvoteText.length + 3 + downvoteText.length CustomDropDown(
) expanded = optionsExpanded,
} onDismiss = {
} else { optionsExpanded = false
val text = score.toString()
append(text)
if (upVoted) {
addStyle(
style = SpanStyle(color = upvoteColor ?: defaultUpvoteColor),
start = 0,
end = text.length
)
} else if (downVoted) {
addStyle(
style = SpanStyle(color = downvoteColor ?: defaultDownVoteColor),
start = 0,
end = length
)
}
}
}, },
style = MaterialTheme.typography.labelLarge, offset = DpOffset(
color = MaterialTheme.colorScheme.onBackground, x = optionsOffset.x.toLocalDp(),
) y = optionsOffset.y.toLocalDp(),
Image(
modifier = buttonModifier
.onClick(
rememberCallback {
onDownVote?.invoke()
},
),
imageVector = Icons.Default.ArrowCircleDown,
contentDescription = null,
colorFilter = ColorFilter.tint(
color = if (downVoted) {
downvoteColor ?: defaultDownVoteColor
} else {
MaterialTheme.colorScheme.onSurface
},
), ),
) ) {
options.forEach { option ->
Text(
modifier = Modifier.padding(
horizontal = Spacing.m,
vertical = Spacing.s,
).onClick(
onClick = rememberCallback {
optionsExpanded = false
onOptionSelected?.invoke(option.id)
},
),
text = option.text,
)
}
}
} }
} }
} }

View File

@ -17,4 +17,6 @@ sealed class OptionId(val value: Int) {
data object InfoInstance : OptionId(8) data object InfoInstance : OptionId(8)
data object Block : OptionId(9) data object Block : OptionId(9)
data object BlockInstance : OptionId(10) data object BlockInstance : OptionId(10)
data object MarkRead : OptionId(11)
data object MarkUnread : OptionId(12)
} }

View File

@ -26,14 +26,17 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize import androidx.compose.ui.unit.toSize
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.web.WebViewScreen
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.looksLikeAnImage import com.github.diegoberaldin.raccoonforlemmy.core.utils.looksLikeAnImage
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalPixel import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalPixel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
@ -63,6 +66,7 @@ fun PostCard(
onImageClick: ((String) -> Unit)? = null, onImageClick: ((String) -> Unit)? = null,
onOptionSelected: ((OptionId) -> Unit)? = null, onOptionSelected: ((OptionId) -> Unit)? = null,
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
) { ) {
Box( Box(
modifier = modifier.let { modifier = modifier.let {
@ -74,7 +78,10 @@ fun PostCard(
} else { } else {
it it
} }
}.onClick(rememberCallback { onClick?.invoke() }), }.onClick(
onClick = onClick ?: {},
onDoubleClick = onDoubleClick ?: {},
),
) { ) {
if (postLayout != PostLayout.Compact) { if (postLayout != PostLayout.Compact) {
ExtendedPost( ExtendedPost(
@ -101,6 +108,7 @@ fun PostCard(
onImageClick = onImageClick, onImageClick = onImageClick,
onOptionSelected = onOptionSelected, onOptionSelected = onOptionSelected,
onClick = onClick, onClick = onClick,
onDoubleClick = onDoubleClick,
) )
} else { } else {
CompactPost( CompactPost(
@ -119,6 +127,7 @@ fun PostCard(
onImageClick = onImageClick, onImageClick = onImageClick,
onOptionSelected = onOptionSelected, onOptionSelected = onOptionSelected,
onClick = onClick, onClick = onClick,
onDoubleClick = onDoubleClick,
) )
} }
} }
@ -142,6 +151,7 @@ private fun CompactPost(
onImageClick: ((String) -> Unit)? = null, onImageClick: ((String) -> Unit)? = null,
onOptionSelected: ((OptionId) -> Unit)? = null, onOptionSelected: ((OptionId) -> Unit)? = null,
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
) { ) {
Column( Column(
modifier = modifier.background(MaterialTheme.colorScheme.background), modifier = modifier.background(MaterialTheme.colorScheme.background),
@ -153,6 +163,7 @@ private fun CompactPost(
onOpenCommunity = onOpenCommunity, onOpenCommunity = onOpenCommunity,
onOpenCreator = onOpenCreator, onOpenCreator = onOpenCreator,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
onDoubleClick = onDoubleClick,
) )
Row( Row(
modifier = Modifier.padding(horizontal = Spacing.s), modifier = Modifier.padding(horizontal = Spacing.s),
@ -165,6 +176,7 @@ private fun CompactPost(
text = post.title, text = post.title,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
onClick = onClick, onClick = onClick,
onDoubleClick = onDoubleClick,
) )
} }
PostCardImage( PostCardImage(
@ -180,6 +192,7 @@ private fun CompactPost(
}, },
blurred = blurNsfw && post.nsfw, blurred = blurNsfw && post.nsfw,
onImageClick = onImageClick, onImageClick = onImageClick,
onDoubleClick = onDoubleClick,
) )
} }
PostCardFooter( PostCardFooter(
@ -225,6 +238,7 @@ private fun ExtendedPost(
onImageClick: ((String) -> Unit)? = null, onImageClick: ((String) -> Unit)? = null,
onOptionSelected: ((OptionId) -> Unit)? = null, onOptionSelected: ((OptionId) -> Unit)? = null,
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
) { ) {
Column( Column(
modifier = modifier.background(backgroundColor), modifier = modifier.background(backgroundColor),
@ -237,6 +251,7 @@ private fun ExtendedPost(
onOpenCommunity = onOpenCommunity, onOpenCommunity = onOpenCommunity,
onOpenCreator = onOpenCreator, onOpenCreator = onOpenCreator,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
onDoubleClick = onDoubleClick,
) )
ScaledContent { ScaledContent {
PostCardTitle( PostCardTitle(
@ -247,6 +262,7 @@ private fun ExtendedPost(
text = post.title, text = post.title,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
onClick = onClick, onClick = onClick,
onDoubleClick = onDoubleClick,
) )
} }
@ -269,6 +285,7 @@ private fun ExtendedPost(
imageUrl = post.imageUrl, imageUrl = post.imageUrl,
blurred = blurNsfw && post.nsfw, blurred = blurNsfw && post.nsfw,
onImageClick = onImageClick, onImageClick = onImageClick,
onDoubleClick = onDoubleClick,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
) )
if (showBody) { if (showBody) {
@ -290,6 +307,7 @@ private fun ExtendedPost(
text = post.text, text = post.text,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
onClick = onClick, onClick = onClick,
onDoubleClick = onDoubleClick,
) )
if (limitBodyHeight && textHeightPx >= maxHeightPx) { if (limitBodyHeight && textHeightPx >= maxHeightPx) {
Box( Box(
@ -307,9 +325,25 @@ private fun ExtendedPost(
} }
} }
} }
if (post.url != post.imageUrl) { if (post.url != post.imageUrl && !post.url.isNullOrEmpty()) {
val url = post.url.orEmpty()
val settingsRepository = remember { getSettingsRepository() }
val uriHandler = LocalUriHandler.current
val navigationCoordinator = remember { getNavigationCoordinator() }
PostLinkBanner( PostLinkBanner(
modifier = Modifier.padding(vertical = Spacing.xs), modifier = Modifier
.padding(vertical = Spacing.xs)
.onClick(
onClick = {
if (settingsRepository.currentSettings.value.openUrlsInExternalBrowser) {
uriHandler.openUri(url)
} else {
navigationCoordinator.pushScreen(WebViewScreen(url))
}
},
onDoubleClick = onDoubleClick ?: {},
),
url = post.url?.takeIf { !it.looksLikeAnImage }.orEmpty(), url = post.url?.takeIf { !it.looksLikeAnImage }.orEmpty(),
) )
} }

View File

@ -15,6 +15,7 @@ fun PostCardBody(
text: String, text: String,
autoLoadImages: Boolean = true, autoLoadImages: Boolean = true,
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
) { ) {
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
val navigationCoordinator = remember { getNavigationCoordinator() } val navigationCoordinator = remember { getNavigationCoordinator() }
@ -27,17 +28,17 @@ fun PostCardBody(
inlineImages = false, inlineImages = false,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
onOpenUrl = { url -> onOpenUrl = { url ->
handleUrl( navigationCoordinator.handleUrl(
url = url, url = url,
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser, openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
uriHandler = uriHandler, uriHandler = uriHandler,
navigator = navigationCoordinator.getRootNavigator()
) )
}, },
onOpenImage = { url -> onOpenImage = { url ->
navigationCoordinator.getRootNavigator()?.push(ZoomableImageScreen(url)) navigationCoordinator.pushScreen(ZoomableImageScreen(url))
}, },
onClick = onClick, onClick = onClick,
onDoubleClick = onDoubleClick,
) )
} }
} }

View File

@ -79,7 +79,7 @@ fun PostCardFooter(
Image( Image(
modifier = buttonModifier.padding(1.dp) modifier = buttonModifier.padding(1.dp)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onReply?.invoke() onReply?.invoke()
}, },
), ),
@ -110,11 +110,12 @@ fun PostCardFooter(
if (options.isNotEmpty()) { if (options.isNotEmpty()) {
Icon( Icon(
modifier = buttonModifier modifier = buttonModifier
.padding(top = Spacing.xxs)
.onGloballyPositioned { .onGloballyPositioned {
optionsOffset = it.positionInParent() optionsOffset = it.positionInParent()
} }
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
optionsExpanded = true optionsExpanded = true
}, },
), ),
@ -125,7 +126,7 @@ fun PostCardFooter(
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Image( Image(
modifier = buttonModifier.onClick( modifier = buttonModifier.onClick(
rememberCallback { onClick = rememberCallback {
onSave?.invoke() onSave?.invoke()
}, },
), ),
@ -146,7 +147,7 @@ fun PostCardFooter(
Image( Image(
modifier = buttonModifier modifier = buttonModifier
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onUpVote?.invoke() onUpVote?.invoke()
}, },
), ),
@ -206,7 +207,7 @@ fun PostCardFooter(
Image( Image(
modifier = buttonModifier modifier = buttonModifier
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onDownVote?.invoke() onDownVote?.invoke()
}, },
), ),
@ -238,7 +239,7 @@ fun PostCardFooter(
horizontal = Spacing.m, horizontal = Spacing.m,
vertical = Spacing.s, vertical = Spacing.s,
).onClick( ).onClick(
rememberCallback { onClick = rememberCallback {
optionsExpanded = false optionsExpanded = false
onOptionSelected?.invoke(option.id) onOptionSelected?.invoke(option.id)
}, },

View File

@ -19,7 +19,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.resources.MR import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.stringResource import dev.icerock.moko.resources.compose.stringResource
@ -33,6 +32,7 @@ fun PostCardImage(
maxHeight: Dp = Dp.Unspecified, maxHeight: Dp = Dp.Unspecified,
blurred: Boolean = false, blurred: Boolean = false,
onImageClick: ((String) -> Unit)? = null, onImageClick: ((String) -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
) { ) {
if (imageUrl.isNotEmpty()) { if (imageUrl.isNotEmpty()) {
CustomImage( CustomImage(
@ -40,9 +40,8 @@ fun PostCardImage(
.heightIn(min = minHeight, max = maxHeight) .heightIn(min = minHeight, max = maxHeight)
.blur(radius = if (blurred) 60.dp else 0.dp) .blur(radius = if (blurred) 60.dp else 0.dp)
.onClick( .onClick(
rememberCallback { onClick = { onImageClick?.invoke(imageUrl) },
onImageClick?.invoke(imageUrl) onDoubleClick = onDoubleClick ?: {},
},
), ),
url = imageUrl, url = imageUrl,
quality = FilterQuality.Low, quality = FilterQuality.Low,

View File

@ -15,6 +15,7 @@ fun PostCardTitle(
autoLoadImages: Boolean = true, autoLoadImages: Boolean = true,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
) { ) {
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
val navigationCoordinator = remember { getNavigationCoordinator() } val navigationCoordinator = remember { getNavigationCoordinator() }
@ -25,16 +26,16 @@ fun PostCardTitle(
content = text, content = text,
autoLoadImages = autoLoadImages, autoLoadImages = autoLoadImages,
onOpenUrl = { url -> onOpenUrl = { url ->
handleUrl( navigationCoordinator.handleUrl(
url = url, url = url,
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser, openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
uriHandler = uriHandler, uriHandler = uriHandler,
navigator = navigationCoordinator.getRootNavigator(),
) )
}, },
onOpenImage = { url -> onOpenImage = { url ->
navigationCoordinator.getRootNavigator()?.push(ZoomableImageScreen(url)) navigationCoordinator.pushScreen(ZoomableImageScreen(url))
}, },
onClick = onClick, onClick = onClick,
onDoubleClick = onDoubleClick,
) )
} }

View File

@ -29,24 +29,12 @@ fun PostLinkBanner(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
url: String, url: String,
) { ) {
val uriHandler = LocalUriHandler.current
val navigationCoordinator = remember { getNavigationCoordinator() }
val settingsRepository = remember { getSettingsRepository() }
if (url.isNotEmpty()) { if (url.isNotEmpty()) {
Row( Row(
modifier = modifier modifier = modifier
.background( .background(
color = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.1f), color = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.1f),
shape = RoundedCornerShape(CornerSize.l), shape = RoundedCornerShape(CornerSize.l),
).onClick(
rememberCallback {
if (settingsRepository.currentSettings.value.openUrlsInExternalBrowser) {
uriHandler.openUri(url)
} else {
navigationCoordinator.getRootNavigator()?.push(WebViewScreen(url))
}
},
).padding( ).padding(
horizontal = Spacing.m, horizontal = Spacing.m,
vertical = Spacing.s, vertical = Spacing.s,

View File

@ -145,7 +145,7 @@ fun TextFormattingBar(
) )
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onSelectImage() onSelectImage()
}, },
), ),

View File

@ -1,8 +1,8 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.components package com.github.diegoberaldin.raccoonforlemmy.core.commonui.components
import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.platform.UriHandler
import cafe.adriel.voyager.navigator.Navigator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation.NavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.web.WebViewScreen import com.github.diegoberaldin.raccoonforlemmy.core.commonui.web.WebViewScreen
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
@ -39,18 +39,17 @@ fun getPostFromUrl(url: String?): Pair<PostModel, String>? {
} }
} }
fun handleUrl( fun NavigationCoordinator.handleUrl(
url: String, url: String,
openExternal: Boolean, openExternal: Boolean,
uriHandler: UriHandler, uriHandler: UriHandler,
navigator: Navigator? = null,
) { ) {
val community = getCommunityFromUrl(url) val community = getCommunityFromUrl(url)
val user = getUserFromUrl(url) val user = getUserFromUrl(url)
when { when {
community != null && navigator != null -> { community != null -> {
navigator.push( pushScreen(
CommunityDetailScreen( CommunityDetailScreen(
community = community, community = community,
otherInstance = community.host otherInstance = community.host
@ -58,8 +57,8 @@ fun handleUrl(
) )
} }
user != null && navigator != null -> { user != null -> {
navigator.push( pushScreen(
UserDetailScreen( UserDetailScreen(
user = user, user = user,
otherInstance = user.host otherInstance = user.host
@ -71,11 +70,7 @@ fun handleUrl(
uriHandler.openUri(url) uriHandler.openUri(url)
} }
navigator != null -> { else -> pushScreen(WebViewScreen(url))
navigator.push(WebViewScreen(url))
}
else -> Unit
} }
} }

View File

@ -90,7 +90,7 @@ fun UserHeader(
.size(IconSize.xxl) .size(IconSize.xxl)
.clip(RoundedCornerShape(IconSize.xxl / 2)) .clip(RoundedCornerShape(IconSize.xxl / 2))
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onOpenImage?.invoke(userAvatar) onOpenImage?.invoke(userAvatar)
}, },
), ),

View File

@ -126,7 +126,7 @@ class CreateCommentScreen(
CreateCommentMviModel.Effect.Success -> { CreateCommentMviModel.Effect.Success -> {
notificationCenter.getObserver(NotificationCenterContractKeys.CommentCreated) notificationCenter.getObserver(NotificationCenterContractKeys.CommentCreated)
?.also { o -> o.invoke(Unit) } ?.also { o -> o.invoke(Unit) }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
} }
is CreateCommentMviModel.Effect.AddImageToText -> { is CreateCommentMviModel.Effect.AddImageToText -> {

View File

@ -176,7 +176,7 @@ class CreatePostScreen(
CreatePostMviModel.Effect.Success -> { CreatePostMviModel.Effect.Success -> {
notificationCenter.getObserver(NotificationCenterContractKeys.PostCreated) notificationCenter.getObserver(NotificationCenterContractKeys.PostCreated)
?.also { o -> o.invoke(Unit) } ?.also { o -> o.invoke(Unit) }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
} }
is CreatePostMviModel.Effect.AddImageToBody -> { is CreatePostMviModel.Effect.AddImageToBody -> {
@ -190,7 +190,7 @@ class CreatePostScreen(
DisposableEffect(key) { DisposableEffect(key) {
notificationCenter.addObserver( notificationCenter.addObserver(
{ {
(it as CommunityModel)?.also { community -> (it as? CommunityModel)?.also { community ->
model.reduce(CreatePostMviModel.Intent.SetCommunity(community)) model.reduce(CreatePostMviModel.Intent.SetCommunity(community))
focusManager.clearFocus() focusManager.clearFocus()
} }
@ -357,7 +357,7 @@ class CreatePostScreen(
trailingIcon = { trailingIcon = {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
openImagePicker = true openImagePicker = true
}, },
), ),

View File

@ -190,7 +190,7 @@ object ModalDrawerContent : Tab {
items(uiState.multiCommunities) { community -> items(uiState.multiCommunities) { community ->
MultiCommunityItem( MultiCommunityItem(
modifier = Modifier.fillMaxWidth().onClick( modifier = Modifier.fillMaxWidth().onClick(
rememberCallback { onClick = rememberCallback {
scope.launch { scope.launch {
coordinator.toggleDrawer() coordinator.toggleDrawer()
coordinator.sendEvent( coordinator.sendEvent(
@ -207,7 +207,7 @@ object ModalDrawerContent : Tab {
items(uiState.communities) { community -> items(uiState.communities) { community ->
CommunityItem( CommunityItem(
modifier = Modifier.fillMaxWidth().onClick( modifier = Modifier.fillMaxWidth().onClick(
rememberCallback { onClick = rememberCallback {
scope.launch { scope.launch {
coordinator.toggleDrawer() coordinator.toggleDrawer()
coordinator.sendEvent( coordinator.sendEvent(
@ -338,7 +338,7 @@ private fun DrawerHeader(
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onOpenChangeInstance?.invoke() onOpenChangeInstance?.invoke()
}, },
), ),
@ -362,7 +362,7 @@ private fun DrawerShortcut(
horizontal = Spacing.s, horizontal = Spacing.s,
vertical = Spacing.xs, vertical = Spacing.xs,
).onClick( ).onClick(
rememberCallback { onClick = rememberCallback {
onSelected?.invoke() onSelected?.invoke()
}, },
), ),
@ -443,7 +443,7 @@ private fun ChangeInstanceDialog(
if (instanceName.isNotEmpty()) { if (instanceName.isNotEmpty()) {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onChangeInstanceName?.invoke("") onChangeInstanceName?.invoke("")
}, },
), ),

View File

@ -81,8 +81,8 @@ class ZoomableImageScreen(
navigationIcon = { navigationIcon = {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -93,7 +93,7 @@ class ZoomableImageScreen(
actions = { actions = {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
model.reduce(ZoomableImageMviModel.Intent.SaveToGallery(url)) model.reduce(ZoomableImageMviModel.Intent.SaveToGallery(url))
}, },
), ),
@ -104,7 +104,7 @@ class ZoomableImageScreen(
Spacer(Modifier.width(Spacing.s)) Spacer(Modifier.width(Spacing.s))
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
model.reduce(ZoomableImageMviModel.Intent.Share(url)) model.reduce(ZoomableImageMviModel.Intent.Share(url))
}, },
), ),

View File

@ -114,8 +114,8 @@ class InstanceInfoScreen(
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -145,7 +145,7 @@ class InstanceInfoScreen(
} }
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
val sheet = SortBottomSheet( val sheet = SortBottomSheet(
values = listOf( values = listOf(
SortType.Active, SortType.Active,
@ -158,7 +158,7 @@ class InstanceInfoScreen(
), ),
expandTop = true, expandTop = true,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
), ),
imageVector = uiState.sortType.toIcon(), imageVector = uiState.sortType.toIcon(),
@ -232,8 +232,8 @@ class InstanceInfoScreen(
items(uiState.communities) { items(uiState.communities) {
CommunityItem( CommunityItem(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen( CommunityDetailScreen(
community = it, community = it,
otherInstance = instanceName, otherInstance = instanceName,

View File

@ -95,13 +95,13 @@ class ColorBottomSheet : Screen {
horizontal = Spacing.s, horizontal = Spacing.s,
vertical = Spacing.s, vertical = Spacing.s,
).fillMaxWidth().onClick( ).fillMaxWidth().onClick(
rememberCallback { onClick = rememberCallback {
if (!isChooseCustom) { if (!isChooseCustom) {
notificationCenter.getObserver(NotificationCenterContractKeys.ChangeColor) notificationCenter.getObserver(NotificationCenterContractKeys.ChangeColor)
?.also { ?.also {
it.invoke(value.first ?: Unit) it.invoke(value.first ?: Unit)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
} else { } else {
customPickerDialogOpened = true customPickerDialogOpened = true
} }
@ -149,7 +149,7 @@ class ColorBottomSheet : Screen {
?.also { ?.also {
it.invoke(color) it.invoke(color)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
} }
) )
} }

View File

@ -73,13 +73,13 @@ class DurationBottomSheet(
horizontal = Spacing.s, horizontal = Spacing.s,
vertical = Spacing.s, vertical = Spacing.s,
).fillMaxWidth().onClick( ).fillMaxWidth().onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getObserver( notificationCenter.getObserver(
NotificationCenterContractKeys.ChangeZombieInterval NotificationCenterContractKeys.ChangeZombieInterval
)?.also { )?.also {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -74,14 +74,14 @@ class FontFamilyBottomSheet(
) )
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getObserver( notificationCenter.getObserver(
NotificationCenterContractKeys.ChangeFontFamily NotificationCenterContractKeys.ChangeFontFamily
) )
?.also { ?.also {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -75,11 +75,11 @@ class FontScaleBottomSheet(
) )
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getObserver(contract)?.also { notificationCenter.getObserver(contract)?.also {
it.invoke(value.scaleFactor) it.invoke(value.scaleFactor)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -61,12 +61,12 @@ class InboxTypeSheet : Screen {
) )
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getObserver(NotificationCenterContractKeys.ChangeInboxType) notificationCenter.getObserver(NotificationCenterContractKeys.ChangeInboxType)
?.also { ?.also {
it.invoke(true) it.invoke(true)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {
@ -83,12 +83,12 @@ class InboxTypeSheet : Screen {
horizontal = Spacing.s, horizontal = Spacing.s,
vertical = Spacing.m, vertical = Spacing.m,
).onClick( ).onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getObserver(NotificationCenterContractKeys.ChangeInboxType) notificationCenter.getObserver(NotificationCenterContractKeys.ChangeInboxType)
?.also { ?.also {
it.invoke(false) it.invoke(false)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -75,14 +75,14 @@ class LanguageBottomSheet : Screen {
) )
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getObserver( notificationCenter.getObserver(
NotificationCenterContractKeys.ChangeLanguage NotificationCenterContractKeys.ChangeLanguage
) )
?.also { ?.also {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -77,13 +77,13 @@ class ListingTypeBottomSheet(
) )
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getAllObservers( notificationCenter.getAllObservers(
NotificationCenterContractKeys.ChangeFeedType NotificationCenterContractKeys.ChangeFeedType
).forEach { ).forEach {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -67,12 +67,12 @@ class PostLayoutBottomSheet : Screen {
horizontal = Spacing.s, horizontal = Spacing.s,
vertical = Spacing.m, vertical = Spacing.m,
).fillMaxWidth().onClick( ).fillMaxWidth().onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getObserver(NotificationCenterContractKeys.ChangePostLayout) notificationCenter.getObserver(NotificationCenterContractKeys.ChangePostLayout)
?.also { ?.also {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -76,7 +76,7 @@ class SliderBottomSheet(
?.also { ?.also {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
) { ) {
Text(text = stringResource(MR.strings.button_confirm)) Text(text = stringResource(MR.strings.button_confirm))

View File

@ -109,14 +109,14 @@ internal class SortBottomSheetMain(
) )
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
if (value == SortType.Top.Generic && expandTop) { if (value == SortType.Top.Generic && expandTop) {
navigator.push(SortBottomSheetTop(contract = contract)) navigator.push(SortBottomSheetTop(contract = contract))
} else { } else {
notificationCenter.getAllObservers(contract).forEach { notificationCenter.getAllObservers(contract).forEach {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
} }
}, },
), ),
@ -172,7 +172,7 @@ internal class SortBottomSheetTop(
) { ) {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigator.pop() navigator.pop()
}, },
), ),
@ -199,13 +199,13 @@ internal class SortBottomSheetTop(
) )
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getAllObservers( notificationCenter.getAllObservers(
contract, contract,
).forEach { ).forEach {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -75,12 +75,12 @@ class ThemeBottomSheet : Screen {
) )
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
notificationCenter.getObserver(NotificationCenterContractKeys.ChangeTheme) notificationCenter.getObserver(NotificationCenterContractKeys.ChangeTheme)
?.also { ?.also {
it.invoke(value) it.invoke(value)
} }
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
}, },
), ),
) { ) {

View File

@ -1,20 +1,31 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation package com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator
import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.Tab
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
private const val NAVIGATION_DELAY = 250L
private const val BOTTOM_NAVIGATION_DELAY = 150L
private const val DEEP_LINK_DELAY = 500L
internal class DefaultNavigationCoordinator : NavigationCoordinator { internal class DefaultNavigationCoordinator : NavigationCoordinator {
override val onDoubleTabSelection = MutableSharedFlow<Tab>() override val onDoubleTabSelection = MutableSharedFlow<Tab>()
override val deepLinkUrl = MutableSharedFlow<String>() override val deepLinkUrl = MutableSharedFlow<String>()
override val inboxUnread = MutableStateFlow(0)
override val canPop: Boolean
get() = navigator?.canPop == true
private var connection: NestedScrollConnection? = null private var connection: NestedScrollConnection? = null
private var navigator: Navigator? = null private var navigator: Navigator? = null
@ -22,14 +33,12 @@ internal class DefaultNavigationCoordinator : NavigationCoordinator {
private var currentTab: Tab? = null private var currentTab: Tab? = null
private val scope = CoroutineScope(SupervisorJob()) private val scope = CoroutineScope(SupervisorJob())
private var canGoBackCallback: (() -> Boolean)? = null private var canGoBackCallback: (() -> Boolean)? = null
override val inboxUnread = MutableStateFlow(0) private var job: Job? = null
override fun setRootNavigator(value: Navigator?) { override fun setRootNavigator(value: Navigator?) {
navigator = value navigator = value
} }
override fun getRootNavigator(): Navigator? = navigator
override fun setBottomBarScrollConnection(value: NestedScrollConnection?) { override fun setBottomBarScrollConnection(value: NestedScrollConnection?) {
connection = value connection = value
} }
@ -48,8 +57,11 @@ internal class DefaultNavigationCoordinator : NavigationCoordinator {
override fun submitDeeplink(url: String) { override fun submitDeeplink(url: String) {
scope.launch { scope.launch {
delay(500) delay(DEEP_LINK_DELAY)
deepLinkUrl.emit(url) runCatching {
ensureActive()
deepLinkUrl.emit(url)
}
} }
} }
@ -67,7 +79,47 @@ internal class DefaultNavigationCoordinator : NavigationCoordinator {
bottomNavigator = value bottomNavigator = value
} }
override fun getBottomNavigator(): BottomSheetNavigator? { override fun showBottomSheet(screen: Screen) {
return bottomNavigator job?.cancel()
job = scope.launch {
delay(BOTTOM_NAVIGATION_DELAY)
runCatching {
ensureActive()
bottomNavigator?.show(screen)
}
}
}
override fun pushScreen(screen: Screen) {
job?.cancel()
job = scope.launch {
delay(NAVIGATION_DELAY)
runCatching {
ensureActive()
navigator?.push(screen)
}
}
}
override fun hideBottomSheet() {
job?.cancel()
job = scope.launch {
delay(BOTTOM_NAVIGATION_DELAY)
runCatching {
ensureActive()
bottomNavigator?.hide()
}
}
}
override fun popScreen() {
job?.cancel()
job = scope.launch {
delay(NAVIGATION_DELAY)
runCatching {
ensureActive()
navigator?.pop()
}
}
} }
} }

View File

@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator
import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.Tab
@ -14,26 +15,19 @@ interface NavigationCoordinator {
val onDoubleTabSelection: Flow<Tab> val onDoubleTabSelection: Flow<Tab>
val inboxUnread: StateFlow<Int> val inboxUnread: StateFlow<Int>
val deepLinkUrl: Flow<String?> val deepLinkUrl: Flow<String?>
val canPop: Boolean
fun setCurrentSection(tab: Tab) fun setCurrentSection(tab: Tab)
fun submitDeeplink(url: String) fun submitDeeplink(url: String)
fun setRootNavigator(value: Navigator?) fun setRootNavigator(value: Navigator?)
fun setCanGoBackCallback(value: (() -> Boolean)?) fun setCanGoBackCallback(value: (() -> Boolean)?)
fun getCanGoBackCallback(): (() -> Boolean)? fun getCanGoBackCallback(): (() -> Boolean)?
fun getRootNavigator(): Navigator?
fun setBottomBarScrollConnection(value: NestedScrollConnection?) fun setBottomBarScrollConnection(value: NestedScrollConnection?)
fun getBottomBarScrollConnection(): NestedScrollConnection? fun getBottomBarScrollConnection(): NestedScrollConnection?
fun setInboxUnread(count: Int) fun setInboxUnread(count: Int)
fun setBottomNavigator(value: BottomSheetNavigator?) fun setBottomNavigator(value: BottomSheetNavigator?)
fun showBottomSheet(screen: Screen)
fun getBottomNavigator(): BottomSheetNavigator? fun hideBottomSheet()
fun pushScreen(screen: Screen)
fun popScreen()
} }

View File

@ -43,6 +43,7 @@ interface PostDetailMviModel :
val comments: List<CommentModel> = emptyList(), val comments: List<CommentModel> = emptyList(),
val currentUserId: Int? = null, val currentUserId: Int? = null,
val swipeActionsEnabled: Boolean = true, val swipeActionsEnabled: Boolean = true,
val doubleTapActionEnabled: Boolean = true,
val postLayout: PostLayout = PostLayout.Card, val postLayout: PostLayout = PostLayout.Card,
val fullHeightImages: Boolean = true, val fullHeightImages: Boolean = true,
val separateUpAndDownVotes: Boolean = false, val separateUpAndDownVotes: Boolean = false,

View File

@ -230,7 +230,7 @@ class PostDetailScreen(
model.effects.onEach { evt -> model.effects.onEach { evt ->
when (evt) { when (evt) {
PostDetailMviModel.Effect.Close -> { PostDetailMviModel.Effect.Close -> {
navigationCoordinator.getRootNavigator()?.pop() navigationCoordinator.popScreen()
} }
is PostDetailMviModel.Effect.ScrollToComment -> { is PostDetailMviModel.Effect.ScrollToComment -> {
@ -263,7 +263,7 @@ class PostDetailScreen(
actions = { actions = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
val sheet = SortBottomSheet( val sheet = SortBottomSheet(
values = listOf( values = listOf(
SortType.Hot, SortType.Hot,
@ -273,7 +273,7 @@ class PostDetailScreen(
SortType.Controversial, SortType.Controversial,
), ),
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
), ),
imageVector = uiState.sortType.toIcon(), imageVector = uiState.sortType.toIcon(),
@ -282,12 +282,11 @@ class PostDetailScreen(
) )
}, },
navigationIcon = { navigationIcon = {
val navigator = navigationCoordinator.getRootNavigator() if (navigationCoordinator.canPop) {
if (navigator?.canPop == true) {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigator.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -328,7 +327,7 @@ class PostDetailScreen(
val screen = CreateCommentScreen( val screen = CreateCommentScreen(
originalPost = uiState.post, originalPost = uiState.post,
) )
navigationCoordinator.getBottomNavigator()?.show(screen) navigationCoordinator.showBottomSheet(screen)
}, },
) )
} }
@ -365,12 +364,12 @@ class PostDetailScreen(
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
blurNsfw = false, blurNsfw = false,
onOpenCommunity = rememberCallbackArgs { community -> onOpenCommunity = rememberCallbackArgs { community ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen(community = community) CommunityDetailScreen(community = community)
) )
}, },
onOpenCreator = rememberCallbackArgs { user -> onOpenCreator = rememberCallbackArgs { user ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen(user = user) UserDetailScreen(user = user)
) )
}, },
@ -405,7 +404,7 @@ class PostDetailScreen(
val screen = CreateCommentScreen( val screen = CreateCommentScreen(
originalPost = uiState.post, originalPost = uiState.post,
) )
navigationCoordinator.getBottomNavigator()?.show(screen) navigationCoordinator.showBottomSheet(screen)
} }
}, },
options = buildList { options = buildList {
@ -455,19 +454,19 @@ class PostDetailScreen(
OptionId.Delete -> model.reduce(PostDetailMviModel.Intent.DeletePost) OptionId.Delete -> model.reduce(PostDetailMviModel.Intent.DeletePost)
OptionId.Edit -> { OptionId.Edit -> {
navigationCoordinator.getBottomNavigator()?.show( navigationCoordinator.showBottomSheet(
CreatePostScreen(editedPost = uiState.post) CreatePostScreen(editedPost = uiState.post)
) )
} }
OptionId.Report -> { OptionId.Report -> {
navigationCoordinator.getBottomNavigator()?.show( navigationCoordinator.showBottomSheet(
CreateReportScreen(postId = uiState.post.id) CreateReportScreen(postId = uiState.post.id)
) )
} }
OptionId.CrossPost -> { OptionId.CrossPost -> {
navigationCoordinator.getBottomNavigator()?.show( navigationCoordinator.showBottomSheet(
CreatePostScreen(crossPost = uiState.post) CreatePostScreen(crossPost = uiState.post)
) )
} }
@ -482,7 +481,7 @@ class PostDetailScreen(
} }
}, },
onImageClick = rememberCallbackArgs { url -> onImageClick = rememberCallbackArgs { url ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
ZoomableImageScreen(url), ZoomableImageScreen(url),
) )
}, },
@ -523,15 +522,14 @@ class PostDetailScreen(
} }
Text( Text(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
val post = PostModel( val post = PostModel(
id = crossPost.id, id = crossPost.id,
community = community, community = community,
) )
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(
?.push( PostDetailScreen(post)
PostDetailScreen(post) )
)
}, },
), ),
text = string, text = string,
@ -639,6 +637,18 @@ class PostDetailScreen(
) )
) )
}, },
onDoubleClick = if (!uiState.doubleTapActionEnabled) {
null
} else {
rememberCallback(model) {
model.reduce(
PostDetailMviModel.Intent.UpVoteComment(
commentId = comment.id,
feedback = true,
),
)
}
},
onUpVote = rememberCallback(model) { onUpVote = rememberCallback(model) {
if (uiState.isLogged && !isOnOtherInstance) { if (uiState.isLogged && !isOnOtherInstance) {
model.reduce( model.reduce(
@ -675,32 +685,31 @@ class PostDetailScreen(
originalPost = uiState.post, originalPost = uiState.post,
originalComment = comment, originalComment = comment,
) )
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show(screen) screen
)
} }
}, },
onOpenCreator = rememberCallbackArgs { onOpenCreator = rememberCallbackArgs {
val user = comment.creator val user = comment.creator
if (user != null) { if (user != null) {
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(
?.push( UserDetailScreen(
UserDetailScreen( user = user,
user = user, otherInstance = otherInstanceName,
otherInstance = otherInstanceName, ),
), )
)
} }
}, },
onOpenCommunity = rememberCallbackArgs { onOpenCommunity = rememberCallbackArgs {
val community = comment.community val community = comment.community
if (community != null) { if (community != null) {
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(
?.push( CommunityDetailScreen(
CommunityDetailScreen( community = community,
community = community, otherInstance = otherInstanceName,
otherInstance = otherInstanceName, ),
), )
)
} }
}, },
options = buildList { options = buildList {
@ -742,21 +751,19 @@ class PostDetailScreen(
) )
OptionId.Edit -> { OptionId.Edit -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreateCommentScreen(
CreateCommentScreen( editedComment = comment,
editedComment = comment,
)
) )
)
} }
OptionId.Report -> { OptionId.Report -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreateReportScreen(
CreateReportScreen( commentId = comment.id
commentId = comment.id
)
) )
)
} }
OptionId.SeeRaw -> { OptionId.SeeRaw -> {
@ -815,14 +822,13 @@ class PostDetailScreen(
originalPost = uiState.post, originalPost = uiState.post,
originalComment = comment, originalComment = comment,
) )
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(screen)
?.show(screen)
} }
}, },
onOpenCreator = rememberCallbackArgs { onOpenCreator = rememberCallbackArgs {
val user = comment.creator val user = comment.creator
if (user != null) { if (user != null) {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen( UserDetailScreen(
user = user, user = user,
otherInstance = otherInstanceName, otherInstance = otherInstanceName,
@ -867,21 +873,19 @@ class PostDetailScreen(
) )
OptionId.Edit -> { OptionId.Edit -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreateCommentScreen(
CreateCommentScreen( editedComment = comment,
editedComment = comment,
)
) )
)
} }
OptionId.Report -> { OptionId.Report -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreateReportScreen(
CreateReportScreen( commentId = comment.id
commentId = comment.id
)
) )
)
} }
OptionId.SeeRaw -> { OptionId.SeeRaw -> {

View File

@ -70,6 +70,7 @@ class PostDetailViewModel(
mvi.updateState { mvi.updateState {
it.copy( it.copy(
swipeActionsEnabled = settings.enableSwipeActions, swipeActionsEnabled = settings.enableSwipeActions,
doubleTapActionEnabled = settings.enableDoubleTapAction,
sortType = settings.defaultCommentSortType.toSortType(), sortType = settings.defaultCommentSortType.toSortType(),
separateUpAndDownVotes = settings.separateUpAndDownVotes, separateUpAndDownVotes = settings.separateUpAndDownVotes,
autoLoadImages = settings.autoLoadImages, autoLoadImages = settings.autoLoadImages,

View File

@ -73,7 +73,7 @@ class CreateReportScreen(
} }
CreateReportMviModel.Effect.Success -> { CreateReportMviModel.Effect.Success -> {
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
} }
} }
}.launchIn(this) }.launchIn(this)

View File

@ -138,7 +138,7 @@ class SavedItemsScreen : Screen {
actions = { actions = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
val sheet = SortBottomSheet( val sheet = SortBottomSheet(
values = listOf( values = listOf(
SortType.Hot, SortType.Hot,
@ -146,7 +146,7 @@ class SavedItemsScreen : Screen {
SortType.Old, SortType.Old,
), ),
) )
navigatorCoordinator.getBottomNavigator()?.show(sheet) navigatorCoordinator.showBottomSheet(sheet)
}, },
), ),
imageVector = uiState.sortType.toIcon(), imageVector = uiState.sortType.toIcon(),
@ -157,8 +157,8 @@ class SavedItemsScreen : Screen {
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigatorCoordinator.getRootNavigator()?.pop() navigatorCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -250,19 +250,18 @@ class SavedItemsScreen : Screen {
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
blurNsfw = uiState.blurNsfw, blurNsfw = uiState.blurNsfw,
onClick = { onClick = {
navigatorCoordinator.getRootNavigator()?.push( navigatorCoordinator.pushScreen(
PostDetailScreen(post), PostDetailScreen(post),
) )
}, },
onOpenCommunity = { community -> onOpenCommunity = { community ->
navigatorCoordinator.getRootNavigator()?.push( navigatorCoordinator.pushScreen(
CommunityDetailScreen(community), CommunityDetailScreen(community),
) )
}, },
onOpenCreator = { u -> onOpenCreator = { u ->
if (u.id != uiState.user?.id) { if (u.id != uiState.user?.id) {
navigatorCoordinator.getRootNavigator() navigatorCoordinator.pushScreen(UserDetailScreen(u))
?.push(UserDetailScreen(u))
} }
}, },
onUpVote = { onUpVote = {
@ -293,10 +292,10 @@ class SavedItemsScreen : Screen {
val screen = CreateCommentScreen( val screen = CreateCommentScreen(
originalPost = post, originalPost = post,
) )
navigatorCoordinator.getBottomNavigator()?.show(screen) navigatorCoordinator.showBottomSheet(screen)
}, },
onImageClick = { url -> onImageClick = { url ->
navigatorCoordinator.getRootNavigator()?.push( navigatorCoordinator.pushScreen(
ZoomableImageScreen(url), ZoomableImageScreen(url),
) )
}, },
@ -323,7 +322,7 @@ class SavedItemsScreen : Screen {
onOptionSelected = { optionIndex -> onOptionSelected = { optionIndex ->
when (optionIndex) { when (optionIndex) {
OptionId.Report -> { OptionId.Report -> {
navigatorCoordinator.getBottomNavigator()?.show( navigatorCoordinator.showBottomSheet(
CreateReportScreen( CreateReportScreen(
postId = post.id postId = post.id
) )
@ -373,7 +372,7 @@ class SavedItemsScreen : Screen {
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
hideIndent = true, hideIndent = true,
onClick = { onClick = {
navigatorCoordinator.getRootNavigator()?.push( navigatorCoordinator.pushScreen(
PostDetailScreen( PostDetailScreen(
post = PostModel(id = comment.postId), post = PostModel(id = comment.postId),
highlightCommentId = comment.id, highlightCommentId = comment.id,
@ -409,7 +408,7 @@ class SavedItemsScreen : Screen {
originalPost = PostModel(id = comment.postId), originalPost = PostModel(id = comment.postId),
originalComment = comment, originalComment = comment,
) )
navigatorCoordinator.getBottomNavigator()?.show(screen) navigatorCoordinator.showBottomSheet(screen)
}, },
options = buildList { options = buildList {
add( add(
@ -428,7 +427,7 @@ class SavedItemsScreen : Screen {
onOptionSelected = { optionIndex -> onOptionSelected = { optionIndex ->
when (optionIndex) { when (optionIndex) {
OptionId.Report -> { OptionId.Report -> {
navigatorCoordinator.getBottomNavigator()?.show( navigatorCoordinator.showBottomSheet(
CreateReportScreen( CreateReportScreen(
commentId = comment.id commentId = comment.id
) )

View File

@ -97,7 +97,7 @@ class SelectCommunityDialog : Screen {
trailingIcon = { trailingIcon = {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
if (uiState.searchText.isNotEmpty()) { if (uiState.searchText.isNotEmpty()) {
model.reduce(SelectCommunityMviModel.Intent.SetSearch("")) model.reduce(SelectCommunityMviModel.Intent.SetSearch(""))
} }
@ -127,16 +127,18 @@ class SelectCommunityDialog : Screen {
CommunityItem( CommunityItem(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
.background(MaterialTheme.colorScheme.background) .background(MaterialTheme.colorScheme.background)
.onClick(rememberCallback { .onClick(
notificationCenter.getObserver( onClick = rememberCallback {
NotificationCenterContractKeys.SelectCommunity notificationCenter.getObserver(
) NotificationCenterContractKeys.SelectCommunity
?.invoke(community) )
notificationCenter.getObserver( ?.invoke(community)
NotificationCenterContractKeys.CloseDialog notificationCenter.getObserver(
) NotificationCenterContractKeys.CloseDialog
?.invoke(Unit) )
}), ?.invoke(Unit)
},
),
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
community = community, community = community,
) )

View File

@ -46,6 +46,7 @@ interface UserDetailMviModel :
val user: UserModel = UserModel(), val user: UserModel = UserModel(),
val blurNsfw: Boolean = true, val blurNsfw: Boolean = true,
val swipeActionsEnabled: Boolean = true, val swipeActionsEnabled: Boolean = true,
val doubleTapActionEnabled: Boolean = true,
val postLayout: PostLayout = PostLayout.Card, val postLayout: PostLayout = PostLayout.Card,
val fullHeightImages: Boolean = true, val fullHeightImages: Boolean = true,
val separateUpAndDownVotes: Boolean = false, val separateUpAndDownVotes: Boolean = false,

View File

@ -227,11 +227,11 @@ class UserDetailScreen(
// sort button // sort button
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
val sheet = SortBottomSheet( val sheet = SortBottomSheet(
expandTop = true, expandTop = true,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
), ),
imageVector = uiState.sortType.toIcon(), imageVector = uiState.sortType.toIcon(),
@ -257,7 +257,7 @@ class UserDetailScreen(
modifier = Modifier.onGloballyPositioned { modifier = Modifier.onGloballyPositioned {
optionsOffset = it.positionInParent() optionsOffset = it.positionInParent()
}.padding(start = Spacing.s).onClick( }.padding(start = Spacing.s).onClick(
rememberCallback { onClick = rememberCallback {
optionsExpanded = true optionsExpanded = true
}, },
), ),
@ -281,7 +281,7 @@ class UserDetailScreen(
horizontal = Spacing.m, horizontal = Spacing.m,
vertical = Spacing.s, vertical = Spacing.s,
).onClick( ).onClick(
rememberCallback { onClick = rememberCallback {
optionsExpanded = false optionsExpanded = false
when (option.id) { when (option.id) {
OptionId.BlockInstance -> model.reduce( OptionId.BlockInstance -> model.reduce(
@ -303,12 +303,11 @@ class UserDetailScreen(
} }
}, },
navigationIcon = { navigationIcon = {
val navigator = navigationCoordinator.getRootNavigator() if (navigationCoordinator.canPop) {
if (navigator?.canPop == true) {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigator.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -348,7 +347,7 @@ class UserDetailScreen(
text = stringResource(MR.strings.action_chat), text = stringResource(MR.strings.action_chat),
onSelected = rememberCallback { onSelected = rememberCallback {
val screen = InboxChatScreen(otherUserId = user.id) val screen = InboxChatScreen(otherUserId = user.id)
navigationCoordinator.getRootNavigator()?.push(screen) navigationCoordinator.pushScreen(screen)
}, },
) )
} }
@ -391,8 +390,7 @@ class UserDetailScreen(
user = uiState.user, user = uiState.user,
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
onOpenImage = rememberCallbackArgs { url -> onOpenImage = rememberCallbackArgs { url ->
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(ZoomableImageScreen(url))
?.push(ZoomableImageScreen(url))
}, },
) )
SectionSelector( SectionSelector(
@ -486,8 +484,21 @@ class UserDetailScreen(
separateUpAndDownVotes = uiState.separateUpAndDownVotes, separateUpAndDownVotes = uiState.separateUpAndDownVotes,
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
onClick = rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(
?.push(PostDetailScreen(post = post)) PostDetailScreen(post = post),
)
},
onDoubleClick = if (!uiState.doubleTapActionEnabled) {
null
} else {
rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.UpVotePost(
id = post.id,
feedback = true,
),
)
}
}, },
onUpVote = if (!uiState.isLogged || isOnOtherInstance) { onUpVote = if (!uiState.isLogged || isOnOtherInstance) {
null null
@ -526,8 +537,9 @@ class UserDetailScreen(
} }
}, },
onOpenCommunity = rememberCallbackArgs { community -> onOpenCommunity = rememberCallbackArgs { community ->
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(
?.push(CommunityDetailScreen(community)) CommunityDetailScreen(community),
)
}, },
onReply = if (!uiState.isLogged || isOnOtherInstance) { onReply = if (!uiState.isLogged || isOnOtherInstance) {
null null
@ -536,12 +548,11 @@ class UserDetailScreen(
val screen = CreateCommentScreen( val screen = CreateCommentScreen(
originalPost = post, originalPost = post,
) )
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(screen)
?.show(screen)
} }
}, },
onImageClick = rememberCallbackArgs { url -> onImageClick = rememberCallbackArgs { url ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
ZoomableImageScreen(url), ZoomableImageScreen(url),
) )
}, },
@ -576,19 +587,17 @@ class UserDetailScreen(
onOptionSelected = rememberCallbackArgs { optionId -> onOptionSelected = rememberCallbackArgs { optionId ->
when (optionId) { when (optionId) {
OptionId.Report -> { OptionId.Report -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreateReportScreen(
CreateReportScreen( postId = post.id
postId = post.id
)
) )
)
} }
OptionId.CrossPost -> { OptionId.CrossPost -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreatePostScreen(crossPost = post)
CreatePostScreen(crossPost = post) )
)
} }
OptionId.SeeRaw -> { OptionId.SeeRaw -> {
@ -689,13 +698,25 @@ class UserDetailScreen(
hideAuthor = true, hideAuthor = true,
hideIndent = true, hideIndent = true,
onClick = rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
PostDetailScreen( PostDetailScreen(
post = PostModel(id = comment.postId), post = PostModel(id = comment.postId),
highlightCommentId = comment.id, highlightCommentId = comment.id,
) )
) )
}, },
onDoubleClick = if (!uiState.doubleTapActionEnabled) {
null
} else {
rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.UpVoteComment(
id = comment.id,
feedback = true,
),
)
}
},
onSave = if (!uiState.isLogged || isOnOtherInstance) { onSave = if (!uiState.isLogged || isOnOtherInstance) {
null null
} else { } else {
@ -740,13 +761,15 @@ class UserDetailScreen(
originalPost = PostModel(id = comment.postId), originalPost = PostModel(id = comment.postId),
originalComment = comment, originalComment = comment,
) )
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(screen)
?.show(screen)
} }
}, },
onOpenCommunity = rememberCallbackArgs { community -> onOpenCommunity = rememberCallbackArgs { community ->
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(
?.push(CommunityDetailScreen(community)) CommunityDetailScreen(
community
)
)
}, },
options = buildList { options = buildList {
add( add(
@ -767,12 +790,11 @@ class UserDetailScreen(
onOptionSelected = rememberCallbackArgs { optionId -> onOptionSelected = rememberCallbackArgs { optionId ->
when (optionId) { when (optionId) {
OptionId.Report -> { OptionId.Report -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreateReportScreen(
CreateReportScreen( commentId = comment.id
commentId = comment.id ),
) )
)
} }
OptionId.SeeRaw -> { OptionId.SeeRaw -> {

View File

@ -79,6 +79,7 @@ class UserDetailViewModel(
it.copy( it.copy(
blurNsfw = settings.blurNsfw, blurNsfw = settings.blurNsfw,
swipeActionsEnabled = settings.enableSwipeActions, swipeActionsEnabled = settings.enableSwipeActions,
doubleTapActionEnabled = settings.enableDoubleTapAction,
sortType = settings.defaultPostSortType.toSortType(), sortType = settings.defaultPostSortType.toSortType(),
separateUpAndDownVotes = settings.separateUpAndDownVotes, separateUpAndDownVotes = settings.separateUpAndDownVotes,
autoLoadImages = settings.autoLoadImages, autoLoadImages = settings.autoLoadImages,

View File

@ -35,7 +35,7 @@ class WebViewScreen(
override fun Content() { override fun Content() {
val navigationCoordinator = remember { getNavigationCoordinator() } val navigationCoordinator = remember { getNavigationCoordinator() }
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
var shareHelper = remember { getShareHelper() } val shareHelper = remember { getShareHelper() }
val drawerCoordinator = remember { getDrawerCoordinator() } val drawerCoordinator = remember { getDrawerCoordinator() }
DisposableEffect(key) { DisposableEffect(key) {
@ -53,8 +53,8 @@ class WebViewScreen(
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -65,7 +65,7 @@ class WebViewScreen(
actions = { actions = {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
shareHelper.share(url, "text/plain") shareHelper.share(url, "text/plain")
}, },
), ),
@ -80,7 +80,6 @@ class WebViewScreen(
Box( Box(
modifier = Modifier.padding(paddingValues) modifier = Modifier.padding(paddingValues)
) { ) {
val navigationCoordinator = remember { getNavigationCoordinator() }
val webNavigator = rememberWebViewNavigator() val webNavigator = rememberWebViewNavigator()
DisposableEffect(key) { DisposableEffect(key) {

View File

@ -46,6 +46,7 @@ kotlin {
api(libs.markdown) api(libs.markdown)
implementation(projects.coreCommonui.components) implementation(projects.coreCommonui.components)
implementation(projects.coreUtils)
implementation(projects.resources) implementation(projects.resources)
} }
} }

View File

@ -3,11 +3,11 @@ package com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose
import android.content.Context import android.content.Context
import android.graphics.Typeface import android.graphics.Typeface
import android.util.TypedValue import android.util.TypedValue
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
@ -30,6 +30,8 @@ import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownColo
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownPadding import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownPadding
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownTypography import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownTypography
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.ReferenceLinkHandlerImpl import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.ReferenceLinkHandlerImpl
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.DateTime
import io.noties.markwon.image.AsyncDrawableSpan import io.noties.markwon.image.AsyncDrawableSpan
import org.intellij.markdown.flavours.MarkdownFlavourDescriptor import org.intellij.markdown.flavours.MarkdownFlavourDescriptor
@ -50,6 +52,7 @@ actual fun CustomMarkdown(
autoLoadImages: Boolean, autoLoadImages: Boolean,
onOpenImage: ((String) -> Unit)?, onOpenImage: ((String) -> Unit)?,
onClick: (() -> Unit)?, onClick: (() -> Unit)?,
onDoubleClick: (() -> Unit)?,
) { ) {
CompositionLocalProvider( CompositionLocalProvider(
LocalReferenceLinkHandler provides ReferenceLinkHandlerImpl(), LocalReferenceLinkHandler provides ReferenceLinkHandlerImpl(),
@ -64,10 +67,10 @@ actual fun CustomMarkdown(
) )
} }
BoxWithConstraints( BoxWithConstraints(
modifier = modifier.clickable( modifier = modifier.onClick(
interactionSource = remember { MutableInteractionSource() }, onClick = onClick ?: {},
indication = null onDoubleClick = onDoubleClick ?: {},
) { onClick?.invoke() } )
) { ) {
val style = LocalMarkdownTypography.current.text val style = LocalMarkdownTypography.current.text
val fontScale = LocalDensity.current.fontScale * 1.25f val fontScale = LocalDensity.current.fontScale * 1.25f
@ -93,9 +96,35 @@ actual fun CustomMarkdown(
typeface = typeface, typeface = typeface,
fontSize = style.fontSize * fontScale, fontSize = style.fontSize * fontScale,
).apply { ).apply {
setOnClickListener { val gestureDetector =
onClick?.invoke() GestureDetector(
} ctx,
object : GestureDetector.SimpleOnGestureListener() {
private var lastClickTime = 0L
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
val currentTime = DateTime.epochMillis()
if ((currentTime - lastClickTime) < 300) return false
lastClickTime = currentTime
onClick?.invoke()
return true
}
override fun onDoubleTap(e: MotionEvent): Boolean {
val currentTime = DateTime.epochMillis()
if ((currentTime - lastClickTime) < 300) return false
lastClickTime = currentTime
onDoubleClick?.invoke()
return true
}
override fun onDown(e: MotionEvent): Boolean {
return true
}
}
)
setOnTouchListener { _, evt -> gestureDetector.onTouchEvent(evt) }
} }
}, },
update = { textView -> update = { textView ->

View File

@ -38,4 +38,5 @@ expect fun CustomMarkdown(
autoLoadImages: Boolean = true, autoLoadImages: Boolean = true,
onOpenImage: ((String) -> Unit)? = null, onOpenImage: ((String) -> Unit)? = null,
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
) )

View File

@ -4,9 +4,7 @@ import androidx.compose.animation.core.InfiniteRepeatableSpec
import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
@ -40,6 +38,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose.LocalMarkd
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose.LocalReferenceLinkHandler import com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose.LocalReferenceLinkHandler
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.utils.TAG_IMAGE_URL import com.github.diegoberaldin.raccoonforlemmy.core.markdown.utils.TAG_IMAGE_URL
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.utils.TAG_URL import com.github.diegoberaldin.raccoonforlemmy.core.markdown.utils.TAG_URL
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.resources.MR import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.stringResource import dev.icerock.moko.resources.compose.stringResource
@ -121,12 +120,9 @@ internal fun MarkdownText(
CustomImage( CustomImage(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable( .onClick(
interactionSource = remember { MutableInteractionSource() }, onClick = { onOpenImage?.invoke(imageUrl) },
indication = null ),
) {
onOpenImage?.invoke(imageUrl)
},
url = link, url = link,
autoload = autoLoadImages, autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,
@ -175,12 +171,10 @@ internal fun MarkdownText(
// TODO: improve fixed values // TODO: improve fixed values
.heightIn(min = 200.dp, max = Dp.Unspecified) .heightIn(min = 200.dp, max = Dp.Unspecified)
.clip(RoundedCornerShape(20.dp)) .clip(RoundedCornerShape(20.dp))
.clickable( .onClick(
interactionSource = remember { MutableInteractionSource() }, onClick = { onOpenImage?.invoke(imageUrl) },
indication = null onDoubleClick = {},
) { ),
onOpenImage?.invoke(imageUrl)
},
url = imageUrl, url = imageUrl,
autoload = autoLoadImages, autoload = autoLoadImages,
quality = FilterQuality.Low, quality = FilterQuality.Low,

View File

@ -44,3 +44,4 @@ internal fun List<ASTNode>.innerList(): List<ASTNode> = this.subList(1, this.siz
internal fun List<ASTNode>.filterNonListTypes(): List<ASTNode> = this.filter { n -> internal fun List<ASTNode>.filterNonListTypes(): List<ASTNode> = this.filter { n ->
n.type != MarkdownElementTypes.ORDERED_LIST && n.type != MarkdownElementTypes.UNORDERED_LIST && n.type != MarkdownTokenTypes.EOL n.type != MarkdownElementTypes.ORDERED_LIST && n.type != MarkdownElementTypes.UNORDERED_LIST && n.type != MarkdownTokenTypes.EOL
} }

View File

@ -1,13 +1,10 @@
package com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose package com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose.elements.MarkdownBlockQuote import com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose.elements.MarkdownBlockQuote
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose.elements.MarkdownBulletList import com.github.diegoberaldin.raccoonforlemmy.core.markdown.compose.elements.MarkdownBulletList
@ -22,6 +19,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownColo
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownPadding import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownPadding
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownTypography import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownTypography
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.ReferenceLinkHandlerImpl import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.ReferenceLinkHandlerImpl
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import org.intellij.markdown.MarkdownElementTypes import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.MarkdownElementTypes.ATX_1 import org.intellij.markdown.MarkdownElementTypes.ATX_1
import org.intellij.markdown.MarkdownElementTypes.ATX_2 import org.intellij.markdown.MarkdownElementTypes.ATX_2
@ -62,6 +60,7 @@ actual fun CustomMarkdown(
autoLoadImages: Boolean, autoLoadImages: Boolean,
onOpenImage: ((String) -> Unit)?, onOpenImage: ((String) -> Unit)?,
onClick: (() -> Unit)?, onClick: (() -> Unit)?,
onDoubleClick: (() -> Unit)?,
) { ) {
val matches = Regex("::: spoiler (?<title>.*?)\\n(?<content>.*?)\\n:::\\n").findAll(content) val matches = Regex("::: spoiler (?<title>.*?)\\n(?<content>.*?)\\n:::\\n").findAll(content)
val mangledContent = buildString { val mangledContent = buildString {
@ -97,10 +96,12 @@ actual fun CustomMarkdown(
LocalMarkdownColors provides colors, LocalMarkdownColors provides colors,
LocalMarkdownTypography provides typography, LocalMarkdownTypography provides typography,
) { ) {
Column(modifier.clickable( Column(
interactionSource = remember { MutableInteractionSource() }, modifier = modifier.onClick(
indication = null onClick = onClick ?: {},
) { onClick?.invoke() }) { onDoubleClick = onDoubleClick ?: {},
)
) {
val parsedTree = MarkdownParser(flavour).buildMarkdownTreeFromString(mangledContent) val parsedTree = MarkdownParser(flavour).buildMarkdownTreeFromString(mangledContent)
parsedTree.children.forEach { node -> parsedTree.children.forEach { node ->
if (!node.handleElement( if (!node.handleElement(

View File

@ -20,6 +20,7 @@ data class SettingsModel(
val dynamicColors: Boolean = false, val dynamicColors: Boolean = false,
val openUrlsInExternalBrowser: Boolean = false, val openUrlsInExternalBrowser: Boolean = false,
val enableSwipeActions: Boolean = true, val enableSwipeActions: Boolean = true,
val enableDoubleTapAction: Boolean = false,
val customSeedColor: Int? = null, val customSeedColor: Int? = null,
val upvoteColor: Int? = null, val upvoteColor: Int? = null,
val downvoteColor: Int? = null, val downvoteColor: Int? = null,

View File

@ -25,6 +25,7 @@ private object KeyStoreKeys {
const val DynamicColors = "dynamicColors" const val DynamicColors = "dynamicColors"
const val OpenUrlsInExternalBrowser = "openUrlsInExternalBrowser" const val OpenUrlsInExternalBrowser = "openUrlsInExternalBrowser"
const val EnableSwipeActions = "enableSwipeActions" const val EnableSwipeActions = "enableSwipeActions"
const val EnableDoubleTapAction = "enableDoubleTapAction"
const val CustomSeedColor = "customPrimaryColor" const val CustomSeedColor = "customPrimaryColor"
const val PostLayout = "postLayout" const val PostLayout = "postLayout"
const val SeparateUpAndDownVotes = "separateUpAndDownVotes" const val SeparateUpAndDownVotes = "separateUpAndDownVotes"
@ -64,6 +65,7 @@ internal class DefaultSettingsRepository(
dynamicColors = if (settings.dynamicColors) 1L else 0L, dynamicColors = if (settings.dynamicColors) 1L else 0L,
openUrlsInExternalBrowser = if (settings.openUrlsInExternalBrowser) 1L else 0L, openUrlsInExternalBrowser = if (settings.openUrlsInExternalBrowser) 1L else 0L,
enableSwipeActions = if (settings.enableSwipeActions) 1L else 0L, enableSwipeActions = if (settings.enableSwipeActions) 1L else 0L,
enableDoubleTapAction = if (settings.enableDoubleTapAction) 1L else 0L,
customSeedColor = settings.customSeedColor?.toLong(), customSeedColor = settings.customSeedColor?.toLong(),
account_id = accountId, account_id = accountId,
postLayout = settings.postLayout.toLong(), postLayout = settings.postLayout.toLong(),
@ -100,6 +102,7 @@ internal class DefaultSettingsRepository(
dynamicColors = keyStore[KeyStoreKeys.DynamicColors, false], dynamicColors = keyStore[KeyStoreKeys.DynamicColors, false],
openUrlsInExternalBrowser = keyStore[KeyStoreKeys.OpenUrlsInExternalBrowser, false], openUrlsInExternalBrowser = keyStore[KeyStoreKeys.OpenUrlsInExternalBrowser, false],
enableSwipeActions = keyStore[KeyStoreKeys.EnableSwipeActions, true], enableSwipeActions = keyStore[KeyStoreKeys.EnableSwipeActions, true],
enableDoubleTapAction = keyStore[KeyStoreKeys.EnableDoubleTapAction, false],
customSeedColor = if (!keyStore.containsKey(KeyStoreKeys.CustomSeedColor)) null else keyStore[KeyStoreKeys.CustomSeedColor, 0], customSeedColor = if (!keyStore.containsKey(KeyStoreKeys.CustomSeedColor)) null else keyStore[KeyStoreKeys.CustomSeedColor, 0],
postLayout = keyStore[KeyStoreKeys.PostLayout, 0], postLayout = keyStore[KeyStoreKeys.PostLayout, 0],
separateUpAndDownVotes = keyStore[KeyStoreKeys.SeparateUpAndDownVotes, false], separateUpAndDownVotes = keyStore[KeyStoreKeys.SeparateUpAndDownVotes, false],
@ -148,6 +151,7 @@ internal class DefaultSettingsRepository(
value = settings.openUrlsInExternalBrowser value = settings.openUrlsInExternalBrowser
) )
keyStore.save(KeyStoreKeys.EnableSwipeActions, settings.enableSwipeActions) keyStore.save(KeyStoreKeys.EnableSwipeActions, settings.enableSwipeActions)
keyStore.save(KeyStoreKeys.EnableDoubleTapAction, settings.enableDoubleTapAction)
if (settings.customSeedColor != null) { if (settings.customSeedColor != null) {
keyStore.save(KeyStoreKeys.CustomSeedColor, settings.customSeedColor) keyStore.save(KeyStoreKeys.CustomSeedColor, settings.customSeedColor)
} else { } else {
@ -196,6 +200,7 @@ internal class DefaultSettingsRepository(
dynamicColors = if (settings.dynamicColors) 1L else 0L, dynamicColors = if (settings.dynamicColors) 1L else 0L,
openUrlsInExternalBrowser = if (settings.openUrlsInExternalBrowser) 1L else 0L, openUrlsInExternalBrowser = if (settings.openUrlsInExternalBrowser) 1L else 0L,
enableSwipeActions = if (settings.enableSwipeActions) 1L else 0L, enableSwipeActions = if (settings.enableSwipeActions) 1L else 0L,
enableDoubleTapAction = if (settings.enableDoubleTapAction) 1L else 0L,
customSeedColor = settings.customSeedColor?.toLong(), customSeedColor = settings.customSeedColor?.toLong(),
postLayout = settings.postLayout.toLong(), postLayout = settings.postLayout.toLong(),
separateUpAndDownVotes = if (settings.separateUpAndDownVotes) 1L else 0L, separateUpAndDownVotes = if (settings.separateUpAndDownVotes) 1L else 0L,
@ -233,6 +238,7 @@ private fun GetBy.toModel() = SettingsModel(
dynamicColors = dynamicColors != 0L, dynamicColors = dynamicColors != 0L,
openUrlsInExternalBrowser = openUrlsInExternalBrowser != 0L, openUrlsInExternalBrowser = openUrlsInExternalBrowser != 0L,
enableSwipeActions = enableSwipeActions != 0L, enableSwipeActions = enableSwipeActions != 0L,
enableDoubleTapAction = enableDoubleTapAction != 0L,
customSeedColor = customSeedColor?.toInt(), customSeedColor = customSeedColor?.toInt(),
postLayout = postLayout.toInt(), postLayout = postLayout.toInt(),
separateUpAndDownVotes = separateUpAndDownVotes != 0L, separateUpAndDownVotes = separateUpAndDownVotes != 0L,

View File

@ -14,6 +14,7 @@ CREATE TABLE SettingsEntity (
dynamicColors INTEGER NOT NULL DEFAULT 0, dynamicColors INTEGER NOT NULL DEFAULT 0,
openUrlsInExternalBrowser INTEGER NOT NULL DEFAULT 0, openUrlsInExternalBrowser INTEGER NOT NULL DEFAULT 0,
enableSwipeActions INTEGER NOT NULL DEFAULT 1, enableSwipeActions INTEGER NOT NULL DEFAULT 1,
enableDoubleTapAction INTEGER NOT NULL DEFAULT 1,
customSeedColor INTEGER DEFAULT NULL, customSeedColor INTEGER DEFAULT NULL,
postLayout INTEGER NOT NULL DEFAULT 0, postLayout INTEGER NOT NULL DEFAULT 0,
separateUpAndDownVotes INTEGER NOT NULL DEFAULT 0, separateUpAndDownVotes INTEGER NOT NULL DEFAULT 0,
@ -46,6 +47,7 @@ INSERT OR IGNORE INTO SettingsEntity (
dynamicColors, dynamicColors,
openUrlsInExternalBrowser, openUrlsInExternalBrowser,
enableSwipeActions, enableSwipeActions,
enableDoubleTapAction,
customSeedColor, customSeedColor,
postLayout, postLayout,
separateUpAndDownVotes, separateUpAndDownVotes,
@ -84,6 +86,7 @@ INSERT OR IGNORE INTO SettingsEntity (
?, ?,
?, ?,
?, ?,
?,
? ?
); );
@ -103,6 +106,7 @@ SET theme = ?,
dynamicColors = ?, dynamicColors = ?,
openUrlsInExternalBrowser = ?, openUrlsInExternalBrowser = ?,
enableSwipeActions = ?, enableSwipeActions = ?,
enableDoubleTapAction = ?,
customSeedColor = ?, customSeedColor = ?,
postLayout = ?, postLayout = ?,
separateUpAndDownVotes = ?, separateUpAndDownVotes = ?,
@ -133,6 +137,7 @@ SELECT
dynamicColors, dynamicColors,
openUrlsInExternalBrowser, openUrlsInExternalBrowser,
enableSwipeActions, enableSwipeActions,
enableDoubleTapAction,
customSeedColor, customSeedColor,
postLayout, postLayout,
separateUpAndDownVotes, separateUpAndDownVotes,

View File

@ -0,0 +1,2 @@
ALTER TABLE SettingsEntity
ADD COLUMN enableDoubleTapAction INTEGER NOT NULL DEFAULT 1;

View File

@ -1,16 +1,37 @@
package com.github.diegoberaldin.raccoonforlemmy.core.utils.compose package com.github.diegoberaldin.raccoonforlemmy.core.utils.compose
import androidx.compose.foundation.clickable import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.composed import androidx.compose.ui.composed
import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.DateTime
fun Modifier.onClick(onClick: () -> Unit): Modifier = composed { @OptIn(ExperimentalFoundationApi::class)
clickable( fun Modifier.onClick(
debounceInterval: Long = 300,
onClick: () -> Unit = {},
onDoubleClick: () -> Unit = {},
): Modifier = composed {
var lastClickTime by remember { mutableStateOf(0L) }
combinedClickable(
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
) { onClick = {
onClick() val currentTime = DateTime.epochMillis()
} if ((currentTime - lastClickTime) < debounceInterval) return@combinedClickable
lastClickTime = currentTime
onClick()
},
onDoubleClick = {
val currentTime = DateTime.epochMillis()
if ((currentTime - lastClickTime) < debounceInterval) return@combinedClickable
lastClickTime = currentTime
onDoubleClick()
},
)
} }

View File

@ -35,6 +35,6 @@ val PostModel.shareUrl: String
} }
val PostModel.imageUrl: String val PostModel.imageUrl: String
get() = thumbnailUrl?.takeIf { it.isNotEmpty() } ?: run { get() = url?.takeIf { it.looksLikeAnImage }?.takeIf { it.isNotEmpty() } ?: run {
url?.takeIf { it.looksLikeAnImage } thumbnailUrl
}.orEmpty() }.orEmpty()

View File

@ -44,6 +44,7 @@ interface PostListMviModel :
val blurNsfw: Boolean = true, val blurNsfw: Boolean = true,
val currentUserId: Int? = null, val currentUserId: Int? = null,
val swipeActionsEnabled: Boolean = true, val swipeActionsEnabled: Boolean = true,
val doubleTapActionEnabled: Boolean = false,
val postLayout: PostLayout = PostLayout.Card, val postLayout: PostLayout = PostLayout.Card,
val fullHeightImages: Boolean = true, val fullHeightImages: Boolean = true,
val separateUpAndDownVotes: Boolean = false, val separateUpAndDownVotes: Boolean = false,

View File

@ -198,13 +198,13 @@ class PostListScreen : Screen {
val sheet = ListingTypeBottomSheet( val sheet = ListingTypeBottomSheet(
isLogged = uiState.isLogged, isLogged = uiState.isLogged,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
onSelectSortType = rememberCallback { onSelectSortType = rememberCallback {
val sheet = SortBottomSheet( val sheet = SortBottomSheet(
expandTop = true, expandTop = true,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
}, },
@ -315,6 +315,14 @@ class PostListScreen : Screen {
SwipeableCard( SwipeableCard(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled, enabled = uiState.swipeActionsEnabled,
directions = if (!uiState.isLogged) {
emptySet()
} else {
setOf(
DismissDirection.StartToEnd,
DismissDirection.EndToStart,
)
},
backgroundColor = rememberCallbackArgs { backgroundColor = rememberCallbackArgs {
when (it) { when (it) {
DismissValue.DismissedToStart -> upvoteColor DismissValue.DismissedToStart -> upvoteColor
@ -356,51 +364,72 @@ class PostListScreen : Screen {
blurNsfw = uiState.blurNsfw, blurNsfw = uiState.blurNsfw,
onClick = rememberCallback(model) { onClick = rememberCallback(model) {
model.reduce(PostListMviModel.Intent.MarkAsRead(post.id)) model.reduce(PostListMviModel.Intent.MarkAsRead(post.id))
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
PostDetailScreen(post), PostDetailScreen(post),
) )
}, },
onDoubleClick = if (!uiState.doubleTapActionEnabled || !uiState.isLogged) {
null
} else {
rememberCallback(model) {
model.reduce(
PostListMviModel.Intent.UpVotePost(
id = post.id,
feedback = true,
),
)
}
},
onOpenCommunity = rememberCallbackArgs { community -> onOpenCommunity = rememberCallbackArgs { community ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen(community), CommunityDetailScreen(community),
) )
}, },
onOpenCreator = rememberCallbackArgs { user -> onOpenCreator = rememberCallbackArgs { user ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen(user), UserDetailScreen(user),
) )
}, },
onUpVote = rememberCallback(model) { onUpVote = rememberCallback(model) {
model.reduce( if (uiState.isLogged) {
PostListMviModel.Intent.UpVotePost( model.reduce(
id = post.id, PostListMviModel.Intent.UpVotePost(
feedback = true, id = post.id,
), feedback = true,
) ),
)
}
}, },
onDownVote = rememberCallback(model) { onDownVote = rememberCallback(model) {
model.reduce( if (uiState.isLogged) {
PostListMviModel.Intent.DownVotePost( model.reduce(
id = post.id, PostListMviModel.Intent.DownVotePost(
feedback = true, id = post.id,
), feedback = true,
) ),
)
}
}, },
onSave = rememberCallback(model) { onSave = rememberCallback(model) {
model.reduce( if (uiState.isLogged) {
PostListMviModel.Intent.SavePost( model.reduce(
id = post.id, PostListMviModel.Intent.SavePost(
feedback = true, id = post.id,
), feedback = true,
) ),
)
}
}, },
onReply = rememberCallback(model) { onReply = rememberCallback(model) {
val screen = CreateCommentScreen(originalPost = post) if (uiState.isLogged) {
navigationCoordinator.getBottomNavigator()?.show(screen) val screen =
CreateCommentScreen(originalPost = post)
navigationCoordinator.showBottomSheet(screen)
}
}, },
onImageClick = rememberCallbackArgs(model, post) { url -> onImageClick = rememberCallbackArgs(model, post) { url ->
model.reduce(PostListMviModel.Intent.MarkAsRead(post.id)) model.reduce(PostListMviModel.Intent.MarkAsRead(post.id))
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
ZoomableImageScreen(url) ZoomableImageScreen(url)
) )
}, },
@ -411,7 +440,7 @@ class PostListScreen : Screen {
stringResource(MR.strings.post_action_share) stringResource(MR.strings.post_action_share)
) )
) )
if (uiState.currentUserId != null) { if (uiState.isLogged) {
add( add(
Option( Option(
OptionId.Hide, OptionId.Hide,
@ -425,7 +454,7 @@ class PostListScreen : Screen {
stringResource(MR.strings.post_action_see_raw) stringResource(MR.strings.post_action_see_raw)
) )
) )
if (uiState.currentUserId != null) { if (uiState.isLogged) {
add( add(
Option( Option(
OptionId.CrossPost, OptionId.CrossPost,
@ -461,24 +490,21 @@ class PostListScreen : Screen {
) )
OptionId.Edit -> { OptionId.Edit -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreatePostScreen(editedPost = post)
CreatePostScreen(editedPost = post) )
)
} }
OptionId.Report -> { OptionId.Report -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreateReportScreen(postId = post.id)
CreateReportScreen(postId = post.id) )
)
} }
OptionId.CrossPost -> { OptionId.CrossPost -> {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(
?.show( CreatePostScreen(crossPost = post)
CreatePostScreen(crossPost = post) )
)
} }
OptionId.SeeRaw -> { OptionId.SeeRaw -> {

View File

@ -106,6 +106,7 @@ class PostListViewModel(
it.copy( it.copy(
blurNsfw = settings.blurNsfw, blurNsfw = settings.blurNsfw,
swipeActionsEnabled = settings.enableSwipeActions, swipeActionsEnabled = settings.enableSwipeActions,
doubleTapActionEnabled = settings.enableDoubleTapAction,
separateUpAndDownVotes = settings.separateUpAndDownVotes, separateUpAndDownVotes = settings.separateUpAndDownVotes,
autoLoadImages = settings.autoLoadImages, autoLoadImages = settings.autoLoadImages,
fullHeightImages = settings.fullHeightImages, fullHeightImages = settings.fullHeightImages,

View File

@ -47,7 +47,7 @@ internal fun PostsTopBar(
onHamburgerTapped != null -> { onHamburgerTapped != null -> {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onHamburgerTapped() onHamburgerTapped()
}, },
), ),
@ -60,7 +60,7 @@ internal fun PostsTopBar(
listingType != null -> { listingType != null -> {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onSelectListingType?.invoke() onSelectListingType?.invoke()
}, },
), ),
@ -80,7 +80,7 @@ internal fun PostsTopBar(
modifier = Modifier modifier = Modifier
.padding(horizontal = Spacing.s) .padding(horizontal = Spacing.s)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onSelectListingType?.invoke() onSelectListingType?.invoke()
}, },
), ),
@ -115,7 +115,7 @@ internal fun PostsTopBar(
if (sortType != null) { if (sortType != null) {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onSelectSortType?.invoke() onSelectSortType?.invoke()
}, },
), ),

View File

@ -105,7 +105,7 @@ object InboxScreen : Tab {
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
scope.launch { scope.launch {
drawerCoordinator.toggleDrawer() drawerCoordinator.toggleDrawer()
} }
@ -128,9 +128,9 @@ object InboxScreen : Tab {
} }
Text( Text(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
val sheet = InboxTypeSheet() val sheet = InboxTypeSheet()
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
), ),
text = text, text = text,
@ -142,7 +142,7 @@ object InboxScreen : Tab {
if (uiState.isLogged == true) { if (uiState.isLogged == true) {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
model.reduce(InboxMviModel.Intent.ReadAll) model.reduce(InboxMviModel.Intent.ReadAll)
}, },
), ),

View File

@ -46,6 +46,8 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.Co
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCard import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCardPlaceholder import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCardPlaceholder
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCardType import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCardType
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Option
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.OptionId
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SwipeableCard import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SwipeableCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
@ -181,7 +183,7 @@ class InboxMentionsScreen : Tab {
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
separateUpAndDownVotes = uiState.separateUpAndDownVotes, separateUpAndDownVotes = uiState.separateUpAndDownVotes,
onOpenPost = rememberCallbackArgs { post -> onOpenPost = rememberCallbackArgs { post ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
PostDetailScreen( PostDetailScreen(
post = post, post = post,
highlightCommentId = mention.comment.id, highlightCommentId = mention.comment.id,
@ -189,12 +191,12 @@ class InboxMentionsScreen : Tab {
) )
}, },
onOpenCreator = rememberCallbackArgs { user -> onOpenCreator = rememberCallbackArgs { user ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen(user), UserDetailScreen(user),
) )
}, },
onOpenCommunity = rememberCallbackArgs { community -> onOpenCommunity = rememberCallbackArgs { community ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen(community), CommunityDetailScreen(community),
) )
}, },
@ -208,6 +210,39 @@ class InboxMentionsScreen : Tab {
) )
) )
}, },
options = buildList {
add(
Option(
OptionId.MarkRead,
stringResource(MR.strings.inbox_action_mark_read)
)
)
add(
Option(
OptionId.MarkUnread,
stringResource(MR.strings.inbox_action_mark_unread)
)
)
},
onOptionSelected = rememberCallbackArgs(model) { optionId ->
when (optionId) {
OptionId.MarkRead -> model.reduce(
InboxMentionsMviModel.Intent.MarkAsRead(
read = true,
id = mention.id,
),
)
OptionId.MarkUnread -> model.reduce(
InboxMentionsMviModel.Intent.MarkAsRead(
read = false,
id = mention.id,
),
)
else -> Unit
}
}
) )
}, },
) )

View File

@ -1,32 +1,47 @@
package com.github.diegoberaldin.raccoonforlemmy.feature.inbox.messages package com.github.diegoberaldin.raccoonforlemmy.feature.inbox.messages
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreHoriz
import androidx.compose.material.icons.filled.Schedule import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomDropDown
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomImage import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomImage
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Option
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.OptionId
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PlaceholderImage import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PlaceholderImage
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.ScaledContent import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.ScaledContent
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.prettifyDate import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.prettifyDate
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
@Composable @Composable
@ -36,24 +51,30 @@ internal fun ChatCard(
lastMessage: String, lastMessage: String,
lastMessageDate: String? = null, lastMessageDate: String? = null,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
options: List<Option> = emptyList(),
onOpenUser: ((UserModel) -> Unit)? = null, onOpenUser: ((UserModel) -> Unit)? = null,
onOpen: (() -> Unit)? = null, onOpen: (() -> Unit)? = null,
onOptionSelected: ((OptionId) -> Unit)? = null,
) { ) {
var optionsExpanded by remember { mutableStateOf(false) }
var optionsOffset by remember { mutableStateOf(Offset.Zero) }
val creatorName = user?.name.orEmpty()
val creatorHost = user?.host.orEmpty()
val creatorAvatar = user?.avatar.orEmpty()
val iconSize = IconSize.xl
Row( Row(
modifier = modifier modifier = modifier
.padding(Spacing.xs) .padding(Spacing.xs)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onOpen?.invoke() onOpen?.invoke()
}, },
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(Spacing.m), horizontalArrangement = Arrangement.spacedBy(Spacing.m),
) { ) {
val creatorName = user?.name.orEmpty()
val creatorHost = user?.host.orEmpty()
val creatorAvatar = user?.avatar.orEmpty()
val iconSize = 46.dp
if (creatorAvatar.isNotEmpty()) { if (creatorAvatar.isNotEmpty()) {
CustomImage( CustomImage(
@ -62,7 +83,7 @@ internal fun ChatCard(
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)) .clip(RoundedCornerShape(iconSize / 2))
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
if (user != null) { if (user != null) {
onOpenUser?.invoke(user) onOpenUser?.invoke(user)
} }
@ -77,7 +98,7 @@ internal fun ChatCard(
} else { } else {
PlaceholderImage( PlaceholderImage(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
if (user != null) { if (user != null) {
onOpenUser?.invoke(user) onOpenUser?.invoke(user)
} }
@ -115,22 +136,67 @@ internal fun ChatCard(
// last message date // last message date
if (lastMessageDate != null) { if (lastMessageDate != null) {
Row( Box {
horizontalArrangement = Arrangement.spacedBy(Spacing.xxs), Row(
verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Spacing.xxs),
) { verticalAlignment = Alignment.CenterVertically,
val buttonModifier = Modifier.size(IconSize.m).padding(3.25.dp) ) {
Icon( val buttonModifier = Modifier.size(IconSize.m).padding(3.5.dp)
modifier = buttonModifier.padding(1.dp), Icon(
imageVector = Icons.Default.Schedule, modifier = buttonModifier.padding(1.dp),
contentDescription = null, imageVector = Icons.Default.Schedule,
tint = MaterialTheme.colorScheme.onBackground, contentDescription = null,
) tint = MaterialTheme.colorScheme.onBackground,
Text( )
text = lastMessageDate.prettifyDate(), Text(
style = MaterialTheme.typography.labelMedium, text = lastMessageDate.prettifyDate(),
color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.labelMedium,
) color = MaterialTheme.colorScheme.onSurface,
)
Spacer(modifier = Modifier.weight(1f))
if (options.isNotEmpty()) {
Icon(
modifier = buttonModifier
.padding(top = Spacing.xxs)
.onGloballyPositioned {
optionsOffset = it.positionInParent()
}
.onClick(
onClick = rememberCallback {
optionsExpanded = true
},
),
imageVector = Icons.Default.MoreHoriz,
contentDescription = null,
)
}
}
CustomDropDown(
expanded = optionsExpanded,
onDismiss = {
optionsExpanded = false
},
offset = DpOffset(
x = optionsOffset.x.toLocalDp(),
y = optionsOffset.y.toLocalDp(),
),
) {
options.forEach { option ->
Text(
modifier = Modifier.padding(
horizontal = Spacing.m,
vertical = Spacing.s,
).onClick(
onClick = rememberCallback {
optionsExpanded = false
onOptionSelected?.invoke(option.id)
},
),
text = option.text,
)
}
}
} }
} }
} }

View File

@ -118,18 +118,18 @@ class InboxMessagesScreen : Tab {
lastMessage = chat.content.orEmpty(), lastMessage = chat.content.orEmpty(),
lastMessageDate = chat.publishDate, lastMessageDate = chat.publishDate,
onOpenUser = rememberCallbackArgs { user -> onOpenUser = rememberCallbackArgs { user ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen(user) UserDetailScreen(user)
) )
}, },
onOpen = rememberCallback { onOpen = rememberCallback {
val userId = otherUser?.id val userId = otherUser?.id
if (userId != null) { if (userId != null) {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
InboxChatScreen(userId) InboxChatScreen(userId)
) )
} }
} },
) )
} }
item { item {

View File

@ -46,6 +46,8 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.Co
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCard import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCardPlaceholder import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCardPlaceholder
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCardType import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.InboxCardType
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Option
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.OptionId
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SwipeableCard import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SwipeableCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
@ -180,7 +182,7 @@ class InboxRepliesScreen : Tab {
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
separateUpAndDownVotes = uiState.separateUpAndDownVotes, separateUpAndDownVotes = uiState.separateUpAndDownVotes,
onOpenPost = rememberCallbackArgs { post -> onOpenPost = rememberCallbackArgs { post ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
PostDetailScreen( PostDetailScreen(
post = post, post = post,
highlightCommentId = reply.comment.id, highlightCommentId = reply.comment.id,
@ -188,12 +190,12 @@ class InboxRepliesScreen : Tab {
) )
}, },
onOpenCreator = rememberCallbackArgs { user -> onOpenCreator = rememberCallbackArgs { user ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen(user), UserDetailScreen(user),
) )
}, },
onOpenCommunity = rememberCallbackArgs { community -> onOpenCommunity = rememberCallbackArgs { community ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen(community), CommunityDetailScreen(community),
) )
}, },
@ -203,6 +205,39 @@ class InboxRepliesScreen : Tab {
onDownVote = rememberCallbackArgs(model) { onDownVote = rememberCallbackArgs(model) {
model.reduce(InboxRepliesMviModel.Intent.DownVoteComment(reply.id)) model.reduce(InboxRepliesMviModel.Intent.DownVoteComment(reply.id))
}, },
options = buildList {
add(
Option(
OptionId.MarkRead,
stringResource(MR.strings.inbox_action_mark_read)
)
)
add(
Option(
OptionId.MarkUnread,
stringResource(MR.strings.inbox_action_mark_unread)
)
)
},
onOptionSelected = rememberCallbackArgs(model) { optionId ->
when (optionId) {
OptionId.MarkRead -> model.reduce(
InboxRepliesMviModel.Intent.MarkAsRead(
read = true,
id = reply.id,
),
)
OptionId.MarkUnread -> model.reduce(
InboxRepliesMviModel.Intent.MarkAsRead(
read = false,
id = reply.id,
),
)
else -> Unit
}
}
) )
}, },
) )

View File

@ -142,8 +142,7 @@ internal object ProfileLoggedScreen : Tab {
user = user, user = user,
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
onOpenImage = rememberCallbackArgs { url -> onOpenImage = rememberCallbackArgs { url ->
navigationCoordinator.getRootNavigator() navigationCoordinator.pushScreen(ZoomableImageScreen(url))
?.push(ZoomableImageScreen(url))
}, },
) )
SectionSelector( SectionSelector(
@ -193,17 +192,17 @@ internal object ProfileLoggedScreen : Tab {
hideAuthor = true, hideAuthor = true,
blurNsfw = false, blurNsfw = false,
onClick = rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
PostDetailScreen(post), PostDetailScreen(post),
) )
}, },
onOpenCommunity = rememberCallbackArgs { community -> onOpenCommunity = rememberCallbackArgs { community ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen(community), CommunityDetailScreen(community),
) )
}, },
onImageClick = rememberCallbackArgs { url -> onImageClick = rememberCallbackArgs { url ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
ZoomableImageScreen(url), ZoomableImageScreen(url),
) )
}, },
@ -264,7 +263,7 @@ internal object ProfileLoggedScreen : Tab {
) )
OptionId.Edit -> { OptionId.Edit -> {
navigationCoordinator.getBottomNavigator()?.show( navigationCoordinator.showBottomSheet(
CreatePostScreen( CreatePostScreen(
editedPost = post, editedPost = post,
) )
@ -324,7 +323,7 @@ internal object ProfileLoggedScreen : Tab {
hideAuthor = true, hideAuthor = true,
hideIndent = true, hideIndent = true,
onClick = rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
PostDetailScreen( PostDetailScreen(
post = PostModel(id = comment.postId), post = PostModel(id = comment.postId),
highlightCommentId = comment.id, highlightCommentId = comment.id,
@ -386,7 +385,7 @@ internal object ProfileLoggedScreen : Tab {
} }
OptionId.Edit -> { OptionId.Edit -> {
navigationCoordinator.getBottomNavigator()?.show( navigationCoordinator.showBottomSheet(
CreateCommentScreen(editedComment = comment) CreateCommentScreen(editedComment = comment)
) )
} }

View File

@ -87,7 +87,7 @@ class LoginBottomSheet : Screen {
} }
LoginBottomSheetMviModel.Effect.LoginSuccess -> { LoginBottomSheetMviModel.Effect.LoginSuccess -> {
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
} }
} }
}.launchIn(this) }.launchIn(this)
@ -125,12 +125,11 @@ class LoginBottomSheet : Screen {
IconButton( IconButton(
modifier = Modifier.align(Alignment.TopEnd), modifier = Modifier.align(Alignment.TopEnd),
onClick = { onClick = {
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
handleUrl( navigationCoordinator.handleUrl(
url = HELP_URL, url = HELP_URL,
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser, openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
uriHandler = uriHandler, uriHandler = uriHandler,
navigator = navigationCoordinator.getRootNavigator(),
) )
}, },
) { ) {
@ -181,7 +180,7 @@ class LoginBottomSheet : Screen {
if (uiState.instanceName.isNotEmpty()) { if (uiState.instanceName.isNotEmpty()) {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback(model) { onClick = rememberCallback(model) {
model.reduce( model.reduce(
LoginBottomSheetMviModel.Intent.SetInstanceName("") LoginBottomSheetMviModel.Intent.SetInstanceName("")
) )
@ -254,7 +253,7 @@ class LoginBottomSheet : Screen {
trailingIcon = { trailingIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
transformation = transformation =
if (transformation == VisualTransformation.None) { if (transformation == VisualTransformation.None) {
PasswordVisualTransformation() PasswordVisualTransformation()

View File

@ -86,7 +86,7 @@ internal object ProfileMainScreen : Tab {
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
scope.launch { scope.launch {
drawerCoordinator.toggleDrawer() drawerCoordinator.toggleDrawer()
} }
@ -108,9 +108,8 @@ internal object ProfileMainScreen : Tab {
if (uiState.logged == true) { if (uiState.logged == true) {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getBottomNavigator() navigationCoordinator.showBottomSheet(ManageAccountsScreen())
?.show(ManageAccountsScreen())
}, },
), ),
imageVector = Icons.Default.ManageAccounts, imageVector = Icons.Default.ManageAccounts,
@ -122,7 +121,7 @@ internal object ProfileMainScreen : Tab {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
model.reduce(ProfileMainMviModel.Intent.Logout) model.reduce(ProfileMainMviModel.Intent.Logout)
}, },
), ),

View File

@ -64,7 +64,7 @@ class ManageAccountsScreen : Screen {
model.effects.onEach { effect -> model.effects.onEach { effect ->
when (effect) { when (effect) {
ManageAccountsMviModel.Effect.Close -> { ManageAccountsMviModel.Effect.Close -> {
navigationCoordinator.getBottomNavigator()?.hide() navigationCoordinator.hideBottomSheet()
} }
} }
}.launchIn(this) }.launchIn(this)
@ -104,7 +104,7 @@ class ManageAccountsScreen : Screen {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
model.reduce(ManageAccountsMviModel.Intent.SwitchAccount(idx)) model.reduce(ManageAccountsMviModel.Intent.SwitchAccount(idx))
}, },
) )
@ -151,7 +151,7 @@ class ManageAccountsScreen : Screen {
Spacer(modifier = Modifier.height(Spacing.m)) Spacer(modifier = Modifier.height(Spacing.m))
Button( Button(
onClick = { onClick = {
navigationCoordinator.getBottomNavigator()?.show(LoginBottomSheet()) navigationCoordinator.showBottomSheet(LoginBottomSheet())
}, },
) { ) {
Row( Row(

View File

@ -42,7 +42,7 @@ internal object ProfileNotLoggedScreen : Tab {
Button( Button(
modifier = Modifier.align(Alignment.CenterHorizontally), modifier = Modifier.align(Alignment.CenterHorizontally),
onClick = { onClick = {
navigationCoordinator.getBottomNavigator()?.show( navigationCoordinator.showBottomSheet(
LoginBottomSheet(), LoginBottomSheet(),
) )
}, },

View File

@ -18,6 +18,7 @@ interface ExploreMviModel :
data class SetListingType(val value: ListingType) : Intent data class SetListingType(val value: ListingType) : Intent
data class SetSortType(val value: SortType) : Intent data class SetSortType(val value: SortType) : Intent
data class SetResultType(val value: SearchResultType) : Intent data class SetResultType(val value: SearchResultType) : Intent
data object HapticIndication : Intent
data class UpVotePost(val id: 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 DownVotePost(val id: Int, val feedback: Boolean = false) : Intent
data class SavePost(val id: Int, val feedback: Boolean = false) : Intent data class SavePost(val id: Int, val feedback: Boolean = false) : Intent
@ -31,6 +32,8 @@ interface ExploreMviModel :
val loading: Boolean = false, val loading: Boolean = false,
val canFetchMore: Boolean = true, val canFetchMore: Boolean = true,
val isLogged: Boolean = false, val isLogged: Boolean = false,
val swipeActionsEnabled: Boolean = false,
val doubleTapActionEnabled: Boolean = false,
val blurNsfw: Boolean = true, val blurNsfw: Boolean = true,
val instance: String = "", val instance: String = "",
val searchText: String = "", val searchText: String = "",

View File

@ -15,12 +15,16 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowCircleDown
import androidx.compose.material.icons.filled.ArrowCircleUp
import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DismissDirection
import androidx.compose.material3.DismissValue
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -42,6 +46,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@ -52,6 +57,7 @@ import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout 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 import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen import com.github.diegoberaldin.raccoonforlemmy.core.commonui.communitydetail.CommunityDetailScreen
@ -59,6 +65,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Comment
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CommunityItem import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CommunityItem
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCard 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.PostCardPlaceholder
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SwipeableCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserItem import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.UserItem
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.createcomment.CreateCommentScreen import com.github.diegoberaldin.raccoonforlemmy.core.commonui.createcomment.CreateCommentScreen
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getDrawerCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getDrawerCoordinator
@ -114,6 +121,12 @@ class ExploreScreen : Screen {
} }
val settingsRepository = remember { getSettingsRepository() } val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState() val settings by settingsRepository.currentSettings.collectAsState()
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
val downvoteColor by themeRepository.downvoteColor.collectAsState()
val defaultUpvoteColor = MaterialTheme.colorScheme.primary
val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary
DisposableEffect(key) { DisposableEffect(key) {
onDispose { onDispose {
notificationCenter.removeObserver(key) notificationCenter.removeObserver(key)
@ -176,14 +189,14 @@ class ExploreScreen : Screen {
val sheet = ListingTypeBottomSheet( val sheet = ListingTypeBottomSheet(
isLogged = uiState.isLogged, isLogged = uiState.isLogged,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
onSelectSortType = rememberCallback { onSelectSortType = rememberCallback {
focusManager.clearFocus() focusManager.clearFocus()
val sheet = SortBottomSheet( val sheet = SortBottomSheet(
expandTop = true, expandTop = true,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
onHamburgerTapped = rememberCallback { onHamburgerTapped = rememberCallback {
scope.launch { scope.launch {
@ -217,7 +230,7 @@ class ExploreScreen : Screen {
trailingIcon = { trailingIcon = {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
if (uiState.searchText.isNotEmpty()) { if (uiState.searchText.isNotEmpty()) {
model.reduce(ExploreMviModel.Intent.SetSearch("")) model.reduce(ExploreMviModel.Intent.SetSearch(""))
} }
@ -308,8 +321,8 @@ class ExploreScreen : Screen {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen(result), CommunityDetailScreen(result),
) )
}, },
@ -320,61 +333,110 @@ class ExploreScreen : Screen {
} }
is PostModel -> { is PostModel -> {
PostCard( SwipeableCard(
post = result, modifier = Modifier.fillMaxWidth(),
postLayout = uiState.postLayout, enabled = uiState.swipeActionsEnabled,
fullHeightImage = uiState.fullHeightImages, backgroundColor = rememberCallbackArgs {
separateUpAndDownVotes = uiState.separateUpAndDownVotes, when (it) {
autoLoadImages = uiState.autoLoadImages, DismissValue.DismissedToStart -> upvoteColor
blurNsfw = uiState.blurNsfw, ?: defaultUpvoteColor
onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.push( DismissValue.DismissedToEnd -> downvoteColor
PostDetailScreen(result), ?: defaultDownVoteColor
DismissValue.Default -> Color.Transparent
}
},
onGestureBegin = {
model.reduce(ExploreMviModel.Intent.HapticIndication)
},
onDismissToStart = rememberCallback(model) {
model.reduce(ExploreMviModel.Intent.UpVotePost(result.id))
},
onDismissToEnd = rememberCallback(model) {
model.reduce(ExploreMviModel.Intent.DownVotePost(result.id))
},
swipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
}
androidx.compose.material.Icon(
imageVector = icon,
contentDescription = null,
tint = Color.White,
) )
}, },
onOpenCommunity = rememberCallbackArgs { community -> content = {
navigationCoordinator.getRootNavigator()?.push( PostCard(
CommunityDetailScreen(community), post = result,
) postLayout = uiState.postLayout,
}, fullHeightImage = uiState.fullHeightImages,
onOpenCreator = rememberCallbackArgs { user -> separateUpAndDownVotes = uiState.separateUpAndDownVotes,
navigationCoordinator.getRootNavigator()?.push( autoLoadImages = uiState.autoLoadImages,
UserDetailScreen(user), blurNsfw = uiState.blurNsfw,
) onClick = rememberCallback {
}, navigationCoordinator.pushScreen(
onUpVote = rememberCallback(model) { PostDetailScreen(result),
model.reduce( )
ExploreMviModel.Intent.UpVotePost( },
id = result.id, onDoubleClick = if (!uiState.doubleTapActionEnabled) {
feedback = true, null
), } else {
) rememberCallback(model) {
}, model.reduce(
onDownVote = rememberCallback(model) { ExploreMviModel.Intent.UpVotePost(
model.reduce( id = result.id,
ExploreMviModel.Intent.DownVotePost( feedback = true,
id = result.id, ),
feedback = true, )
), }
) },
}, onOpenCommunity = rememberCallbackArgs { community ->
onSave = rememberCallback(model) { navigationCoordinator.pushScreen(
model.reduce( CommunityDetailScreen(community),
ExploreMviModel.Intent.SavePost( )
id = result.id, },
feedback = true, onOpenCreator = rememberCallbackArgs { user ->
), navigationCoordinator.pushScreen(
) UserDetailScreen(user),
}, )
onReply = rememberCallback { },
val screen = CreateCommentScreen( onUpVote = rememberCallback(model) {
originalPost = result, model.reduce(
) ExploreMviModel.Intent.UpVotePost(
navigationCoordinator.getBottomNavigator()?.show(screen) id = result.id,
}, feedback = true,
onImageClick = rememberCallbackArgs { url -> ),
navigationCoordinator.getRootNavigator()?.push( )
ZoomableImageScreen(url), },
onDownVote = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.DownVotePost(
id = result.id,
feedback = true,
),
)
},
onSave = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.SavePost(
id = result.id,
feedback = true,
),
)
},
onReply = rememberCallback {
val screen = CreateCommentScreen(
originalPost = result,
)
navigationCoordinator.showBottomSheet(screen)
},
onImageClick = rememberCallbackArgs { url ->
navigationCoordinator.pushScreen(
ZoomableImageScreen(url),
)
},
) )
}, },
) )
@ -386,59 +448,124 @@ class ExploreScreen : Screen {
} }
is CommentModel -> { is CommentModel -> {
CommentCard( SwipeableCard(
modifier = Modifier.background(MaterialTheme.colorScheme.background), modifier = Modifier.fillMaxWidth(),
comment = result, enabled = uiState.swipeActionsEnabled,
separateUpAndDownVotes = uiState.separateUpAndDownVotes, directions = if (!uiState.isLogged) {
autoLoadImages = uiState.autoLoadImages, emptySet()
hideIndent = true, } else {
onClick = rememberCallback { setOf(
navigationCoordinator.getRootNavigator()?.push( DismissDirection.StartToEnd,
PostDetailScreen( DismissDirection.EndToStart,
post = PostModel(id = result.postId),
highlightCommentId = result.id,
),
) )
}, },
onUpVote = rememberCallback(model) { backgroundColor = rememberCallbackArgs {
when (it) {
DismissValue.DismissedToStart -> upvoteColor
?: defaultUpvoteColor
DismissValue.DismissedToEnd -> downvoteColor
?: defaultDownVoteColor
DismissValue.Default -> Color.Transparent
}
},
onGestureBegin = rememberCallback(model) {
model.reduce(ExploreMviModel.Intent.HapticIndication)
},
onDismissToStart = rememberCallback(model) {
model.reduce( model.reduce(
ExploreMviModel.Intent.UpVoteComment( ExploreMviModel.Intent.UpVoteComment(
id = result.id, id = result.id
feedback = true,
), ),
) )
}, },
onDownVote = rememberCallback(model) { onDismissToEnd = rememberCallback(model) {
model.reduce( model.reduce(
ExploreMviModel.Intent.DownVoteComment( ExploreMviModel.Intent.DownVoteComment(
id = result.id, id = result.id
feedback = true,
), ),
) )
}, },
onSave = rememberCallback(model) { swipeContent = { direction ->
model.reduce( val icon = when (direction) {
ExploreMviModel.Intent.SaveComment( DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
id = result.id, DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
feedback = true, }
), androidx.compose.material.Icon(
imageVector = icon,
contentDescription = null,
tint = Color.White,
) )
}, },
onReply = rememberCallback { content = {
val screen = CreateCommentScreen( CommentCard(
originalPost = PostModel(id = result.postId), modifier = Modifier.background(MaterialTheme.colorScheme.background),
originalComment = result, comment = result,
) separateUpAndDownVotes = uiState.separateUpAndDownVotes,
navigationCoordinator.getBottomNavigator()?.show(screen) autoLoadImages = uiState.autoLoadImages,
}, hideIndent = true,
onOpenCommunity = rememberCallbackArgs { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen(it) PostDetailScreen(
) post = PostModel(id = result.postId),
}, highlightCommentId = result.id,
onOpenCreator = rememberCallbackArgs { ),
navigationCoordinator.getRootNavigator()?.push( )
UserDetailScreen(it) },
onDoubleClick = if (!uiState.doubleTapActionEnabled) {
null
} else {
rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.UpVoteComment(
id = result.id,
feedback = true,
),
)
}
},
onUpVote = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.UpVoteComment(
id = result.id,
feedback = true,
),
)
},
onDownVote = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.DownVoteComment(
id = result.id,
feedback = true,
),
)
},
onSave = rememberCallback(model) {
model.reduce(
ExploreMviModel.Intent.SaveComment(
id = result.id,
feedback = true,
),
)
},
onReply = rememberCallback {
val screen = CreateCommentScreen(
originalPost = PostModel(id = result.postId),
originalComment = result,
)
navigationCoordinator.showBottomSheet(screen)
},
onOpenCommunity = rememberCallbackArgs {
navigationCoordinator.pushScreen(
CommunityDetailScreen(it)
)
},
onOpenCreator = rememberCallbackArgs {
navigationCoordinator.pushScreen(
UserDetailScreen(it)
)
},
) )
}, },
) )
@ -453,8 +580,8 @@ class ExploreScreen : Screen {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen(result), UserDetailScreen(result),
) )
}, },

View File

@ -46,7 +46,7 @@ internal fun ExploreTopBar(
onHamburgerTapped != null -> { onHamburgerTapped != null -> {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onHamburgerTapped() onHamburgerTapped()
}, },
), ),
@ -59,7 +59,7 @@ internal fun ExploreTopBar(
listingType != null -> { listingType != null -> {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onSelectListingType?.invoke() onSelectListingType?.invoke()
}, },
), ),
@ -79,7 +79,7 @@ internal fun ExploreTopBar(
modifier = Modifier modifier = Modifier
.padding(horizontal = Spacing.s) .padding(horizontal = Spacing.s)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onSelectListingType?.invoke() onSelectListingType?.invoke()
}, },
), ),
@ -109,7 +109,7 @@ internal fun ExploreTopBar(
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
onSelectSortType?.invoke() onSelectSortType?.invoke()
}, },
), ),

View File

@ -89,6 +89,8 @@ class ExploreViewModel(
separateUpAndDownVotes = settings.separateUpAndDownVotes, separateUpAndDownVotes = settings.separateUpAndDownVotes,
autoLoadImages = settings.autoLoadImages, autoLoadImages = settings.autoLoadImages,
fullHeightImages = settings.fullHeightImages, fullHeightImages = settings.fullHeightImages,
swipeActionsEnabled = settings.enableSwipeActions,
doubleTapActionEnabled = settings.enableDoubleTapAction,
) )
} }
}.launchIn(this) }.launchIn(this)
@ -126,6 +128,7 @@ class ExploreViewModel(
} }
} }
ExploreMviModel.Intent.HapticIndication -> hapticFeedback.vibrate()
is ExploreMviModel.Intent.SetSearch -> setSearch(intent.value) is ExploreMviModel.Intent.SetSearch -> setSearch(intent.value)
is ExploreMviModel.Intent.SetListingType -> changeListingType(intent.value) is ExploreMviModel.Intent.SetListingType -> changeListingType(intent.value)
is ExploreMviModel.Intent.SetSortType -> changeSortType(intent.value) is ExploreMviModel.Intent.SetSortType -> changeSortType(intent.value)

View File

@ -108,8 +108,8 @@ class ManageSubscriptionsScreen : Screen {
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigatorCoordinator.getRootNavigator()?.pop() navigatorCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -178,8 +178,8 @@ class ManageSubscriptionsScreen : Screen {
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigatorCoordinator.getRootNavigator()?.push( navigatorCoordinator.pushScreen(
MultiCommunityEditorScreen() MultiCommunityEditorScreen()
) )
}, },
@ -206,7 +206,7 @@ class ManageSubscriptionsScreen : Screen {
model.reduce(ManageSubscriptionsMviModel.Intent.HapticIndication) model.reduce(ManageSubscriptionsMviModel.Intent.HapticIndication)
}, },
onDismissToStart = rememberCallback { onDismissToStart = rememberCallback {
navigatorCoordinator.getRootNavigator()?.push( navigatorCoordinator.pushScreen(
MultiCommunityEditorScreen(community), MultiCommunityEditorScreen(community),
) )
}, },
@ -233,8 +233,8 @@ class ManageSubscriptionsScreen : Screen {
MultiCommunityItem( MultiCommunityItem(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
.background(MaterialTheme.colorScheme.background).onClick( .background(MaterialTheme.colorScheme.background).onClick(
rememberCallback { onClick = rememberCallback {
navigatorCoordinator.getRootNavigator()?.push( navigatorCoordinator.pushScreen(
MultiCommunityScreen(community), MultiCommunityScreen(community),
) )
}, },
@ -294,8 +294,8 @@ class ManageSubscriptionsScreen : Screen {
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
.background(MaterialTheme.colorScheme.background) .background(MaterialTheme.colorScheme.background)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
navigatorCoordinator.getRootNavigator()?.push( navigatorCoordinator.pushScreen(
CommunityDetailScreen(community), CommunityDetailScreen(community),
) )
}, },

View File

@ -159,8 +159,8 @@ class MultiCommunityScreen(
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -184,9 +184,9 @@ class MultiCommunityScreen(
if (sortType != null) { if (sortType != null) {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
val sheet = SortBottomSheet(expandTop = true) val sheet = SortBottomSheet(expandTop = true)
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
), ),
imageVector = sortType.toIcon(), imageVector = sortType.toIcon(),
@ -316,17 +316,29 @@ class MultiCommunityScreen(
blurNsfw = uiState.blurNsfw, blurNsfw = uiState.blurNsfw,
onClick = { onClick = {
model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(post.id)) model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(post.id))
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
PostDetailScreen(post), PostDetailScreen(post),
) )
}, },
onDoubleClick = if (uiState.swipeActionsEnabled) {
null
} else {
rememberCallback(model) {
model.reduce(
MultiCommunityMviModel.Intent.UpVotePost(
id = post.id,
feedback = true,
),
)
}
},
onOpenCommunity = { community -> onOpenCommunity = { community ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen(community), CommunityDetailScreen(community),
) )
}, },
onOpenCreator = { user -> onOpenCreator = { user ->
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
UserDetailScreen(user), UserDetailScreen(user),
) )
}, },
@ -358,11 +370,11 @@ class MultiCommunityScreen(
val screen = CreateCommentScreen( val screen = CreateCommentScreen(
originalPost = post, originalPost = post,
) )
navigationCoordinator.getBottomNavigator()?.show(screen) navigationCoordinator.showBottomSheet(screen)
}, },
onImageClick = { url -> onImageClick = { url ->
model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(post.id)) model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(post.id))
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
ZoomableImageScreen(url), ZoomableImageScreen(url),
) )
}, },
@ -391,7 +403,7 @@ class MultiCommunityScreen(
onOptionSelected = { optionId -> onOptionSelected = { optionId ->
when (optionId) { when (optionId) {
OptionId.Report -> { OptionId.Report -> {
navigationCoordinator.getBottomNavigator()?.show( navigationCoordinator.showBottomSheet(
CreateReportScreen( CreateReportScreen(
postId = post.id postId = post.id
) )

View File

@ -88,7 +88,7 @@ class MultiCommunityEditorScreen(
model.effects.onEach { model.effects.onEach {
when (it) { when (it) {
MultiCommunityEditorMviModel.Effect.Close -> { MultiCommunityEditorMviModel.Effect.Close -> {
navigationCoordinator.getRootNavigator()?.pop() navigationCoordinator.popScreen()
} }
} }
}.launchIn(this) }.launchIn(this)
@ -114,8 +114,8 @@ class MultiCommunityEditorScreen(
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
navigationCoordinator.getRootNavigator()?.pop() navigationCoordinator.popScreen()
}, },
), ),
imageVector = Icons.Default.ArrowBack, imageVector = Icons.Default.ArrowBack,
@ -219,7 +219,7 @@ class MultiCommunityEditorScreen(
it it
} }
}.onClick( }.onClick(
rememberCallback(model) { onClick = rememberCallback(model) {
model.reduce( model.reduce(
MultiCommunityEditorMviModel.Intent.SelectImage( MultiCommunityEditorMviModel.Intent.SelectImage(
idx, idx,
@ -260,7 +260,7 @@ class MultiCommunityEditorScreen(
it it
} }
}.onClick( }.onClick(
rememberCallback { onClick = rememberCallback {
model.reduce( model.reduce(
MultiCommunityEditorMviModel.Intent.SelectImage( MultiCommunityEditorMviModel.Intent.SelectImage(
null, null,
@ -307,7 +307,7 @@ class MultiCommunityEditorScreen(
trailingIcon = { trailingIcon = {
Icon( Icon(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
if (uiState.searchText.isNotEmpty()) { if (uiState.searchText.isNotEmpty()) {
model.reduce( model.reduce(
MultiCommunityEditorMviModel.Intent.SetSearch("") MultiCommunityEditorMviModel.Intent.SetSearch("")

View File

@ -75,7 +75,7 @@ class AboutDialog : Screen {
viewModel.effects.onEach { effect -> viewModel.effects.onEach { effect ->
when (effect) { when (effect) {
is AboutDialogMviModel.Effect.OpenCommunity -> { is AboutDialogMviModel.Effect.OpenCommunity -> {
navigationCoordinator.getRootNavigator()?.push( navigationCoordinator.pushScreen(
CommunityDetailScreen( CommunityDetailScreen(
community = effect.community, community = effect.community,
otherInstance = effect.instance, otherInstance = effect.instance,
@ -122,11 +122,10 @@ class AboutDialog : Screen {
vector = Icons.Default.OpenInBrowser, vector = Icons.Default.OpenInBrowser,
textDecoration = TextDecoration.Underline, textDecoration = TextDecoration.Underline,
onClick = { onClick = {
handleUrl( navigationCoordinator.handleUrl(
url = CHANGELOG_URL, url = CHANGELOG_URL,
openExternal = settings.openUrlsInExternalBrowser, openExternal = settings.openUrlsInExternalBrowser,
uriHandler = uriHandler, uriHandler = uriHandler,
navigator = navigationCoordinator.getRootNavigator(),
) )
} }
) )
@ -134,11 +133,10 @@ class AboutDialog : Screen {
item { item {
Button( Button(
onClick = { onClick = {
handleUrl( navigationCoordinator.handleUrl(
url = REPORT_URL, url = REPORT_URL,
openExternal = settings.openUrlsInExternalBrowser, openExternal = settings.openUrlsInExternalBrowser,
uriHandler = uriHandler, uriHandler = uriHandler,
navigator = navigationCoordinator.getRootNavigator(),
) )
}, },
) { ) {
@ -166,11 +164,10 @@ class AboutDialog : Screen {
text = stringResource(MR.strings.settings_about_view_github), text = stringResource(MR.strings.settings_about_view_github),
textDecoration = TextDecoration.Underline, textDecoration = TextDecoration.Underline,
onClick = { onClick = {
handleUrl( navigationCoordinator.handleUrl(
url = WEBSITE_URL, url = WEBSITE_URL,
openExternal = settings.openUrlsInExternalBrowser, openExternal = settings.openUrlsInExternalBrowser,
uriHandler = uriHandler, uriHandler = uriHandler,
navigator = navigationCoordinator.getRootNavigator(),
) )
}, },
) )
@ -212,7 +209,7 @@ class AboutDialog : Screen {
horizontal = Spacing.xs, horizontal = Spacing.xs,
vertical = Spacing.s, vertical = Spacing.s,
).onClick( ).onClick(
rememberCallback { onClick = rememberCallback {
onClick?.invoke() onClick?.invoke()
}, },
), ),

View File

@ -32,6 +32,7 @@ interface SettingsMviModel :
data class ChangeBlurNsfw(val value: Boolean) : Intent data class ChangeBlurNsfw(val value: Boolean) : Intent
data class ChangeOpenUrlsInExternalBrowser(val value: Boolean) : Intent data class ChangeOpenUrlsInExternalBrowser(val value: Boolean) : Intent
data class ChangeEnableSwipeActions(val value: Boolean) : Intent data class ChangeEnableSwipeActions(val value: Boolean) : Intent
data class ChangeEnableDoubleTapAction(val value: Boolean) : Intent
data class ChangeCustomSeedColor(val value: Color?) : Intent data class ChangeCustomSeedColor(val value: Color?) : Intent
data class ChangeUpvoteColor(val value: Color?) : Intent data class ChangeUpvoteColor(val value: Color?) : Intent
data class ChangeDownvoteColor(val value: Color?) : Intent data class ChangeDownvoteColor(val value: Color?) : Intent
@ -66,6 +67,7 @@ interface SettingsMviModel :
val blurNsfw: Boolean = true, val blurNsfw: Boolean = true,
val openUrlsInExternalBrowser: Boolean = false, val openUrlsInExternalBrowser: Boolean = false,
val enableSwipeActions: Boolean = true, val enableSwipeActions: Boolean = true,
val enableDoubleTapAction: Boolean = true,
val crashReportEnabled: Boolean = false, val crashReportEnabled: Boolean = false,
val separateUpAndDownVotes: Boolean = false, val separateUpAndDownVotes: Boolean = false,
val autoLoadImages: Boolean = false, val autoLoadImages: Boolean = false,

View File

@ -65,10 +65,10 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomS
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.ThemeBottomSheet import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.ThemeBottomSheet
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.getPrettyDuration
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs
import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.getPrettyDuration
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLanguageName import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLanguageName
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
@ -278,7 +278,7 @@ class SettingsScreen : Screen {
navigationIcon = { navigationIcon = {
Image( Image(
modifier = Modifier.onClick( modifier = Modifier.onClick(
rememberCallback { onClick = rememberCallback {
scope.launch { scope.launch {
drawerCoordinator.toggleDrawer() drawerCoordinator.toggleDrawer()
} }
@ -318,7 +318,7 @@ class SettingsScreen : Screen {
value = uiState.lang.toLanguageName(), value = uiState.lang.toLanguageName(),
onTap = rememberCallback { onTap = rememberCallback {
val sheet = LanguageBottomSheet() val sheet = LanguageBottomSheet()
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
@ -328,13 +328,14 @@ class SettingsScreen : Screen {
value = uiState.uiTheme.toReadableName(), value = uiState.uiTheme.toReadableName(),
onTap = rememberCallback { onTap = rememberCallback {
val sheet = ThemeBottomSheet() val sheet = ThemeBottomSheet()
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
// dynamic colors // dynamic colors
if (uiState.supportsDynamicColors) { if (uiState.supportsDynamicColors) {
SettingsSwitchRow(title = stringResource(MR.strings.settings_dynamic_colors), SettingsSwitchRow(
title = stringResource(MR.strings.settings_dynamic_colors),
value = uiState.dynamicColors, value = uiState.dynamicColors,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -342,7 +343,8 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
} }
val colorSchemeProvider = remember { getColorSchemeProvider() } val colorSchemeProvider = remember { getColorSchemeProvider() }
@ -355,7 +357,7 @@ class SettingsScreen : Screen {
).primary, ).primary,
onTap = rememberCallback { onTap = rememberCallback {
val sheet = ColorBottomSheet() val sheet = ColorBottomSheet()
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
// upvote and downvote colors // upvote and downvote colors
@ -380,7 +382,7 @@ class SettingsScreen : Screen {
value = uiState.uiFontFamily.toReadableName(), value = uiState.uiFontFamily.toReadableName(),
onTap = rememberCallback { onTap = rememberCallback {
val sheet = FontFamilyBottomSheet() val sheet = FontFamilyBottomSheet()
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
// font scale // font scale
@ -396,7 +398,7 @@ class SettingsScreen : Screen {
), ),
contract = NotificationCenterContractKeys.ChangeUiFontSize contract = NotificationCenterContractKeys.ChangeUiFontSize
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
SettingsRow( SettingsRow(
@ -406,7 +408,7 @@ class SettingsScreen : Screen {
val sheet = FontScaleBottomSheet( val sheet = FontScaleBottomSheet(
contract = NotificationCenterContractKeys.ChangeContentFontSize, contract = NotificationCenterContractKeys.ChangeContentFontSize,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
@ -416,12 +418,13 @@ class SettingsScreen : Screen {
value = uiState.postLayout.toReadableName(), value = uiState.postLayout.toReadableName(),
onTap = rememberCallback { onTap = rememberCallback {
val sheet = PostLayoutBottomSheet() val sheet = PostLayoutBottomSheet()
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
// separate upvotes and downvotes // separate upvotes and downvotes
SettingsSwitchRow(title = stringResource(MR.strings.settings_separate_up_and_downvotes), SettingsSwitchRow(
title = stringResource(MR.strings.settings_separate_up_and_downvotes),
value = uiState.separateUpAndDownVotes, value = uiState.separateUpAndDownVotes,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -429,10 +432,12 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
// full height images // full height images
SettingsSwitchRow(title = stringResource(MR.strings.settings_full_height_images), SettingsSwitchRow(
title = stringResource(MR.strings.settings_full_height_images),
value = uiState.fullHeightImages, value = uiState.fullHeightImages,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -440,10 +445,12 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
// navigation bar titles // navigation bar titles
SettingsSwitchRow(title = stringResource(MR.strings.settings_navigation_bar_titles_visible), SettingsSwitchRow(
title = stringResource(MR.strings.settings_navigation_bar_titles_visible),
value = uiState.navBarTitlesVisible, value = uiState.navBarTitlesVisible,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -451,7 +458,8 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
SettingsHeader( SettingsHeader(
icon = Icons.Default.Tune, icon = Icons.Default.Tune,
@ -466,7 +474,7 @@ class SettingsScreen : Screen {
val sheet = ListingTypeBottomSheet( val sheet = ListingTypeBottomSheet(
isLogged = uiState.isLogged, isLogged = uiState.isLogged,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
@ -479,7 +487,7 @@ class SettingsScreen : Screen {
expandTop = true, expandTop = true,
contract = NotificationCenterContractKeys.ChangeSortType, contract = NotificationCenterContractKeys.ChangeSortType,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
@ -498,7 +506,7 @@ class SettingsScreen : Screen {
SortType.Controversial, SortType.Controversial,
), ),
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
@ -517,7 +525,7 @@ class SettingsScreen : Screen {
), ),
onTap = rememberCallback { onTap = rememberCallback {
val sheet = DurationBottomSheet() val sheet = DurationBottomSheet()
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
@ -535,12 +543,13 @@ class SettingsScreen : Screen {
max = screenWidth, max = screenWidth,
initial = uiState.zombieModeScrollAmount, initial = uiState.zombieModeScrollAmount,
) )
navigationCoordinator.getBottomNavigator()?.show(sheet) navigationCoordinator.showBottomSheet(sheet)
}, },
) )
// swipe actions // swipe actions
SettingsSwitchRow(title = stringResource(MR.strings.settings_enable_swipe_actions), SettingsSwitchRow(
title = stringResource(MR.strings.settings_enable_swipe_actions),
value = uiState.enableSwipeActions, value = uiState.enableSwipeActions,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -548,10 +557,25 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
// double tap
SettingsSwitchRow(
title = stringResource(MR.strings.settings_enable_double_tap),
value = uiState.enableDoubleTapAction,
onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce(
SettingsMviModel.Intent.ChangeEnableDoubleTapAction(
value
)
)
},
)
// bottom navigation hiding // bottom navigation hiding
SettingsSwitchRow(title = stringResource(MR.strings.settings_hide_navigation_bar), SettingsSwitchRow(
title = stringResource(MR.strings.settings_hide_navigation_bar),
value = uiState.hideNavigationBarWhileScrolling, value = uiState.hideNavigationBarWhileScrolling,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -559,10 +583,12 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
// URL open // URL open
SettingsSwitchRow(title = stringResource(MR.strings.settings_open_url_external), SettingsSwitchRow(
title = stringResource(MR.strings.settings_open_url_external),
value = uiState.openUrlsInExternalBrowser, value = uiState.openUrlsInExternalBrowser,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -570,10 +596,12 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
// auto-expand comments // auto-expand comments
SettingsSwitchRow(title = stringResource(MR.strings.settings_auto_expand_comments), SettingsSwitchRow(
title = stringResource(MR.strings.settings_auto_expand_comments),
value = uiState.autoExpandComments, value = uiState.autoExpandComments,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -581,10 +609,12 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
// image loading // image loading
SettingsSwitchRow(title = stringResource(MR.strings.settings_auto_load_images), SettingsSwitchRow(
title = stringResource(MR.strings.settings_auto_load_images),
value = uiState.autoLoadImages, value = uiState.autoLoadImages,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce( model.reduce(
@ -592,7 +622,8 @@ class SettingsScreen : Screen {
value value
) )
) )
}) },
)
SettingsHeader( SettingsHeader(
icon = Icons.Default.Shield, icon = Icons.Default.Shield,
@ -600,16 +631,19 @@ class SettingsScreen : Screen {
) )
// NSFW options // NSFW options
SettingsSwitchRow(title = stringResource(MR.strings.settings_include_nsfw), SettingsSwitchRow(
title = stringResource(MR.strings.settings_include_nsfw),
value = uiState.includeNsfw, value = uiState.includeNsfw,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce(SettingsMviModel.Intent.ChangeIncludeNsfw(value)) model.reduce(SettingsMviModel.Intent.ChangeIncludeNsfw(value))
}) })
SettingsSwitchRow(title = stringResource(MR.strings.settings_blur_nsfw), SettingsSwitchRow(
title = stringResource(MR.strings.settings_blur_nsfw),
value = uiState.blurNsfw, value = uiState.blurNsfw,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce(SettingsMviModel.Intent.ChangeBlurNsfw(value)) model.reduce(SettingsMviModel.Intent.ChangeBlurNsfw(value))
}) },
)
SettingsHeader( SettingsHeader(
icon = Icons.Default.BugReport, icon = Icons.Default.BugReport,
@ -617,11 +651,13 @@ class SettingsScreen : Screen {
) )
// enable crash report // enable crash report
SettingsSwitchRow(title = stringResource(MR.strings.settings_enable_crash_report), SettingsSwitchRow(
title = stringResource(MR.strings.settings_enable_crash_report),
value = uiState.crashReportEnabled, value = uiState.crashReportEnabled,
onValueChanged = rememberCallbackArgs(model) { value -> onValueChanged = rememberCallbackArgs(model) { value ->
model.reduce(SettingsMviModel.Intent.ChangeCrashReportEnabled(value)) model.reduce(SettingsMviModel.Intent.ChangeCrashReportEnabled(value))
}) },
)
// about // about
SettingsRow( SettingsRow(

View File

@ -109,6 +109,7 @@ class SettingsViewModel(
supportsDynamicColors = colorSchemeProvider.supportsDynamicColors, supportsDynamicColors = colorSchemeProvider.supportsDynamicColors,
openUrlsInExternalBrowser = settings.openUrlsInExternalBrowser, openUrlsInExternalBrowser = settings.openUrlsInExternalBrowser,
enableSwipeActions = settings.enableSwipeActions, enableSwipeActions = settings.enableSwipeActions,
enableDoubleTapAction = settings.enableDoubleTapAction,
crashReportEnabled = crashReportConfiguration.isEnabled(), crashReportEnabled = crashReportConfiguration.isEnabled(),
separateUpAndDownVotes = settings.separateUpAndDownVotes, separateUpAndDownVotes = settings.separateUpAndDownVotes,
autoLoadImages = settings.autoLoadImages, autoLoadImages = settings.autoLoadImages,
@ -179,6 +180,10 @@ class SettingsViewModel(
changeEnableSwipeActions(intent.value) changeEnableSwipeActions(intent.value)
} }
is SettingsMviModel.Intent.ChangeEnableDoubleTapAction -> {
changeEnableDoubleTapAction(intent.value)
}
is SettingsMviModel.Intent.ChangeCustomSeedColor -> changeCustomSeedColor( is SettingsMviModel.Intent.ChangeCustomSeedColor -> changeCustomSeedColor(
intent.value intent.value
) )
@ -389,6 +394,16 @@ class SettingsViewModel(
} }
} }
private fun changeEnableDoubleTapAction(value: Boolean) {
mvi.updateState { it.copy(enableDoubleTapAction = value) }
mvi.scope?.launch {
val settings = settingsRepository.currentSettings.value.copy(
enableDoubleTapAction = value
)
saveSettings(settings)
}
}
private fun changePostLayout(value: PostLayout) { private fun changePostLayout(value: PostLayout) {
themeRepository.changePostLayout(value) themeRepository.changePostLayout(value)
mvi.scope?.launch { mvi.scope?.launch {

View File

@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.settings.ui.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -24,25 +25,37 @@ internal fun SettingsColorRow(
title: String, title: String,
value: Color, value: Color,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
subtitle: String? = null,
onTap: (() -> Unit)? = null, onTap: (() -> Unit)? = null,
) { ) {
Row( Row(
modifier = modifier modifier = modifier
.padding(vertical = Spacing.s, horizontal = Spacing.m) .padding(vertical = Spacing.s, horizontal = Spacing.m)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onTap?.invoke() onTap?.invoke()
}, },
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( Column {
text = title, Text(
style = MaterialTheme.typography.bodyMedium, text = title,
color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.bodyMedium,
maxLines = 1, color = MaterialTheme.colorScheme.onBackground,
overflow = TextOverflow.Ellipsis, maxLines = 1,
) overflow = TextOverflow.Ellipsis,
)
if (subtitle != null) {
Text(
text = subtitle,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onBackground,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Box( Box(
modifier = Modifier modifier = Modifier

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.feature.settings.ui.components package com.github.diegoberaldin.raccoonforlemmy.feature.settings.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -18,25 +19,37 @@ internal fun SettingsRow(
title: String, title: String,
value: String, value: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
subtitle: String? = null,
onTap: (() -> Unit)? = null, onTap: (() -> Unit)? = null,
) { ) {
Row( Row(
modifier = modifier modifier = modifier
.padding(vertical = Spacing.s, horizontal = Spacing.m) .padding(vertical = Spacing.s, horizontal = Spacing.m)
.onClick( .onClick(
rememberCallback { onClick = rememberCallback {
onTap?.invoke() onTap?.invoke()
}, },
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( Column {
text = title, Text(
style = MaterialTheme.typography.bodyMedium, text = title,
color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.bodyMedium,
maxLines = 1, color = MaterialTheme.colorScheme.onBackground,
overflow = TextOverflow.Ellipsis, maxLines = 1,
) overflow = TextOverflow.Ellipsis,
)
if (subtitle != null) {
Text(
text = subtitle,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onBackground,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Text( Text(
text = value, text = value,

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.feature.settings.ui.components package com.github.diegoberaldin.raccoonforlemmy.feature.settings.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -16,19 +17,31 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
internal fun SettingsSwitchRow( internal fun SettingsSwitchRow(
title: String, title: String,
value: Boolean, value: Boolean,
subtitle: String? = null,
onValueChanged: (Boolean) -> Unit, onValueChanged: (Boolean) -> Unit,
) { ) {
Row( Row(
modifier = Modifier.padding(horizontal = Spacing.m), modifier = Modifier.padding(horizontal = Spacing.m),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( Column {
text = title, Text(
style = MaterialTheme.typography.bodyMedium, text = title,
color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.bodyMedium,
maxLines = 1, color = MaterialTheme.colorScheme.onBackground,
overflow = TextOverflow.Ellipsis, maxLines = 1,
) overflow = TextOverflow.Ellipsis,
)
if (subtitle != null) {
Text(
text = subtitle,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onBackground,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Switch( Switch(
checked = value, checked = value,

View File

@ -80,6 +80,8 @@
<string name="home_sort_type_top_week_short">week</string> <string name="home_sort_type_top_week_short">week</string>
<string name="home_sort_type_top_year">Top year</string> <string name="home_sort_type_top_year">Top year</string>
<string name="home_sort_type_top_year_short">year</string> <string name="home_sort_type_top_year_short">year</string>
<string name="inbox_action_mark_read">Mark as read</string>
<string name="inbox_action_mark_unread">Mark as unread</string>
<string name="inbox_chat_message">Message</string> <string name="inbox_chat_message">Message</string>
<string name="inbox_item_mention">mentioned you in</string> <string name="inbox_item_mention">mentioned you in</string>
<string name="inbox_item_reply_comment">replied to your comment in</string> <string name="inbox_item_reply_comment">replied to your comment in</string>
@ -200,6 +202,7 @@
<string name="settings_downvote_color">Downvote color</string> <string name="settings_downvote_color">Downvote color</string>
<string name="settings_dynamic_colors">Use dynamic colors</string> <string name="settings_dynamic_colors">Use dynamic colors</string>
<string name="settings_enable_crash_report">Enable crash reporting</string> <string name="settings_enable_crash_report">Enable crash reporting</string>
<string name="settings_enable_double_tap">Enable double tap action</string>
<string name="settings_enable_swipe_actions">Enable swipe actions</string> <string name="settings_enable_swipe_actions">Enable swipe actions</string>
<string name="settings_full_height_images">Full height images</string> <string name="settings_full_height_images">Full height images</string>
<string name="settings_include_nsfw">Include NSFW contents</string> <string name="settings_include_nsfw">Include NSFW contents</string>

View File

@ -87,6 +87,8 @@
<string name="inbox_listing_type_all">Alle</string> <string name="inbox_listing_type_all">Alle</string>
<string name="inbox_listing_type_title">Posteingangstyp</string> <string name="inbox_listing_type_title">Posteingangstyp</string>
<string name="inbox_listing_type_unread">Ungelesen</string> <string name="inbox_listing_type_unread">Ungelesen</string>
<string name="inbox_action_mark_read">Als gelesen markieren</string>
<string name="inbox_action_mark_unread">Als ungelesen markieren</string>
<string name="inbox_not_logged_message">Sie sind derzeit nicht angemeldet.\nBitte fügen Sie über <string name="inbox_not_logged_message">Sie sind derzeit nicht angemeldet.\nBitte fügen Sie über
den Profilbildschirm ein Konto hinzu, um Ihren Posteingang anzuzeigen. den Profilbildschirm ein Konto hinzu, um Ihren Posteingang anzuzeigen.
</string> </string>
@ -193,6 +195,7 @@
<string name="settings_downvote_color">Farbe für Downvotes</string> <string name="settings_downvote_color">Farbe für Downvotes</string>
<string name="settings_dynamic_colors">Verwenden Sie dynamische Farben</string> <string name="settings_dynamic_colors">Verwenden Sie dynamische Farben</string>
<string name="settings_enable_crash_report">Absturzberichte aktivieren</string> <string name="settings_enable_crash_report">Absturzberichte aktivieren</string>
<string name="settings_enable_double_tap">Doppeltipp-Aktion aktivieren</string>
<string name="settings_enable_swipe_actions">Wischaktionen aktivieren</string> <string name="settings_enable_swipe_actions">Wischaktionen aktivieren</string>
<string name="settings_full_height_images">Bilder in voller Höhe</string> <string name="settings_full_height_images">Bilder in voller Höhe</string>
<string name="settings_include_nsfw">NSFW-Inhalte einbeziehen</string> <string name="settings_include_nsfw">NSFW-Inhalte einbeziehen</string>

View File

@ -87,6 +87,8 @@
<string name="inbox_listing_type_all">Όλα</string> <string name="inbox_listing_type_all">Όλα</string>
<string name="inbox_listing_type_title">Τύπος εισερχομένων</string> <string name="inbox_listing_type_title">Τύπος εισερχομένων</string>
<string name="inbox_listing_type_unread">Μη αναγνωσμένα</string> <string name="inbox_listing_type_unread">Μη αναγνωσμένα</string>
<string name="inbox_action_mark_read">Σημειώσε ως αναγνωσμένο</string>
<string name="inbox_action_mark_unread">Σημειώστε ως αδιάβαστο</string>
<string name="inbox_not_logged_message">Προς το παρόν, δεν έχετε συνδεθεί.\nΠαρακαλούμε <string name="inbox_not_logged_message">Προς το παρόν, δεν έχετε συνδεθεί.\nΠαρακαλούμε
προσθέστε έναν λογαριασμό από την οθόνη προφίλ για να δείτε τα εισερχόμενά σας. προσθέστε έναν λογαριασμό από την οθόνη προφίλ για να δείτε τα εισερχόμενά σας.
</string> </string>
@ -194,6 +196,7 @@
<string name="settings_downvote_color">Χρώμα ψήφου κατώτερου</string> <string name="settings_downvote_color">Χρώμα ψήφου κατώτερου</string>
<string name="settings_dynamic_colors">Χρήση δυναμικών χρωμάτων</string> <string name="settings_dynamic_colors">Χρήση δυναμικών χρωμάτων</string>
<string name="settings_enable_crash_report">Ενεργοποίηση αναφοράς καταρρεύσεων</string> <string name="settings_enable_crash_report">Ενεργοποίηση αναφοράς καταρρεύσεων</string>
<string name="settings_enable_double_tap">Ενεργοποίηση της δράσης διπλού πατήματος</string>
<string name="settings_enable_swipe_actions">Ενεργοποίηση κινήσεων σαρώσεων</string> <string name="settings_enable_swipe_actions">Ενεργοποίηση κινήσεων σαρώσεων</string>
<string name="settings_full_height_images">Εικόνες πλήρους ύψους</string> <string name="settings_full_height_images">Εικόνες πλήρους ύψους</string>
<string name="settings_include_nsfw">Συμπερίληψη περιεχομένων NSFW</string> <string name="settings_include_nsfw">Συμπερίληψη περιεχομένων NSFW</string>

View File

@ -84,6 +84,8 @@
<string name="inbox_item_mention">te ha mencionado en</string> <string name="inbox_item_mention">te ha mencionado en</string>
<string name="inbox_item_reply_comment">ha contestado a tu comentario en</string> <string name="inbox_item_reply_comment">ha contestado a tu comentario en</string>
<string name="inbox_item_reply_post">ha contestado a tu publicación en</string> <string name="inbox_item_reply_post">ha contestado a tu publicación en</string>
<string name="inbox_action_mark_read">mMrcar como leído</string>
<string name="inbox_action_mark_unread">Marcar como no leído</string>
<string name="inbox_listing_type_all">Todos</string> <string name="inbox_listing_type_all">Todos</string>
<string name="inbox_listing_type_title">Tipo de mensajes</string> <string name="inbox_listing_type_title">Tipo de mensajes</string>
<string name="inbox_listing_type_unread">No leídos</string> <string name="inbox_listing_type_unread">No leídos</string>
@ -192,6 +194,7 @@
<string name="settings_downvote_color">Color votos negativos</string> <string name="settings_downvote_color">Color votos negativos</string>
<string name="settings_dynamic_colors">Utilizar colores dinámicos</string> <string name="settings_dynamic_colors">Utilizar colores dinámicos</string>
<string name="settings_enable_crash_report">Activar notificación de accidentes</string> <string name="settings_enable_crash_report">Activar notificación de accidentes</string>
<string name="settings_enable_double_tap">Activar la acción de doble toque</string>
<string name="settings_enable_swipe_actions">Activar acciones de deslizamiento</string> <string name="settings_enable_swipe_actions">Activar acciones de deslizamiento</string>
<string name="settings_full_height_images">Altura completa imágenes</string> <string name="settings_full_height_images">Altura completa imágenes</string>
<string name="settings_include_nsfw">Incluir contenidos NSFW</string> <string name="settings_include_nsfw">Incluir contenidos NSFW</string>

View File

@ -87,6 +87,8 @@
<string name="inbox_listing_type_all">Tous</string> <string name="inbox_listing_type_all">Tous</string>
<string name="inbox_listing_type_title">Type de boîte de réception</string> <string name="inbox_listing_type_title">Type de boîte de réception</string>
<string name="inbox_listing_type_unread">Non lus</string> <string name="inbox_listing_type_unread">Non lus</string>
<string name="inbox_action_mark_read">Marquer comme lu</string>
<string name="inbox_action_mark_unread">Marquer comme non lu</string>
<string name="inbox_not_logged_message">Vous n\'êtes pas connecté.\nVeuillez ajouter un compte à <string name="inbox_not_logged_message">Vous n\'êtes pas connecté.\nVeuillez ajouter un compte à
partir de l\'écran de profil pour voir votre boîte de réception. partir de l\'écran de profil pour voir votre boîte de réception.
</string> </string>
@ -192,6 +194,7 @@
<string name="settings_downvote_color">Couleur votes négatifs</string> <string name="settings_downvote_color">Couleur votes négatifs</string>
<string name="settings_dynamic_colors">Utiliser couleur dynamique</string> <string name="settings_dynamic_colors">Utiliser couleur dynamique</string>
<string name="settings_enable_crash_report">Activer les rapports d\'accidents</string> <string name="settings_enable_crash_report">Activer les rapports d\'accidents</string>
<string name="settings_enable_double_tap">Activer l\'action double tap</string>
<string name="settings_enable_swipe_actions">Activer les actions de glissement</string> <string name="settings_enable_swipe_actions">Activer les actions de glissement</string>
<string name="settings_full_height_images">Images en pleine hauteur</string> <string name="settings_full_height_images">Images en pleine hauteur</string>
<string name="settings_include_nsfw">Inclure contenus NSFW</string> <string name="settings_include_nsfw">Inclure contenus NSFW</string>

View File

@ -87,6 +87,8 @@
<string name="inbox_listing_type_all">Tutti</string> <string name="inbox_listing_type_all">Tutti</string>
<string name="inbox_listing_type_title">Tipo inbox</string> <string name="inbox_listing_type_title">Tipo inbox</string>
<string name="inbox_listing_type_unread">Non letti</string> <string name="inbox_listing_type_unread">Non letti</string>
<string name="inbox_action_mark_read">Segna come letto</string>
<string name="inbox_action_mark_unread">Segna come non letto</string>
<string name="inbox_not_logged_message">Login non effettuato.\nAggiungi un account dalla <string name="inbox_not_logged_message">Login non effettuato.\nAggiungi un account dalla
schermata Profilo per accedere alla inbox. schermata Profilo per accedere alla inbox.
</string> </string>
@ -192,6 +194,7 @@
<string name="settings_downvote_color">Colore voti negativi</string> <string name="settings_downvote_color">Colore voti negativi</string>
<string name="settings_dynamic_colors">Usa colori dinamici</string> <string name="settings_dynamic_colors">Usa colori dinamici</string>
<string name="settings_enable_crash_report">Abilitare segnalazioni di crash</string> <string name="settings_enable_crash_report">Abilitare segnalazioni di crash</string>
<string name="settings_enable_double_tap">Abilita azione doppio tap</string>
<string name="settings_enable_swipe_actions">Consenti azioni allo swipe</string> <string name="settings_enable_swipe_actions">Consenti azioni allo swipe</string>
<string name="settings_full_height_images">Altezza completa immagini</string> <string name="settings_full_height_images">Altezza completa immagini</string>
<string name="settings_include_nsfw">Includi contenuti NSFW</string> <string name="settings_include_nsfw">Includi contenuti NSFW</string>

View File

@ -87,6 +87,8 @@
<string name="inbox_listing_type_all">Alle</string> <string name="inbox_listing_type_all">Alle</string>
<string name="inbox_listing_type_title">Type inbox</string> <string name="inbox_listing_type_title">Type inbox</string>
<string name="inbox_listing_type_unread">Ongelezen</string> <string name="inbox_listing_type_unread">Ongelezen</string>
<string name="inbox_action_mark_read">Markeren als gelezen</string>
<string name="inbox_action_mark_unread">Markeren als ongelezen</string>
<string name="inbox_not_logged_message">U bent momenteel niet ingelogd.\nVoeg een account toe <string name="inbox_not_logged_message">U bent momenteel niet ingelogd.\nVoeg een account toe
vanuit het profielscherm om je inbox te zien. vanuit het profielscherm om je inbox te zien.
</string> </string>
@ -200,6 +202,7 @@
<string name="settings_downvote_color">Kleur voor downvote</string> <string name="settings_downvote_color">Kleur voor downvote</string>
<string name="settings_dynamic_colors">Dynamische kleuren gebruiken</string> <string name="settings_dynamic_colors">Dynamische kleuren gebruiken</string>
<string name="settings_enable_crash_report">Crashrapportage inschakelen</string> <string name="settings_enable_crash_report">Crashrapportage inschakelen</string>
<string name="settings_enable_double_tap">Dubbele tik actie inschakelen</string>
<string name="settings_enable_swipe_actions">Veegacties inschakelen</string> <string name="settings_enable_swipe_actions">Veegacties inschakelen</string>
<string name="settings_full_height_images">Afbeeldingen op volledige hoogte</string> <string name="settings_full_height_images">Afbeeldingen op volledige hoogte</string>
<string name="settings_include_nsfw">NSFW-inhoud opnemen</string> <string name="settings_include_nsfw">NSFW-inhoud opnemen</string>

View File

@ -87,6 +87,8 @@
<string name="inbox_listing_type_all">Wszystkie</string> <string name="inbox_listing_type_all">Wszystkie</string>
<string name="inbox_listing_type_title">Typ skrzynki</string> <string name="inbox_listing_type_title">Typ skrzynki</string>
<string name="inbox_listing_type_unread">Nieprzeczytany</string> <string name="inbox_listing_type_unread">Nieprzeczytany</string>
<string name="inbox_action_mark_read">Oznacz jako przeczytane</string>
<string name="inbox_action_mark_unread">Oznacz jako nieprzeczytane</string>
<string name="inbox_not_logged_message">Obecnie nie jesteś zalogowany.\nDodaj konto na ekranie <string name="inbox_not_logged_message">Obecnie nie jesteś zalogowany.\nDodaj konto na ekranie
profilu, aby zobaczyć swoją skrzynkę odbiorczą." profilu, aby zobaczyć swoją skrzynkę odbiorczą."
</string> </string>
@ -191,6 +193,7 @@
<string name="settings_downvote_color">Kolor głosów negatywnych</string> <string name="settings_downvote_color">Kolor głosów negatywnych</string>
<string name="settings_dynamic_colors">Używaj dynamicznych kolorów</string> <string name="settings_dynamic_colors">Używaj dynamicznych kolorów</string>
<string name="settings_enable_crash_report">Włącz raportowanie awarii</string> <string name="settings_enable_crash_report">Włącz raportowanie awarii</string>
<string name="settings_enable_double_tap">Włącz akcje podwójnego dotknięcia</string>
<string name="settings_enable_swipe_actions">Włącz akcje przesuwania</string> <string name="settings_enable_swipe_actions">Włącz akcje przesuwania</string>
<string name="settings_full_height_images">Obrazy o pełnej wysokości</string> <string name="settings_full_height_images">Obrazy o pełnej wysokości</string>
<string name="settings_hide_navigation_bar">Ukryj pasek nawigacji podczas przewijania</string> <string name="settings_hide_navigation_bar">Ukryj pasek nawigacji podczas przewijania</string>

View File

@ -87,6 +87,8 @@
<string name="inbox_listing_type_all">Todas</string> <string name="inbox_listing_type_all">Todas</string>
<string name="inbox_listing_type_title">Tipo de caixa</string> <string name="inbox_listing_type_title">Tipo de caixa</string>
<string name="inbox_listing_type_unread">Não lidas</string> <string name="inbox_listing_type_unread">Não lidas</string>
<string name="inbox_action_mark_read">Marcar como lido</string>
<string name="inbox_action_mark_unread">Marcar como não lido</string>
<string name="inbox_not_logged_message">Atualmente, não tem sessão iniciada.\nAdicione uma conta <string name="inbox_not_logged_message">Atualmente, não tem sessão iniciada.\nAdicione uma conta
a partir do ecrã de perfil para ver a sua caixa de entrada. a partir do ecrã de perfil para ver a sua caixa de entrada.
</string> </string>
@ -190,6 +192,7 @@
<string name="settings_downvote_color">Cor votos negativos</string> <string name="settings_downvote_color">Cor votos negativos</string>
<string name="settings_dynamic_colors">Usar cores dinâmicas</string> <string name="settings_dynamic_colors">Usar cores dinâmicas</string>
<string name="settings_enable_crash_report">Ativar relatórios de falhas</string> <string name="settings_enable_crash_report">Ativar relatórios de falhas</string>
<string name="settings_enable_double_tap">Ativar a ação de duplo toque</string>
<string name="settings_enable_swipe_actions">Ativar ações de deslizar</string> <string name="settings_enable_swipe_actions">Ativar ações de deslizar</string>
<string name="settings_full_height_images">Imagens de altura natural</string> <string name="settings_full_height_images">Imagens de altura natural</string>
<string name="settings_include_nsfw">Incluir conteúdo NSFW</string> <string name="settings_include_nsfw">Incluir conteúdo NSFW</string>

View File

@ -87,6 +87,8 @@
<string name="inbox_listing_type_all">Toate</string> <string name="inbox_listing_type_all">Toate</string>
<string name="inbox_listing_type_title">Tip de inbox</string> <string name="inbox_listing_type_title">Tip de inbox</string>
<string name="inbox_listing_type_unread">Necitit</string> <string name="inbox_listing_type_unread">Necitit</string>
<string name="inbox_action_mark_read">Marchează ca citit</string>
<string name="inbox_action_mark_unread">Marchează ca necitit</string>
<string name="inbox_not_logged_message">Momentan nu sunteți autentificat.\nVă rugăm să adăugați <string name="inbox_not_logged_message">Momentan nu sunteți autentificat.\nVă rugăm să adăugați
un cont din ecranul de profil pentru a vedea mesajele. un cont din ecranul de profil pentru a vedea mesajele.
</string> </string>
@ -191,6 +193,7 @@
<string name="settings_downvote_color">Culoare voturilor negative</string> <string name="settings_downvote_color">Culoare voturilor negative</string>
<string name="settings_dynamic_colors">Folosește culori dinamice</string> <string name="settings_dynamic_colors">Folosește culori dinamice</string>
<string name="settings_enable_crash_report">Activează raporturi erorilor</string> <string name="settings_enable_crash_report">Activează raporturi erorilor</string>
<string name="settings_enable_double_tap">Activează acțiunea de dublă atingere</string>
<string name="settings_enable_swipe_actions">Activează acțiunile de glisare</string> <string name="settings_enable_swipe_actions">Activează acțiunile de glisare</string>
<string name="settings_full_height_images">Imagini pe toată înălțimea</string> <string name="settings_full_height_images">Imagini pe toată înălțimea</string>
<string name="settings_include_nsfw">Include conținuturile NSFW</string> <string name="settings_include_nsfw">Include conținuturile NSFW</string>

View File

@ -172,7 +172,7 @@ fun App() {
else -> null else -> null
} }
if (newScreen != null) { if (newScreen != null) {
navigationCoordinator.getRootNavigator()?.push(newScreen) navigationCoordinator.pushScreen(newScreen)
} }
}.launchIn(this) }.launchIn(this)
} }
@ -182,7 +182,6 @@ fun App() {
val drawerGestureEnabled by drawerCoordinator.gesturesEnabled.collectAsState() val drawerGestureEnabled by drawerCoordinator.gesturesEnabled.collectAsState()
LaunchedEffect(drawerCoordinator) { LaunchedEffect(drawerCoordinator) {
drawerCoordinator.toggleEvents.onEach { evt -> drawerCoordinator.toggleEvents.onEach { evt ->
val navigator = navigationCoordinator.getRootNavigator()
when (evt) { when (evt) {
DrawerEvent.Toggled -> { DrawerEvent.Toggled -> {
drawerState.apply { drawerState.apply {
@ -197,19 +196,19 @@ fun App() {
} }
is DrawerEvent.OpenCommunity -> { is DrawerEvent.OpenCommunity -> {
navigator?.push(CommunityDetailScreen(evt.community)) navigationCoordinator.pushScreen(CommunityDetailScreen(evt.community))
} }
is DrawerEvent.OpenMultiCommunity -> { is DrawerEvent.OpenMultiCommunity -> {
navigator?.push(MultiCommunityScreen(evt.community)) navigationCoordinator.pushScreen(MultiCommunityScreen(evt.community))
} }
DrawerEvent.ManageSubscriptions -> { DrawerEvent.ManageSubscriptions -> {
navigator?.push(ManageSubscriptionsScreen()) navigationCoordinator.pushScreen(ManageSubscriptionsScreen())
} }
DrawerEvent.OpenBookmarks -> { DrawerEvent.OpenBookmarks -> {
navigator?.push(SavedItemsScreen()) navigationCoordinator.pushScreen(SavedItemsScreen())
} }
} }
}.launchIn(this) }.launchIn(this)