feat: enable support for third action in swipe cards (#374); closes #268

* feat: enable second action gestures

* chore: update post list

* chore: update community detail

* chore: update user detail

* chore: update multi-community

* chore: disable reply from explore and profile

* feat: configure in settings

* chore: add l10ns
This commit is contained in:
Diego Beraldin 2023-12-25 13:48:54 +01:00 committed by GitHub
parent 2e1982dabc
commit 07060acaee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 394 additions and 85 deletions

View File

@ -49,4 +49,12 @@ fun CommentBarTheme?.toDownVoteColor(): Color = when (this) {
CommentBarTheme.Green -> Color(0xFFAD4877)
CommentBarTheme.Blue -> Color(0xFF9400D3)
else -> Color.Transparent
}
fun CommentBarTheme?.toReplyColor(): Color = when (this) {
CommentBarTheme.Rainbow -> Color(0xFFFF5722)
CommentBarTheme.Red -> Color(0xFF8BC34A)
CommentBarTheme.Green -> Color(0xFFFF9800)
CommentBarTheme.Blue -> Color(0xFF388E3C)
else -> Color.Transparent
}

View File

@ -20,6 +20,7 @@ internal class DefaultThemeRepository : ThemeRepository {
override val customSeedColor = MutableStateFlow<Color?>(null)
override val upvoteColor = MutableStateFlow<Color?>(null)
override val downvoteColor = MutableStateFlow<Color?>(null)
override val replyColor = MutableStateFlow<Color?>(null)
override val postLayout = MutableStateFlow<PostLayout>(PostLayout.Card)
override val commentBarTheme = MutableStateFlow<CommentBarTheme>(CommentBarTheme.Blue)
@ -72,6 +73,10 @@ internal class DefaultThemeRepository : ThemeRepository {
downvoteColor.value = color
}
override fun changeReplyColor(color: Color?) {
replyColor.value = color
}
override fun changePostLayout(value: PostLayout) {
postLayout.value = value
}

View File

@ -21,6 +21,7 @@ interface ThemeRepository {
val customSeedColor: StateFlow<Color?>
val upvoteColor: StateFlow<Color?>
val downvoteColor: StateFlow<Color?>
val replyColor: StateFlow<Color?>
val postLayout: StateFlow<PostLayout>
val commentBarTheme: StateFlow<CommentBarTheme>
@ -46,6 +47,8 @@ interface ThemeRepository {
fun changeDownvoteColor(color: Color?)
fun changeReplyColor(color: Color?)
fun changePostLayout(value: PostLayout)
fun changeCommentBarTheme(value: CommentBarTheme)

View File

@ -27,6 +27,8 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
private const val SECOND_ACTION_THRESHOLD = 0.35f
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SwipeableCard(
@ -36,44 +38,78 @@ fun SwipeableCard(
DismissDirection.EndToStart,
),
enabled: Boolean = true,
enableSecondAction: (DismissValue) -> Boolean = { false },
content: @Composable () -> Unit,
swipeContent: @Composable (DismissDirection) -> Unit,
secondSwipeContent: @Composable ((DismissDirection) -> Unit)? = null,
backgroundColor: (DismissValue) -> Color,
secondBackgroundColor: ((DismissValue) -> Color)? = null,
onGestureBegin: (() -> Unit)? = null,
onDismissToEnd: (() -> Unit)? = null,
onSecondDismissToEnd: (() -> Unit)? = null,
onDismissToStart: (() -> Unit)? = null,
onSecondDismissToStart: (() -> Unit)? = null,
) {
if (enabled) {
var notified by remember { mutableStateOf(false) }
var secondNotified by remember { mutableStateOf(false) }
val dismissToEndCallback by rememberUpdatedState(onDismissToEnd)
val dismissToStartCallback by rememberUpdatedState(onDismissToStart)
val secondDismissToEndCallback by rememberUpdatedState(onSecondDismissToEnd)
val secondDismissToStartCallback by rememberUpdatedState(onSecondDismissToStart)
val gestureBeginCallback by rememberUpdatedState(onGestureBegin)
var lastProgress by remember { mutableStateOf(0.0f) }
val dismissState = rememberDismissState(
confirmValueChange = rememberCallbackArgs { direction ->
when (direction) {
confirmValueChange = rememberCallbackArgs { value ->
when (value) {
DismissValue.DismissedToEnd -> {
dismissToEndCallback?.invoke()
if (lastProgress >= SECOND_ACTION_THRESHOLD) {
secondDismissToEndCallback?.invoke()
} else {
dismissToEndCallback?.invoke()
}
}
DismissValue.DismissedToStart -> {
dismissToStartCallback?.invoke()
if (lastProgress >= SECOND_ACTION_THRESHOLD) {
secondDismissToStartCallback?.invoke()
} else {
dismissToStartCallback?.invoke()
}
}
else -> Unit
}
notified = false
secondNotified = false
// return false to stay dismissed
false
},
positionalThreshold = { _ -> 56.dp.toPx() }
)
var notified by remember { mutableStateOf(false) }
LaunchedEffect(dismissState) {
snapshotFlow { dismissState.progress }.stateIn(this).onEach { progress ->
if (progress in 0.0..<1.0 && !notified) {
notified = true
gestureBeginCallback?.invoke()
} else if (progress >= 1) {
notified = false
if (!enableSecondAction(dismissState.targetValue)) {
when {
progress in 0.0f..<1.0f && !notified -> {
notified = true
gestureBeginCallback?.invoke()
}
}
} else {
when {
progress in 0.0f..<SECOND_ACTION_THRESHOLD && !notified -> {
notified = true
gestureBeginCallback?.invoke()
}
progress in SECOND_ACTION_THRESHOLD..<1.0f && !secondNotified -> {
secondNotified = true
gestureBeginCallback?.invoke()
}
}
}
lastProgress = progress
}.launchIn(this)
}
@ -84,7 +120,16 @@ fun SwipeableCard(
background = {
val direction = dismissState.dismissDirection ?: DismissDirection.StartToEnd
val bgColor by animateColorAsState(
backgroundColor(dismissState.targetValue),
targetValue = if (
dismissState.progress < SECOND_ACTION_THRESHOLD
|| dismissState.targetValue == DismissValue.Default
|| !enableSecondAction(dismissState.targetValue)
) {
backgroundColor(dismissState.targetValue)
} else {
secondBackgroundColor?.invoke(dismissState.targetValue)
?: backgroundColor(dismissState.targetValue)
},
)
val alignment = when (direction) {
DismissDirection.StartToEnd -> Alignment.CenterStart
@ -96,7 +141,14 @@ fun SwipeableCard(
.padding(horizontal = 20.dp),
contentAlignment = alignment,
) {
swipeContent(direction)
if (
dismissState.progress < SECOND_ACTION_THRESHOLD
|| !enableSecondAction(dismissState.targetValue)
) {
swipeContent(direction)
} else {
secondSwipeContent?.invoke(direction)
}
}
},
dismissContent = {

View File

@ -31,6 +31,7 @@ import cafe.adriel.voyager.core.screen.Screen
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.CommentBarTheme
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toDownVoteColor
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toReadableName
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toReplyColor
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.toUpVoteColor
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
@ -44,7 +45,7 @@ import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.stringResource
class VoteThemeBottomSheet(
val downvote: Boolean,
val actionType: Int,
) : Screen {
@Composable
@ -54,6 +55,7 @@ class VoteThemeBottomSheet(
var customPickerDialogOpened by remember { mutableStateOf(false) }
val settingsRepository = remember { getSettingsRepository() }
val defaultUpvoteColor = MaterialTheme.colorScheme.primary
val defaultReplyColor = MaterialTheme.colorScheme.secondary
val defaultDownvoteColor = MaterialTheme.colorScheme.tertiary
Column(
@ -76,10 +78,10 @@ class VoteThemeBottomSheet(
top = Spacing.s,
end = Spacing.s,
),
text = if (downvote) {
stringResource(MR.strings.settings_downvote_color)
} else {
stringResource(MR.strings.settings_upvote_color)
text = when (actionType) {
2 -> stringResource(MR.strings.settings_reply_color)
1 -> stringResource(MR.strings.settings_downvote_color)
else -> stringResource(MR.strings.settings_upvote_color)
},
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onBackground,
@ -113,13 +115,21 @@ class VoteThemeBottomSheet(
onClick = rememberCallback {
if (!isChooseCustom) {
notificationCenter.send(
NotificationCenterEvent.ChangeVoteColor(
color = if (downvote) {
value?.toDownVoteColor() ?: defaultDownvoteColor
} else {
value?.toUpVoteColor() ?: defaultUpvoteColor
NotificationCenterEvent.ChangeActionColor(
color = when (actionType) {
2 -> {
value?.toReplyColor() ?: defaultReplyColor
}
1 -> {
value?.toDownVoteColor() ?: defaultDownvoteColor
}
else -> {
value?.toUpVoteColor() ?: defaultUpvoteColor
}
},
downvote = downvote,
actionType = actionType,
)
)
navigationCoordinator.hideBottomSheet()
@ -141,10 +151,10 @@ class VoteThemeBottomSheet(
modifier = Modifier
.size(36.dp)
.background(
color = if (downvote) {
value.toDownVoteColor()
} else {
value.toUpVoteColor()
color = when (actionType) {
2 -> value.toReplyColor()
1 -> value.toDownVoteColor()
else -> value.toUpVoteColor()
},
shape = CircleShape
)
@ -162,10 +172,18 @@ class VoteThemeBottomSheet(
}
if (customPickerDialogOpened) {
val current = if (downvote) {
settingsRepository.currentSettings.value.downvoteColor?.let { Color(it) }
} else {
settingsRepository.currentSettings.value.upvoteColor?.let { Color(it) }
val current = when (actionType) {
2 -> {
settingsRepository.currentSettings.value.replyColor?.let { Color(it) }
}
1 -> {
settingsRepository.currentSettings.value.downvoteColor?.let { Color(it) }
}
else -> {
settingsRepository.currentSettings.value.upvoteColor?.let { Color(it) }
}
}
ColorPickerDialog(
initialValue = current ?: MaterialTheme.colorScheme.primary,
@ -174,9 +192,9 @@ class VoteThemeBottomSheet(
},
onSubmit = { color ->
notificationCenter.send(
NotificationCenterEvent.ChangeVoteColor(
NotificationCenterEvent.ChangeActionColor(
color = color,
downvote = downvote,
actionType = actionType,
)
)
navigationCoordinator.hideBottomSheet()

View File

@ -42,7 +42,7 @@ sealed interface NotificationCenterEvent {
data class CommentUpdated(val model: CommentModel) : NotificationCenterEvent
data class PostDeleted(val model: PostModel) : NotificationCenterEvent
data class ChangeColor(val color: Color?) : NotificationCenterEvent
data class ChangeVoteColor(val color: Color?, val downvote: Boolean) : NotificationCenterEvent
data class ChangeActionColor(val color: Color?, val actionType: Int) : NotificationCenterEvent
data class ChangeZombieScrollAmount(val value: Float) : NotificationCenterEvent
data class MultiCommunityCreated(val model: MultiCommunityModel) : NotificationCenterEvent
data object CloseDialog : NotificationCenterEvent

View File

@ -37,6 +37,6 @@ data class SettingsModel(
val zombieModeScrollAmount: Float = 55f,
val markAsReadWhileScrolling: Boolean = false,
val commentBarTheme: Int = 0,
val sharePostOriginal: Boolean = true,
val replyColor: Int? = null,
val searchPostTitleOnly: Boolean = false,
) : JavaSerializable

View File

@ -42,7 +42,7 @@ private object KeyStoreKeys {
const val ZombieModeScrollAmount = "zombieModeScrollAmount"
const val MarkAsReadWhileScrolling = "markAsReadWhileScrolling"
const val CommentBarTheme = "commentBarTheme"
const val SharePostOriginal = "sharePostOriginal"
const val ReplyColor = "replyColor"
const val SearchPostTitleOnly = "searchPostTitleOnly"
const val ContentFontFamily = "contentFontFamily"
}
@ -89,7 +89,7 @@ internal class DefaultSettingsRepository(
zombieModeScrollAmount = settings.zombieModeScrollAmount.toDouble(),
markAsReadWhileScrolling = if (settings.markAsReadWhileScrolling) 1 else 0,
commentBarTheme = settings.commentBarTheme.toLong(),
sharePostOriginal = if (settings.sharePostOriginal) 1 else 0,
replyColor = settings.replyColor?.toLong(),
searchPostTitleOnly = if (settings.searchPostTitleOnly) 1 else 0,
contentFontFamily = settings.contentFontFamily.toLong(),
)
@ -131,7 +131,7 @@ internal class DefaultSettingsRepository(
zombieModeScrollAmount = keyStore[KeyStoreKeys.ZombieModeScrollAmount, 55f],
markAsReadWhileScrolling = keyStore[KeyStoreKeys.MarkAsReadWhileScrolling, false],
commentBarTheme = keyStore[KeyStoreKeys.CommentBarTheme, 0],
sharePostOriginal = keyStore[KeyStoreKeys.SharePostOriginal, true],
replyColor = if (!keyStore.containsKey(KeyStoreKeys.ReplyColor)) null else keyStore[KeyStoreKeys.ReplyColor, 0],
searchPostTitleOnly = keyStore[KeyStoreKeys.SearchPostTitleOnly, false],
contentFontFamily = keyStore[KeyStoreKeys.ContentFontFamily, 0],
)
@ -210,10 +210,11 @@ internal class DefaultSettingsRepository(
settings.markAsReadWhileScrolling,
)
keyStore.save(KeyStoreKeys.CommentBarTheme, settings.commentBarTheme)
keyStore.save(
KeyStoreKeys.SharePostOriginal,
settings.sharePostOriginal,
)
if (settings.replyColor != null) {
keyStore.save(KeyStoreKeys.ReplyColor, settings.replyColor)
} else {
keyStore.remove(KeyStoreKeys.ReplyColor)
}
keyStore.save(
KeyStoreKeys.SearchPostTitleOnly,
settings.searchPostTitleOnly,
@ -251,7 +252,7 @@ internal class DefaultSettingsRepository(
zombieModeScrollAmount = settings.zombieModeScrollAmount.toDouble(),
markAsReadWhileScrolling = if (settings.markAsReadWhileScrolling) 1L else 0L,
commentBarTheme = settings.commentBarTheme.toLong(),
sharePostOriginal = if (settings.sharePostOriginal) 1L else 0L,
replyColor = settings.replyColor?.toLong(),
searchPostTitleOnly = if (settings.searchPostTitleOnly) 1L else 0L,
contentFontFamily = settings.contentFontFamily.toLong(),
)
@ -294,7 +295,7 @@ private fun GetBy.toModel() = SettingsModel(
zombieModeScrollAmount = zombieModeScrollAmount.toFloat(),
markAsReadWhileScrolling = markAsReadWhileScrolling != 0L,
commentBarTheme = commentBarTheme.toInt(),
sharePostOriginal = sharePostOriginal != 0L,
replyColor = replyColor?.toInt(),
searchPostTitleOnly = searchPostTitleOnly != 0L,
contentFontFamily = contentFontFamily.toInt(),
)

View File

@ -29,7 +29,7 @@ CREATE TABLE SettingsEntity (
zombieModeScrollAmount REAL NOT NULL DEFAULT 100,
markAsReadWhileScrolling INTEGER NOT NULL DEFAULT 0,
commentBarTheme INTEGER NOT NULL DEFAULT 0,
sharePostOriginal INTEGER NOT NULL DEFAULT 1,
replyColor INTEGER DEFAULT NULL,
searchPostTitleOnly INTEGER NOT NULL DEFAULT 0,
contentFontFamily INTEGER NOT NULL DEFAULT 0,
account_id INTEGER,
@ -68,7 +68,7 @@ INSERT OR IGNORE INTO SettingsEntity (
zombieModeScrollAmount,
markAsReadWhileScrolling,
commentBarTheme,
sharePostOriginal,
replyColor,
searchPostTitleOnly,
contentFontFamily,
account_id
@ -139,7 +139,7 @@ SET theme = ?,
zombieModeScrollAmount = ?,
markAsReadWhileScrolling = ?,
commentBarTheme = ?,
sharePostOriginal = ?,
replyColor = ?,
searchPostTitleOnly = ?,
contentFontFamily = ?
WHERE account_id = ?;
@ -176,7 +176,7 @@ SELECT
zombieModeScrollAmount,
markAsReadWhileScrolling,
commentBarTheme,
sharePostOriginal,
replyColor,
searchPostTitleOnly,
contentFontFamily
FROM SettingsEntity

View File

@ -0,0 +1,2 @@
ALTER TABLE SettingsEntity
RENAME COLUMN sharePostOriginal TO replyColor;

View File

@ -85,7 +85,6 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SearchResult
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SearchResultType
import com.github.diegoberaldin.raccoonforlemmy.feature.search.di.getExploreViewModel
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import com.github.diegoberaldin.raccoonforlemmy.unit.createcomment.CreateCommentScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.web.WebViewScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.zoomableimage.ZoomableImageScreen
import dev.icerock.moko.resources.compose.stringResource
@ -421,13 +420,6 @@ class ExploreScreen : Screen {
)
}
},
onReply = rememberCallback {
if (uiState.isLogged) {
detailOpener.openPostDetail(
result.model,
)
}
},
onOpenImage = rememberCallbackArgs { url ->
navigationCoordinator.pushScreen(
ZoomableImageScreen(url),
@ -561,18 +553,6 @@ class ExploreScreen : Screen {
)
}
},
onReply = rememberCallback {
if (uiState.isLogged) {
with(navigationCoordinator) {
setBottomSheetGesturesEnabled(false)
val screen = CreateCommentScreen(
originalPost = PostModel(id = result.model.postId),
originalComment = result.model,
)
showBottomSheet(screen)
}
}
},
onOpenCommunity = rememberCallbackArgs { community, instance ->
detailOpener.openCommunityDetail(
community,

View File

@ -39,6 +39,7 @@ interface SettingsMviModel :
data class ChangeCustomSeedColor(val value: Color?) : Intent
data class ChangeUpvoteColor(val value: Color?) : Intent
data class ChangeDownvoteColor(val value: Color?) : Intent
data class ChangeReplyColor(val value: Color?) : Intent
data class ChangeCrashReportEnabled(val value: Boolean) : Intent
data class ChangeVoteFormat(val value: VoteFormat) : Intent
data class ChangeAutoLoadImages(val value: Boolean) : Intent
@ -59,6 +60,7 @@ interface SettingsMviModel :
val customSeedColor: Color? = null,
val upvoteColor: Color? = null,
val downvoteColor: Color? = null,
val replyColor: Color? = null,
val uiFontScale: FontScale = FontScale.Normal,
val contentFontScale: FontScale = FontScale.Normal,
val contentFontFamily: UiFontFamily = UiFontFamily.Poppins,

View File

@ -253,7 +253,7 @@ class SettingsScreen : Screen {
value = uiState.upvoteColor ?: MaterialTheme.colorScheme.primary,
onTap = rememberCallback {
val screen = VoteThemeBottomSheet(
downvote = false,
actionType = 0,
)
navigationCoordinator.showBottomSheet(screen)
},
@ -263,7 +263,17 @@ class SettingsScreen : Screen {
value = uiState.downvoteColor ?: MaterialTheme.colorScheme.tertiary,
onTap = rememberCallback {
val screen = VoteThemeBottomSheet(
downvote = true,
actionType = 1,
)
navigationCoordinator.showBottomSheet(screen)
},
)
SettingsColorRow(
title = stringResource(MR.strings.settings_reply_color),
value = uiState.replyColor ?: MaterialTheme.colorScheme.secondary,
onTap = rememberCallback {
val screen = VoteThemeBottomSheet(
actionType = 2,
)
navigationCoordinator.showBottomSheet(screen)
},

View File

@ -90,6 +90,9 @@ class SettingsViewModel(
themeRepository.downvoteColor.onEach { value ->
mvi.updateState { it.copy(downvoteColor = value) }
}.launchIn(this)
themeRepository.replyColor.onEach { value ->
mvi.updateState { it.copy(replyColor = value) }
}.launchIn(this)
themeRepository.commentBarTheme.onEach { value ->
mvi.updateState { it.copy(commentBarTheme = value) }
}.launchIn(this)
@ -167,12 +170,12 @@ class SettingsViewModel(
.onEach { evt ->
changeCommentBarTheme(evt.value)
}.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.ChangeVoteColor::class)
notificationCenter.subscribe(NotificationCenterEvent.ChangeActionColor::class)
.onEach { evt ->
if (evt.downvote) {
changeDownvoteColor(evt.color)
} else {
changeUpvoteColor(evt.color)
when (evt.actionType) {
2 -> changeReplyColor(evt.color)
1 -> changeDownvoteColor(evt.color)
else -> changeUpvoteColor(evt.color)
}
}.launchIn(this)
@ -315,6 +318,10 @@ class SettingsViewModel(
changeDownvoteColor(intent.value)
}
is SettingsMviModel.Intent.ChangeReplyColor -> {
changeReplyColor(intent.value)
}
is SettingsMviModel.Intent.ChangeHideNavigationBarWhileScrolling -> {
changeHideNavigationBarWhileScrolling(intent.value)
}
@ -505,6 +512,16 @@ class SettingsViewModel(
}
}
private fun changeReplyColor(value: Color?) {
themeRepository.changeReplyColor(value)
mvi.scope?.launch(Dispatchers.IO) {
val settings = settingsRepository.currentSettings.value.copy(
replyColor = value?.toArgb()
)
saveSettings(settings)
}
}
private fun changeOpenUrlsInExternalBrowser(value: Boolean) {
mvi.updateState { it.copy(openUrlsInExternalBrowser = value) }
mvi.scope?.launch(Dispatchers.IO) {

View File

@ -283,4 +283,5 @@
<string name="user_info_moderates">مشرف</string>
<string name="user_info_admin">مدير</string>
<string name="settings_about_chat_matrix">الدردشة على Matrix</string>
<string name="settings_reply_color">لون عمل الرد</string>
</resources>

View File

@ -314,4 +314,5 @@
<string name="user_info_moderates">Moderator of</string>
<string name="user_info_admin">administrator</string>
<string name="settings_about_chat_matrix">Chat on Matrix</string>
<string name="settings_reply_color">Reply action color</string>
</resources>

View File

@ -293,4 +293,5 @@
<string name="user_info_moderates">Модератор на</string>
<string name="user_info_admin">администратор</string>
<string name="settings_about_chat_matrix">Чат в Matrix</string>
<string name="settings_reply_color">цвят на отговора</string>
</resources>

View File

@ -285,4 +285,5 @@
<string name="user_info_moderates">Moderátor</string>
<string name="user_info_admin">správce</string>
<string name="settings_about_chat_matrix">Chat na Matrixu</string>
<string name="settings_reply_color">Barva akce odpovědi</string>
</resources>

View File

@ -285,4 +285,5 @@
<string name="user_info_moderates">Moderator af</string>
<string name="user_info_admin">administrator</string>
<string name="settings_about_chat_matrix">Chat på Matrix</string>
<string name="settings_reply_color">Farve på svarhandling</string>
</resources>

View File

@ -293,4 +293,5 @@
<string name="user_info_moderates">Moderator von</string>
<string name="user_info_admin">Administrator</string>
<string name="settings_about_chat_matrix">Chatten Sie auf Matrix</string>
<string name="settings_reply_color">Farbe der Antwortaktion</string>
</resources>

View File

@ -294,4 +294,5 @@
<string name="user_info_moderates">Συντονιστής των</string>
<string name="user_info_admin">διαχειριστής</string>
<string name="settings_about_chat_matrix">Συνομιλία στο Matrix</string>
<string name="settings_reply_color">Χρώμα της ενέργειας απάντησης</string>
</resources>

View File

@ -284,4 +284,5 @@
<string name="user_info_moderates">Moderatoro de</string>
<string name="user_info_admin">administranto</string>
<string name="settings_about_chat_matrix">Babilu sur Matrix</string>
<string name="settings_reply_color">Koloro de responda ago</string>
</resources>

View File

@ -289,4 +289,5 @@
<string name="user_info_moderates">Moderador de</string>
<string name="user_info_admin">administrador</string>
<string name="settings_about_chat_matrix">Chat en Matrix</string>
<string name="settings_reply_color">Color de la acción de respuesta</string>
</resources>

View File

@ -285,4 +285,5 @@
<string name="user_info_moderates">Moderaator</string>
<string name="user_info_admin">administraator</string>
<string name="settings_about_chat_matrix">Vestelge Matrixis</string>
<string name="settings_reply_color">Vastuse toimingu värv</string>
</resources>

View File

@ -285,4 +285,5 @@
<string name="user_info_moderates">Moderaattori</string>
<string name="user_info_admin">järjestelmänvalvoja</string>
<string name="settings_about_chat_matrix">Chat Matrixissa</string>
<string name="settings_reply_color">Vastaustoiminnon väri</string>
</resources>

View File

@ -290,4 +290,5 @@
<string name="user_info_moderates">Modérateur de</string>
<string name="user_info_admin">administrateur</string>
<string name="settings_about_chat_matrix">Discutez sur Matrix</string>
<string name="settings_reply_color">Couleur de l\'action de réponse</string>
</resources>

View File

@ -294,4 +294,5 @@
<string name="user_info_moderates">Modhnóir na</string>
<string name="user_info_admin">riarthóir</string>
<string name="settings_about_chat_matrix">Comhrá ar Matrix</string>
<string name="settings_reply_color">Dath an ghnímh freagartha</string>
</resources>

View File

@ -290,4 +290,5 @@
<string name="user_info_moderates">Moderator od</string>
<string name="user_info_admin">administrator</string>
<string name="settings_about_chat_matrix">Chat na Matrixu</string>
<string name="settings_reply_color">Boja radnje odgovora</string>
</resources>

View File

@ -289,4 +289,5 @@
<string name="user_info_moderates">Moderátora</string>
<string name="user_info_admin">adminisztrátor</string>
<string name="settings_about_chat_matrix">Chat a Matrixon</string>
<string name="settings_reply_color">Válaszművelet színe</string>
</resources>

View File

@ -289,4 +289,5 @@
<string name="user_info_moderates">Moderatore di</string>
<string name="user_info_admin">amministratore</string>
<string name="settings_about_chat_matrix">Chat su Matrix</string>
<string name="settings_reply_color">Colore azione di risposta</string>
</resources>

View File

@ -287,4 +287,5 @@
<string name="user_info_moderates">Moderatorius</string>
<string name="user_info_admin">administratorius</string>
<string name="settings_about_chat_matrix">Pokalbis Matrix</string>
<string name="settings_reply_color">Atsakymo veiksmo spalva</string>
</resources>

View File

@ -289,4 +289,5 @@
<string name="user_info_moderates">Moderators no</string>
<string name="user_info_admin">administrators</string>
<string name="settings_about_chat_matrix">Tērzējiet Matrix</string>
<string name="settings_reply_color">Atbildes darbības krāsa</string>
</resources>

View File

@ -290,4 +290,5 @@
<string name="user_info_moderates">Moderatur ta\'</string>
<string name="user_info_admin">amministratur</string>
<string name="settings_about_chat_matrix">Chat fuq Matrix</string>
<string name="settings_reply_color">Kulur ta\' azzjoni ta\' risposta</string>
</resources>

View File

@ -288,4 +288,5 @@
<string name="user_info_moderates">Moderator van</string>
<string name="user_info_admin">beheerder</string>
<string name="settings_about_chat_matrix">Chatten op Matrix</string>
<string name="settings_reply_color">Kleur van antwoordactie</string>
</resources>

View File

@ -287,4 +287,5 @@
<string name="user_info_moderates">Moderator for</string>
<string name="user_info_admin">administrator</string>
<string name="settings_about_chat_matrix">Chat på Matrix</string>
<string name="settings_reply_color">Fargen på svarhandlingen</string>
</resources>

View File

@ -288,4 +288,5 @@
<string name="user_info_moderates">Moderator</string>
<string name="user_info_admin">administrator</string>
<string name="settings_about_chat_matrix">Czat na Matrixie</string>
<string name="settings_reply_color">Kolor akcji odpowiedzi</string>
</resources>

View File

@ -287,4 +287,5 @@
<string name="user_info_moderates">Moderador de</string>
<string name="user_info_admin">administrador</string>
<string name="settings_about_chat_matrix">Conversar no Matrix</string>
<string name="settings_reply_color">Cor da ação de resposta</string>
</resources>

View File

@ -286,4 +286,5 @@
<string name="user_info_moderates">Moderator al</string>
<string name="user_info_admin">administrator</string>
<string name="settings_about_chat_matrix">Chat pe Matrix</string>
<string name="settings_reply_color">Culoare acțiunii de răspuns</string>
</resources>

View File

@ -288,4 +288,5 @@
<string name="user_info_moderates">Модератор</string>
<string name="user_info_admin">администратор</string>
<string name="settings_about_chat_matrix">чат в Matrix</string>
<string name="settings_reply_color">Цвет действия ответа</string>
</resources>

View File

@ -286,4 +286,5 @@
<string name="user_info_moderates">Moderator för</string>
<string name="user_info_admin">administratör</string>
<string name="settings_about_chat_matrix">Chatta på Matrix</string>
<string name="settings_reply_color">Färg på svarsåtgärd</string>
</resources>

View File

@ -287,4 +287,5 @@
<string name="user_info_moderates">Moderátorka</string>
<string name="user_info_admin">správca</string>
<string name="settings_about_chat_matrix">Chat na Matrixe</string>
<string name="settings_reply_color">Farba akcie odpovede</string>
</resources>

View File

@ -285,4 +285,5 @@
<string name="user_info_moderates">Moderator od</string>
<string name="user_info_admin">skrbnik</string>
<string name="settings_about_chat_matrix">Klepet na Matrixu</string>
<string name="settings_reply_color">Barva dejanja odgovora</string>
</resources>

View File

@ -291,4 +291,5 @@
<string name="user_info_moderates">Moderator i</string>
<string name="user_info_admin">administratori</string>
<string name="settings_about_chat_matrix">Bisedoni në Matrix</string>
<string name="settings_reply_color">Ngjyra e veprimit të përgjigjes</string>
</resources>

View File

@ -288,4 +288,5 @@
<string name="user_info_moderates">Moderatörü</string>
<string name="user_info_admin">yönetici</string>
<string name="settings_about_chat_matrix">Matrix\'te sohbet</string>
<string name="settings_reply_color">yanıt eyleminin rengi</string>
</resources>

View File

@ -287,4 +287,5 @@
<string name="user_info_moderates">Модератор</string>
<string name="user_info_admin">адміністратор</string>
<string name="settings_about_chat_matrix">чат на Matrix</string>
<string name="settings_reply_color">колір дії відповіді</string>
</resources>

View File

@ -120,6 +120,7 @@ fun App(onLoadingFinished: () -> Unit = {}) {
with(themeRepository) {
changeUpvoteColor(currentSettings.upvoteColor?.let { Color(it) })
changeDownvoteColor(currentSettings.downvoteColor?.let { Color(it) })
changeReplyColor(currentSettings.replyColor?.let { Color(it) })
}
}
@ -153,6 +154,7 @@ fun App(onLoadingFinished: () -> Unit = {}) {
with(themeRepository) {
changeUpvoteColor(settings.upvoteColor?.let { Color(it) })
changeDownvoteColor(settings.downvoteColor?.let { Color(it) })
changeReplyColor(settings.replyColor?.let { Color(it) })
}
}
}

View File

@ -28,6 +28,7 @@ import androidx.compose.material.icons.filled.ClearAll
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Reply
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.icons.filled.SyncDisabled
import androidx.compose.material.icons.outlined.AddCircleOutline
@ -155,7 +156,9 @@ class CommunityDetailScreen(
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
val downvoteColor by themeRepository.downvoteColor.collectAsState()
val replyColor by themeRepository.replyColor.collectAsState()
val defaultUpvoteColor = MaterialTheme.colorScheme.primary
val defaultReplyColor = MaterialTheme.colorScheme.secondary
val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary
var rawContent by remember { mutableStateOf<Any?>(null) }
val settingsRepository = remember { getSettingsRepository() }
@ -540,6 +543,13 @@ class CommunityDetailScreen(
DismissDirection.EndToStart,
)
},
enableSecondAction = rememberCallbackArgs { value ->
if (!uiState.isLogged) {
false
} else {
value == DismissValue.DismissedToStart
}
},
backgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> upvoteColor
@ -551,6 +561,14 @@ class CommunityDetailScreen(
else -> Color.Transparent
}
},
secondBackgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> replyColor
?: defaultReplyColor
else -> Color.Transparent
}
},
swipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
@ -562,6 +580,17 @@ class CommunityDetailScreen(
tint = Color.White,
)
},
secondSwipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.Reply
}
Icon(
imageVector = icon,
contentDescription = null,
tint = Color.White,
)
},
onGestureBegin = rememberCallback(model) {
model.reduce(CommunityDetailMviModel.Intent.HapticIndication)
},
@ -570,6 +599,15 @@ class CommunityDetailScreen(
CommunityDetailMviModel.Intent.UpVotePost(post.id),
)
},
onSecondDismissToStart = rememberCallback(model) {
with(navigationCoordinator) {
setBottomSheetGesturesEnabled(false)
val screen = CreateCommentScreen(
originalPost = post,
)
showBottomSheet(screen)
}
},
onDismissToEnd = rememberCallback(model) {
model.reduce(
CommunityDetailMviModel.Intent.DownVotePost(post.id),

View File

@ -23,6 +23,7 @@ import androidx.compose.material.icons.filled.ArrowCircleDown
import androidx.compose.material.icons.filled.ArrowCircleUp
import androidx.compose.material.icons.filled.ClearAll
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.Reply
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
@ -77,6 +78,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallb
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.getAdditionalLabel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.toIcon
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import com.github.diegoberaldin.raccoonforlemmy.unit.createcomment.CreateCommentScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.createreport.CreateReportScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.multicommunity.di.getMultiCommunityViewModel
import com.github.diegoberaldin.raccoonforlemmy.unit.web.WebViewScreen
@ -100,7 +102,9 @@ class MultiCommunityScreen(
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
val downvoteColor by themeRepository.downvoteColor.collectAsState()
val replyColor by themeRepository.replyColor.collectAsState()
val defaultUpvoteColor = MaterialTheme.colorScheme.primary
val defaultReplyColor = MaterialTheme.colorScheme.secondary
val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary
val lazyListState = rememberLazyListState()
val scope = rememberCoroutineScope()
@ -251,6 +255,13 @@ class MultiCommunityScreen(
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled,
enableSecondAction = rememberCallbackArgs { value ->
if (!uiState.isLogged) {
false
} else {
value == DismissValue.DismissedToStart
}
},
backgroundColor = {
when (it) {
DismissValue.DismissedToStart -> upvoteColor
@ -262,12 +273,27 @@ class MultiCommunityScreen(
DismissValue.Default -> Color.Transparent
}
},
secondBackgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> replyColor ?: defaultReplyColor
else -> Color.Transparent
}
},
onGestureBegin = rememberCallback(model) {
model.reduce(MultiCommunityMviModel.Intent.HapticIndication)
},
onDismissToStart = {
model.reduce(MultiCommunityMviModel.Intent.UpVotePost(post.id))
},
onSecondDismissToStart = rememberCallback(model) {
with(navigationCoordinator) {
setBottomSheetGesturesEnabled(false)
val screen = CreateCommentScreen(
originalPost = post,
)
showBottomSheet(screen)
}
},
onDismissToEnd = {
model.reduce(MultiCommunityMviModel.Intent.DownVotePost(post.id))
},
@ -282,6 +308,17 @@ class MultiCommunityScreen(
tint = Color.White,
)
},
secondSwipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.Reply
}
Icon(
imageVector = icon,
contentDescription = null,
tint = Color.White,
)
},
content = {
PostCard(
post = post,

View File

@ -230,9 +230,6 @@ object ProfileLoggedScreen : Tab {
)
)
},
onReply = rememberCallback {
detailOpener.openPostDetail(post)
},
options = buildList {
add(
Option(
@ -375,12 +372,6 @@ object ProfileLoggedScreen : Tab {
)
)
},
onReply = rememberCallback {
detailOpener.openPostDetail(
post = PostModel(id = comment.postId),
highlightCommentId = comment.id,
)
},
options = buildList {
add(
Option(

View File

@ -22,6 +22,7 @@ import androidx.compose.material.icons.filled.ArrowCircleDown
import androidx.compose.material.icons.filled.ArrowCircleUp
import androidx.compose.material.icons.filled.ClearAll
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.Reply
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.icons.filled.SyncDisabled
import androidx.compose.material.pullrefresh.PullRefreshIndicator
@ -107,7 +108,9 @@ class PostListScreen : Screen {
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
val downvoteColor by themeRepository.downvoteColor.collectAsState()
val replyColor by themeRepository.replyColor.collectAsState()
val defaultUpvoteColor = MaterialTheme.colorScheme.primary
val defaultReplyColor = MaterialTheme.colorScheme.secondary
val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary
val lazyListState = rememberLazyListState()
val drawerCoordinator = remember { getDrawerCoordinator() }
@ -308,6 +311,13 @@ class PostListScreen : Screen {
DismissDirection.EndToStart,
)
},
enableSecondAction = rememberCallbackArgs { value ->
if (!uiState.isLogged) {
false
} else {
value == DismissValue.DismissedToStart
}
},
backgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> upvoteColor
@ -319,12 +329,29 @@ class PostListScreen : Screen {
DismissValue.Default -> Color.Transparent
}
},
secondBackgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> replyColor
?: defaultReplyColor
else -> Color.Transparent
}
},
onGestureBegin = rememberCallback(model) {
model.reduce(PostListMviModel.Intent.HapticIndication)
},
onDismissToStart = rememberCallback(model) {
model.reduce(PostListMviModel.Intent.UpVotePost(post.id))
},
onSecondDismissToStart = rememberCallback(model) {
with(navigationCoordinator) {
setBottomSheetGesturesEnabled(false)
val screen = CreateCommentScreen(
originalPost = post,
)
showBottomSheet(screen)
}
},
onDismissToEnd = rememberCallback(model) {
model.reduce(PostListMviModel.Intent.DownVotePost(post.id))
},
@ -339,6 +366,17 @@ class PostListScreen : Screen {
tint = Color.White,
)
},
secondSwipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.Reply
}
Icon(
imageVector = icon,
contentDescription = null,
tint = Color.White,
)
},
content = {
PostCard(
post = post,

View File

@ -25,6 +25,7 @@ import androidx.compose.material.icons.filled.ArrowCircleUp
import androidx.compose.material.icons.filled.Chat
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Reply
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
@ -143,7 +144,9 @@ class UserDetailScreen(
val themeRepository = remember { getThemeRepository() }
val upvoteColor by themeRepository.upvoteColor.collectAsState()
val downvoteColor by themeRepository.downvoteColor.collectAsState()
val replyColor by themeRepository.replyColor.collectAsState()
val defaultUpvoteColor = MaterialTheme.colorScheme.primary
val defaultReplyColor = MaterialTheme.colorScheme.secondary
val defaultDownVoteColor = MaterialTheme.colorScheme.tertiary
val navigationCoordinator = remember { getNavigationCoordinator() }
var rawContent by remember { mutableStateOf<Any?>(null) }
@ -430,6 +433,13 @@ class UserDetailScreen(
DismissDirection.EndToStart,
)
},
enableSecondAction = rememberCallbackArgs { value ->
if (!uiState.isLogged) {
false
} else {
value == DismissValue.DismissedToStart
}
},
backgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> upvoteColor
@ -441,6 +451,14 @@ class UserDetailScreen(
else -> Color.Transparent
}
},
secondBackgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> replyColor
?: defaultReplyColor
else -> Color.Transparent
}
},
swipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
@ -453,6 +471,17 @@ class UserDetailScreen(
tint = Color.White,
)
},
secondSwipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.Reply
}
Icon(
imageVector = icon,
contentDescription = null,
tint = Color.White,
)
},
onGestureBegin = rememberCallback(model) {
model.reduce(UserDetailMviModel.Intent.HapticIndication)
},
@ -461,6 +490,15 @@ class UserDetailScreen(
UserDetailMviModel.Intent.UpVotePost(post.id),
)
},
onSecondDismissToStart = rememberCallback(model) {
with(navigationCoordinator) {
setBottomSheetGesturesEnabled(false)
val screen = CreateCommentScreen(
originalPost = post,
)
showBottomSheet(screen)
}
},
onDismissToEnd = rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.DownVotePost(post.id),
@ -666,6 +704,13 @@ class UserDetailScreen(
DismissDirection.EndToStart,
)
},
enableSecondAction = rememberCallbackArgs { value ->
if (!uiState.isLogged) {
false
} else {
value == DismissValue.DismissedToStart
}
},
backgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> upvoteColor
@ -677,6 +722,14 @@ class UserDetailScreen(
else -> Color.Transparent
}
},
secondBackgroundColor = rememberCallbackArgs { direction ->
when (direction) {
DismissValue.DismissedToStart -> replyColor
?: defaultReplyColor
else -> Color.Transparent
}
},
swipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
@ -688,6 +741,17 @@ class UserDetailScreen(
tint = Color.White,
)
},
secondSwipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.Reply
}
Icon(
imageVector = icon,
contentDescription = null,
tint = Color.White,
)
},
onGestureBegin = rememberCallback(model) {
model.reduce(UserDetailMviModel.Intent.HapticIndication)
},
@ -696,6 +760,16 @@ class UserDetailScreen(
UserDetailMviModel.Intent.UpVoteComment(comment.id),
)
},
onSecondDismissToStart = rememberCallback(model) {
with(navigationCoordinator) {
setBottomSheetGesturesEnabled(false)
val screen = CreateCommentScreen(
originalPost = PostModel(id = comment.postId),
originalComment = comment,
)
showBottomSheet(screen)
}
},
onDismissToEnd = rememberCallback(model) {
model.reduce(
UserDetailMviModel.Intent.DownVoteComment(comment.id),