mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-09 11:38:40 +01:00
feat: report post and comments
This commit is contained in:
parent
42b1ef5a9a
commit
745755ff88
@ -0,0 +1,17 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CommentReport(
|
||||
@SerialName("id") val id: CommentReportId,
|
||||
@SerialName("creator_id") val creatorId: PersonId,
|
||||
@SerialName("comment_id") val commentId: CommentId,
|
||||
@SerialName("original_comment_text") val originalCommentText: String,
|
||||
@SerialName("reason") val reason: String,
|
||||
@SerialName("resolved") val resolved: Boolean,
|
||||
@SerialName("resolver_id") val resolverId: PersonId? = null,
|
||||
@SerialName("published") val published: String,
|
||||
@SerialName("updated") val updated: String? = null,
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CommentReportResponse(
|
||||
@SerialName("comment_report_view") val commentReportView: CommentReportView,
|
||||
)
|
@ -0,0 +1,18 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CommentReportView(
|
||||
@SerialName("comment_report") val commentReport: CommentReport,
|
||||
@SerialName("comment") val comment: Comment,
|
||||
@SerialName("post") val post: Post,
|
||||
@SerialName("community") val community: Community,
|
||||
@SerialName("creator") val creator: Person,
|
||||
@SerialName("comment_creator") val commentCreator: Person,
|
||||
@SerialName("counts") val counts: CommentAggregates,
|
||||
@SerialName("creator_banned_from_community") val creatorBannedFromCommunity: Boolean,
|
||||
@SerialName("my_vote") val myVote: Int? = null,
|
||||
@SerialName("resolver") val resolver: Person? = null,
|
||||
)
|
@ -12,4 +12,6 @@ typealias LocalUserId = Int
|
||||
typealias CustomEmojiId = Int
|
||||
typealias PersonMentionId = Int
|
||||
typealias CommentReplyId = Int
|
||||
typealias CommentReportId = Int
|
||||
typealias PostReportId = Int
|
||||
typealias PrivateMessageId = Int
|
||||
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CreateCommentReportForm(
|
||||
@SerialName("comment_id") val commentId: CommentId,
|
||||
@SerialName("reason") val reason: String,
|
||||
@SerialName("auto") val auth: String,
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CreatePostReportForm(
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("reason") val reason: String,
|
||||
@SerialName("auto") val auth: String,
|
||||
)
|
@ -0,0 +1,19 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PostReport(
|
||||
@SerialName("id") val id: PostReportId,
|
||||
@SerialName("creator_id") val creatorId: PersonId,
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("original_post_name") val originalPostName: String,
|
||||
@SerialName("original_post_url") val originalPostUrl: String? = null,
|
||||
@SerialName("original_post_body") val originalPostBody: String? = null,
|
||||
@SerialName("reason") val reason: String,
|
||||
@SerialName("resolved") val resolved: Boolean,
|
||||
@SerialName("resolver_id") val resolverId: PersonId? = null,
|
||||
@SerialName("published") val published: String,
|
||||
@SerialName("updated") val updated: String? = null,
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PostReportResponse(
|
||||
@SerialName("post_report_view") val postReportView: PostReportView,
|
||||
)
|
@ -0,0 +1,17 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PostReportView(
|
||||
@SerialName("post_report") val postReport: PostReport,
|
||||
@SerialName("post") val post: Post,
|
||||
@SerialName("community") val community: Community,
|
||||
@SerialName("creator") val creator: Person,
|
||||
@SerialName("post_creator") val postCreator: Person,
|
||||
@SerialName("creator_banned_from_community") val creatorBannedFromCommunity: Boolean,
|
||||
@SerialName("my_vote") val myVote: Int? = null,
|
||||
@SerialName("counts") val counts: PostAggregates,
|
||||
@SerialName("resolver") val resolver: Person? = null,
|
||||
)
|
@ -1,10 +1,12 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.service
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentReplyResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentReportResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentSortType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreateCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreateCommentLikeForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreateCommentReportForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.DeleteCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.EditCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.GetCommentResponse
|
||||
@ -86,4 +88,11 @@ interface CommentService {
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: DeleteCommentForm,
|
||||
): Response<CommentResponse>
|
||||
|
||||
@POST("comment/report")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun createReport(
|
||||
@Body form: CreateCommentReportForm,
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
): Response<CommentReportResponse>
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.github.diegoberaldin.raccoonforlemmy.core.api.service
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentReplyResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreatePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreatePostLikeForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreatePostReportForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.DeletePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.EditPostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.GetPostResponse
|
||||
@ -10,6 +11,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.GetPostsResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.MarkPostAsReadForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PictrsImages
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PostReportResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PostResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SavePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SortType
|
||||
@ -97,4 +99,11 @@ interface PostService {
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body content: MultiPartFormDataContent,
|
||||
): Response<PictrsImages>
|
||||
|
||||
@POST("post/report")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun createReport(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: CreatePostReportForm,
|
||||
): Response<PostReportResponse>
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImag
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.instanceinfo.InstanceInfoMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation.NavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
|
||||
@ -122,3 +123,13 @@ actual fun getModalDrawerViewModel(): ModalDrawerMviModel {
|
||||
val res: ModalDrawerMviModel by inject(ModalDrawerMviModel::class.java)
|
||||
return res
|
||||
}
|
||||
|
||||
actual fun getCreateReportViewModel(
|
||||
postId: Int?,
|
||||
commentId: Int?,
|
||||
): CreateReportMviModel {
|
||||
val res: CreateReportMviModel by inject(CreateReportMviModel::class.java, parameters = {
|
||||
parametersOf(postId, commentId)
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImag
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.instanceinfo.InstanceInfoScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
@ -427,6 +428,7 @@ class CommunityDetailScreen(
|
||||
options = buildList {
|
||||
add(stringResource(MR.strings.post_action_share))
|
||||
add(stringResource(MR.strings.post_action_hide))
|
||||
add(stringResource(MR.strings.post_action_report))
|
||||
if (post.creator?.id == uiState.currentUserId && !isOnOtherInstance) {
|
||||
add(stringResource(MR.strings.post_action_edit))
|
||||
add(stringResource(MR.strings.comment_action_delete))
|
||||
@ -493,13 +495,13 @@ class CommunityDetailScreen(
|
||||
},
|
||||
onOptionSelected = { optionIdx ->
|
||||
when (optionIdx) {
|
||||
3 -> model.reduce(
|
||||
4 -> model.reduce(
|
||||
CommunityDetailMviModel.Intent.DeletePost(
|
||||
post.id
|
||||
)
|
||||
)
|
||||
|
||||
2 -> {
|
||||
3 -> {
|
||||
notificationCenter.addObserver(
|
||||
{
|
||||
model.reduce(CommunityDetailMviModel.Intent.Refresh)
|
||||
@ -514,6 +516,14 @@ class CommunityDetailScreen(
|
||||
)
|
||||
}
|
||||
|
||||
2 -> {
|
||||
bottomSheetNavigator.show(
|
||||
CreateReportScreen(
|
||||
postId = post.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
1 -> model.reduce(
|
||||
CommunityDetailMviModel.Intent.Hide(
|
||||
idx
|
||||
|
@ -23,6 +23,8 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation.Default
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation.NavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailMviModel
|
||||
@ -172,4 +174,14 @@ val commonUiModule = module {
|
||||
settingsRepository = get(),
|
||||
)
|
||||
}
|
||||
factory<CreateReportMviModel> {
|
||||
CreateReportViewModel(
|
||||
postId = it[0],
|
||||
commentId = it[1],
|
||||
mvi = DefaultMviModel(CreateReportMviModel.UiState()),
|
||||
identityRepository = get(),
|
||||
postRepository = get(),
|
||||
commentRepository = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImag
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.instanceinfo.InstanceInfoMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation.NavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
|
||||
@ -62,4 +63,9 @@ expect fun getInboxChatViewModel(otherUserId: Int): InboxChatMviModel
|
||||
|
||||
expect fun getSavedItemsViewModel(): SavedItemsMviModel
|
||||
|
||||
expect fun getModalDrawerViewModel(): ModalDrawerMviModel
|
||||
expect fun getModalDrawerViewModel(): ModalDrawerMviModel
|
||||
|
||||
expect fun getCreateReportViewModel(
|
||||
postId: Int? = null,
|
||||
commentId: Int? = null,
|
||||
): CreateReportMviModel
|
@ -87,6 +87,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCo
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getPostDetailViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImageScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
@ -302,6 +303,7 @@ class PostDetailScreen(
|
||||
},
|
||||
options = buildList {
|
||||
add(stringResource(MR.strings.post_action_share))
|
||||
add(stringResource(MR.strings.post_action_report))
|
||||
if (statePost.creator?.id == uiState.currentUserId && !isOnOtherInstance) {
|
||||
add(stringResource(MR.strings.post_action_edit))
|
||||
add(stringResource(MR.strings.comment_action_delete))
|
||||
@ -347,7 +349,9 @@ class PostDetailScreen(
|
||||
},
|
||||
onOptionSelected = { idx ->
|
||||
when (idx) {
|
||||
1 -> {
|
||||
3 -> model.reduce(PostDetailMviModel.Intent.DeletePost)
|
||||
|
||||
2 -> {
|
||||
notificationCenter.addObserver(
|
||||
{
|
||||
model.reduce(PostDetailMviModel.Intent.RefreshPost)
|
||||
@ -360,7 +364,9 @@ class PostDetailScreen(
|
||||
)
|
||||
}
|
||||
|
||||
2 -> model.reduce(PostDetailMviModel.Intent.DeletePost)
|
||||
1 -> {
|
||||
bottomSheetNavigator.show(CreateReportScreen(postId = statePost.id))
|
||||
}
|
||||
|
||||
else -> model.reduce(PostDetailMviModel.Intent.SharePost)
|
||||
}
|
||||
@ -501,6 +507,7 @@ class PostDetailScreen(
|
||||
separateUpAndDownVotes = uiState.separateUpAndDownVotes,
|
||||
autoLoadImages = uiState.autoLoadImages,
|
||||
options = buildList {
|
||||
add(stringResource(MR.strings.post_action_report))
|
||||
if (comment.creator?.id == uiState.currentUserId) {
|
||||
add(stringResource(MR.strings.post_action_edit))
|
||||
add(stringResource(MR.strings.comment_action_delete))
|
||||
@ -575,15 +582,15 @@ class PostDetailScreen(
|
||||
)
|
||||
}
|
||||
},
|
||||
onOptionSelected = { idx ->
|
||||
when (idx) {
|
||||
1 -> model.reduce(
|
||||
onOptionSelected = { optionId ->
|
||||
when (optionId) {
|
||||
2 -> model.reduce(
|
||||
PostDetailMviModel.Intent.DeleteComment(
|
||||
comment.id
|
||||
)
|
||||
)
|
||||
|
||||
else -> {
|
||||
1 -> {
|
||||
notificationCenter.addObserver(
|
||||
{
|
||||
model.reduce(PostDetailMviModel.Intent.Refresh)
|
||||
@ -598,6 +605,14 @@ class PostDetailScreen(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
bottomSheetNavigator.show(
|
||||
CreateReportScreen(
|
||||
commentId = comment.id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -0,0 +1,27 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.report
|
||||
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import dev.icerock.moko.resources.desc.StringDesc
|
||||
|
||||
interface CreateReportMviModel :
|
||||
MviModel<CreateReportMviModel.Intent, CreateReportMviModel.UiState, CreateReportMviModel.Effect>,
|
||||
ScreenModel {
|
||||
|
||||
sealed interface Intent {
|
||||
data class SetText(val value: String) : Intent
|
||||
data object Send : Intent
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
val text: String = "",
|
||||
val textError: StringDesc? = null,
|
||||
val loading: Boolean = false,
|
||||
)
|
||||
|
||||
sealed interface Effect {
|
||||
data object Success : Effect
|
||||
|
||||
data class Failure(val message: String?) : Effect
|
||||
}
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.report
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.ProgressHud
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getCreateReportViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
import dev.icerock.moko.resources.compose.localized
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
class CreateReportScreen(
|
||||
private val postId: Int? = null,
|
||||
private val commentId: Int? = null,
|
||||
) : Screen {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val model = rememberScreenModel {
|
||||
getCreateReportViewModel(
|
||||
postId = postId,
|
||||
commentId = commentId,
|
||||
)
|
||||
}
|
||||
model.bindToLifecycle(key)
|
||||
val uiState by model.uiState.collectAsState()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val genericError = stringResource(MR.strings.message_generic_error)
|
||||
val bottomSheetNavigator = LocalBottomSheetNavigator.current
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
|
||||
LaunchedEffect(model) {
|
||||
model.effects.onEach {
|
||||
when (it) {
|
||||
is CreateReportMviModel.Effect.Failure -> {
|
||||
snackbarHostState.showSnackbar(it.message ?: genericError)
|
||||
}
|
||||
|
||||
CreateReportMviModel.Effect.Success -> {
|
||||
bottomSheetNavigator.hide()
|
||||
}
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = Spacing.s),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
BottomSheetHandle()
|
||||
val title = when {
|
||||
commentId != null -> stringResource(MR.strings.create_report_title_comment)
|
||||
else -> stringResource(MR.strings.create_report_title_post)
|
||||
}
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
|
||||
val commentFocusRequester = remember { FocusRequester() }
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(commentFocusRequester)
|
||||
.heightIn(min = 300.dp, max = 500.dp)
|
||||
.fillMaxWidth(),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
),
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.create_report_placeholder))
|
||||
},
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
value = uiState.text,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
autoCorrect = true,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(CreateReportMviModel.Intent.SetText(value))
|
||||
},
|
||||
isError = uiState.textError != null,
|
||||
supportingText = {
|
||||
if (uiState.textError != null) {
|
||||
Text(
|
||||
text = uiState.textError?.localized().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
IconButton(
|
||||
content = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Send,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
model.reduce(CreateReportMviModel.Intent.Send)
|
||||
},
|
||||
)
|
||||
})
|
||||
Spacer(Modifier.height(Spacing.xxl))
|
||||
}
|
||||
|
||||
if (uiState.loading) {
|
||||
ProgressHud()
|
||||
}
|
||||
|
||||
SnackbarHost(
|
||||
modifier = Modifier.padding(bottom = Spacing.xxxl),
|
||||
hostState = snackbarHostState
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.report
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CreateReportViewModel(
|
||||
private val postId: Int?,
|
||||
private val commentId: Int?,
|
||||
private val mvi: DefaultMviModel<CreateReportMviModel.Intent, CreateReportMviModel.UiState, CreateReportMviModel.Effect>,
|
||||
private val identityRepository: IdentityRepository,
|
||||
private val postRepository: PostRepository,
|
||||
private val commentRepository: CommentRepository,
|
||||
) : CreateReportMviModel,
|
||||
MviModel<CreateReportMviModel.Intent, CreateReportMviModel.UiState, CreateReportMviModel.Effect> by mvi {
|
||||
|
||||
override fun reduce(intent: CreateReportMviModel.Intent) {
|
||||
when (intent) {
|
||||
is CreateReportMviModel.Intent.SetText -> {
|
||||
mvi.updateState {
|
||||
it.copy(text = intent.value)
|
||||
}
|
||||
}
|
||||
|
||||
CreateReportMviModel.Intent.Send -> submit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun submit() {
|
||||
if (mvi.uiState.value.loading) {
|
||||
return
|
||||
}
|
||||
val text = uiState.value.text
|
||||
|
||||
mvi.updateState { it.copy(loading = true) }
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
if (postId != null) {
|
||||
postRepository.report(
|
||||
postId = postId,
|
||||
reason = text,
|
||||
auth = auth,
|
||||
)
|
||||
} else if (commentId != null) {
|
||||
commentRepository.report(
|
||||
commentId = commentId,
|
||||
reason = text,
|
||||
auth = auth,
|
||||
)
|
||||
}
|
||||
mvi.emitEffect(CreateReportMviModel.Effect.Success)
|
||||
} catch (e: Throwable) {
|
||||
val message = e.message
|
||||
mvi.emitEffect(CreateReportMviModel.Effect.Failure(message))
|
||||
} finally {
|
||||
mvi.updateState { it.copy(loading = false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -84,6 +84,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getUserDetailVi
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImageScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
|
||||
@ -390,6 +391,7 @@ class UserDetailScreen(
|
||||
autoLoadImages = uiState.autoLoadImages,
|
||||
options = buildList {
|
||||
add(stringResource(MR.strings.post_action_share))
|
||||
add(stringResource(MR.strings.post_action_report))
|
||||
},
|
||||
onUpVote = if (isOnOtherInstance) {
|
||||
null
|
||||
@ -454,6 +456,14 @@ class UserDetailScreen(
|
||||
},
|
||||
onOptionSelected = { optionIdx ->
|
||||
when (optionIdx) {
|
||||
1 -> {
|
||||
bottomSheetNavigator.show(
|
||||
CreateReportScreen(
|
||||
postId = post.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> model.reduce(
|
||||
UserDetailMviModel.Intent.SharePost(idx)
|
||||
)
|
||||
@ -599,6 +609,20 @@ class UserDetailScreen(
|
||||
onOpenCommunity = { community ->
|
||||
navigator?.push(CommunityDetailScreen(community))
|
||||
},
|
||||
options = buildList {
|
||||
add(stringResource(MR.strings.post_action_report))
|
||||
},
|
||||
onOptionSelected = { optionId ->
|
||||
when (optionId) {
|
||||
else -> {
|
||||
bottomSheetNavigator.show(
|
||||
CreateReportScreen(
|
||||
commentId = comment.id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
Divider(
|
||||
modifier = Modifier.padding(vertical = Spacing.xxxs),
|
||||
|
@ -11,6 +11,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImag
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.instanceinfo.InstanceInfoMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.navigation.NavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
|
||||
@ -71,6 +72,11 @@ actual fun getSavedItemsViewModel(): SavedItemsMviModel =
|
||||
actual fun getModalDrawerViewModel(): ModalDrawerMviModel =
|
||||
CommonUiViewModelHelper.modalDrawerViewModel
|
||||
|
||||
actual fun getCreateReportViewModel(
|
||||
postId: Int?,
|
||||
commentId: Int?,
|
||||
): CreateReportMviModel = CommonUiViewModelHelper.getCreateReportModel(postId, commentId)
|
||||
|
||||
object CommonUiViewModelHelper : KoinComponent {
|
||||
|
||||
val navigationCoordinator: NavigationCoordinator by inject()
|
||||
@ -145,4 +151,14 @@ object CommonUiViewModelHelper : KoinComponent {
|
||||
)
|
||||
return model
|
||||
}
|
||||
|
||||
fun getCreateReportModel(
|
||||
postId: Int?,
|
||||
commentId: Int?,
|
||||
): CreateReportMviModel {
|
||||
val model: CreateReportMviModel by inject(
|
||||
parameters = { parametersOf(postId, commentId) }
|
||||
)
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreateCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreateCommentLikeForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreateCommentReportForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.DeleteCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.EditCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SaveCommentForm
|
||||
@ -247,4 +248,16 @@ class CommentRepository(
|
||||
)
|
||||
services.comment.delete(authHeader = auth.toAuthHeader(), form = data)
|
||||
}
|
||||
|
||||
suspend fun report(commentId: Int, reason: String, auth: String) {
|
||||
val data = CreateCommentReportForm(
|
||||
commentId = commentId,
|
||||
reason = reason,
|
||||
auth = auth,
|
||||
)
|
||||
services.comment.createReport(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreatePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreatePostLikeForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CreatePostReportForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.DeletePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.EditPostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.MarkPostAsReadForm
|
||||
@ -246,4 +247,16 @@ class PostRepository(
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
|
||||
suspend fun report(postId: Int, reason: String, auth: String) {
|
||||
val data = CreatePostReportForm(
|
||||
postId = postId,
|
||||
reason = reason,
|
||||
auth = auth,
|
||||
)
|
||||
services.post.createReport(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImag
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.ListingTypeBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
@ -306,6 +307,7 @@ class PostListScreen : Screen {
|
||||
options = buildList {
|
||||
add(stringResource(MR.strings.post_action_share))
|
||||
add(stringResource(MR.strings.post_action_hide))
|
||||
add(stringResource(MR.strings.post_action_report))
|
||||
if (post.creator?.id == uiState.currentUserId) {
|
||||
add(stringResource(MR.strings.post_action_edit))
|
||||
add(stringResource(MR.strings.comment_action_delete))
|
||||
@ -367,13 +369,13 @@ class PostListScreen : Screen {
|
||||
},
|
||||
onOptionSelected = { optionIdx ->
|
||||
when (optionIdx) {
|
||||
3 -> model.reduce(
|
||||
4 -> model.reduce(
|
||||
PostListMviModel.Intent.DeletePost(
|
||||
post.id
|
||||
)
|
||||
)
|
||||
|
||||
2 -> {
|
||||
3 -> {
|
||||
notificationCenter.addObserver(
|
||||
{
|
||||
model.reduce(PostListMviModel.Intent.Refresh)
|
||||
@ -388,6 +390,14 @@ class PostListScreen : Screen {
|
||||
)
|
||||
}
|
||||
|
||||
2 -> {
|
||||
bottomSheetNavigator.show(
|
||||
CreateReportScreen(
|
||||
postId = post.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
1 -> model.reduce(
|
||||
PostListMviModel.Intent.Hide(idx)
|
||||
)
|
||||
|
@ -18,10 +18,10 @@ import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
@ -65,7 +65,6 @@ class LoginBottomSheet : Screen {
|
||||
private const val HELP_URL = "https://join-lemmy.org/docs/users/01-getting-started.html"
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val model = rememberScreenModel { getLoginBottomSheetViewModel() }
|
||||
@ -75,7 +74,6 @@ class LoginBottomSheet : Screen {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val genericError = stringResource(MR.strings.message_generic_error)
|
||||
val bottomSheetNavigator = LocalBottomSheetNavigator.current
|
||||
val successfulLoginMessage = stringResource(MR.strings.message_login_successful)
|
||||
|
||||
LaunchedEffect(model) {
|
||||
model.effects.onEach {
|
||||
@ -87,7 +85,6 @@ class LoginBottomSheet : Screen {
|
||||
}
|
||||
|
||||
LoginBottomSheetMviModel.Effect.LoginSuccess -> {
|
||||
snackbarHostState.showSnackbar(message = successfulLoginMessage)
|
||||
bottomSheetNavigator.hide()
|
||||
}
|
||||
}
|
||||
@ -98,214 +95,223 @@ class LoginBottomSheet : Screen {
|
||||
val navigator = remember { getNavigationCoordinator().getRootNavigator() }
|
||||
val settingsRepository = remember { getSettingsRepository() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
Box(
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
) {
|
||||
Box {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
BottomSheetHandle()
|
||||
Text(
|
||||
modifier = Modifier.padding(start = Spacing.s, top = Spacing.s),
|
||||
text = stringResource(MR.strings.profile_button_login),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
modifier = Modifier.align(Alignment.TopEnd),
|
||||
onClick = {
|
||||
bottomSheetNavigator.hide()
|
||||
handleUrl(
|
||||
url = HELP_URL,
|
||||
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
|
||||
uriHandler = uriHandler,
|
||||
navigator = navigator
|
||||
)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.HelpOutline,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val instanceFocusRequester = remember { FocusRequester() }
|
||||
val usernameFocusRequester = remember { FocusRequester() }
|
||||
val passwordFocusRequester = remember { FocusRequester() }
|
||||
val tokenFocusRequester = remember { FocusRequester() }
|
||||
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(instanceFocusRequester),
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.login_field_instance_name))
|
||||
},
|
||||
singleLine = true,
|
||||
value = uiState.instanceName,
|
||||
isError = uiState.instanceNameError != null,
|
||||
keyboardActions = KeyboardActions(
|
||||
onNext = {
|
||||
usernameFocusRequester.requestFocus()
|
||||
},
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Email,
|
||||
autoCorrect = false,
|
||||
imeAction = ImeAction.Next,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.SetInstanceName(value))
|
||||
},
|
||||
supportingText = {
|
||||
if (uiState.instanceNameError != null) {
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
BottomSheetHandle()
|
||||
Text(
|
||||
text = uiState.instanceNameError?.localized().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.padding(start = Spacing.s, top = Spacing.s),
|
||||
text = stringResource(MR.strings.profile_button_login),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(usernameFocusRequester),
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.login_field_user_name))
|
||||
},
|
||||
singleLine = true,
|
||||
value = uiState.username,
|
||||
isError = uiState.usernameError != null,
|
||||
keyboardActions = KeyboardActions(
|
||||
onNext = {
|
||||
passwordFocusRequester.requestFocus()
|
||||
},
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Email,
|
||||
autoCorrect = false,
|
||||
imeAction = ImeAction.Next,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.SetUsername(value))
|
||||
},
|
||||
supportingText = {
|
||||
if (uiState.usernameError != null) {
|
||||
Text(
|
||||
text = uiState.usernameError?.localized().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
IconButton(
|
||||
modifier = Modifier.align(Alignment.TopEnd),
|
||||
onClick = {
|
||||
bottomSheetNavigator.hide()
|
||||
handleUrl(
|
||||
url = HELP_URL,
|
||||
openExternal = settingsRepository.currentSettings.value.openUrlsInExternalBrowser,
|
||||
uriHandler = uriHandler,
|
||||
navigator = navigator
|
||||
)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.HelpOutline,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
var transformation: VisualTransformation by remember {
|
||||
mutableStateOf(PasswordVisualTransformation())
|
||||
}
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(passwordFocusRequester),
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.login_field_password))
|
||||
},
|
||||
singleLine = true,
|
||||
value = uiState.password,
|
||||
isError = uiState.passwordError != null,
|
||||
keyboardActions = KeyboardActions(
|
||||
onNext = {
|
||||
tokenFocusRequester.requestFocus()
|
||||
val instanceFocusRequester = remember { FocusRequester() }
|
||||
val usernameFocusRequester = remember { FocusRequester() }
|
||||
val passwordFocusRequester = remember { FocusRequester() }
|
||||
val tokenFocusRequester = remember { FocusRequester() }
|
||||
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(instanceFocusRequester),
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.login_field_instance_name))
|
||||
},
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Next,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.SetPassword(value))
|
||||
},
|
||||
visualTransformation = transformation,
|
||||
trailingIcon = {
|
||||
Image(
|
||||
modifier = Modifier.onClick {
|
||||
transformation = if (transformation == VisualTransformation.None) {
|
||||
PasswordVisualTransformation()
|
||||
singleLine = true,
|
||||
value = uiState.instanceName,
|
||||
isError = uiState.instanceNameError != null,
|
||||
keyboardActions = KeyboardActions(
|
||||
onNext = {
|
||||
usernameFocusRequester.requestFocus()
|
||||
},
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Email,
|
||||
autoCorrect = false,
|
||||
imeAction = ImeAction.Next,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.SetInstanceName(value))
|
||||
},
|
||||
supportingText = {
|
||||
if (uiState.instanceNameError != null) {
|
||||
Text(
|
||||
text = uiState.instanceNameError?.localized().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(usernameFocusRequester),
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.login_field_user_name))
|
||||
},
|
||||
singleLine = true,
|
||||
value = uiState.username,
|
||||
isError = uiState.usernameError != null,
|
||||
keyboardActions = KeyboardActions(
|
||||
onNext = {
|
||||
passwordFocusRequester.requestFocus()
|
||||
},
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Email,
|
||||
autoCorrect = false,
|
||||
imeAction = ImeAction.Next,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.SetUsername(value))
|
||||
},
|
||||
supportingText = {
|
||||
if (uiState.usernameError != null) {
|
||||
Text(
|
||||
text = uiState.usernameError?.localized().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
var transformation: VisualTransformation by remember {
|
||||
mutableStateOf(PasswordVisualTransformation())
|
||||
}
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(passwordFocusRequester),
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.login_field_password))
|
||||
},
|
||||
singleLine = true,
|
||||
value = uiState.password,
|
||||
isError = uiState.passwordError != null,
|
||||
keyboardActions = KeyboardActions(
|
||||
onNext = {
|
||||
tokenFocusRequester.requestFocus()
|
||||
},
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Next,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.SetPassword(value))
|
||||
},
|
||||
visualTransformation = transformation,
|
||||
trailingIcon = {
|
||||
Image(
|
||||
modifier = Modifier.onClick {
|
||||
transformation = if (transformation == VisualTransformation.None) {
|
||||
PasswordVisualTransformation()
|
||||
} else {
|
||||
VisualTransformation.None
|
||||
}
|
||||
},
|
||||
imageVector = if (transformation == VisualTransformation.None) {
|
||||
Icons.Default.VisibilityOff
|
||||
} else {
|
||||
VisualTransformation.None
|
||||
}
|
||||
},
|
||||
imageVector = if (transformation == VisualTransformation.None) {
|
||||
Icons.Default.VisibilityOff
|
||||
} else {
|
||||
Icons.Default.Visibility
|
||||
},
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onBackground),
|
||||
)
|
||||
},
|
||||
supportingText = {
|
||||
if (uiState.passwordError != null) {
|
||||
Text(
|
||||
text = uiState.passwordError?.localized().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
Icons.Default.Visibility
|
||||
},
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onBackground),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
supportingText = {
|
||||
if (uiState.passwordError != null) {
|
||||
Text(
|
||||
text = uiState.passwordError?.localized().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(tokenFocusRequester),
|
||||
label = {
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(tokenFocusRequester),
|
||||
label = {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.login_field_token))
|
||||
Text(
|
||||
text = stringResource(MR.strings.login_field_label_optional),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
value = uiState.totp2faToken,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.SetTotp2faToken(value))
|
||||
},
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(Spacing.m))
|
||||
Button(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
onClick = {
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.Confirm)
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.login_field_token))
|
||||
Text(
|
||||
text = stringResource(MR.strings.login_field_label_optional),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
if (uiState.loading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
)
|
||||
}
|
||||
Text(stringResource(MR.strings.button_confirm))
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
value = uiState.totp2faToken,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
onValueChange = { value ->
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.SetTotp2faToken(value))
|
||||
},
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(Spacing.m))
|
||||
Button(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
onClick = {
|
||||
model.reduce(LoginBottomSheetMviModel.Intent.Confirm)
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (uiState.loading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
)
|
||||
}
|
||||
Text(stringResource(MR.strings.button_confirm))
|
||||
}
|
||||
Spacer(modifier = Modifier.height(Spacing.m))
|
||||
}
|
||||
Spacer(modifier = Modifier.height(Spacing.m))
|
||||
|
||||
SnackbarHost(
|
||||
modifier = Modifier.padding(bottom = Spacing.xxxl),
|
||||
hostState = snackbarHostState
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCo
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImageScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.report.CreateReportScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
@ -317,6 +318,7 @@ class MultiCommunityScreen(
|
||||
options = buildList {
|
||||
add(stringResource(MR.strings.post_action_share))
|
||||
add(stringResource(MR.strings.post_action_hide))
|
||||
add(stringResource(MR.strings.post_action_report))
|
||||
},
|
||||
blurNsfw = uiState.blurNsfw,
|
||||
onOpenCommunity = { community ->
|
||||
@ -374,6 +376,14 @@ class MultiCommunityScreen(
|
||||
},
|
||||
onOptionSelected = { optionIdx ->
|
||||
when (optionIdx) {
|
||||
2 -> {
|
||||
bottomSheetNavigator.show(
|
||||
CreateReportScreen(
|
||||
postId = post.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
1 -> model.reduce(MultiCommunityMviModel.Intent.Hide(idx))
|
||||
else -> model.reduce(
|
||||
MultiCommunityMviModel.Intent.SharePost(idx)
|
||||
|
@ -20,7 +20,6 @@
|
||||
<string name="message_missing_field">Missing field</string>
|
||||
<string name="message_invalid_field">Invalid field</string>
|
||||
<string name="message_image_loading_error">Image loading error</string>
|
||||
<string name="message_login_successful">🎉 Login successful! 🎉</string>
|
||||
<string name="message_empty_list">No items to display</string>
|
||||
<string name="message_operation_successful">Operation completed succcessfully</string>
|
||||
|
||||
@ -63,11 +62,16 @@
|
||||
|
||||
<string name="post_detail_load_more_comments">Load more comments…</string>
|
||||
<string name="comment_action_delete">Delete</string>
|
||||
<string name="post_action_share">Share</string>
|
||||
<string name="post_action_share">Share…</string>
|
||||
<string name="post_action_edit">Edit</string>
|
||||
<string name="post_action_hide">Hide</string>
|
||||
<string name="post_action_report">Report…</string>
|
||||
<string name="post_detail_cross_posts">also posted to:</string>
|
||||
|
||||
<string name="create_report_title_post">Report post</string>
|
||||
<string name="create_report_title_comment">Report comment</string>
|
||||
<string name="create_report_placeholder">Report text (optional)</string>
|
||||
|
||||
<string name="explore_result_type_all">All</string>
|
||||
<string name="explore_result_type_posts">Posts</string>
|
||||
<string name="explore_result_type_comments">Comments</string>
|
||||
|
@ -16,7 +16,6 @@
|
||||
<string name="message_missing_field">Campo obligatorio</string>
|
||||
<string name="message_invalid_field">Campo no válido</string>
|
||||
<string name="message_image_loading_error">No ha sido posible descargar la imagen</string>
|
||||
<string name="message_login_successful">🎉 ¡Acceso completado con éxito! 🎉</string>
|
||||
<string name="message_empty_list">Ningún elemento para mostrar</string>
|
||||
<string name="message_operation_successful">Operación completada con éxito</string>
|
||||
|
||||
@ -59,11 +58,16 @@
|
||||
|
||||
<string name="post_detail_load_more_comments">Descarga más comentarios…</string>
|
||||
<string name="comment_action_delete">Eliminar</string>
|
||||
<string name="post_action_share">Compartir</string>
|
||||
<string name="post_action_share">Compartir…</string>
|
||||
<string name="post_action_edit">Modificar</string>
|
||||
<string name="post_action_hide">Esconder</string>
|
||||
<string name="post_action_report">Crear informe…</string>
|
||||
<string name="post_detail_cross_posts">también publicado en:</string>
|
||||
|
||||
<string name="create_report_title_post">Crear informe sobre publicación</string>
|
||||
<string name="create_report_title_comment">Crear informe sobre comentario</string>
|
||||
<string name="create_report_placeholder">Texto del informe (opcional)</string>
|
||||
|
||||
<string name="explore_result_type_all">Todos</string>
|
||||
<string name="explore_result_type_posts">Publicaciones</string>
|
||||
<string name="explore_result_type_comments">Comentarios</string>
|
||||
|
@ -16,7 +16,6 @@
|
||||
<string name="message_missing_field">Campo obbligatorio</string>
|
||||
<string name="message_invalid_field">Campo non valido</string>
|
||||
<string name="message_image_loading_error">Errore caricamento immagine</string>
|
||||
<string name="message_login_successful">🎉 Login effettuato con successo! 🎉</string>
|
||||
<string name="message_empty_list">Nessun elemento da visualizzare</string>
|
||||
<string name="message_operation_successful">Operazione completata con successo</string>
|
||||
|
||||
@ -59,11 +58,16 @@
|
||||
|
||||
<string name="post_detail_load_more_comments">Carica altri commenti…</string>
|
||||
<string name="comment_action_delete">Elimina</string>
|
||||
<string name="post_action_share">Condividi</string>
|
||||
<string name="post_action_share">Condividi…</string>
|
||||
<string name="post_action_edit">Modifica</string>
|
||||
<string name="post_action_hide">Nascondi</string>
|
||||
<string name="post_action_report">Segnala…</string>
|
||||
<string name="post_detail_cross_posts">postato anche in:</string>
|
||||
|
||||
<string name="create_report_title_post">Segnala post</string>
|
||||
<string name="create_report_title_comment">Segnala commento</string>
|
||||
<string name="create_report_placeholder">Testo segnalazione (opzionale)</string>
|
||||
|
||||
<string name="explore_result_type_all">Tutti</string>
|
||||
<string name="explore_result_type_posts">Post</string>
|
||||
<string name="explore_result_type_comments">Commenti</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user