feat: add language selection (#381); closes #331

* chore: font reordering

* chore: second swipe sensitivity

* chore: update models

* chore: update repositories

* chore: update formatting bar

* chore: add dialog

* chore: update comment creation

* chore: update post creation
This commit is contained in:
Diego Beraldin 2023-12-26 21:07:36 +01:00 committed by GitHub
parent c2ef07d4be
commit 7ca04ace88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 604 additions and 328 deletions

View File

@ -5,38 +5,38 @@ import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.stringResource import dev.icerock.moko.resources.compose.stringResource
enum class UiFontFamily { enum class UiFontFamily {
Default,
TitilliumWeb, TitilliumWeb,
NotoSans, NotoSans,
CharisSIL, CharisSIL,
Comfortaa,
Poppins, Poppins,
Default, Comfortaa,
} }
fun Int.toUiFontFamily() = when (this) { fun Int.toUiFontFamily() = when (this) {
7 -> UiFontFamily.Default 0 -> UiFontFamily.Poppins
6 -> UiFontFamily.TitilliumWeb
5 -> UiFontFamily.Comfortaa
4 -> UiFontFamily.CharisSIL
3 -> UiFontFamily.NotoSans 3 -> UiFontFamily.NotoSans
else -> UiFontFamily.Poppins 4 -> UiFontFamily.CharisSIL
5 -> UiFontFamily.Comfortaa
6 -> UiFontFamily.TitilliumWeb
else -> UiFontFamily.Default
} }
fun UiFontFamily.toInt() = when (this) { fun UiFontFamily.toInt() = when (this) {
UiFontFamily.Poppins -> 0 UiFontFamily.Poppins -> 0
UiFontFamily.Comfortaa -> 5
UiFontFamily.CharisSIL -> 4
UiFontFamily.NotoSans -> 3 UiFontFamily.NotoSans -> 3
UiFontFamily.CharisSIL -> 4
UiFontFamily.Comfortaa -> 5
UiFontFamily.TitilliumWeb -> 6 UiFontFamily.TitilliumWeb -> 6
UiFontFamily.Default -> 7 UiFontFamily.Default -> 7
} }
@Composable @Composable
fun UiFontFamily.toReadableName() = when (this) { fun UiFontFamily.toReadableName() = when (this) {
UiFontFamily.Default -> stringResource(MR.strings.settings_font_family_default)
UiFontFamily.Poppins -> "Poppins" UiFontFamily.Poppins -> "Poppins"
UiFontFamily.Comfortaa -> "Comfortaa"
UiFontFamily.CharisSIL -> "Charis SIL"
UiFontFamily.NotoSans -> "Noto Sans" UiFontFamily.NotoSans -> "Noto Sans"
else -> "Titillium Web" UiFontFamily.CharisSIL -> "Charis SIL"
UiFontFamily.Comfortaa -> "Comfortaa"
UiFontFamily.TitilliumWeb -> "Titillium Web"
UiFontFamily.Default -> stringResource(MR.strings.settings_font_family_default)
} }

View File

@ -13,11 +13,11 @@ import dev.icerock.moko.resources.compose.fontFamilyResource
@Composable @Composable
fun UiFontFamily.toTypography(): Typography { fun UiFontFamily.toTypography(): Typography {
val fontFamily = when (this) { val fontFamily = when (this) {
UiFontFamily.CharisSIL -> fontFamilyResource(MR.fonts.CharisSIL.regular)
UiFontFamily.NotoSans -> fontFamilyResource(MR.fonts.NotoSans.regular)
UiFontFamily.Comfortaa -> fontFamilyResource(MR.fonts.Comfortaa.regular)
UiFontFamily.Poppins -> fontFamilyResource(MR.fonts.Poppins.regular)
UiFontFamily.TitilliumWeb -> fontFamilyResource(MR.fonts.TitilliumWeb.regular) UiFontFamily.TitilliumWeb -> fontFamilyResource(MR.fonts.TitilliumWeb.regular)
UiFontFamily.NotoSans -> fontFamilyResource(MR.fonts.NotoSans.regular)
UiFontFamily.CharisSIL -> fontFamilyResource(MR.fonts.CharisSIL.regular)
UiFontFamily.Poppins -> fontFamilyResource(MR.fonts.Poppins.regular)
UiFontFamily.Comfortaa -> fontFamilyResource(MR.fonts.Comfortaa.regular)
else -> FontFamily.Default else -> FontFamily.Default
} }
return Typography( return Typography(

View File

@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
private const val SECOND_ACTION_THRESHOLD = 0.35f private const val SECOND_ACTION_THRESHOLD = 0.38f
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable

View File

@ -1,7 +1,13 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui package com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Code import androidx.compose.material.icons.filled.Code
import androidx.compose.material.icons.filled.FormatBold import androidx.compose.material.icons.filled.FormatBold
@ -13,292 +19,390 @@ import androidx.compose.material.icons.filled.FormatStrikethrough
import androidx.compose.material.icons.filled.Image import androidx.compose.material.icons.filled.Image
import androidx.compose.material.icons.filled.InsertLink import androidx.compose.material.icons.filled.InsertLink
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.Dp
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize
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.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.LanguageModel
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.fontFamilyResource
@Composable @Composable
fun TextFormattingBar( fun TextFormattingBar(
textFieldValue: TextFieldValue, textFieldValue: TextFieldValue,
onTextFieldValueChanged: (TextFieldValue) -> Unit, onTextFieldValueChanged: (TextFieldValue) -> Unit,
onSelectImage: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onSelectImage: (() -> Unit)? = null,
currentLanguageId: Int? = null,
availableLanguages: List<LanguageModel> = emptyList(),
onSelectLanguage: (() -> Unit)? = null,
) { ) {
Row( val textPlaceholder = "text here"
val urlPlaceholder = "URL"
LazyRow(
modifier = modifier, modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(Spacing.m), horizontalArrangement = Arrangement.spacedBy(Spacing.m),
) { ) {
Icon( // bold
modifier = Modifier.onClick( item {
onClick = { Icon(
val selection = textFieldValue.selection modifier = Modifier.onClick(
val newValue = textFieldValue.let { onClick = {
val newText = buildString { val selection = textFieldValue.selection
append(it.text.substring(0, selection.start)) val newValue = textFieldValue.let {
append("**") val newText = buildString {
if (selection.length == 0) { append(it.text.substring(0, selection.start))
append("text here") append("**")
} else { if (selection.length == 0) {
append(textPlaceholder)
} else {
append(
it.text.substring(
selection.start,
selection.end
)
)
}
append("**")
append( append(
it.text.substring( it.text.substring(
selection.start, selection.end,
selection.end it.text.length
) )
) )
} }
append("**") val newSelection = if (selection.length == 0) {
append( TextRange(index = selection.start + textPlaceholder.length + 2)
it.text.substring(
selection.end,
it.text.length
)
)
}
val newSelection = if (selection.length == 0) {
TextRange(index = selection.start + 11)
} else {
TextRange(start = it.selection.start + 2, end = it.selection.end + 2)
}
it.copy(text = newText, selection = newSelection)
}
onTextFieldValueChanged(newValue)
}),
imageVector = Icons.Default.FormatBold,
contentDescription = null,
)
Icon(
modifier = Modifier.onClick(
onClick = {
val selection = textFieldValue.selection
val newValue = textFieldValue.let {
val newText = buildString {
append(it.text.substring(0, selection.start))
append("*")
if (selection.length == 0) {
append("text here")
} else { } else {
TextRange(
start = it.selection.start + 2,
end = it.selection.end + 2
)
}
it.copy(text = newText, selection = newSelection)
}
onTextFieldValueChanged(newValue)
}),
imageVector = Icons.Default.FormatBold,
contentDescription = null,
)
}
// italic
item {
Icon(
modifier = Modifier.onClick(
onClick = {
val selection = textFieldValue.selection
val newValue = textFieldValue.let {
val newText = buildString {
append(it.text.substring(0, selection.start))
append("*")
if (selection.length == 0) {
append(textPlaceholder)
} else {
append(
it.text.substring(
selection.start,
selection.end
)
)
}
append("*")
append( append(
it.text.substring( it.text.substring(
selection.start, selection.end,
selection.end it.text.length
) )
) )
} }
append("*") val newSelection = if (selection.length == 0) {
append( TextRange(index = selection.start + textPlaceholder.length + 1)
it.text.substring(
selection.end,
it.text.length
)
)
}
val newSelection = if (selection.length == 0) {
TextRange(index = selection.start + 10)
} else {
TextRange(start = it.selection.start + 1, end = it.selection.end + 1)
}
it.copy(text = newText, selection = newSelection)
}
onTextFieldValueChanged(newValue)
}),
imageVector = Icons.Default.FormatItalic,
contentDescription = null,
)
Icon(
modifier = Modifier.onClick(
onClick = {
val selection = textFieldValue.selection
val newValue = textFieldValue.let {
val newText = buildString {
append(it.text.substring(0, selection.start))
append("~~")
if (selection.length == 0) {
append("text here")
} else { } else {
TextRange(
start = it.selection.start + 1,
end = it.selection.end + 1
)
}
it.copy(text = newText, selection = newSelection)
}
onTextFieldValueChanged(newValue)
}),
imageVector = Icons.Default.FormatItalic,
contentDescription = null,
)
}
// strikethrough
item {
Icon(
modifier = Modifier.onClick(
onClick = {
val selection = textFieldValue.selection
val newValue = textFieldValue.let {
val newText = buildString {
append(it.text.substring(0, selection.start))
append("~~")
if (selection.length == 0) {
append(textPlaceholder)
} else {
append(
it.text.substring(
selection.start,
selection.end
)
)
}
append("~~")
append( append(
it.text.substring( it.text.substring(
selection.start, selection.end,
selection.end it.text.length
) )
) )
} }
append("~~") val newSelection = if (selection.length == 0) {
append( TextRange(index = selection.start + textPlaceholder.length + 2)
it.text.substring(
selection.end,
it.text.length
)
)
}
val newSelection = if (selection.length == 0) {
TextRange(index = selection.start + 11)
} else {
TextRange(start = it.selection.start + 2, end = it.selection.end + 2)
}
it.copy(text = newText, selection = newSelection)
}
onTextFieldValueChanged(newValue)
}),
imageVector = Icons.Default.FormatStrikethrough,
contentDescription = null,
)
Icon(
modifier = Modifier.onClick(
onClick = rememberCallback {
onSelectImage()
},
),
imageVector = Icons.Default.Image,
contentDescription = null,
)
Icon(
modifier = Modifier.onClick(
onClick = {
val newValue = textFieldValue.let {
val selection = it.selection
val newText = buildString {
append(it.text.substring(0, selection.start))
append("[")
if (selection.length == 0) {
append("text here")
} else { } else {
TextRange(
start = it.selection.start + 2,
end = it.selection.end + 2
)
}
it.copy(text = newText, selection = newSelection)
}
onTextFieldValueChanged(newValue)
}),
imageVector = Icons.Default.FormatStrikethrough,
contentDescription = null,
)
}
// image
if (onSelectImage != null) {
item {
Icon(
modifier = Modifier.onClick(
onClick = rememberCallback {
onSelectImage.invoke()
},
),
imageVector = Icons.Default.Image,
contentDescription = null,
)
}
}
// hyperlink
item {
Icon(
modifier = Modifier.onClick(
onClick = {
val newValue = textFieldValue.let {
val selection = it.selection
val newText = buildString {
append(it.text.substring(0, selection.start))
append("[")
if (selection.length == 0) {
append(textPlaceholder)
} else {
append(
it.text.substring(
selection.start,
selection.end
)
)
}
append("](")
append(urlPlaceholder)
append(")")
append( append(
it.text.substring( it.text.substring(
selection.start, selection.end,
selection.end it.text.length
) )
) )
} }
append("](URL here)") val newSelection = if (selection.length == 0) {
append( TextRange(index = selection.start + textPlaceholder.length + 1)
it.text.substring(
selection.end,
it.text.length
)
)
}
val newSelection = if (selection.length == 0) {
TextRange(index = selection.start + 10)
} else {
TextRange(start = it.selection.start + 1, end = it.selection.end + 1)
}
it.copy(text = newText, selection = newSelection)
}
onTextFieldValueChanged(newValue)
}),
imageVector = Icons.Default.InsertLink,
contentDescription = null,
)
Icon(
modifier = Modifier.onClick(
onClick = {
val newValue = textFieldValue.let {
val selection = it.selection
val newText = buildString {
append(it.text.substring(0, selection.start))
append("`")
if (selection.length == 0) {
append("text here")
} else { } else {
TextRange(
start = it.selection.start + 1,
end = it.selection.end + 1
)
}
it.copy(text = newText, selection = newSelection)
}
onTextFieldValueChanged(newValue)
}),
imageVector = Icons.Default.InsertLink,
contentDescription = null,
)
}
// inline code
item {
Icon(
modifier = Modifier.onClick(
onClick = {
val newValue = textFieldValue.let {
val selection = it.selection
val newText = buildString {
append(it.text.substring(0, selection.start))
append("`")
if (selection.length == 0) {
append(textPlaceholder)
} else {
append(
it.text.substring(
selection.start,
selection.end
)
)
}
append("`")
append( append(
it.text.substring( it.text.substring(
selection.start, selection.end,
selection.end it.text.length
) )
) )
} }
append("`") val newSelection = if (selection.length == 0) {
append( TextRange(index = selection.start + textPlaceholder.length + 1)
it.text.substring( } else {
selection.end, TextRange(
it.text.length start = it.selection.start + 1,
end = it.selection.end + 1
) )
) }
it.copy(text = newText, selection = newSelection)
} }
val newSelection = if (selection.length == 0) { onTextFieldValueChanged(newValue)
TextRange(index = selection.start + 10) }),
} else { imageVector = Icons.Default.Code,
TextRange(start = it.selection.start + 1, end = it.selection.end + 1) contentDescription = null,
} )
it.copy(text = newText, selection = newSelection) }
}
onTextFieldValueChanged(newValue) // block quote
}), item {
imageVector = Icons.Default.Code, Icon(
contentDescription = null, modifier = Modifier.onClick(
) onClick = {
Icon( val newValue = textFieldValue.let {
modifier = Modifier.onClick( val selection = it.selection
onClick = { val newText = buildString {
val newValue = textFieldValue.let { append(it.text.substring(0, selection.start))
val selection = it.selection append("\n> ")
val newText = buildString { append(
append(it.text.substring(0, selection.start)) it.text.substring(
append("\n> ") selection.end,
append( it.text.length
it.text.substring( )
selection.end,
it.text.length
) )
) }
val newSelection = TextRange(index = selection.start + 3)
it.copy(text = newText, selection = newSelection)
} }
val newSelection = TextRange(index = selection.start + 3) onTextFieldValueChanged(newValue)
it.copy(text = newText, selection = newSelection) }),
} imageVector = Icons.Default.FormatQuote,
onTextFieldValueChanged(newValue) contentDescription = null,
}), )
imageVector = Icons.Default.FormatQuote, }
contentDescription = null,
) // bulleted list
Icon( item {
modifier = Modifier.onClick( Icon(
onClick = { modifier = Modifier.onClick(
val newValue = textFieldValue.let { onClick = {
val selection = it.selection val newValue = textFieldValue.let {
val newText = buildString { val selection = it.selection
append(it.text.substring(0, selection.start)) val newText = buildString {
append("\n- ") append(it.text.substring(0, selection.start))
append( append("\n- ")
it.text.substring( append(
selection.end, it.text.substring(
it.text.length selection.end,
it.text.length
)
) )
) }
val newSelection = TextRange(index = selection.start + 3)
it.copy(text = newText, selection = newSelection)
} }
val newSelection = TextRange(index = selection.start + 3) onTextFieldValueChanged(newValue)
it.copy(text = newText, selection = newSelection) }),
} imageVector = Icons.Default.FormatListBulleted,
onTextFieldValueChanged(newValue) contentDescription = null,
}), )
imageVector = Icons.Default.FormatListBulleted, }
contentDescription = null,
) // numbered list
Icon( item {
modifier = Modifier.onClick( Icon(
onClick = { modifier = Modifier.onClick(
val newValue = textFieldValue.let { onClick = {
val selection = it.selection val newValue = textFieldValue.let {
val newText = buildString { val selection = it.selection
append(it.text.substring(0, selection.start)) val newText = buildString {
append("\n1. ") append(it.text.substring(0, selection.start))
append( append("\n1. ")
it.text.substring( append(
selection.end, it.text.substring(
it.text.length selection.end,
it.text.length
)
) )
) }
val newSelection = TextRange(index = selection.start + 4)
it.copy(text = newText, selection = newSelection)
} }
val newSelection = TextRange(index = selection.start + 4) onTextFieldValueChanged(newValue)
it.copy(text = newText, selection = newSelection) }),
} imageVector = Icons.Default.FormatListNumbered,
onTextFieldValueChanged(newValue) contentDescription = null,
}), )
imageVector = Icons.Default.FormatListNumbered, }
contentDescription = null,
) // language selection
if (onSelectLanguage != null) {
item {
Box(
modifier = Modifier
.size(IconSize.m)
.border(
color = MaterialTheme.colorScheme.onBackground,
width = Dp.Hairline,
shape = RoundedCornerShape(CornerSize.m)
)
.clickable(onClick = onSelectLanguage)
.padding(Spacing.xxxs),
contentAlignment = Alignment.Center,
) {
val languageCode = availableLanguages.firstOrNull { l ->
l.id == currentLanguageId
}?.takeIf { l ->
l.id > 0 // undetermied language
}?.code ?: "λ"
Text(
text = languageCode,
style = MaterialTheme.typography.labelSmall,
fontFamily = fontFamilyResource(MR.fonts.TitilliumWeb.regular),
color = MaterialTheme.colorScheme.onBackground,
)
}
}
}
} }
} }

View File

@ -97,7 +97,7 @@ class FontFamilyBottomSheet(
UiFontFamily.Comfortaa -> fontFamilyResource(MR.fonts.Comfortaa.regular) UiFontFamily.Comfortaa -> fontFamilyResource(MR.fonts.Comfortaa.regular)
UiFontFamily.Poppins -> fontFamilyResource(MR.fonts.Poppins.regular) UiFontFamily.Poppins -> fontFamilyResource(MR.fonts.Poppins.regular)
UiFontFamily.TitilliumWeb -> fontFamilyResource(MR.fonts.TitilliumWeb.regular) UiFontFamily.TitilliumWeb -> fontFamilyResource(MR.fonts.TitilliumWeb.regular)
else -> FontFamily.Default UiFontFamily.Default -> FontFamily.Default
} }
Text( Text(
text = value.toReadableName(), text = value.toReadableName(),

View File

@ -0,0 +1,112 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.LanguageModel
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.stringResource
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SelectLanguageDialog(
currentLanguageId: Int? = null,
languages: List<LanguageModel> = emptyList(),
onSelect: ((Int?) -> Unit)? = null,
onDismiss: (() -> Unit)? = null,
) {
AlertDialog(
onDismissRequest = { onDismiss?.invoke() },
) {
Column(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.surface)
.padding(vertical = Spacing.s),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(MR.strings.settings_language),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onBackground,
)
Spacer(modifier = Modifier.height(Spacing.xs))
LazyColumn(
modifier = Modifier.fillMaxHeight(0.6f)
) {
items(items = languages, key = { it.id }) { lang ->
LanguageItem(
name = lang.name,
selected = (currentLanguageId ?: 0) == lang.id,
onSelected = rememberCallback {
onSelect?.invoke(lang.id)
},
)
}
}
Spacer(modifier = Modifier.height(Spacing.xxs))
Button(
onClick = {
onDismiss?.invoke()
},
) {
Text(text = stringResource(MR.strings.button_close))
}
}
}
}
@Composable
private fun LanguageItem(
name: String,
modifier: Modifier = Modifier,
selected: Boolean = false,
onSelected: (() -> Unit)? = null,
) {
Row(
modifier = modifier
.fillMaxWidth()
.onClick(
onClick = {
onSelected?.invoke()
},
).padding(
horizontal = Spacing.m,
vertical = Spacing.s,
),
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = name,
color = MaterialTheme.colorScheme.onBackground,
)
Spacer(modifier = Modifier.weight(1f))
if (selected) {
RadioButton(
selected = true,
onClick = null,
)
}
}
}

View File

@ -26,6 +26,7 @@ data class CommentModel(
val visible: Boolean = true, val visible: Boolean = true,
@Transient @Transient
val loadMoreButtonVisible: Boolean = false, val loadMoreButtonVisible: Boolean = false,
val languageId: Int = 0,
) : JavaSerializable { ) : JavaSerializable {
val depth: Int get() = (path.split(".").size - 2).coerceAtLeast(0) val depth: Int get() = (path.split(".").size - 2).coerceAtLeast(0)
val parentId: String? val parentId: String?

View File

@ -0,0 +1,7 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data
data class LanguageModel(
val id: Int,
val name: String = "",
val code: String = "",
)

View File

@ -27,6 +27,7 @@ data class PostModel(
val featuredCommunity: Boolean = false, val featuredCommunity: Boolean = false,
val removed: Boolean = false, val removed: Boolean = false,
val locked: Boolean = false, val locked: Boolean = false,
val languageId: Int = 0,
) : JavaSerializable ) : JavaSerializable
val PostModel.imageUrl: String val PostModel.imageUrl: String

View File

@ -48,7 +48,7 @@ interface CommentRepository {
suspend fun downVote( suspend fun downVote(
comment: CommentModel, comment: CommentModel,
auth: String, auth: String,
downVoted: Boolean downVoted: Boolean,
): Result<Response<CommentResponse>> ): Result<Response<CommentResponse>>
fun asSaved(comment: CommentModel, saved: Boolean): CommentModel fun asSaved(comment: CommentModel, saved: Boolean): CommentModel
@ -56,19 +56,21 @@ interface CommentRepository {
suspend fun save( suspend fun save(
comment: CommentModel, comment: CommentModel,
auth: String, auth: String,
saved: Boolean saved: Boolean,
): Result<Response<CommentResponse>> ): Result<Response<CommentResponse>>
suspend fun create( suspend fun create(
postId: Int, postId: Int,
parentId: Int?, parentId: Int?,
text: String, text: String,
languageId: Int? = null,
auth: String, auth: String,
) )
suspend fun edit( suspend fun edit(
commentId: Int, commentId: Int,
text: String, text: String,
languageId: Int? = null,
auth: String, auth: String,
) )

View File

@ -214,12 +214,14 @@ internal class DefaultCommentRepository(
postId: Int, postId: Int,
parentId: Int?, parentId: Int?,
text: String, text: String,
languageId: Int?,
auth: String, auth: String,
) { ) {
val data = CreateCommentForm( val data = CreateCommentForm(
content = text, content = text,
postId = postId, postId = postId,
parentId = parentId, parentId = parentId,
languageId = languageId,
auth = auth, auth = auth,
) )
services.comment.create(authHeader = auth.toAuthHeader(), form = data) services.comment.create(authHeader = auth.toAuthHeader(), form = data)
@ -228,11 +230,13 @@ internal class DefaultCommentRepository(
override suspend fun edit( override suspend fun edit(
commentId: Int, commentId: Int,
text: String, text: String,
languageId: Int?,
auth: String, auth: String,
) { ) {
val data = EditCommentForm( val data = EditCommentForm(
content = text, content = text,
commentId = commentId, commentId = commentId,
languageId = languageId,
auth = auth, auth = auth,
) )
services.comment.edit(authHeader = auth.toAuthHeader(), form = data) services.comment.edit(authHeader = auth.toAuthHeader(), form = data)

View File

@ -169,6 +169,7 @@ internal class DefaultPostRepository(
body: String?, body: String?,
url: String?, url: String?,
nsfw: Boolean, nsfw: Boolean,
languageId: Int?,
auth: String, auth: String,
) { ) {
val data = CreatePostForm( val data = CreatePostForm(
@ -177,6 +178,7 @@ internal class DefaultPostRepository(
body = body, body = body,
url = url, url = url,
nsfw = nsfw, nsfw = nsfw,
languageId = languageId,
auth = auth, auth = auth,
) )
services.post.create( services.post.create(
@ -191,6 +193,7 @@ internal class DefaultPostRepository(
body: String?, body: String?,
url: String?, url: String?,
nsfw: Boolean, nsfw: Boolean,
languageId: Int?,
auth: String, auth: String,
) { ) {
val data = EditPostForm( val data = EditPostForm(
@ -199,6 +202,7 @@ internal class DefaultPostRepository(
body = body, body = body,
url = url, url = url,
nsfw = nsfw, nsfw = nsfw,
languageId = languageId,
auth = auth, auth = auth,
) )
services.post.edit( services.post.edit(

View File

@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockSiteForm import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockSiteForm
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.LanguageModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.MetadataModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.MetadataModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toAuthHeader import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toAuthHeader
@ -57,4 +58,10 @@ internal class DefaultSiteRepository(
) )
response.body()?.metadata?.toModel() response.body()?.metadata?.toModel()
}.getOrNull() }.getOrNull()
override suspend fun getLanguages(auth: String?): List<LanguageModel> = runCatching {
val response = services.site.get(auth = auth)
val dto = response.body()
dto?.allLanguages?.map { it.toModel() }.orEmpty()
}.getOrElse { emptyList() }
} }

View File

@ -57,6 +57,7 @@ interface PostRepository {
body: String?, body: String?,
url: String? = null, url: String? = null,
nsfw: Boolean = false, nsfw: Boolean = false,
languageId: Int? = null,
auth: String, auth: String,
) )
@ -66,6 +67,7 @@ interface PostRepository {
body: String?, body: String?,
url: String? = null, url: String? = null,
nsfw: Boolean = false, nsfw: Boolean = false,
languageId: Int? = null,
auth: String, auth: String,
) )

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.LanguageModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.MetadataModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.MetadataModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
@ -11,4 +12,6 @@ interface SiteRepository {
suspend fun block(id: Int, blocked: Boolean, auth: String? = null): Result<Unit> suspend fun block(id: Int, blocked: Boolean, auth: String? = null): Result<Unit>
suspend fun getMetadata(url: String): MetadataModel? suspend fun getMetadata(url: String): MetadataModel?
suspend fun getLanguages(auth: String?): List<LanguageModel>
} }

View File

@ -7,6 +7,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentSortType
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentView import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentView
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.Community import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.Community
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommunityView import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommunityView
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.Language
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.All import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.All
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.Local import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.Local
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.Subscribed import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.Subscribed
@ -48,6 +49,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SubscribedType
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.CommentReportModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentReportModel
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.LanguageModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.MetadataModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.MetadataModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ModlogItem import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ModlogItem
@ -125,29 +127,16 @@ internal fun PersonAggregates.toModel() = UserScoreModel(
commentScore = commentScore ?: 0, commentScore = commentScore ?: 0,
) )
internal fun PostView.toModel() = PostModel( internal fun PostView.toModel() = post.toModel().copy(
id = post.id,
originalUrl = post.apId,
title = post.name,
text = post.body.orEmpty(),
score = counts.score, score = counts.score,
upvotes = counts.upvotes, upvotes = counts.upvotes,
downvotes = counts.downvotes, downvotes = counts.downvotes,
comments = counts.comments, comments = counts.comments,
thumbnailUrl = post.thumbnailUrl.orEmpty(),
url = post.url,
community = community.toModel(), community = community.toModel(),
creator = creator.toModel(), creator = creator.toModel(),
saved = saved, saved = saved,
myVote = myVote ?: 0, myVote = myVote ?: 0,
publishDate = post.published,
updateDate = post.updated,
nsfw = post.nsfw,
embedVideoUrl = post.embedVideoUrl,
read = read, read = read,
featuredCommunity = post.featuredCommunity,
removed = post.removed,
locked = post.locked,
) )
internal fun Post.toModel() = PostModel( internal fun Post.toModel() = PostModel(
@ -163,11 +152,10 @@ internal fun Post.toModel() = PostModel(
featuredCommunity = featuredCommunity, featuredCommunity = featuredCommunity,
removed = removed, removed = removed,
locked = locked, locked = locked,
languageId = languageId,
) )
internal fun CommentView.toModel() = CommentModel( internal fun CommentView.toModel() = comment.toModel().copy(
id = comment.id,
text = comment.content,
community = community.toModel(), community = community.toModel(),
creator = creator.toModel(), creator = creator.toModel(),
score = counts.score, score = counts.score,
@ -175,13 +163,7 @@ internal fun CommentView.toModel() = CommentModel(
downvotes = counts.downvotes, downvotes = counts.downvotes,
saved = saved, saved = saved,
myVote = myVote ?: 0, myVote = myVote ?: 0,
publishDate = comment.published,
updateDate = comment.updated,
postId = comment.postId,
comments = counts.childCount, comments = counts.childCount,
path = comment.path,
distinguished = comment.distinguished,
removed = comment.removed,
) )
internal fun Comment.toModel() = CommentModel( internal fun Comment.toModel() = CommentModel(
@ -193,6 +175,7 @@ internal fun Comment.toModel() = CommentModel(
path = path, path = path,
distinguished = distinguished, distinguished = distinguished,
removed = removed, removed = removed,
languageId = languageId,
) )
internal fun Community.toModel() = CommunityModel( internal fun Community.toModel() = CommunityModel(
@ -222,37 +205,17 @@ internal fun CommunityView.toModel() = community.toModel().copy(
internal fun PersonMentionView.toModel() = PersonMentionModel( internal fun PersonMentionView.toModel() = PersonMentionModel(
id = personMention.id, id = personMention.id,
read = personMention.read, read = personMention.read,
post = PostModel( post = post.toModel().copy(
id = post.id,
originalUrl = post.apId,
title = post.name,
text = post.body.orEmpty(),
score = counts.score, score = counts.score,
upvotes = counts.upvotes, upvotes = counts.upvotes,
downvotes = counts.downvotes, downvotes = counts.downvotes,
thumbnailUrl = post.thumbnailUrl.orEmpty(),
url = post.url,
community = community.toModel(), community = community.toModel(),
creator = creator.toModel(), creator = creator.toModel(),
saved = saved, saved = saved,
myVote = myVote ?: 0, myVote = myVote ?: 0,
publishDate = post.published,
updateDate = post.updated,
nsfw = post.nsfw,
embedVideoUrl = post.embedVideoUrl,
featuredCommunity = post.featuredCommunity,
removed = post.removed,
locked = post.locked,
), ),
comment = CommentModel( comment = comment.toModel().copy(
id = comment.id,
postId = comment.postId,
text = comment.content,
community = community.toModel(), community = community.toModel(),
publishDate = comment.published,
updateDate = comment.updated,
distinguished = comment.distinguished,
removed = comment.removed,
), ),
creator = creator.toModel(), creator = creator.toModel(),
community = community.toModel(), community = community.toModel(),
@ -267,37 +230,17 @@ internal fun PersonMentionView.toModel() = PersonMentionModel(
internal fun CommentReplyView.toModel() = PersonMentionModel( internal fun CommentReplyView.toModel() = PersonMentionModel(
id = commentReply.id, id = commentReply.id,
read = commentReply.read, read = commentReply.read,
post = PostModel( post = post.toModel().copy(
id = post.id,
originalUrl = post.apId,
title = post.name,
text = post.body.orEmpty(),
score = counts.score, score = counts.score,
upvotes = counts.upvotes, upvotes = counts.upvotes,
downvotes = counts.downvotes, downvotes = counts.downvotes,
thumbnailUrl = post.thumbnailUrl.orEmpty(),
url = post.url,
community = community.toModel(), community = community.toModel(),
creator = UserModel(id = post.creatorId), creator = creator.toModel(),
saved = saved, saved = saved,
myVote = myVote ?: 0, myVote = myVote ?: 0,
publishDate = post.published,
updateDate = post.updated,
nsfw = post.nsfw,
embedVideoUrl = post.embedVideoUrl,
featuredCommunity = post.featuredCommunity,
removed = post.removed,
locked = post.locked,
), ),
comment = CommentModel( comment = comment.toModel().copy(
id = comment.id,
postId = comment.postId,
text = comment.content,
community = community.toModel(), community = community.toModel(),
publishDate = comment.published,
updateDate = comment.updated,
distinguished = comment.distinguished,
removed = comment.removed,
), ),
creator = creator.toModel(), creator = creator.toModel(),
community = community.toModel(), community = community.toModel(),
@ -436,3 +379,9 @@ internal fun ModTransferCommunityView.toDto() = ModlogItem.ModTransferCommunity(
moderator = moderator?.toModel(), moderator = moderator?.toModel(),
user = moddedPerson.toModel(), user = moddedPerson.toModel(),
) )
internal fun Language.toModel() = LanguageModel(
id = id,
code = code,
name = name,
)

View File

@ -6,6 +6,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.VoteFormat import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.VoteFormat
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.CreatePostSection import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.CreatePostSection
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.LanguageModel
import dev.icerock.moko.resources.desc.StringDesc import dev.icerock.moko.resources.desc.StringDesc
@Stable @Stable
@ -30,6 +31,8 @@ interface CreateCommentMviModel :
} }
} }
data class ChangeLanguage(val value: Int?) : Intent
data class Send(val text: String) : Intent data class Send(val text: String) : Intent
} }
@ -43,6 +46,8 @@ interface CreateCommentMviModel :
val autoLoadImages: Boolean = true, val autoLoadImages: Boolean = true,
val currentInstance: String = "", val currentInstance: String = "",
val currentUser: String = "", val currentUser: String = "",
val currentLanguageId: Int? = null,
val availableLanguages: List<LanguageModel> = emptyList(),
) )
sealed interface Effect { sealed interface Effect {

View File

@ -63,11 +63,13 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.PostCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.PostCardBody import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.PostCardBody
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.TextFormattingBar import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.TextFormattingBar
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.RawContentDialog import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.RawContentDialog
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SelectLanguageDialog
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
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.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.gallery.getGalleryHelper import com.github.diegoberaldin.raccoonforlemmy.core.utils.gallery.getGalleryHelper
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.PostModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
@ -116,8 +118,12 @@ class CreateCommentScreen(
val themeRepository = remember { getThemeRepository() } val themeRepository = remember { getThemeRepository() }
val contentFontFamily by themeRepository.contentFontFamily.collectAsState() val contentFontFamily by themeRepository.contentFontFamily.collectAsState()
val typography = contentFontFamily.toTypography() val typography = contentFontFamily.toTypography()
var selectLanguageDialogOpen by remember { mutableStateOf(false) }
LaunchedEffect(model) { LaunchedEffect(model) {
if (editedComment != null) {
model.reduce(CreateCommentMviModel.Intent.ChangeLanguage(editedComment.languageId))
}
model.effects.onEach { effect -> model.effects.onEach { effect ->
when (effect) { when (effect) {
is CreateCommentMviModel.Effect.Failure -> { is CreateCommentMviModel.Effect.Failure -> {
@ -248,7 +254,12 @@ class CreateCommentScreen(
}, },
onSelectImage = { onSelectImage = {
openImagePicker = true openImagePicker = true
} },
currentLanguageId = uiState.currentLanguageId,
availableLanguages = uiState.availableLanguages,
onSelectLanguage = {
selectLanguageDialogOpen = true
},
) )
TextField( TextField(
modifier = Modifier modifier = Modifier
@ -426,5 +437,19 @@ class CreateCommentScreen(
} }
} }
} }
if (selectLanguageDialogOpen) {
SelectLanguageDialog(
languages = uiState.availableLanguages,
currentLanguageId = uiState.currentLanguageId,
onSelect = rememberCallbackArgs { langId ->
model.reduce(CreateCommentMviModel.Intent.ChangeLanguage(langId))
selectLanguageDialogOpen = false
},
onDismiss = rememberCallback {
selectLanguageDialogOpen = false
}
)
}
} }
} }

View File

@ -42,11 +42,13 @@ class CreateCommentViewModel(
if (uiState.value.currentUser.isEmpty()) { if (uiState.value.currentUser.isEmpty()) {
val auth = identityRepository.authToken.value.orEmpty() val auth = identityRepository.authToken.value.orEmpty()
val currentUser = siteRepository.getCurrentUser(auth) val currentUser = siteRepository.getCurrentUser(auth)
val languages = siteRepository.getLanguages(auth)
if (currentUser != null) { if (currentUser != null) {
mvi.updateState { mvi.updateState {
it.copy( it.copy(
currentUser = currentUser.name, currentUser = currentUser.name,
currentInstance = currentUser.host, currentInstance = currentUser.host,
availableLanguages = languages,
) )
} }
} }
@ -65,11 +67,18 @@ class CreateCommentViewModel(
override fun reduce(intent: CreateCommentMviModel.Intent) { override fun reduce(intent: CreateCommentMviModel.Intent) {
when (intent) { when (intent) {
is CreateCommentMviModel.Intent.ChangeSection -> mvi.updateState { is CreateCommentMviModel.Intent.ChangeSection -> {
it.copy(section = intent.value) mvi.updateState { it.copy(section = intent.value) }
}
is CreateCommentMviModel.Intent.ImageSelected -> {
loadImageAndAppendUrlInBody(intent.value)
}
is CreateCommentMviModel.Intent.ChangeLanguage -> {
mvi.updateState { it.copy(currentLanguageId = intent.value) }
} }
is CreateCommentMviModel.Intent.ImageSelected -> loadImageAndAppendUrlInBody(intent.value)
is CreateCommentMviModel.Intent.Send -> submit(intent.text) is CreateCommentMviModel.Intent.Send -> submit(intent.text)
} }
} }
@ -85,6 +94,7 @@ class CreateCommentViewModel(
) )
} }
var valid = true var valid = true
val languageId = uiState.value.currentLanguageId
if (text.isEmpty()) { if (text.isEmpty()) {
mvi.updateState { mvi.updateState {
it.copy( it.copy(
@ -106,12 +116,14 @@ class CreateCommentViewModel(
postId = postId, postId = postId,
parentId = parentId, parentId = parentId,
text = text, text = text,
languageId = languageId,
auth = auth, auth = auth,
) )
} else if (editedCommentId != null) { } else if (editedCommentId != null) {
commentRepository.edit( commentRepository.edit(
commentId = editedCommentId, commentId = editedCommentId,
text = text, text = text,
languageId = languageId,
auth = auth, auth = auth,
) )
} }

View File

@ -7,6 +7,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.VoteFormat
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.CreatePostSection import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.CreatePostSection
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.LanguageModel
import dev.icerock.moko.resources.desc.StringDesc import dev.icerock.moko.resources.desc.StringDesc
@Stable @Stable
@ -50,6 +51,8 @@ interface CreatePostMviModel :
} }
data class ChangeSection(val value: CreatePostSection) : Intent data class ChangeSection(val value: CreatePostSection) : Intent
data class ChangeLanguage(val value: Int?) : Intent
data class Send(val body: String) : Intent data class Send(val body: String) : Intent
} }
@ -71,6 +74,8 @@ interface CreatePostMviModel :
val autoLoadImages: Boolean = true, val autoLoadImages: Boolean = true,
val currentInstance: String = "", val currentInstance: String = "",
val currentUser: String = "", val currentUser: String = "",
val currentLanguageId: Int? = null,
val availableLanguages: List<LanguageModel> = emptyList(),
) )
sealed interface Effect { sealed interface Effect {

View File

@ -66,6 +66,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Section
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.CreatePostSection import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.CreatePostSection
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.PostCard import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.PostCard
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.TextFormattingBar import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.TextFormattingBar
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SelectLanguageDialog
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
@ -141,11 +142,15 @@ class CreatePostScreen(
val themeRepository = remember { getThemeRepository() } val themeRepository = remember { getThemeRepository() }
val contentFontFamily by themeRepository.contentFontFamily.collectAsState() val contentFontFamily by themeRepository.contentFontFamily.collectAsState()
val typography = contentFontFamily.toTypography() val typography = contentFontFamily.toTypography()
var selectLanguageDialogOpen by remember { mutableStateOf(false) }
LaunchedEffect(model) { LaunchedEffect(model) {
val referencePost = editedPost ?: crossPost val referencePost = editedPost ?: crossPost
model.reduce(CreatePostMviModel.Intent.SetTitle(referencePost?.title.orEmpty())) model.reduce(CreatePostMviModel.Intent.SetTitle(referencePost?.title.orEmpty()))
model.reduce(CreatePostMviModel.Intent.SetUrl(referencePost?.url.orEmpty())) model.reduce(CreatePostMviModel.Intent.SetUrl(referencePost?.url.orEmpty()))
if (editedPost != null) {
model.reduce(CreatePostMviModel.Intent.ChangeLanguage(editedPost.languageId))
}
when { when {
communityId != null -> model.reduce( communityId != null -> model.reduce(
CreatePostMviModel.Intent.SetCommunity(CommunityModel(id = communityId)) CreatePostMviModel.Intent.SetCommunity(CommunityModel(id = communityId))
@ -439,7 +444,12 @@ class CreatePostScreen(
}, },
onSelectImage = { onSelectImage = {
openImagePickerInBody = true openImagePickerInBody = true
} },
currentLanguageId = uiState.currentLanguageId,
availableLanguages = uiState.availableLanguages,
onSelectLanguage = {
selectLanguageDialogOpen = true
},
) )
TextField( TextField(
modifier = Modifier modifier = Modifier
@ -518,6 +528,20 @@ class CreatePostScreen(
} }
} }
if (selectLanguageDialogOpen) {
SelectLanguageDialog(
languages = uiState.availableLanguages,
currentLanguageId = uiState.currentLanguageId,
onSelect = rememberCallbackArgs { langId ->
model.reduce(CreatePostMviModel.Intent.ChangeLanguage(langId))
selectLanguageDialogOpen = false
},
onDismiss = rememberCallback {
selectLanguageDialogOpen = false
}
)
}
if (uiState.loading) { if (uiState.loading) {
ProgressHud() ProgressHud()
} }

View File

@ -47,11 +47,13 @@ class CreatePostViewModel(
if (uiState.value.currentUser.isEmpty()) { if (uiState.value.currentUser.isEmpty()) {
val auth = identityRepository.authToken.value.orEmpty() val auth = identityRepository.authToken.value.orEmpty()
val currentUser = siteRepository.getCurrentUser(auth) val currentUser = siteRepository.getCurrentUser(auth)
val languages = siteRepository.getLanguages(auth)
if (currentUser != null) { if (currentUser != null) {
mvi.updateState { mvi.updateState {
it.copy( it.copy(
currentUser = currentUser.name, currentUser = currentUser.name,
currentInstance = currentUser.host, currentInstance = currentUser.host,
availableLanguages = languages,
) )
} }
} }
@ -108,6 +110,10 @@ class CreatePostViewModel(
it.copy(section = intent.value) it.copy(section = intent.value)
} }
is CreatePostMviModel.Intent.ChangeLanguage -> mvi.updateState {
it.copy(currentLanguageId = intent.value)
}
is CreatePostMviModel.Intent.Send -> submit(intent.body) is CreatePostMviModel.Intent.Send -> submit(intent.body)
} }
} }
@ -165,6 +171,7 @@ class CreatePostViewModel(
val title = uiState.value.title val title = uiState.value.title
val url = uiState.value.url.takeIf { it.isNotEmpty() }?.trim() val url = uiState.value.url.takeIf { it.isNotEmpty() }?.trim()
val nsfw = uiState.value.nsfw val nsfw = uiState.value.nsfw
val languageId = uiState.value.currentLanguageId
var valid = true var valid = true
if (title.isEmpty()) { if (title.isEmpty()) {
mvi.updateState { mvi.updateState {
@ -215,6 +222,7 @@ class CreatePostViewModel(
body = body, body = body,
url = url, url = url,
nsfw = nsfw, nsfw = nsfw,
languageId = languageId,
auth = auth, auth = auth,
) )
} }
@ -226,6 +234,7 @@ class CreatePostViewModel(
body = body, body = body,
url = url, url = url,
nsfw = nsfw, nsfw = nsfw,
languageId = languageId,
auth = auth, auth = auth,
) )
} }