mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-03 02:47:34 +01:00
feat: mod tools (#153)
* update services and DTOs * update repositories and models * feat: add remove bottom sheet * update community detail * update post detail * feat: show featured posts and distinguished comments * feat: report list * chore: translations
This commit is contained in:
parent
d136837d04
commit
70fadd93cf
@ -0,0 +1,15 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BanFromCommunityForm(
|
||||
@SerialName("community_id") val communityId: CommunityId,
|
||||
@SerialName("personId") val personId: PersonId,
|
||||
@SerialName("ban") val ban: Boolean,
|
||||
@SerialName("remove_data") val removeData: Boolean,
|
||||
@SerialName("reason") val reson: String?,
|
||||
@SerialName("expires") val expires: Long?,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BanFromCommunityResponse(
|
||||
@SerialName("person_view") val personView: PersonView,
|
||||
@SerialName("banned") val banned: Boolean,
|
||||
)
|
@ -6,6 +6,6 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class CreateCommentReportForm(
|
||||
@SerialName("comment_id") val commentId: CommentId,
|
||||
@SerialName("reason") val reason: String,
|
||||
@SerialName("reason") val reason: String?,
|
||||
@SerialName("auth") 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 DistinguishCommentForm(
|
||||
@SerialName("comment_id") val commentId: CommentId,
|
||||
@SerialName("distinguished") val distinguished: Boolean,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,12 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class FeaturePostForm(
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("auth") val auth: String,
|
||||
@SerialName("featured") val featured: Boolean,
|
||||
@SerialName("feature_type") val featureType: PostFeatureType,
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ListCommentReportsResponse(
|
||||
@SerialName("comment_reports") val commentReports: List<CommentReportView>,
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ListPostReportsResponse(
|
||||
@SerialName("post_reports") val postReports: List<PostReportView>,
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class LockPostForm(
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("locked") val locked: Boolean,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
||||
enum class PostFeatureType {
|
||||
@SerialName("Local")
|
||||
Local,
|
||||
|
||||
@SerialName("Community")
|
||||
Community,
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RemoveCommentForm(
|
||||
@SerialName("comment_id") val commentId: CommentId,
|
||||
@SerialName("reason") val reason: String?,
|
||||
@SerialName("removed") val removed: Boolean,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -0,0 +1,12 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RemovePostForm(
|
||||
@SerialName("post_id") val postId: PostId,
|
||||
@SerialName("reason") val reason: String?,
|
||||
@SerialName("removed") val removed: Boolean,
|
||||
@SerialName("auth") 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 ResolveCommentReportForm(
|
||||
@SerialName("report_id") val reportId: CommentReportId,
|
||||
@SerialName("resolved") val resolved: Boolean,
|
||||
@SerialName("auth") 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 ResolvePostReportForm(
|
||||
@SerialName("report_id") val reportId: PostReportId,
|
||||
@SerialName("resolved") val resolved: Boolean,
|
||||
@SerialName("auth") val auth: String,
|
||||
)
|
@ -8,11 +8,15 @@ 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.DistinguishCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.EditCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.GetCommentResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.GetCommentsResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListCommentReportsResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.MarkCommentAsReadForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.RemoveCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ResolveCommentReportForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SaveCommentForm
|
||||
import de.jensklingenberg.ktorfit.Response
|
||||
import de.jensklingenberg.ktorfit.http.Body
|
||||
@ -95,4 +99,36 @@ interface CommentService {
|
||||
@Body form: CreateCommentReportForm,
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
): Response<CommentReportResponse>
|
||||
|
||||
@POST("comment/remove")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun remove(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: RemoveCommentForm,
|
||||
): Response<CommentResponse>
|
||||
|
||||
@POST("comment/distinguish")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun distinguish(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: DistinguishCommentForm,
|
||||
): Response<CommentResponse>
|
||||
|
||||
@GET("comment/report/list")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun listReports(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Query("auth") auth: String? = null,
|
||||
@Query("limit") limit: Int? = null,
|
||||
@Query("page") page: Int? = null,
|
||||
@Query("unresolved_only") unresolvedOnly: Boolean? = null,
|
||||
@Query("community_id") communityId: Int? = null,
|
||||
): Response<ListCommentReportsResponse>
|
||||
|
||||
@PUT("comment/report/resolve")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun resolveReport(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: ResolveCommentReportForm,
|
||||
): Response<CommentReportResponse>
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.api.service
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BanFromCommunityForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BanFromCommunityResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockCommunityForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockCommunityResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommunityResponse
|
||||
@ -48,4 +50,11 @@ interface CommunityService {
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: BlockCommunityForm,
|
||||
): Response<BlockCommunityResponse>
|
||||
|
||||
@POST("community/ban_user")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun ban(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: BanFromCommunityForm,
|
||||
): Response<BanFromCommunityResponse>
|
||||
}
|
||||
|
@ -5,13 +5,18 @@ 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.FeaturePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.GetPostResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.GetPostsResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListPostReportsResponse
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.LockPostForm
|
||||
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.RemovePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ResolvePostReportForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SavePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SortType
|
||||
import de.jensklingenberg.ktorfit.Response
|
||||
@ -105,4 +110,43 @@ interface PostService {
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: CreatePostReportForm,
|
||||
): Response<PostReportResponse>
|
||||
|
||||
@POST("post/feature")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun feature(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: FeaturePostForm,
|
||||
): Response<PostResponse>
|
||||
|
||||
@POST("post/remove")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun remove(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: RemovePostForm,
|
||||
): Response<PostResponse>
|
||||
|
||||
@POST("post/lock")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun lock(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: LockPostForm,
|
||||
): Response<PostResponse>
|
||||
|
||||
@GET("post/report/list")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun listReports(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Query("auth") auth: String? = null,
|
||||
@Query("limit") limit: Int? = null,
|
||||
@Query("page") page: Int? = null,
|
||||
@Query("unresolved_only") unresolvedOnly: Boolean? = null,
|
||||
@Query("community_id") communityId: Int? = null,
|
||||
): Response<ListPostReportsResponse>
|
||||
|
||||
@PUT("post/report/resolve")
|
||||
@Headers("Content-Type: application/json")
|
||||
suspend fun resolveReport(
|
||||
@Header("Authorization") authHeader: String? = null,
|
||||
@Body form: ResolvePostReportForm,
|
||||
): Response<PostReportResponse>
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ 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.remove.RemoveMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist.ReportListMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.selectcommunity.SelectCommunityMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailMviModel
|
||||
@ -56,10 +58,11 @@ actual fun getPostDetailViewModel(
|
||||
post: PostModel,
|
||||
otherInstance: String,
|
||||
highlightCommentId: Int?,
|
||||
isModerator: Boolean,
|
||||
): PostDetailMviModel {
|
||||
val res: PostDetailMviModel by inject(
|
||||
clazz = PostDetailMviModel::class.java,
|
||||
parameters = { parametersOf(post, otherInstance, highlightCommentId) },
|
||||
parameters = { parametersOf(post, otherInstance, highlightCommentId, isModerator) },
|
||||
)
|
||||
return res
|
||||
}
|
||||
@ -168,4 +171,23 @@ actual fun getCustomTextToolbar(
|
||||
onShare = onShare,
|
||||
onQuote = onQuote,
|
||||
)
|
||||
}
|
||||
|
||||
actual fun getRemoveViewModel(
|
||||
postId: Int?,
|
||||
commentId: Int?,
|
||||
): RemoveMviModel {
|
||||
val res: RemoveMviModel by inject(RemoveMviModel::class.java, parameters = {
|
||||
parametersOf(postId, commentId)
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
actual fun getReportListViewModel(
|
||||
communityId: Int,
|
||||
): ReportListMviModel {
|
||||
val res: ReportListMviModel by inject(ReportListMviModel::class.java, parameters = {
|
||||
parametersOf(communityId)
|
||||
})
|
||||
return res
|
||||
}
|
@ -32,6 +32,8 @@ interface CommunityDetailMviModel :
|
||||
data object ClearRead : Intent
|
||||
data class StartZombieMode(val index: Int) : Intent
|
||||
data object PauseZombieMode : Intent
|
||||
data class ModFeaturePost(val id: Int) : Intent
|
||||
data class ModLockPost(val id: Int) : Intent
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
@ -52,6 +54,7 @@ interface CommunityDetailMviModel :
|
||||
val separateUpAndDownVotes: Boolean = false,
|
||||
val autoLoadImages: Boolean = true,
|
||||
val zombieModeActive: Boolean = false,
|
||||
val isModerator: Boolean = false,
|
||||
)
|
||||
|
||||
sealed interface Effect {
|
||||
|
@ -96,6 +96,8 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.instanceinfo.Insta
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.RawContentDialog
|
||||
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.remove.RemoveScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist.ReportListScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
@ -252,24 +254,31 @@ class CommunityDetailScreen(
|
||||
|
||||
// options menu
|
||||
Box {
|
||||
val options = listOf(
|
||||
Option(
|
||||
val options = buildList {
|
||||
this += Option(
|
||||
OptionId.Info,
|
||||
stringResource(MR.strings.community_detail_info)
|
||||
),
|
||||
Option(
|
||||
)
|
||||
this += Option(
|
||||
OptionId.InfoInstance,
|
||||
stringResource(MR.strings.community_detail_instance_info)
|
||||
),
|
||||
Option(
|
||||
)
|
||||
this += Option(
|
||||
OptionId.Block,
|
||||
stringResource(MR.strings.community_detail_block)
|
||||
),
|
||||
Option(
|
||||
)
|
||||
this += Option(
|
||||
OptionId.BlockInstance,
|
||||
stringResource(MR.strings.community_detail_block_instance)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
if (uiState.isModerator) {
|
||||
this += Option(
|
||||
OptionId.OpenReports,
|
||||
stringResource(MR.strings.mod_action_open_reports)
|
||||
)
|
||||
}
|
||||
}
|
||||
var optionsExpanded by remember { mutableStateOf(false) }
|
||||
var optionsOffset by remember { mutableStateOf(Offset.Zero) }
|
||||
Image(
|
||||
@ -325,6 +334,13 @@ class CommunityDetailScreen(
|
||||
)
|
||||
}
|
||||
|
||||
OptionId.OpenReports -> {
|
||||
val screen = ReportListScreen(
|
||||
communityId = uiState.community.id
|
||||
)
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
},
|
||||
@ -545,6 +561,7 @@ class CommunityDetailScreen(
|
||||
PostDetailScreen(
|
||||
post = post,
|
||||
otherInstance = otherInstanceName,
|
||||
isMod = uiState.isModerator,
|
||||
),
|
||||
)
|
||||
},
|
||||
@ -617,52 +634,60 @@ class CommunityDetailScreen(
|
||||
)
|
||||
},
|
||||
options = buildList {
|
||||
add(
|
||||
Option(
|
||||
OptionId.Share,
|
||||
stringResource(MR.strings.post_action_share)
|
||||
)
|
||||
this += Option(
|
||||
OptionId.Share,
|
||||
stringResource(MR.strings.post_action_share),
|
||||
)
|
||||
if (uiState.isLogged && !isOnOtherInstance) {
|
||||
add(
|
||||
Option(
|
||||
OptionId.Hide,
|
||||
stringResource(MR.strings.post_action_hide)
|
||||
)
|
||||
this += Option(
|
||||
OptionId.Hide,
|
||||
stringResource(MR.strings.post_action_hide),
|
||||
)
|
||||
}
|
||||
add(
|
||||
Option(
|
||||
OptionId.SeeRaw,
|
||||
stringResource(MR.strings.post_action_see_raw)
|
||||
)
|
||||
this += Option(
|
||||
OptionId.SeeRaw,
|
||||
stringResource(MR.strings.post_action_see_raw),
|
||||
)
|
||||
if (uiState.isLogged && !isOnOtherInstance) {
|
||||
add(
|
||||
Option(
|
||||
OptionId.CrossPost,
|
||||
stringResource(MR.strings.post_action_cross_post)
|
||||
)
|
||||
this += Option(
|
||||
OptionId.CrossPost,
|
||||
stringResource(MR.strings.post_action_cross_post)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.Report,
|
||||
stringResource(MR.strings.post_action_report)
|
||||
)
|
||||
this += Option(
|
||||
OptionId.Report,
|
||||
stringResource(MR.strings.post_action_report),
|
||||
)
|
||||
}
|
||||
if (post.creator?.id == uiState.currentUserId && !isOnOtherInstance) {
|
||||
add(
|
||||
Option(
|
||||
OptionId.Edit,
|
||||
stringResource(MR.strings.post_action_edit)
|
||||
)
|
||||
this += Option(
|
||||
OptionId.Edit,
|
||||
stringResource(MR.strings.post_action_edit),
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.Delete,
|
||||
stringResource(MR.strings.comment_action_delete)
|
||||
)
|
||||
this += Option(
|
||||
OptionId.Delete,
|
||||
stringResource(MR.strings.comment_action_delete),
|
||||
)
|
||||
}
|
||||
if (uiState.isModerator) {
|
||||
this += Option(
|
||||
OptionId.FeaturePost,
|
||||
if (post.featuredCommunity) {
|
||||
stringResource(MR.strings.mod_action_unmark_as_featured)
|
||||
} else {
|
||||
stringResource(MR.strings.mod_action_mark_as_featured)
|
||||
},
|
||||
)
|
||||
this += Option(
|
||||
OptionId.LockPost,
|
||||
if (post.locked) {
|
||||
stringResource(MR.strings.mod_action_unlock)
|
||||
} else {
|
||||
stringResource(MR.strings.mod_action_lock)
|
||||
},
|
||||
)
|
||||
this += Option(
|
||||
OptionId.Remove,
|
||||
stringResource(MR.strings.mod_action_remove),
|
||||
)
|
||||
}
|
||||
},
|
||||
@ -702,6 +727,21 @@ class CommunityDetailScreen(
|
||||
CommunityDetailMviModel.Intent.SharePost(post.id)
|
||||
)
|
||||
|
||||
OptionId.FeaturePost -> model.reduce(
|
||||
CommunityDetailMviModel.Intent.ModFeaturePost(
|
||||
post.id
|
||||
)
|
||||
)
|
||||
|
||||
OptionId.LockPost -> model.reduce(
|
||||
CommunityDetailMviModel.Intent.ModLockPost(post.id)
|
||||
)
|
||||
|
||||
OptionId.Remove -> {
|
||||
val screen = RemoveScreen(postId = post.id)
|
||||
navigationCoordinator.showBottomSheet(screen)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
})
|
||||
|
@ -86,6 +86,17 @@ class CommunityDetailViewModel(
|
||||
notificationCenter.subscribe(NotificationCenterEvent.PostUpdated::class).onEach { evt ->
|
||||
handlePostUpdate(evt.model)
|
||||
}.launchIn(this)
|
||||
notificationCenter.subscribe(NotificationCenterEvent.PostRemoved::class).onEach { evt ->
|
||||
handlePostDelete(evt.model.id)
|
||||
}.launchIn(this)
|
||||
notificationCenter.subscribe(NotificationCenterEvent.CommentRemoved::class)
|
||||
.onEach { evt ->
|
||||
val postId = evt.model.postId
|
||||
uiState.value.posts.firstOrNull { it.id == postId }?.also {
|
||||
val newPost = it.copy(comments = (it.comments - 1).coerceAtLeast(0))
|
||||
handlePostUpdate(newPost)
|
||||
}
|
||||
}.launchIn(this)
|
||||
|
||||
if (uiState.value.currentUserId == null) {
|
||||
val user = siteRepository.getCurrentUser(auth)
|
||||
@ -167,6 +178,16 @@ class CommunityDetailViewModel(
|
||||
interval = settingsRepository.currentSettings.value.zombieModeInterval,
|
||||
)
|
||||
}
|
||||
|
||||
is CommunityDetailMviModel.Intent.ModFeaturePost -> uiState.value.posts.firstOrNull { it.id == intent.id }
|
||||
?.also { post ->
|
||||
feature(post = post)
|
||||
}
|
||||
|
||||
is CommunityDetailMviModel.Intent.ModLockPost -> uiState.value.posts.firstOrNull { it.id == intent.id }
|
||||
?.also { post ->
|
||||
lock(post = post)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,9 +211,19 @@ class CommunityDetailViewModel(
|
||||
name = community.name,
|
||||
)
|
||||
}
|
||||
val isModerator = communityRepository.getModerators(
|
||||
auth = auth,
|
||||
id = community.id,
|
||||
).any { it.id == uiState.value.currentUserId }
|
||||
if (refreshedCommunity != null) {
|
||||
mvi.updateState { it.copy(community = refreshedCommunity) }
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
community = refreshedCommunity,
|
||||
isModerator = isModerator,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
loadNextPage()
|
||||
}
|
||||
}
|
||||
@ -581,4 +612,32 @@ class CommunityDetailViewModel(
|
||||
}
|
||||
markAsRead(post)
|
||||
}
|
||||
|
||||
private fun feature(post: PostModel) {
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newPost = postRepository.featureInCommunity(
|
||||
postId = post.id,
|
||||
auth = auth,
|
||||
featured = !post.featuredCommunity
|
||||
)
|
||||
if (newPost != null) {
|
||||
handlePostUpdate(newPost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun lock(post: PostModel) {
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newPost = postRepository.lock(
|
||||
postId = post.id,
|
||||
auth = auth,
|
||||
locked = !post.locked,
|
||||
)
|
||||
if (newPost != null) {
|
||||
handlePostUpdate(newPost)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ fun CommentCard(
|
||||
onOpenCreator = onOpenCreator,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onToggleExpanded = onToggleExpanded,
|
||||
distinguished = comment.distinguished,
|
||||
)
|
||||
ScaledContent {
|
||||
PostCardBody(
|
||||
|
@ -10,6 +10,9 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
import androidx.compose.material.icons.filled.PushPin
|
||||
import androidx.compose.material.icons.filled.WorkspacePremium
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@ -20,6 +23,7 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||
@ -35,6 +39,9 @@ fun CommunityAndCreatorInfo(
|
||||
autoLoadImages: Boolean = true,
|
||||
community: CommunityModel? = null,
|
||||
creator: UserModel? = null,
|
||||
distinguished: Boolean = false,
|
||||
featured: Boolean = false,
|
||||
locked: Boolean = false,
|
||||
onOpenCommunity: ((CommunityModel) -> Unit)? = null,
|
||||
onOpenCreator: ((UserModel) -> Unit)? = null,
|
||||
onToggleExpanded: (() -> Unit)? = null,
|
||||
@ -163,8 +170,29 @@ fun CommunityAndCreatorInfo(
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
val buttonModifier = Modifier.size(IconSize.m).padding(3.5.dp)
|
||||
if (distinguished) {
|
||||
Icon(
|
||||
modifier = buttonModifier,
|
||||
imageVector = Icons.Default.WorkspacePremium,
|
||||
contentDescription = null,
|
||||
)
|
||||
} else if (featured) {
|
||||
Icon(
|
||||
modifier = buttonModifier,
|
||||
imageVector = Icons.Default.PushPin,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
if (locked) {
|
||||
Icon(
|
||||
modifier = buttonModifier,
|
||||
imageVector = Icons.Default.Lock,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
if (indicatorExpanded != null) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
val expandedModifier = Modifier
|
||||
.padding(end = Spacing.xs)
|
||||
.onClick(
|
||||
|
@ -19,4 +19,10 @@ sealed class OptionId(val value: Int) {
|
||||
data object BlockInstance : OptionId(10)
|
||||
data object MarkRead : OptionId(11)
|
||||
data object MarkUnread : OptionId(12)
|
||||
data object FeaturePost : OptionId(13)
|
||||
data object LockPost : OptionId(14)
|
||||
data object Remove : OptionId(15)
|
||||
data object DistinguishComment : OptionId(16)
|
||||
data object OpenReports : OptionId(17)
|
||||
data object ResolveReport : OptionId(18)
|
||||
}
|
@ -160,6 +160,7 @@ private fun CompactPost(
|
||||
CommunityAndCreatorInfo(
|
||||
community = post.community,
|
||||
creator = post.creator.takeIf { !hideAuthor },
|
||||
featured = post.featuredCommunity,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenCreator = onOpenCreator,
|
||||
autoLoadImages = autoLoadImages,
|
||||
@ -248,6 +249,8 @@ private fun ExtendedPost(
|
||||
modifier = Modifier.padding(horizontal = Spacing.xxs),
|
||||
community = post.community,
|
||||
creator = post.creator.takeIf { !hideAuthor },
|
||||
featured = post.featuredCommunity,
|
||||
locked = post.locked,
|
||||
onOpenCommunity = onOpenCommunity,
|
||||
onOpenCreator = onOpenCreator,
|
||||
autoLoadImages = autoLoadImages,
|
||||
|
@ -27,6 +27,10 @@ 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.remove.RemoveMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.remove.RemoveViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist.ReportListMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist.ReportListViewModel
|
||||
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.selectcommunity.SelectCommunityMviModel
|
||||
@ -57,6 +61,7 @@ val commonUiModule = module {
|
||||
post = params[0],
|
||||
otherInstance = params[1],
|
||||
highlightCommentId = params[2],
|
||||
isModerator = params[3],
|
||||
identityRepository = get(),
|
||||
siteRepository = get(),
|
||||
postRepository = get(),
|
||||
@ -191,10 +196,10 @@ val commonUiModule = module {
|
||||
settingsRepository = get(),
|
||||
)
|
||||
}
|
||||
factory<CreateReportMviModel> {
|
||||
factory<CreateReportMviModel> { params ->
|
||||
CreateReportViewModel(
|
||||
postId = it[0],
|
||||
commentId = it[1],
|
||||
postId = params[0],
|
||||
commentId = params[1],
|
||||
mvi = DefaultMviModel(CreateReportMviModel.UiState()),
|
||||
identityRepository = get(),
|
||||
postRepository = get(),
|
||||
@ -210,4 +215,27 @@ val commonUiModule = module {
|
||||
notificationCenter = get(),
|
||||
)
|
||||
}
|
||||
factory<RemoveMviModel> { params ->
|
||||
RemoveViewModel(
|
||||
postId = params[0],
|
||||
commentId = params[1],
|
||||
mvi = DefaultMviModel(RemoveMviModel.UiState()),
|
||||
identityRepository = get(),
|
||||
postRepository = get(),
|
||||
commentRepository = get(),
|
||||
notificationCenter = get(),
|
||||
)
|
||||
}
|
||||
factory<ReportListMviModel> { params ->
|
||||
ReportListViewModel(
|
||||
communityId = params[0],
|
||||
mvi = DefaultMviModel(ReportListMviModel.UiState()),
|
||||
identityRepository = get(),
|
||||
postRepository = get(),
|
||||
commentRepository = get(),
|
||||
themeRepository = get(),
|
||||
settingsRepository = get(),
|
||||
hapticFeedback = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ 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.remove.RemoveMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist.ReportListMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.selectcommunity.SelectCommunityMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailMviModel
|
||||
@ -35,6 +37,7 @@ expect fun getPostDetailViewModel(
|
||||
post: PostModel,
|
||||
otherInstance: String = "",
|
||||
highlightCommentId: Int? = null,
|
||||
isModerator: Boolean = false,
|
||||
): PostDetailMviModel
|
||||
|
||||
expect fun getCommunityDetailViewModel(
|
||||
@ -84,4 +87,13 @@ expect fun getCustomTextToolbar(
|
||||
onQuote: () -> Unit,
|
||||
): TextToolbar
|
||||
|
||||
expect fun getSelectCommunityViewModel(): SelectCommunityMviModel
|
||||
expect fun getSelectCommunityViewModel(): SelectCommunityMviModel
|
||||
|
||||
expect fun getRemoveViewModel(
|
||||
postId: Int? = null,
|
||||
commentId: Int? = null,
|
||||
): RemoveMviModel
|
||||
|
||||
expect fun getReportListViewModel(
|
||||
communityId: Int,
|
||||
): ReportListMviModel
|
@ -0,0 +1,103 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHandle
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||
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.rememberCallback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
class ReportListTypeSheet : Screen {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
BottomSheetHandle()
|
||||
Text(
|
||||
modifier = Modifier.padding(start = Spacing.s, top = Spacing.s),
|
||||
text = stringResource(MR.strings.report_list_type_title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxxs),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.m,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.onClick(
|
||||
onClick = rememberCallback {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeReportListType(true)
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.report_list_type_unresolved),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.m,
|
||||
).onClick(
|
||||
onClick = rememberCallback {
|
||||
notificationCenter.send(
|
||||
NotificationCenterEvent.ChangeReportListType(false)
|
||||
)
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
},
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.report_list_type_all),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -30,10 +30,14 @@ interface PostDetailMviModel :
|
||||
data object DeletePost : Intent
|
||||
data object HapticIndication : Intent
|
||||
data object SharePost : Intent
|
||||
data object ModFeaturePost : Intent
|
||||
data object ModLockPost : Intent
|
||||
data class ModDistinguishComment(val commentId: Int) : Intent
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
val post: PostModel = PostModel(),
|
||||
val isModerator: Boolean = false,
|
||||
val isLogged: Boolean = false,
|
||||
val refreshing: Boolean = false,
|
||||
val loading: Boolean = false,
|
||||
|
@ -95,6 +95,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getPostDetailVi
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.image.ZoomableImageScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.RawContentDialog
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.remove.RemoveScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
@ -116,6 +117,7 @@ class PostDetailScreen(
|
||||
private val post: PostModel,
|
||||
private val otherInstance: String = "",
|
||||
private val highlightCommentId: Int? = null,
|
||||
private val isMod: Boolean = false,
|
||||
) : Screen {
|
||||
|
||||
@OptIn(
|
||||
@ -132,6 +134,7 @@ class PostDetailScreen(
|
||||
post = post,
|
||||
highlightCommentId = highlightCommentId,
|
||||
otherInstance = otherInstance,
|
||||
isModerator = isMod,
|
||||
)
|
||||
}
|
||||
model.bindToLifecycle(key + post.id.toString())
|
||||
@ -404,6 +407,34 @@ class PostDetailScreen(
|
||||
)
|
||||
)
|
||||
}
|
||||
if (uiState.isModerator) {
|
||||
add(
|
||||
Option(
|
||||
OptionId.FeaturePost,
|
||||
if (uiState.post.featuredCommunity) {
|
||||
stringResource(MR.strings.mod_action_unmark_as_featured)
|
||||
} else {
|
||||
stringResource(MR.strings.mod_action_mark_as_featured)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.LockPost,
|
||||
if (uiState.post.locked) {
|
||||
stringResource(MR.strings.mod_action_unlock)
|
||||
} else {
|
||||
stringResource(MR.strings.mod_action_lock)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.Remove,
|
||||
stringResource(MR.strings.mod_action_remove)
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
onOptionSelected = rememberCallbackArgs(model) { idx ->
|
||||
when (idx) {
|
||||
@ -433,6 +464,19 @@ class PostDetailScreen(
|
||||
|
||||
OptionId.Share -> model.reduce(PostDetailMviModel.Intent.SharePost)
|
||||
|
||||
OptionId.FeaturePost -> model.reduce(
|
||||
PostDetailMviModel.Intent.ModFeaturePost
|
||||
)
|
||||
|
||||
OptionId.LockPost -> model.reduce(
|
||||
PostDetailMviModel.Intent.ModLockPost
|
||||
)
|
||||
|
||||
OptionId.Remove -> {
|
||||
val screen = RemoveScreen(postId = uiState.post.id)
|
||||
navigationCoordinator.showBottomSheet(screen)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
},
|
||||
@ -507,7 +551,7 @@ class PostDetailScreen(
|
||||
}
|
||||
items(
|
||||
uiState.comments.filter { it.visible },
|
||||
key = { c -> c.id }) { comment ->
|
||||
key = { c -> c.id.toString() + c.updateDate }) { comment ->
|
||||
Column {
|
||||
AnimatedContent(
|
||||
targetState = comment.expanded,
|
||||
@ -672,26 +716,44 @@ class PostDetailScreen(
|
||||
add(
|
||||
Option(
|
||||
OptionId.SeeRaw,
|
||||
stringResource(MR.strings.post_action_see_raw)
|
||||
stringResource(MR.strings.post_action_see_raw),
|
||||
)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.Report,
|
||||
stringResource(MR.strings.post_action_report)
|
||||
stringResource(MR.strings.post_action_report),
|
||||
)
|
||||
)
|
||||
if (comment.creator?.id == uiState.currentUserId) {
|
||||
add(
|
||||
Option(
|
||||
OptionId.Edit,
|
||||
stringResource(MR.strings.post_action_edit)
|
||||
stringResource(MR.strings.post_action_edit),
|
||||
)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.Delete,
|
||||
stringResource(MR.strings.comment_action_delete)
|
||||
stringResource(MR.strings.comment_action_delete),
|
||||
)
|
||||
)
|
||||
}
|
||||
if (uiState.isModerator) {
|
||||
add(
|
||||
Option(
|
||||
OptionId.DistinguishComment,
|
||||
if (comment.distinguished) {
|
||||
stringResource(MR.strings.mod_action_unmark_as_distinguished)
|
||||
} else {
|
||||
stringResource(MR.strings.mod_action_mark_as_distinguished)
|
||||
},
|
||||
)
|
||||
)
|
||||
add(
|
||||
Option(
|
||||
OptionId.Remove,
|
||||
stringResource(MR.strings.mod_action_remove),
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -726,6 +788,20 @@ class PostDetailScreen(
|
||||
rawContent = comment
|
||||
}
|
||||
|
||||
OptionId.DistinguishComment -> model.reduce(
|
||||
PostDetailMviModel.Intent.ModDistinguishComment(
|
||||
comment.id
|
||||
)
|
||||
)
|
||||
|
||||
OptionId.Remove -> {
|
||||
val screen =
|
||||
RemoveScreen(commentId = comment.id)
|
||||
navigationCoordinator.showBottomSheet(
|
||||
screen
|
||||
)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
},
|
||||
|
@ -27,6 +27,7 @@ class PostDetailViewModel(
|
||||
private val post: PostModel,
|
||||
private val otherInstance: String,
|
||||
private val highlightCommentId: Int?,
|
||||
private val isModerator: Boolean,
|
||||
private val identityRepository: IdentityRepository,
|
||||
private val siteRepository: SiteRepository,
|
||||
private val postRepository: PostRepository,
|
||||
@ -67,6 +68,13 @@ class PostDetailViewModel(
|
||||
}
|
||||
}.launchIn(this)
|
||||
|
||||
notificationCenter.subscribe(NotificationCenterEvent.PostRemoved::class).onEach { evt ->
|
||||
mvi.emitEffect(PostDetailMviModel.Effect.Close)
|
||||
}.launchIn(this)
|
||||
notificationCenter.subscribe(NotificationCenterEvent.CommentRemoved::class)
|
||||
.onEach { evt ->
|
||||
handleCommentDelete(evt.model.id)
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
@ -82,7 +90,10 @@ class PostDetailViewModel(
|
||||
}
|
||||
|
||||
mvi.updateState {
|
||||
it.copy(post = post)
|
||||
it.copy(
|
||||
post = post,
|
||||
isModerator = isModerator,
|
||||
)
|
||||
}
|
||||
|
||||
val auth = identityRepository.authToken.value
|
||||
@ -105,6 +116,9 @@ class PostDetailViewModel(
|
||||
)
|
||||
highlightCommentPath = comment?.path
|
||||
}
|
||||
if (post.text.isEmpty() && post.title.isEmpty()) {
|
||||
refreshPost()
|
||||
}
|
||||
if (mvi.uiState.value.comments.isEmpty()) {
|
||||
refresh()
|
||||
}
|
||||
@ -214,6 +228,13 @@ class PostDetailViewModel(
|
||||
toggleExpanded(comment)
|
||||
}
|
||||
}
|
||||
|
||||
PostDetailMviModel.Intent.ModFeaturePost -> feature(uiState.value.post)
|
||||
PostDetailMviModel.Intent.ModLockPost -> lock(uiState.value.post)
|
||||
is PostDetailMviModel.Intent.ModDistinguishComment -> uiState.value.comments.firstOrNull { it.id == intent.commentId }
|
||||
?.also { comment ->
|
||||
distinguish(comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,6 +471,20 @@ class PostDetailViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCommentUpdate(comment: CommentModel) {
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
comments = it.comments.map { c ->
|
||||
if (c.id == comment.id) {
|
||||
comment
|
||||
} else {
|
||||
c
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleUpVoteComment(
|
||||
comment: CommentModel,
|
||||
feedback: Boolean,
|
||||
@ -462,17 +497,7 @@ class PostDetailViewModel(
|
||||
comment = comment,
|
||||
voted = newValue,
|
||||
)
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
comments = it.comments.map { c ->
|
||||
if (c.id == comment.id) {
|
||||
newComment
|
||||
} else {
|
||||
c
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
handleCommentUpdate(newComment)
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
@ -486,17 +511,7 @@ class PostDetailViewModel(
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
comments = it.comments.map { c ->
|
||||
if (c.id == comment.id) {
|
||||
comment
|
||||
} else {
|
||||
c
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
handleCommentUpdate(comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -510,17 +525,7 @@ class PostDetailViewModel(
|
||||
hapticFeedback.vibrate()
|
||||
}
|
||||
val newComment = commentRepository.asDownVoted(comment, newValue)
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
comments = it.comments.map { c ->
|
||||
if (c.id == comment.id) {
|
||||
newComment
|
||||
} else {
|
||||
c
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
handleCommentUpdate(newComment)
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
@ -534,17 +539,7 @@ class PostDetailViewModel(
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
comments = it.comments.map { c ->
|
||||
if (c.id == comment.id) {
|
||||
comment
|
||||
} else {
|
||||
c
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
handleCommentUpdate(comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -561,17 +556,7 @@ class PostDetailViewModel(
|
||||
comment = comment,
|
||||
saved = newValue,
|
||||
)
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
comments = it.comments.map { c ->
|
||||
if (c.id == comment.id) {
|
||||
newComment
|
||||
} else {
|
||||
c
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
handleCommentUpdate(newComment)
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
@ -585,17 +570,7 @@ class PostDetailViewModel(
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
comments = it.comments.map { c ->
|
||||
if (c.id == comment.id) {
|
||||
comment
|
||||
} else {
|
||||
c
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
handleCommentUpdate(comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -604,11 +579,15 @@ class PostDetailViewModel(
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
commentRepository.delete(id, auth)
|
||||
refresh()
|
||||
handleCommentDelete(id)
|
||||
refreshPost()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCommentDelete(id: Int) {
|
||||
mvi.updateState { it.copy(comments = it.comments.filter { comment -> comment.id != id }) }
|
||||
}
|
||||
|
||||
private fun deletePost() {
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
@ -658,6 +637,48 @@ class PostDetailViewModel(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun feature(post: PostModel) {
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newPost = postRepository.featureInCommunity(
|
||||
postId = post.id,
|
||||
auth = auth,
|
||||
featured = !post.featuredCommunity
|
||||
)
|
||||
if (newPost != null) {
|
||||
handlePostUpdate(newPost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun lock(post: PostModel) {
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newPost = postRepository.lock(
|
||||
postId = post.id,
|
||||
auth = auth,
|
||||
locked = !post.locked,
|
||||
)
|
||||
if (newPost != null) {
|
||||
handlePostUpdate(newPost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun distinguish(comment: CommentModel) {
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newComment = commentRepository.distinguish(
|
||||
commentId = comment.id,
|
||||
auth = auth,
|
||||
distinguished = !comment.distinguished,
|
||||
)
|
||||
if (newComment != null) {
|
||||
handleCommentUpdate(newComment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class Node(
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.remove
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import dev.icerock.moko.resources.desc.StringDesc
|
||||
|
||||
@Stable
|
||||
interface RemoveMviModel :
|
||||
MviModel<RemoveMviModel.Intent, RemoveMviModel.UiState, RemoveMviModel.Effect>,
|
||||
ScreenModel {
|
||||
|
||||
sealed interface Intent {
|
||||
data class SetText(val value: String) : Intent
|
||||
data object Submit : 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,163 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.remove
|
||||
|
||||
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.material3.Button
|
||||
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 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.getNavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getRemoveViewModel
|
||||
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 RemoveScreen(
|
||||
private val postId: Int? = null,
|
||||
private val commentId: Int? = null,
|
||||
) : Screen {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val model = rememberScreenModel {
|
||||
getRemoveViewModel(
|
||||
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 navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
|
||||
LaunchedEffect(model) {
|
||||
model.effects.onEach {
|
||||
when (it) {
|
||||
is RemoveMviModel.Effect.Failure -> {
|
||||
snackbarHostState.showSnackbar(it.message ?: genericError)
|
||||
}
|
||||
|
||||
RemoveMviModel.Effect.Success -> {
|
||||
navigationCoordinator.hideBottomSheet()
|
||||
}
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = Spacing.s),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
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,
|
||||
)
|
||||
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier.align(Alignment.TopEnd),
|
||||
content = {
|
||||
Text(
|
||||
text = stringResource(MR.strings.button_confirm),
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
model.reduce(RemoveMviModel.Intent.Submit)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
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(RemoveMviModel.Intent.SetText(value))
|
||||
},
|
||||
isError = uiState.textError != null,
|
||||
supportingText = {
|
||||
if (uiState.textError != null) {
|
||||
Text(
|
||||
text = uiState.textError?.localized().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
Spacer(Modifier.height(Spacing.xxl))
|
||||
}
|
||||
|
||||
if (uiState.loading) {
|
||||
ProgressHud()
|
||||
}
|
||||
|
||||
SnackbarHost(
|
||||
modifier = Modifier.padding(bottom = Spacing.xxxl),
|
||||
hostState = snackbarHostState
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.remove
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||
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 RemoveViewModel(
|
||||
private val postId: Int?,
|
||||
private val commentId: Int?,
|
||||
private val mvi: DefaultMviModel<RemoveMviModel.Intent, RemoveMviModel.UiState, RemoveMviModel.Effect>,
|
||||
private val identityRepository: IdentityRepository,
|
||||
private val postRepository: PostRepository,
|
||||
private val commentRepository: CommentRepository,
|
||||
private val notificationCenter: NotificationCenter,
|
||||
) : RemoveMviModel,
|
||||
MviModel<RemoveMviModel.Intent, RemoveMviModel.UiState, RemoveMviModel.Effect> by mvi {
|
||||
|
||||
override fun reduce(intent: RemoveMviModel.Intent) {
|
||||
when (intent) {
|
||||
is RemoveMviModel.Intent.SetText -> {
|
||||
mvi.updateState {
|
||||
it.copy(text = intent.value)
|
||||
}
|
||||
}
|
||||
|
||||
RemoveMviModel.Intent.Submit -> 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.remove(
|
||||
postId = postId,
|
||||
reason = text,
|
||||
auth = auth,
|
||||
removed = true,
|
||||
)?.also { post ->
|
||||
notificationCenter.send(NotificationCenterEvent.PostRemoved(post))
|
||||
}
|
||||
} else if (commentId != null) {
|
||||
commentRepository.remove(
|
||||
commentId = commentId,
|
||||
reason = text,
|
||||
auth = auth,
|
||||
removed = true,
|
||||
)?.also { comment ->
|
||||
notificationCenter.send(NotificationCenterEvent.CommentRemoved(comment))
|
||||
}
|
||||
}
|
||||
mvi.emitEffect(RemoveMviModel.Effect.Success)
|
||||
} catch (e: Throwable) {
|
||||
val message = e.message
|
||||
mvi.emitEffect(RemoveMviModel.Effect.Failure(message))
|
||||
} finally {
|
||||
mvi.updateState { it.copy(loading = false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Option
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.OptionId
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCardBody
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentReportModel
|
||||
|
||||
@Composable
|
||||
internal fun CommentReportCard(
|
||||
report: CommentReportModel,
|
||||
postLayout: PostLayout = PostLayout.Card,
|
||||
modifier: Modifier = Modifier,
|
||||
options: List<Option> = emptyList(),
|
||||
autoLoadImages: Boolean = true,
|
||||
onOpen: (() -> Unit)? = null,
|
||||
onOptionSelected: ((OptionId) -> Unit)? = null,
|
||||
) {
|
||||
InnerReportCard(
|
||||
modifier = modifier,
|
||||
reason = report.reason.orEmpty(),
|
||||
postLayout = postLayout,
|
||||
creator = report.creator,
|
||||
date = report.publishDate,
|
||||
autoLoadImages = autoLoadImages,
|
||||
options = options,
|
||||
onOptionSelected = onOptionSelected,
|
||||
onOpen = onOpen,
|
||||
originalContent = {
|
||||
Column {
|
||||
report.originalText?.also { text ->
|
||||
PostCardBody(
|
||||
modifier = Modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.xs,
|
||||
),
|
||||
text = text,
|
||||
autoLoadImages = autoLoadImages,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreHoriz
|
||||
import androidx.compose.material.icons.filled.OpenInNew
|
||||
import androidx.compose.material.icons.filled.Schedule
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.layout.positionInParent
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomDropDown
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomImage
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Option
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.OptionId
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCardBody
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.ScaledContent
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.prettifyDate
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||
|
||||
@Composable
|
||||
internal fun InnerReportCard(
|
||||
modifier: Modifier = Modifier,
|
||||
reason: String,
|
||||
autoLoadImages: Boolean = true,
|
||||
date: String? = null,
|
||||
creator: UserModel? = null,
|
||||
postLayout: PostLayout = PostLayout.Card,
|
||||
options: List<Option> = emptyList(),
|
||||
onOpenCreator: ((UserModel) -> Unit)? = null,
|
||||
onOpen: (() -> Unit)? = null,
|
||||
originalContent: (@Composable () -> Unit)? = null,
|
||||
onOptionSelected: ((OptionId) -> Unit)? = null,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.let {
|
||||
if (postLayout == PostLayout.Card) {
|
||||
it.padding(horizontal = Spacing.xs)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
).padding(Spacing.s)
|
||||
} else {
|
||||
it.background(MaterialTheme.colorScheme.background)
|
||||
}
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
ReportHeader(
|
||||
creator = creator,
|
||||
autoLoadImages = autoLoadImages,
|
||||
onOpenCreator = onOpenCreator,
|
||||
)
|
||||
ScaledContent {
|
||||
PostCardBody(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = Spacing.xs,
|
||||
),
|
||||
text = reason,
|
||||
)
|
||||
if (originalContent != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
)
|
||||
.padding(all = Spacing.s)
|
||||
) {
|
||||
originalContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
ReportFooter(
|
||||
date = date,
|
||||
onOpenResolve = onOpen,
|
||||
options = options,
|
||||
onOptionSelected = onOptionSelected,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReportHeader(
|
||||
modifier: Modifier = Modifier,
|
||||
creator: UserModel? = null,
|
||||
autoLoadImages: Boolean = true,
|
||||
iconSize: Dp = IconSize.s,
|
||||
onOpenCreator: ((UserModel) -> Unit)? = null,
|
||||
) {
|
||||
val creatorName = creator?.name.orEmpty()
|
||||
val creatorAvatar = creator?.avatar.orEmpty()
|
||||
val creatorHost = creator?.host.orEmpty()
|
||||
if (creatorName.isNotEmpty()) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.onClick(
|
||||
onClick = rememberCallback {
|
||||
if (creator != null) {
|
||||
onOpenCreator?.invoke(creator)
|
||||
}
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
if (creatorAvatar.isNotEmpty() && autoLoadImages) {
|
||||
CustomImage(
|
||||
modifier = Modifier
|
||||
.padding(Spacing.xxxs)
|
||||
.size(iconSize)
|
||||
.clip(RoundedCornerShape(iconSize / 2)),
|
||||
url = creatorAvatar,
|
||||
quality = FilterQuality.Low,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = Spacing.xs),
|
||||
text = buildString {
|
||||
append(creatorName)
|
||||
if (creatorHost.isNotEmpty()) {
|
||||
append("@$creatorHost")
|
||||
}
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReportFooter(
|
||||
date: String? = null,
|
||||
options: List<Option> = emptyList(),
|
||||
onOpenResolve: (() -> Unit)? = null,
|
||||
onOptionSelected: ((OptionId) -> Unit)? = null,
|
||||
) {
|
||||
val buttonModifier = Modifier.size(IconSize.m).padding(3.5.dp)
|
||||
var optionsExpanded by remember { mutableStateOf(false) }
|
||||
var optionsOffset by remember { mutableStateOf(Offset.Zero) }
|
||||
|
||||
Box {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
modifier = buttonModifier,
|
||||
imageVector = Icons.Default.Schedule,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
Text(
|
||||
text = date?.prettifyDate() ?: "",
|
||||
)
|
||||
if (options.isNotEmpty()) {
|
||||
Icon(
|
||||
modifier = buttonModifier
|
||||
.padding(top = Spacing.xxs)
|
||||
.onGloballyPositioned {
|
||||
optionsOffset = it.positionInParent()
|
||||
}
|
||||
.onClick(
|
||||
onClick = rememberCallback {
|
||||
optionsExpanded = true
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.MoreHoriz,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Image(
|
||||
modifier = buttonModifier
|
||||
.onClick(
|
||||
onClick = rememberCallback {
|
||||
onOpenResolve?.invoke()
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.OpenInNew,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface),
|
||||
)
|
||||
}
|
||||
CustomDropDown(
|
||||
expanded = optionsExpanded,
|
||||
onDismiss = {
|
||||
optionsExpanded = false
|
||||
},
|
||||
offset = DpOffset(
|
||||
x = optionsOffset.x.toLocalDp(),
|
||||
y = optionsOffset.y.toLocalDp(),
|
||||
),
|
||||
) {
|
||||
options.forEach { option ->
|
||||
Text(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = Spacing.m,
|
||||
vertical = Spacing.s,
|
||||
).onClick(
|
||||
onClick = rememberCallback {
|
||||
optionsExpanded = false
|
||||
onOptionSelected?.invoke(option.id)
|
||||
},
|
||||
),
|
||||
text = option.text,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Option
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.OptionId
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCardBody
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCardImage
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCardTitle
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostLinkBanner
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.web.WebViewScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostReportModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.imageUrl
|
||||
|
||||
@Composable
|
||||
internal fun PostReportCard(
|
||||
report: PostReportModel,
|
||||
postLayout: PostLayout = PostLayout.Card,
|
||||
modifier: Modifier = Modifier,
|
||||
autoLoadImages: Boolean = true,
|
||||
onOpen: (() -> Unit)? = null,
|
||||
options: List<Option> = emptyList(),
|
||||
onOptionSelected: ((OptionId) -> Unit)? = null,
|
||||
) {
|
||||
InnerReportCard(
|
||||
modifier = modifier,
|
||||
reason = report.reason.orEmpty(),
|
||||
postLayout = postLayout,
|
||||
creator = report.creator,
|
||||
date = report.publishDate,
|
||||
autoLoadImages = autoLoadImages,
|
||||
options = options,
|
||||
onOptionSelected = onOptionSelected,
|
||||
onOpen = onOpen,
|
||||
originalContent = {
|
||||
Column {
|
||||
report.originalTitle?.also { title ->
|
||||
PostCardTitle(
|
||||
modifier = Modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.xs,
|
||||
),
|
||||
text = title,
|
||||
autoLoadImages = autoLoadImages,
|
||||
)
|
||||
}
|
||||
report.imageUrl.takeIf { it.isNotEmpty() }?.also { imageUrl ->
|
||||
PostCardImage(
|
||||
modifier = Modifier
|
||||
.padding(vertical = Spacing.xxs)
|
||||
.clip(RoundedCornerShape(CornerSize.xl)),
|
||||
imageUrl = imageUrl,
|
||||
autoLoadImages = autoLoadImages,
|
||||
)
|
||||
}
|
||||
report.originalText?.also { text ->
|
||||
PostCardBody(
|
||||
modifier = Modifier.padding(
|
||||
vertical = Spacing.xs,
|
||||
horizontal = Spacing.xs,
|
||||
),
|
||||
text = text,
|
||||
autoLoadImages = autoLoadImages,
|
||||
)
|
||||
}
|
||||
report.originalUrl?.also { url ->
|
||||
val settingsRepository = remember { getSettingsRepository() }
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
PostLinkBanner(
|
||||
modifier = Modifier
|
||||
.padding(vertical = Spacing.xs)
|
||||
.onClick(
|
||||
onClick = {
|
||||
if (settingsRepository.currentSettings.value.openUrlsInExternalBrowser) {
|
||||
uriHandler.openUri(url)
|
||||
} else {
|
||||
navigationCoordinator.pushScreen(WebViewScreen(url))
|
||||
}
|
||||
},
|
||||
),
|
||||
url = url,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.shimmerEffect
|
||||
|
||||
@Composable
|
||||
internal fun ReportCardPlaceHolder(
|
||||
postLayout: PostLayout = PostLayout.Card,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.let {
|
||||
if (postLayout == PostLayout.Card) {
|
||||
it.padding(horizontal = Spacing.xs).background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp),
|
||||
shape = RoundedCornerShape(CornerSize.l),
|
||||
).padding(Spacing.s)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect()
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = Spacing.xxxs)
|
||||
.height(80.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect()
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(IconSize.l)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(CornerSize.m))
|
||||
.shimmerEffect()
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist
|
||||
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentReportModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostReportModel
|
||||
|
||||
enum class ReportListSection {
|
||||
Posts,
|
||||
Comments,
|
||||
}
|
||||
|
||||
interface ReportListMviModel :
|
||||
MviModel<ReportListMviModel.Intent, ReportListMviModel.UiState, ReportListMviModel.Effect>,
|
||||
ScreenModel {
|
||||
|
||||
sealed interface Intent {
|
||||
data object HapticIndication : Intent
|
||||
data class ChangeSection(val value: ReportListSection) : Intent
|
||||
data class ChangeUnresolvedOnly(val value: Boolean) : Intent
|
||||
data object LoadNextPage : Intent
|
||||
data object Refresh : Intent
|
||||
data class ResolvePost(val id: Int) : Intent
|
||||
data class ResolveComment(val id: Int) : Intent
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
val section: ReportListSection = ReportListSection.Posts,
|
||||
val unresolvedOnly: Boolean = true,
|
||||
val refreshing: Boolean = false,
|
||||
val loading: Boolean = false,
|
||||
val initial: Boolean = true,
|
||||
val asyncInProgress: Boolean = false,
|
||||
val swipeActionsEnabled: Boolean = true,
|
||||
val autoLoadImages: Boolean = true,
|
||||
val postLayout: PostLayout = PostLayout.Card,
|
||||
val canFetchMore: Boolean = true,
|
||||
val postReports: List<PostReportModel> = emptyList(),
|
||||
val commentReports: List<CommentReportModel> = emptyList(),
|
||||
)
|
||||
|
||||
sealed interface Effect {
|
||||
data object BackToTop : Effect
|
||||
}
|
||||
}
|
@ -0,0 +1,490 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
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.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Report
|
||||
import androidx.compose.material.icons.filled.ReportOff
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DismissDirection
|
||||
import androidx.compose.material3.DismissValue
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.PostLayout
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.bindToLifecycle
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.Option
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.OptionId
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.ProgressHud
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SectionSelector
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.SwipeableCard
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getReportListViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.RawContentDialog
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.ReportListTypeSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDetailScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentReportModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostReportModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ReportListScreen(
|
||||
private val communityId: Int,
|
||||
) : Screen {
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val model = rememberScreenModel { getReportListViewModel(communityId) }
|
||||
model.bindToLifecycle(key)
|
||||
val uiState by model.uiState.collectAsState()
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
var rawContent by remember { mutableStateOf<Any?>(null) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val settingsRepository = remember { getSettingsRepository() }
|
||||
val settings by settingsRepository.currentSettings.collectAsState()
|
||||
val lazyListState = rememberLazyListState()
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = uiState.refreshing,
|
||||
onRefresh = rememberCallback(model) {
|
||||
model.reduce(ReportListMviModel.Intent.Refresh)
|
||||
},
|
||||
)
|
||||
|
||||
LaunchedEffect(notificationCenter) {
|
||||
notificationCenter.subscribe(NotificationCenterEvent.ChangeReportListType::class)
|
||||
.onEach { evt ->
|
||||
model.reduce(
|
||||
ReportListMviModel.Intent.ChangeUnresolvedOnly(evt.unresolvedOnly)
|
||||
)
|
||||
}.launchIn(this)
|
||||
}
|
||||
LaunchedEffect(model) {
|
||||
model.effects.onEach { effect ->
|
||||
when (effect) {
|
||||
ReportListMviModel.Effect.BackToTop -> {
|
||||
scope.launch {
|
||||
lazyListState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.padding(Spacing.xxs),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = {
|
||||
Image(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = rememberCallback {
|
||||
navigationCoordinator.popScreen()
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Column(modifier = Modifier.padding(horizontal = Spacing.s)) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.report_list_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
val text = when (uiState.unresolvedOnly) {
|
||||
true -> stringResource(MR.strings.report_list_type_unresolved)
|
||||
else -> stringResource(MR.strings.report_list_type_all)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.onClick(
|
||||
onClick = rememberCallback {
|
||||
val sheet = ReportListTypeSheet()
|
||||
navigationCoordinator.showBottomSheet(sheet)
|
||||
},
|
||||
),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier.padding(paddingValues).let {
|
||||
if (settings.hideNavigationBarWhileScrolling) {
|
||||
it.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
SectionSelector(
|
||||
modifier = Modifier.padding(vertical = Spacing.s),
|
||||
titles = listOf(
|
||||
stringResource(MR.strings.profile_section_posts),
|
||||
stringResource(MR.strings.profile_section_comments),
|
||||
),
|
||||
currentSection = when (uiState.section) {
|
||||
ReportListSection.Comments -> 1
|
||||
else -> 0
|
||||
},
|
||||
onSectionSelected = {
|
||||
val section = when (it) {
|
||||
1 -> ReportListSection.Comments
|
||||
else -> ReportListSection.Posts
|
||||
}
|
||||
model.reduce(ReportListMviModel.Intent.ChangeSection(section))
|
||||
},
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.let {
|
||||
if (settings.hideNavigationBarWhileScrolling) {
|
||||
it.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
.pullRefresh(pullRefreshState),
|
||||
) {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
) {
|
||||
if (uiState.section == ReportListSection.Posts) {
|
||||
if (uiState.postReports.isEmpty() && uiState.loading && uiState.initial) {
|
||||
items(5) {
|
||||
ReportCardPlaceHolder(uiState.postLayout)
|
||||
if (uiState.postLayout != PostLayout.Card) {
|
||||
Divider(modifier = Modifier.padding(vertical = Spacing.s))
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(Spacing.s))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uiState.postReports.isEmpty() && !uiState.initial) {
|
||||
item {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
text = stringResource(MR.strings.message_empty_list),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
items(
|
||||
uiState.postReports,
|
||||
{ it.id.toString() + it.updateDate },
|
||||
) { report ->
|
||||
val endColor = MaterialTheme.colorScheme.secondary
|
||||
SwipeableCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
directions = setOf(DismissDirection.EndToStart),
|
||||
enabled = uiState.swipeActionsEnabled,
|
||||
backgroundColor = rememberCallbackArgs {
|
||||
when (it) {
|
||||
DismissValue.DismissedToStart -> endColor
|
||||
else -> Color.Transparent
|
||||
}
|
||||
},
|
||||
onGestureBegin = rememberCallback(model) {
|
||||
model.reduce(ReportListMviModel.Intent.HapticIndication)
|
||||
},
|
||||
onDismissToStart = rememberCallback(model) {
|
||||
model.reduce(
|
||||
ReportListMviModel.Intent.ResolvePost(report.id),
|
||||
)
|
||||
},
|
||||
swipeContent = { _ ->
|
||||
val icon = when {
|
||||
report.resolved -> Icons.Default.Report
|
||||
else -> Icons.Default.ReportOff
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.padding(Spacing.xs),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
PostReportCard(
|
||||
report = report,
|
||||
postLayout = uiState.postLayout,
|
||||
autoLoadImages = uiState.autoLoadImages,
|
||||
onOpen = rememberCallback {
|
||||
val screen = PostDetailScreen(
|
||||
post = PostModel(id = report.postId),
|
||||
isMod = true,
|
||||
)
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
},
|
||||
options = buildList {
|
||||
this += Option(
|
||||
OptionId.SeeRaw,
|
||||
stringResource(MR.strings.post_action_see_raw),
|
||||
)
|
||||
this += Option(
|
||||
OptionId.ResolveReport,
|
||||
if (report.resolved) {
|
||||
stringResource(MR.strings.report_action_unresolve)
|
||||
} else {
|
||||
stringResource(MR.strings.report_action_resolve)
|
||||
},
|
||||
)
|
||||
},
|
||||
onOptionSelected = rememberCallbackArgs { optionId ->
|
||||
when (optionId) {
|
||||
OptionId.SeeRaw -> {
|
||||
rawContent = report
|
||||
}
|
||||
|
||||
OptionId.ResolveReport -> {
|
||||
model.reduce(
|
||||
ReportListMviModel.Intent.ResolvePost(
|
||||
report.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
if (uiState.postLayout != PostLayout.Card) {
|
||||
Divider(modifier = Modifier.padding(vertical = Spacing.s))
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(Spacing.s))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (uiState.commentReports.isEmpty() && uiState.loading && uiState.initial) {
|
||||
items(5) {
|
||||
ReportCardPlaceHolder(uiState.postLayout)
|
||||
if (uiState.postLayout != PostLayout.Card) {
|
||||
Divider(modifier = Modifier.padding(vertical = Spacing.s))
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(Spacing.s))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uiState.commentReports.isEmpty() && !uiState.initial) {
|
||||
item {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
text = stringResource(MR.strings.message_empty_list),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
items(
|
||||
uiState.commentReports,
|
||||
{ it.id.toString() + it.updateDate },
|
||||
) { report ->
|
||||
val endColor = MaterialTheme.colorScheme.secondary
|
||||
SwipeableCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
directions = setOf(DismissDirection.EndToStart),
|
||||
enabled = uiState.swipeActionsEnabled,
|
||||
backgroundColor = rememberCallbackArgs {
|
||||
when (it) {
|
||||
DismissValue.DismissedToStart -> endColor
|
||||
else -> Color.Transparent
|
||||
}
|
||||
},
|
||||
onGestureBegin = rememberCallback(model) {
|
||||
model.reduce(ReportListMviModel.Intent.HapticIndication)
|
||||
},
|
||||
onDismissToStart = rememberCallback(model) {
|
||||
model.reduce(
|
||||
ReportListMviModel.Intent.ResolveComment(report.id),
|
||||
)
|
||||
},
|
||||
swipeContent = { _ ->
|
||||
val icon = when {
|
||||
report.resolved -> Icons.Default.Report
|
||||
else -> Icons.Default.ReportOff
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.padding(Spacing.xs),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
CommentReportCard(
|
||||
report = report,
|
||||
postLayout = uiState.postLayout,
|
||||
autoLoadImages = uiState.autoLoadImages,
|
||||
onOpen = rememberCallback {
|
||||
val screen = PostDetailScreen(
|
||||
post = PostModel(id = report.postId),
|
||||
highlightCommentId = report.commentId,
|
||||
isMod = true,
|
||||
)
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
},
|
||||
options = buildList {
|
||||
this += Option(
|
||||
OptionId.SeeRaw,
|
||||
stringResource(MR.strings.post_action_see_raw),
|
||||
)
|
||||
this += Option(
|
||||
OptionId.ResolveReport,
|
||||
if (report.resolved) {
|
||||
stringResource(MR.strings.report_action_unresolve)
|
||||
} else {
|
||||
stringResource(MR.strings.report_action_resolve)
|
||||
},
|
||||
)
|
||||
},
|
||||
onOptionSelected = rememberCallbackArgs { optionId ->
|
||||
when (optionId) {
|
||||
OptionId.SeeRaw -> {
|
||||
rawContent = report
|
||||
}
|
||||
|
||||
OptionId.ResolveReport -> {
|
||||
model.reduce(
|
||||
ReportListMviModel.Intent.ResolveComment(
|
||||
report.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
if (uiState.postLayout != PostLayout.Card) {
|
||||
Divider(modifier = Modifier.padding(vertical = Spacing.s))
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(Spacing.s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
||||
model.reduce(ReportListMviModel.Intent.LoadNextPage)
|
||||
}
|
||||
if (uiState.loading && !uiState.refreshing) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().padding(Spacing.xs),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(25.dp),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(Spacing.s))
|
||||
}
|
||||
}
|
||||
|
||||
if (uiState.asyncInProgress) {
|
||||
ProgressHud()
|
||||
}
|
||||
PullRefreshIndicator(
|
||||
refreshing = uiState.refreshing,
|
||||
state = pullRefreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
backgroundColor = MaterialTheme.colorScheme.background,
|
||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rawContent != null) {
|
||||
when (val content = rawContent) {
|
||||
is PostReportModel -> {
|
||||
RawContentDialog(
|
||||
title = content.originalTitle,
|
||||
date = content.publishDate,
|
||||
url = content.originalUrl,
|
||||
text = content.originalText,
|
||||
onDismiss = {
|
||||
rawContent = null
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
is CommentReportModel -> {
|
||||
RawContentDialog(
|
||||
date = content.publishDate,
|
||||
text = content.originalText,
|
||||
onDismiss = {
|
||||
rawContent = null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.repository.ThemeRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.SettingsRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.vibrate.HapticFeedback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentReportModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostReportModel
|
||||
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.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ReportListViewModel(
|
||||
private val communityId: Int,
|
||||
private val mvi: DefaultMviModel<ReportListMviModel.Intent, ReportListMviModel.UiState, ReportListMviModel.Effect>,
|
||||
private val identityRepository: IdentityRepository,
|
||||
private val postRepository: PostRepository,
|
||||
private val commentRepository: CommentRepository,
|
||||
private val themeRepository: ThemeRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val hapticFeedback: HapticFeedback,
|
||||
) : ReportListMviModel,
|
||||
MviModel<ReportListMviModel.Intent, ReportListMviModel.UiState, ReportListMviModel.Effect> by mvi {
|
||||
|
||||
private var currentPage = 1
|
||||
|
||||
override fun onStarted() {
|
||||
mvi.onStarted()
|
||||
mvi.scope?.launch {
|
||||
themeRepository.postLayout.onEach { layout ->
|
||||
mvi.updateState { it.copy(postLayout = layout) }
|
||||
}.launchIn(this)
|
||||
settingsRepository.currentSettings.onEach { settings ->
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
autoLoadImages = settings.autoLoadImages,
|
||||
swipeActionsEnabled = settings.enableSwipeActions,
|
||||
)
|
||||
}
|
||||
}.launchIn(this)
|
||||
|
||||
if (uiState.value.postReports.isEmpty()) {
|
||||
refresh(initial = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun reduce(intent: ReportListMviModel.Intent) {
|
||||
when (intent) {
|
||||
is ReportListMviModel.Intent.ChangeSection -> changeSection(intent.value)
|
||||
is ReportListMviModel.Intent.ChangeUnresolvedOnly -> changeUnresolvedOnly(intent.value)
|
||||
ReportListMviModel.Intent.Refresh -> refresh()
|
||||
ReportListMviModel.Intent.LoadNextPage -> mvi.scope?.launch(Dispatchers.IO) {
|
||||
loadNextPage()
|
||||
}
|
||||
|
||||
is ReportListMviModel.Intent.ResolveComment -> mvi.uiState.value.commentReports
|
||||
.firstOrNull { it.id == intent.id }?.also {
|
||||
resolve(it)
|
||||
}
|
||||
|
||||
is ReportListMviModel.Intent.ResolvePost -> mvi.uiState.value.postReports
|
||||
.firstOrNull { it.id == intent.id }?.also {
|
||||
resolve(it)
|
||||
}
|
||||
|
||||
ReportListMviModel.Intent.HapticIndication -> hapticFeedback.vibrate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeSection(section: ReportListSection) {
|
||||
currentPage = 1
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
section = section,
|
||||
canFetchMore = true,
|
||||
)
|
||||
}
|
||||
refresh(initial = true)
|
||||
}
|
||||
|
||||
private fun changeUnresolvedOnly(value: Boolean) {
|
||||
mvi.updateState {
|
||||
it.copy(unresolvedOnly = value)
|
||||
}
|
||||
refresh(initial = true)
|
||||
}
|
||||
|
||||
private fun refresh(initial: Boolean = false) {
|
||||
currentPage = 1
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
canFetchMore = true,
|
||||
refreshing = true,
|
||||
initial = initial,
|
||||
)
|
||||
}
|
||||
mvi.scope?.launch {
|
||||
loadNextPage()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadNextPage() {
|
||||
val currentState = mvi.uiState.value
|
||||
if (!currentState.canFetchMore || currentState.loading) {
|
||||
mvi.updateState { it.copy(refreshing = false) }
|
||||
return
|
||||
}
|
||||
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
mvi.updateState { it.copy(loading = true) }
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val refreshing = currentState.refreshing
|
||||
val section = currentState.section
|
||||
val unresolvedOnly = currentState.unresolvedOnly
|
||||
if (section == ReportListSection.Posts) {
|
||||
val itemList = postRepository.getReports(
|
||||
auth = auth,
|
||||
communityId = communityId,
|
||||
page = currentPage,
|
||||
unresolvedOnly = unresolvedOnly,
|
||||
)
|
||||
val commentReports =
|
||||
if (currentPage == 1 && currentState.commentReports.isEmpty()) {
|
||||
// this is needed because otherwise on first selector change
|
||||
// the lazy column scrolls back to top (it must have an empty data set)
|
||||
commentRepository.getReports(
|
||||
auth = auth,
|
||||
communityId = communityId,
|
||||
page = currentPage,
|
||||
unresolvedOnly = unresolvedOnly,
|
||||
).orEmpty()
|
||||
} else {
|
||||
currentState.commentReports
|
||||
}
|
||||
mvi.updateState {
|
||||
val postReports = if (refreshing) {
|
||||
itemList.orEmpty()
|
||||
} else {
|
||||
it.postReports + itemList.orEmpty()
|
||||
}
|
||||
it.copy(
|
||||
postReports = postReports,
|
||||
commentReports = commentReports,
|
||||
loading = false,
|
||||
canFetchMore = itemList?.isEmpty() != true,
|
||||
refreshing = false,
|
||||
initial = false,
|
||||
)
|
||||
}
|
||||
if (!itemList.isNullOrEmpty()) {
|
||||
currentPage++
|
||||
}
|
||||
} else {
|
||||
val itemList = commentRepository.getReports(
|
||||
auth = auth,
|
||||
communityId = communityId,
|
||||
page = currentPage,
|
||||
unresolvedOnly = unresolvedOnly,
|
||||
)
|
||||
|
||||
mvi.updateState {
|
||||
val commentReports = if (refreshing) {
|
||||
itemList.orEmpty()
|
||||
} else {
|
||||
it.commentReports + itemList.orEmpty()
|
||||
}
|
||||
it.copy(
|
||||
commentReports = commentReports,
|
||||
loading = false,
|
||||
canFetchMore = itemList?.isEmpty() != true,
|
||||
refreshing = false,
|
||||
initial = false,
|
||||
)
|
||||
}
|
||||
if (!itemList.isNullOrEmpty()) {
|
||||
currentPage++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolve(report: PostReportModel) {
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
mvi.updateState { it.copy(asyncInProgress = true) }
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newReport = postRepository.resolveReport(
|
||||
reportId = report.id,
|
||||
auth = auth,
|
||||
resolved = !report.resolved
|
||||
)
|
||||
mvi.updateState { it.copy(asyncInProgress = false) }
|
||||
if (newReport != null) {
|
||||
if (uiState.value.unresolvedOnly && newReport.resolved) {
|
||||
handleReporDelete(newReport)
|
||||
} else {
|
||||
handleReportUpdate(newReport)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolve(report: CommentReportModel) {
|
||||
mvi.scope?.launch(Dispatchers.IO) {
|
||||
mvi.updateState { it.copy(asyncInProgress = true) }
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val newReport = commentRepository.resolveReport(
|
||||
reportId = report.id,
|
||||
auth = auth,
|
||||
resolved = !report.resolved
|
||||
)
|
||||
mvi.updateState { it.copy(asyncInProgress = false) }
|
||||
if (newReport != null) {
|
||||
if (uiState.value.unresolvedOnly && newReport.resolved) {
|
||||
handleReporDelete(newReport)
|
||||
} else {
|
||||
handleReportUpdate(newReport)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReportUpdate(report: PostReportModel) {
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
postReports = it.postReports.map { r ->
|
||||
if (r.id == report.id) {
|
||||
report
|
||||
} else {
|
||||
r
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReportUpdate(report: CommentReportModel) {
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
commentReports = it.commentReports.map { r ->
|
||||
if (r.id == report.id) {
|
||||
report
|
||||
} else {
|
||||
r
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReporDelete(report: PostReportModel) {
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
postReports = it.postReports.filter { r -> r.id != report.id }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReporDelete(report: CommentReportModel) {
|
||||
mvi.updateState {
|
||||
it.copy(
|
||||
commentReports = it.commentReports.filter { r -> r.id != report.id }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,8 @@ 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.remove.RemoveMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.reportlist.ReportListMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.selectcommunity.SelectCommunityMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailMviModel
|
||||
@ -46,8 +48,9 @@ actual fun getPostDetailViewModel(
|
||||
post: PostModel,
|
||||
otherInstance: String,
|
||||
highlightCommentId: Int?,
|
||||
isModerator: Boolean,
|
||||
): PostDetailMviModel =
|
||||
CommonUiViewModelHelper.getPostDetailModel(post, otherInstance, highlightCommentId)
|
||||
CommonUiViewModelHelper.getPostDetailModel(post, otherInstance, highlightCommentId, isModerator)
|
||||
|
||||
actual fun getCommunityDetailViewModel(
|
||||
community: CommunityModel,
|
||||
@ -96,6 +99,15 @@ actual fun getCreateReportViewModel(
|
||||
actual fun getSelectCommunityViewModel(): SelectCommunityMviModel =
|
||||
CommonUiViewModelHelper.selectCommunityViewModel
|
||||
|
||||
actual fun getRemoveViewModel(
|
||||
postId: Int?,
|
||||
commentId: Int?,
|
||||
): RemoveMviModel = CommonUiViewModelHelper.getRemoveModel(postId, commentId)
|
||||
|
||||
actual fun getReportListViewModel(
|
||||
communityId: Int,
|
||||
): ReportListMviModel = CommonUiViewModelHelper.getReportListViewModel(communityId)
|
||||
|
||||
object CommonUiViewModelHelper : KoinComponent {
|
||||
|
||||
val navigationCoordinator: NavigationCoordinator by inject()
|
||||
@ -110,9 +122,10 @@ object CommonUiViewModelHelper : KoinComponent {
|
||||
post: PostModel,
|
||||
otherInstance: String,
|
||||
highlightCommentId: Int?,
|
||||
isModerator: Boolean,
|
||||
): PostDetailMviModel {
|
||||
val model: PostDetailMviModel by inject(
|
||||
parameters = { parametersOf(post, otherInstance, highlightCommentId) },
|
||||
parameters = { parametersOf(post, otherInstance, highlightCommentId, isModerator) },
|
||||
)
|
||||
return model
|
||||
}
|
||||
@ -182,6 +195,25 @@ object CommonUiViewModelHelper : KoinComponent {
|
||||
)
|
||||
return model
|
||||
}
|
||||
|
||||
fun getRemoveModel(
|
||||
postId: Int?,
|
||||
commentId: Int?,
|
||||
): RemoveMviModel {
|
||||
val model: RemoveMviModel by inject(
|
||||
parameters = { parametersOf(postId, commentId) }
|
||||
)
|
||||
return model
|
||||
}
|
||||
|
||||
fun getReportListViewModel(
|
||||
communityId: Int,
|
||||
): ReportListMviModel {
|
||||
val model: ReportListMviModel by inject(
|
||||
parameters = { parametersOf(communityId) }
|
||||
)
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -41,4 +41,7 @@ sealed interface NotificationCenterEvent {
|
||||
data class MultiCommunityCreated(val model: MultiCommunityModel) : NotificationCenterEvent
|
||||
data object CloseDialog : NotificationCenterEvent
|
||||
data class SelectCommunity(val model: CommunityModel) : NotificationCenterEvent
|
||||
data class PostRemoved(val model: PostModel) : NotificationCenterEvent
|
||||
data class CommentRemoved(val model: CommentModel) : NotificationCenterEvent
|
||||
data class ChangeReportListType(val unresolvedOnly: Boolean) : NotificationCenterEvent
|
||||
}
|
@ -18,6 +18,8 @@ data class CommentModel(
|
||||
val updateDate: String? = null,
|
||||
val comments: Int? = null,
|
||||
val path: String = "",
|
||||
val distinguished: Boolean = false,
|
||||
val removed: Boolean = false,
|
||||
@Transient
|
||||
val expanded: Boolean = true,
|
||||
@Transient
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data
|
||||
|
||||
data class CommentReportModel(
|
||||
val id: Int = 0,
|
||||
val creator: UserModel? = null,
|
||||
val commentId: Int = 0,
|
||||
val postId: Int = 0,
|
||||
val originalText: String? = null,
|
||||
val reason: String? = null,
|
||||
val resolved: Boolean = false,
|
||||
val resolver: UserModel? = null,
|
||||
val publishDate: String? = null,
|
||||
val updateDate: String? = null,
|
||||
)
|
@ -24,6 +24,9 @@ data class PostModel(
|
||||
val nsfw: Boolean = false,
|
||||
val read: Boolean = false,
|
||||
val crossPosts: List<PostModel> = emptyList(),
|
||||
val featuredCommunity: Boolean = false,
|
||||
val removed: Boolean = false,
|
||||
val locked: Boolean = false,
|
||||
) : JavaSerializable
|
||||
|
||||
val PostModel.imageUrl: String
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.looksLikeAnImage
|
||||
|
||||
data class PostReportModel(
|
||||
val id: Int = 0,
|
||||
val creator: UserModel? = null,
|
||||
val postId: Int = 0,
|
||||
val reason: String? = null,
|
||||
val originalTitle: String? = null,
|
||||
val originalText: String? = null,
|
||||
val originalUrl: String? = null,
|
||||
val thumbnailUrl: String? = null,
|
||||
val resolved: Boolean = false,
|
||||
val resolver: UserModel? = null,
|
||||
val publishDate: String? = null,
|
||||
val updateDate: String? = null,
|
||||
)
|
||||
|
||||
val PostReportModel.imageUrl: String
|
||||
get() = originalUrl?.takeIf { it.looksLikeAnImage }?.takeIf { it.isNotEmpty() } ?: run {
|
||||
thumbnailUrl
|
||||
}.orEmpty()
|
@ -4,10 +4,14 @@ 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.DistinguishCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.EditCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.RemoveCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ResolveCommentReportForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SaveCommentForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
|
||||
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.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
||||
@ -260,4 +264,77 @@ class CommentRepository(
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun remove(
|
||||
commentId: Int,
|
||||
auth: String,
|
||||
removed: Boolean,
|
||||
reason: String,
|
||||
): CommentModel? = runCatching {
|
||||
val data = RemoveCommentForm(
|
||||
commentId = commentId,
|
||||
removed = removed,
|
||||
reason = reason,
|
||||
auth = auth,
|
||||
)
|
||||
val response = services.comment.remove(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
response.body()?.commentView?.toModel()
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun distinguish(
|
||||
commentId: Int,
|
||||
auth: String,
|
||||
distinguished: Boolean,
|
||||
): CommentModel? = runCatching {
|
||||
val data = DistinguishCommentForm(
|
||||
commentId = commentId,
|
||||
distinguished = distinguished,
|
||||
auth = auth,
|
||||
)
|
||||
val response = services.comment.distinguish(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
response.body()?.commentView?.toModel()
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun getReports(
|
||||
auth: String,
|
||||
communityId: Int,
|
||||
page: Int,
|
||||
limit: Int = PostRepository.DEFAULT_PAGE_SIZE,
|
||||
unresolvedOnly: Boolean = true,
|
||||
): List<CommentReportModel>? = runCatching {
|
||||
val response = services.comment.listReports(
|
||||
authHeader = auth.toAuthHeader(),
|
||||
auth = auth,
|
||||
communityId = communityId,
|
||||
page = page,
|
||||
limit = limit,
|
||||
unresolvedOnly = unresolvedOnly
|
||||
)
|
||||
response.body()?.commentReports?.map {
|
||||
it.toModel()
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun resolveReport(
|
||||
reportId: Int,
|
||||
auth: String,
|
||||
resolved: Boolean,
|
||||
): CommentReportModel? = runCatching {
|
||||
val data = ResolveCommentReportForm(
|
||||
reportId = reportId,
|
||||
resolved = resolved,
|
||||
auth = auth,
|
||||
)
|
||||
val response = services.comment.resolveReport(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
response.body()?.commentReportView?.toModel()
|
||||
}.getOrNull()
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SearchResult
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SearchResultType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
||||
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.toDto
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toModel
|
||||
@ -101,6 +102,21 @@ class CommunityRepository(
|
||||
response?.communityView?.toModel()
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun getModerators(
|
||||
auth: String? = null,
|
||||
id: Int? = null,
|
||||
): List<UserModel> = runCatching {
|
||||
val response = services.community.get(
|
||||
authHeader = auth.toAuthHeader(),
|
||||
auth = auth,
|
||||
id = id,
|
||||
).body()
|
||||
response?.moderators?.map {
|
||||
it.moderator.toModel()
|
||||
}.orEmpty()
|
||||
}.getOrElse { emptyList() }
|
||||
|
||||
|
||||
suspend fun subscribe(
|
||||
auth: String? = null,
|
||||
id: Int,
|
||||
|
@ -5,11 +5,17 @@ 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.FeaturePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.LockPostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.MarkPostAsReadForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PostFeatureType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.RemovePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ResolvePostReportForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SavePostForm
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostReportModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toAuthHeader
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils.toDto
|
||||
@ -259,4 +265,95 @@ class PostRepository(
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun featureInCommunity(
|
||||
postId: Int,
|
||||
auth: String,
|
||||
featured: Boolean,
|
||||
): PostModel? = runCatching {
|
||||
val data = FeaturePostForm(
|
||||
postId = postId,
|
||||
auth = auth,
|
||||
featured = featured,
|
||||
featureType = PostFeatureType.Community,
|
||||
)
|
||||
val response = services.post.feature(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
response.body()?.postView?.toModel()
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun lock(
|
||||
postId: Int,
|
||||
auth: String,
|
||||
locked: Boolean,
|
||||
): PostModel? = runCatching {
|
||||
val data = LockPostForm(
|
||||
postId = postId,
|
||||
auth = auth,
|
||||
locked = locked,
|
||||
)
|
||||
val response = services.post.lock(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
response.body()?.postView?.toModel()
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun remove(
|
||||
postId: Int,
|
||||
auth: String,
|
||||
reason: String,
|
||||
removed: Boolean,
|
||||
): PostModel? = runCatching {
|
||||
val data = RemovePostForm(
|
||||
postId = postId,
|
||||
auth = auth,
|
||||
removed = removed,
|
||||
reason = reason,
|
||||
)
|
||||
val response = services.post.remove(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
response.body()?.postView?.toModel()
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun getReports(
|
||||
auth: String,
|
||||
communityId: Int,
|
||||
page: Int,
|
||||
limit: Int = DEFAULT_PAGE_SIZE,
|
||||
unresolvedOnly: Boolean = true,
|
||||
): List<PostReportModel>? = runCatching {
|
||||
val response = services.post.listReports(
|
||||
authHeader = auth.toAuthHeader(),
|
||||
auth = auth,
|
||||
communityId = communityId,
|
||||
page = page,
|
||||
limit = limit,
|
||||
unresolvedOnly = unresolvedOnly
|
||||
)
|
||||
response.body()?.postReports?.map {
|
||||
it.toModel()
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun resolveReport(
|
||||
reportId: Int,
|
||||
auth: String,
|
||||
resolved: Boolean,
|
||||
): PostReportModel? = runCatching {
|
||||
val data = ResolvePostReportForm(
|
||||
reportId = reportId,
|
||||
auth = auth,
|
||||
resolved = resolved,
|
||||
)
|
||||
val response = services.post.resolveReport(
|
||||
form = data,
|
||||
authHeader = auth.toAuthHeader(),
|
||||
)
|
||||
response.body()?.postReportView?.toModel()
|
||||
}.getOrNull()
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.utils
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentReplyView
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.CommentReportView
|
||||
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.Community
|
||||
@ -12,6 +13,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.Person
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PersonAggregates
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PersonMentionView
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PersonView
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PostReportView
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PostView
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.PrivateMessageView
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SearchType
|
||||
@ -31,10 +33,12 @@ import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SortType.TopWeek
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.SortType.TopYear
|
||||
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.CommentReportModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostReportModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PrivateMessageModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SearchResultType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
||||
@ -117,6 +121,9 @@ internal fun PostView.toModel() = PostModel(
|
||||
nsfw = post.nsfw,
|
||||
embedVideoUrl = post.embedVideoUrl,
|
||||
read = read,
|
||||
featuredCommunity = post.featuredCommunity,
|
||||
removed = post.removed,
|
||||
locked = post.locked,
|
||||
)
|
||||
|
||||
internal fun CommentView.toModel() = CommentModel(
|
||||
@ -134,6 +141,8 @@ internal fun CommentView.toModel() = CommentModel(
|
||||
postId = comment.postId,
|
||||
comments = counts.childCount,
|
||||
path = comment.path,
|
||||
distinguished = comment.distinguished,
|
||||
removed = comment.removed,
|
||||
)
|
||||
|
||||
internal fun Community.toModel() = CommunityModel(
|
||||
@ -180,6 +189,9 @@ internal fun PersonMentionView.toModel() = PersonMentionModel(
|
||||
updateDate = post.updated,
|
||||
nsfw = post.nsfw,
|
||||
embedVideoUrl = post.embedVideoUrl,
|
||||
featuredCommunity = post.featuredCommunity,
|
||||
removed = post.removed,
|
||||
locked = post.locked,
|
||||
),
|
||||
comment = CommentModel(
|
||||
id = comment.id,
|
||||
@ -188,6 +200,8 @@ internal fun PersonMentionView.toModel() = PersonMentionModel(
|
||||
community = community.toModel(),
|
||||
publishDate = comment.published,
|
||||
updateDate = comment.updated,
|
||||
distinguished = comment.distinguished,
|
||||
removed = comment.removed,
|
||||
),
|
||||
creator = creator.toModel(),
|
||||
community = community.toModel(),
|
||||
@ -218,7 +232,10 @@ internal fun CommentReplyView.toModel() = PersonMentionModel(
|
||||
publishDate = post.published,
|
||||
updateDate = post.updated,
|
||||
nsfw = post.nsfw,
|
||||
embedVideoUrl = post.embedVideoUrl
|
||||
embedVideoUrl = post.embedVideoUrl,
|
||||
featuredCommunity = post.featuredCommunity,
|
||||
removed = post.removed,
|
||||
locked = post.locked,
|
||||
),
|
||||
comment = CommentModel(
|
||||
id = comment.id,
|
||||
@ -227,6 +244,8 @@ internal fun CommentReplyView.toModel() = PersonMentionModel(
|
||||
community = community.toModel(),
|
||||
publishDate = comment.published,
|
||||
updateDate = comment.updated,
|
||||
distinguished = comment.distinguished,
|
||||
removed = comment.removed,
|
||||
),
|
||||
creator = creator.toModel(),
|
||||
community = community.toModel(),
|
||||
@ -265,3 +284,31 @@ private fun String.communityToInstanceUrl(): String {
|
||||
}
|
||||
return this.substring(0, index)
|
||||
}
|
||||
|
||||
internal fun PostReportView.toModel() = PostReportModel(
|
||||
id = postReport.id,
|
||||
postId = post.id,
|
||||
reason = postReport.reason,
|
||||
creator = creator.toModel(),
|
||||
publishDate = postReport.published,
|
||||
resolved = postReport.resolved,
|
||||
resolver = resolver?.toModel(),
|
||||
originalText = postReport.originalPostBody,
|
||||
originalTitle = postReport.originalPostName,
|
||||
originalUrl = postReport.originalPostUrl,
|
||||
thumbnailUrl = post.thumbnailUrl,
|
||||
updateDate = postReport.updated,
|
||||
)
|
||||
|
||||
internal fun CommentReportView.toModel() = CommentReportModel(
|
||||
id = commentReport.id,
|
||||
postId = comment.postId,
|
||||
commentId = comment.id,
|
||||
reason = commentReport.reason,
|
||||
creator = creator.toModel(),
|
||||
publishDate = commentReport.published,
|
||||
resolved = commentReport.resolved,
|
||||
resolver = resolver?.toModel(),
|
||||
originalText = commentReport.originalCommentText,
|
||||
updateDate = commentReport.updated,
|
||||
)
|
@ -246,4 +246,18 @@
|
||||
<string name="settings_zombie_mode_scroll_amount">Zombie mode scroll amount</string>
|
||||
<string name="settings_mark_as_read_while_scrolling">Mark posts as read while scrolling</string>
|
||||
<string name="action_quote">Quote</string>
|
||||
<string name="mod_action_open_reports">Open reports</string>
|
||||
<string name="mod_action_mark_as_featured">Mark as featured</string>
|
||||
<string name="mod_action_unmark_as_featured">Unmark as featured</string>
|
||||
<string name="mod_action_lock">Lock</string>
|
||||
<string name="mod_action_unlock">Unlock</string>
|
||||
<string name="mod_action_remove">Remove</string>
|
||||
<string name="mod_action_mark_as_distinguished">Mark as distinguished</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Unmark as distinguished</string>
|
||||
<string name="report_list_title">Report list</string>
|
||||
<string name="report_list_type_title">Report list type</string>
|
||||
<string name="report_list_type_all">All</string>
|
||||
<string name="report_list_type_unresolved">Unresolved</string>
|
||||
<string name="report_action_resolve">Resolve</string>
|
||||
<string name="report_action_unresolve">Unresolve</string>
|
||||
</resources>
|
@ -249,4 +249,19 @@
|
||||
превъртате
|
||||
</string>
|
||||
<string name="action_quote">цитат</string>
|
||||
<string name="mod_action_open_reports">Отваряне на отчети</string>
|
||||
<string name="mod_action_mark_as_featured">Маркиране като представено</string>
|
||||
<string name="mod_action_unmark_as_featured">Демаркиране като представено</string>
|
||||
<string name="mod_action_lock">Заключване</string>
|
||||
<string name="mod_action_unlock">Отключване</string>
|
||||
<string name="mod_action_remove">Премахване</string>
|
||||
<string name="mod_action_mark_as_distinguished">Маркиране като отличен</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Премахване на отметката като разграничен
|
||||
</string>
|
||||
<string name="report_list_title">Списък с отчети</string>
|
||||
<string name="report_list_type_title">Тип списък на отчет</string>
|
||||
<string name="report_list_type_all">Всички</string>
|
||||
<string name="report_list_type_unresolved">Неразрешено</string>
|
||||
<string name="report_action_resolve">Разрешаване</string>
|
||||
<string name="report_action_unresolve">Отмяна на разрешаването</string>
|
||||
</resources>
|
@ -235,4 +235,18 @@
|
||||
rolování
|
||||
</string>
|
||||
<string name="action_quote">Citát</string>
|
||||
<string name="mod_action_open_reports">Otevřít přehledy</string>
|
||||
<string name="mod_action_mark_as_featured">Označit jako doporučené</string>
|
||||
<string name="mod_action_unmark_as_featured">Zrušit označení jako doporučené</string>
|
||||
<string name="mod_action_lock">Zamknout</string>
|
||||
<string name="mod_action_unlock">Odemknout</string>
|
||||
<string name="mod_action_remove">Odebrat</string>
|
||||
<string name="mod_action_mark_as_distinguished">Označit jako význačné</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Zrušit označení jako rozlišující</string>
|
||||
<string name="report_list_title">Seznam přehledů</string>
|
||||
<string name="report_list_type_title">Typ seznamu přehledů</string>
|
||||
<string name="report_list_type_all">Vše</string>
|
||||
<string name="report_list_type_unresolved">Nevyřešeno</string>
|
||||
<string name="report_action_resolve">Vyřešit</string>
|
||||
<string name="report_action_unresolve">Nevyřešit</string>
|
||||
</resources>
|
@ -224,4 +224,18 @@
|
||||
<string name="settings_mark_as_read_while_scrolling">Marker indlæg som læst, mens du ruller
|
||||
</string>
|
||||
<string name="action_quote">Citere</string>
|
||||
<string name="mod_action_open_reports">Åbne rapporter</string>
|
||||
<string name="mod_action_mark_as_featured">Markér som fremhævet</string>
|
||||
<string name="mod_action_unmark_as_featured">Fjern markering som fremhævet</string>
|
||||
<string name="mod_action_lock">Lås</string>
|
||||
<string name="mod_action_unlock">Lås op</string>
|
||||
<string name="mod_action_remove">Fjern</string>
|
||||
<string name="mod_action_mark_as_distinguished">Markér som distinguished</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Fjern markering som distinguished</string>
|
||||
<string name="report_list_title">Rapportliste</string>
|
||||
<string name="report_list_type_title">Rapportlistetype</string>
|
||||
<string name="report_list_type_all">Alle</string>
|
||||
<string name="report_list_type_unresolved">Ikke løst</string>
|
||||
<string name="report_action_resolve">Løs</string>
|
||||
<string name="report_action_unresolve">Løs op</string>
|
||||
</resources>
|
@ -228,4 +228,18 @@
|
||||
gelesen
|
||||
</string>
|
||||
<string name="action_quote">Zitat</string>
|
||||
<string name="mod_action_open_reports">Berichte öffnen</string>
|
||||
<string name="mod_action_mark_as_featured">Als vorgestellt markieren</string>
|
||||
<string name="mod_action_unmark_as_featured">Markierung als hervorgehoben aufheben</string>
|
||||
<string name="mod_action_lock">Sperre</string>
|
||||
<string name="mod_action_unlock">Entsperren</string>
|
||||
<string name="mod_action_remove">Entfernen</string>
|
||||
<string name="mod_action_mark_as_distinguished">Als ausgezeichnet markieren</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Markierung als ausgezeichnet aufheben</string>
|
||||
<string name="report_list_title">Berichtsliste</string>
|
||||
<string name="report_list_type_title">Berichtslistentyp</string>
|
||||
<string name="report_list_type_all">Alle</string>
|
||||
<string name="report_list_type_unresolved">Ungelöst</string>
|
||||
<string name="report_action_resolve">Auflösen</string>
|
||||
<string name="report_action_unresolve">Auflösung</string>
|
||||
</resources>
|
@ -232,4 +232,19 @@
|
||||
την κύλιση
|
||||
</string>
|
||||
<string name="action_quote">Παράσου</string>
|
||||
<string name="mod_action_open_reports">Άνοιγμα αναφορών</string>
|
||||
<string name="mod_action_mark_as_featured">Επισήμανση ως επιλεγμένου</string>
|
||||
<string name="mod_action_unmark_as_featured">Κατάργηση επισήμανσης ως επιλεγμένου</string>
|
||||
<string name="mod_action_lock">Κλείδωμα</string>
|
||||
<string name="mod_action_unlock">Ξεκλείδωμα</string>
|
||||
<string name="mod_action_remove">Κατάργηση</string>
|
||||
<string name="mod_action_mark_as_distinguished">Επισήμανση ως διακεκριμένου</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Κατάργηση επισήμανσης ως διακεκριμένου
|
||||
</string>
|
||||
<string name="report_list_title">Λίστα αναφορών</string>
|
||||
<string name="report_list_type_title">Τύπος λίστας αναφοράς</string>
|
||||
<string name="report_list_type_all">Όλα</string>
|
||||
<string name="report_list_type_unresolved">Μη επιλύθηκε</string>
|
||||
<string name="report_action_resolve">Επίλυση</string>
|
||||
<string name="report_action_unresolve">Κατάργηση επίλυσης</string>
|
||||
</resources>
|
@ -230,4 +230,18 @@
|
||||
desplazarse
|
||||
</string>
|
||||
<string name="action_quote">Citar</string>
|
||||
<string name="mod_action_open_reports">Ver informes</string>
|
||||
<string name="mod_action_mark_as_featured">Marcar como destacado</string>
|
||||
<string name="mod_action_unmark_as_featured">Marcar como no destacado</string>
|
||||
<string name="mod_action_lock">Bloquear</string>
|
||||
<string name="mod_action_unlock">Desbloquear</string>
|
||||
<string name="mod_action_remove">Suprimir</string>
|
||||
<string name="mod_action_mark_as_distinguished">Marcar como distinguido</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Marcar como no distinguido</string>
|
||||
<string name="report_list_title">Lista de informes</string>
|
||||
<string name="report_list_type_title">Tipo lista de informes</string>
|
||||
<string name="report_list_type_all">Todos</string>
|
||||
<string name="report_list_type_unresolved">No resueltos</string>
|
||||
<string name="report_action_resolve">Marcar como resuelto</string>
|
||||
<string name="report_action_unresolve">Marcar como no resuelto</string>
|
||||
</resources>
|
@ -243,4 +243,18 @@
|
||||
<string name="settings_mark_as_read_while_scrolling">Märkige postitused kerimise ajal loetuks
|
||||
</string>
|
||||
<string name="action_quote">Tsiteeri</string>
|
||||
<string name="mod_action_open_reports">Ava aruanded</string>
|
||||
<string name="mod_action_mark_as_featured">Märkige esiletõstetuks</string>
|
||||
<string name="mod_action_unmark_as_featured">Tühista esiletõstetud märkimine</string>
|
||||
<string name="mod_action_lock">Lukusta</string>
|
||||
<string name="mod_action_unlock">Ava lukust</string>
|
||||
<string name="mod_action_remove">Eemalda</string>
|
||||
<string name="mod_action_mark_as_distinguished">Märgi eristatuks</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Tühista eristatuks märgitud</string>
|
||||
<string name="report_list_title">Aruannete loend</string>
|
||||
<string name="report_list_type_title">Aruande loendi tüüp</string>
|
||||
<string name="report_list_type_all">Kõik</string>
|
||||
<string name="report_list_type_unresolved">Lahendamata</string>
|
||||
<string name="report_action_resolve">Lahenda</string>
|
||||
<string name="report_action_unresolve">Tühista lahendamine</string>
|
||||
</resources>
|
@ -234,4 +234,18 @@
|
||||
<string name="settings_mark_as_read_while_scrolling">Merkitse viestit luetuiksi vieritettäessä
|
||||
</string>
|
||||
<string name="action_quote">Lainata</string>
|
||||
<string name="mod_action_open_reports">Avaa raportit</string>
|
||||
<string name="mod_action_mark_as_featured">Merkitse suositeltavaksi</string>
|
||||
<string name="mod_action_unmark_as_featured">Poista suositellun merkintä</string>
|
||||
<string name="mod_action_lock">Lukitse</string>
|
||||
<string name="mod_action_unlock">Avaa lukitus</string>
|
||||
<string name="mod_action_remove">Poista</string>
|
||||
<string name="mod_action_mark_as_distinguished">Merkitse erotetuksi</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Poista erotetuksi</string>
|
||||
<string name="report_list_title">Raporttiluettelo</string>
|
||||
<string name="report_list_type_title">Raporttiluettelon tyyppi</string>
|
||||
<string name="report_list_type_all">Kaikki</string>
|
||||
<string name="report_list_type_unresolved">Ratkaisematon</string>
|
||||
<string name="report_action_resolve">Ratkaise</string>
|
||||
<string name="report_action_unresolve">Ei ratkaise</string>
|
||||
</resources>
|
@ -228,4 +228,18 @@
|
||||
défilement
|
||||
</string>
|
||||
<string name="action_quote">Citer</string>
|
||||
<string name="mod_action_open_reports">Ouvrir les rapports</string>
|
||||
<string name="mod_action_mark_as_featured">Marquer comme présenté</string>
|
||||
<string name="mod_action_unmark_as_featured">Ne plus marquer comme présenté</string>
|
||||
<string name="mod_action_lock">Verrouiller</string>
|
||||
<string name="mod_action_unlock">Déverrouiller</string>
|
||||
<string name="mod_action_remove">Supprimer</string>
|
||||
<string name="mod_action_mark_as_distinguished">Marquer comme distingué</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Ne plus marquer comme distingué</string>
|
||||
<string name="report_list_title">Liste des rapports</string>
|
||||
<string name="report_list_type_title">Type de liste de rapports</string>
|
||||
<string name="report_list_type_all">Tous</string>
|
||||
<string name="report_list_type_unresolved">Non résolu</string>
|
||||
<string name="report_action_resolve">Résoudre</string>
|
||||
<string name="report_action_unresolve">Ne pas résoudre</string>
|
||||
</resources>
|
@ -256,4 +256,18 @@
|
||||
scrollú
|
||||
</string>
|
||||
<string name="action_quote">Athfhriotail</string>
|
||||
<string name="mod_action_open_reports">Oscail tuarascálacha</string>
|
||||
<string name="mod_action_mark_as_featured">Marcáil mar atá i gceist</string>
|
||||
<string name="mod_action_unmark_as_featured">Dímharc mar atá i gceist</string>
|
||||
<string name="mod_action_lock">Glasáil</string>
|
||||
<string name="mod_action_unlock">Díghlasáil</string>
|
||||
<string name="mod_action_remove">Bain</string>
|
||||
<string name="mod_action_mark_as_distinguished">Marcáil mar aitheanta</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Dímharcáil mar shainaitheanta</string>
|
||||
<string name="report_list_title">Liosta tuairisce</string>
|
||||
<string name="report_list_type_title">Cineál liosta tuairisce</string>
|
||||
<string name="report_list_type_all">Gach</string>
|
||||
<string name="report_list_type_unresolved">Gan réiteach</string>
|
||||
<string name="report_action_resolve">Réitigh</string>
|
||||
<string name="report_action_unresolve">Díréitigh</string>
|
||||
</resources>
|
@ -246,4 +246,18 @@
|
||||
pomicanja
|
||||
</string>
|
||||
<string name="action_quote">Citat</string>
|
||||
<string name="mod_action_open_reports">Otvori izvješća</string>
|
||||
<string name="mod_action_mark_as_featured">Označi kao istaknuto</string>
|
||||
<string name="mod_action_unmark_as_featured">Ukloni oznaku kao istaknuto</string>
|
||||
<string name="mod_action_lock">Zaključaj</string>
|
||||
<string name="mod_action_unlock">Otključaj</string>
|
||||
<string name="mod_action_remove">Ukloni</string>
|
||||
<string name="mod_action_mark_as_distinguished">Označi kao istaknuto</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Ukloni oznaku kao istaknuto</string>
|
||||
<string name="report_list_title">Popis izvješća</string>
|
||||
<string name="report_list_type_title">Vrsta popisa izvješća</string>
|
||||
<string name="report_list_type_all">Sve</string>
|
||||
<string name="report_list_type_unresolved">Neriješeno</string>
|
||||
<string name="report_action_resolve">Riješi</string>
|
||||
<string name="report_action_unresolve">Poništi rješavanje</string>
|
||||
</resources>
|
@ -243,4 +243,20 @@
|
||||
görgetés közben
|
||||
</string>
|
||||
<string name="action_quote">Idézet</string>
|
||||
<string name="mod_action_open_reports">Jelentések megnyitása</string>
|
||||
<string name="mod_action_mark_as_featured">Megjelölés kiemeltként</string>
|
||||
<string name="mod_action_unmark_as_featured">Kiemeltként való megjelölés eltávolítása</string>
|
||||
<string name="mod_action_lock">Zárolás</string>
|
||||
<string name="mod_action_unlock">Feloldás</string>
|
||||
<string name="mod_action_remove">Eltávolítás</string>
|
||||
<string name="mod_action_mark_as_distinguished">Megjelölés megkülönböztetettként</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Megkülönböztetettként való megjelölés
|
||||
eltávolítása
|
||||
</string>
|
||||
<string name="report_list_title">Jelentéslista</string>
|
||||
<string name="report_list_type_title">Jelentéslista típusa</string>
|
||||
<string name="report_list_type_all">Mind</string>
|
||||
<string name="report_list_type_unresolved">Feloldatlan</string>
|
||||
<string name="report_action_resolve">Megoldás</string>
|
||||
<string name="report_action_unresolve">Feloldás megszüntetése</string>
|
||||
</resources>
|
@ -227,4 +227,18 @@
|
||||
<string name="settings_mark_as_read_while_scrolling">Segna post come letti durante lo scroll
|
||||
</string>
|
||||
<string name="action_quote">Cita</string>
|
||||
<string name="mod_action_open_reports">Apri segnalazioni</string>
|
||||
<string name="mod_action_mark_as_featured">Segna come fissato</string>
|
||||
<string name="mod_action_unmark_as_featured">Segna come non fissato</string>
|
||||
<string name="mod_action_lock">Blocca</string>
|
||||
<string name="mod_action_unlock">Sblocca</string>
|
||||
<string name="mod_action_remove">Rimuovi</string>
|
||||
<string name="mod_action_mark_as_distinguished">Segna come distinto</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Segna come non distinto</string>
|
||||
<string name="report_list_title">Elenco segnalazioni</string>
|
||||
<string name="report_list_type_title">Tipo elenco segnalazioni</string>
|
||||
<string name="report_list_type_all">Tutte</string>
|
||||
<string name="report_list_type_unresolved">Non risolte</string>
|
||||
<string name="report_action_resolve">Segna come risolto</string>
|
||||
<string name="report_action_unresolve">Segna come non risolto</string>
|
||||
</resources>
|
@ -249,4 +249,18 @@
|
||||
skaitytus
|
||||
</string>
|
||||
<string name="action_quote">Citata</string>
|
||||
<string name="mod_action_open_reports">Atidaryti ataskaitas</string>
|
||||
<string name="mod_action_mark_as_featured">Pažymėti kaip siūlomą</string>
|
||||
<string name="mod_action_unmark_as_featured">Panaikinti kaip siūlomo žymėjimą</string>
|
||||
<string name="mod_action_lock">Užrakinti</string>
|
||||
<string name="mod_action_unlock">Atrakinti</string>
|
||||
<string name="mod_action_remove">Pašalinti</string>
|
||||
<string name="mod_action_mark_as_distinguished">Pažymėti kaip išskirtinį</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Panaikinti išskirtinio žymėjimą</string>
|
||||
<string name="report_list_title">Ataskaitų sąrašas</string>
|
||||
<string name="report_list_type_title">Ataskaitų sąrašo tipas</string>
|
||||
<string name="report_list_type_all">Visi</string>
|
||||
<string name="report_list_type_unresolved">Neišspręsta</string>
|
||||
<string name="report_action_resolve">Išspręsti</string>
|
||||
<string name="report_action_unresolve">Neišspręsti</string>
|
||||
</resources>
|
@ -248,4 +248,18 @@
|
||||
laikā
|
||||
</string>
|
||||
<string name="action_quote">Citāts</string>
|
||||
<string name="mod_action_open_reports">Atvērt pārskatus</string>
|
||||
<string name="mod_action_mark_as_featured">Atzīmēt kā piedāvātu</string>
|
||||
<string name="mod_action_unmark_as_featured">Noņemiet atzīmi no piedāvātā</string>
|
||||
<string name="mod_action_lock">Bloķēt</string>
|
||||
<string name="mod_action_unlock">Atbloķēt</string>
|
||||
<string name="mod_action_remove">Noņemt</string>
|
||||
<string name="mod_action_mark_as_distinguished">Atzīmēt kā atšķirīgu</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Noņemt atzīmes kā atšķirīgu</string>
|
||||
<string name="report_list_title">Pārskatu saraksts</string>
|
||||
<string name="report_list_type_title">Pārskatu saraksta veids</string>
|
||||
<string name="report_list_type_all">Visi</string>
|
||||
<string name="report_list_type_unresolved">Neatrisināts</string>
|
||||
<string name="report_action_resolve">Atrisināt</string>
|
||||
<string name="report_action_unresolve">Neatrisināts</string>
|
||||
</resources>
|
@ -235,4 +235,18 @@
|
||||
scrollen
|
||||
</string>
|
||||
<string name="action_quote">Citaat</string>
|
||||
<string name="mod_action_open_reports">Open rapporten</string>
|
||||
<string name="mod_action_mark_as_featured">Markeren als aanbevolen</string>
|
||||
<string name="mod_action_unmark_as_featured">Markeren als aanbevolen opheffen</string>
|
||||
<string name="mod_action_lock">Vergrendelen</string>
|
||||
<string name="mod_action_unlock">Ontgrendelen</string>
|
||||
<string name="mod_action_remove">Verwijderen</string>
|
||||
<string name="mod_action_mark_as_distinguished">Markeren als onderscheiden</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Markeren als onderscheidend</string>
|
||||
<string name="report_list_title">Rapportlijst</string>
|
||||
<string name="report_list_type_title">Rapportlijsttype</string>
|
||||
<string name="report_list_type_all">Alles</string>
|
||||
<string name="report_list_type_unresolved">Onopgelost</string>
|
||||
<string name="report_action_resolve">Oplossen</string>
|
||||
<string name="report_action_unresolve">Onopgelost</string>
|
||||
</resources>
|
@ -237,4 +237,18 @@
|
||||
<string name="settings_mark_as_read_while_scrolling">Merk innlegg som lest mens du ruller
|
||||
</string>
|
||||
<string name="action_quote">Sitat</string>
|
||||
<string name="mod_action_open_reports">Åpne rapporter</string>
|
||||
<string name="mod_action_mark_as_featured">Merk som fremhevet</string>
|
||||
<string name="mod_action_unmark_as_featured">Fjern merking som fremhevet</string>
|
||||
<string name="mod_action_lock">Lås</string>
|
||||
<string name="mod_action_unlock">Lås opp</string>
|
||||
<string name="mod_action_remove">Fjern</string>
|
||||
<string name="mod_action_mark_as_distinguished">Merk som distinguished</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Fjern merking som distinguished</string>
|
||||
<string name="report_list_title">Rapportliste</string>
|
||||
<string name="report_list_type_title">Rapportlistetype</string>
|
||||
<string name="report_list_type_all">Alle</string>
|
||||
<string name="report_list_type_unresolved">Uløst</string>
|
||||
<string name="report_action_resolve">Løs</string>
|
||||
<string name="report_action_unresolve">Løs opp</string>
|
||||
</resources>
|
@ -226,4 +226,18 @@
|
||||
przewijania
|
||||
</string>
|
||||
<string name="action_quote">Cytat</string>
|
||||
<string name="mod_action_open_reports">Otwórz raporty</string>
|
||||
<string name="mod_action_mark_as_featured">Oznacz jako polecane</string>
|
||||
<string name="mod_action_unmark_as_featured">Odznacz jako polecane</string>
|
||||
<string name="mod_action_lock">Zablokuj</string>
|
||||
<string name="mod_action_unlock">Odblokuj</string>
|
||||
<string name="mod_action_remove">Usuń</string>
|
||||
<string name="mod_action_mark_as_distinguished">Oznacz jako wyróżnione</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Odznacz jako wyróżnione</string>
|
||||
<string name="report_list_title">Lista raportów</string>
|
||||
<string name="report_list_type_title">Typ listy raportów</string>
|
||||
<string name="report_list_type_all">Wszystkie</string>
|
||||
<string name="report_list_type_unresolved">Nierozwiązany</string>
|
||||
<string name="report_action_resolve">Rozwiąż</string>
|
||||
<string name="report_action_unresolve">Cofnij rozwiązanie</string>
|
||||
</resources>
|
@ -225,4 +225,18 @@
|
||||
rolar
|
||||
</string>
|
||||
<string name="action_quote">Citar</string>
|
||||
<string name="mod_action_open_reports">Abrir relatórios</string>
|
||||
<string name="mod_action_mark_as_featured">Marcar como destacado</string>
|
||||
<string name="mod_action_unmark_as_featured">Desmarcar como destacado</string>
|
||||
<string name="mod_action_lock">Bloquear</string>
|
||||
<string name="mod_action_unlock">Desbloquear</string>
|
||||
<string name="mod_action_remove">Remover</string>
|
||||
<string name="mod_action_mark_as_distinguished">Marcar como distinto</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Desmarcar como distinto</string>
|
||||
<string name="report_list_title">Lista de relatórios</string>
|
||||
<string name="report_list_type_title">Tipo de lista de relatório</string>
|
||||
<string name="report_list_type_all">Todos</string>
|
||||
<string name="report_list_type_unresolved">Não resolvido</string>
|
||||
<string name="report_action_resolve">Resolver</string>
|
||||
<string name="report_action_unresolve">Não resolver</string>
|
||||
</resources>
|
@ -225,4 +225,18 @@
|
||||
<string name="settings_mark_as_read_while_scrolling">Marchează postările ca citite la derulare
|
||||
</string>
|
||||
<string name="action_quote">Citează</string>
|
||||
<string name="mod_action_open_reports">Deschide rapoarte</string>
|
||||
<string name="mod_action_mark_as_featured">Marchează ca recomandat</string>
|
||||
<string name="mod_action_unmark_as_featured">Anulează marcare ca recomandat</string>
|
||||
<string name="mod_action_lock">Blochează</string>
|
||||
<string name="mod_action_unlock">Deblochează</string>
|
||||
<string name="mod_action_remove">Elimină</string>
|
||||
<string name="mod_action_mark_as_distinguished">Marchează ca distins</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Anulează marcare ca distins</string>
|
||||
<string name="report_list_title">Lista de rapoarte</string>
|
||||
<string name="report_list_type_title">Tipul listei de rapoarte</string>
|
||||
<string name="report_list_type_all">Toate</string>
|
||||
<string name="report_list_type_unresolved">Nerezolvate</string>
|
||||
<string name="report_action_resolve">Marchează ca rezolvat</string>
|
||||
<string name="report_action_unresolve">Anulează marcare ca rezolvat</string>
|
||||
</resources>
|
@ -236,4 +236,18 @@
|
||||
<string name="settings_mark_as_read_while_scrolling">Markera inlägg som lästa medan du rullar
|
||||
</string>
|
||||
<string name="action_quote">Citat</string>
|
||||
<string name="mod_action_open_reports">Öppna rapporter</string>
|
||||
<string name="mod_action_mark_as_featured">Markera som utvald</string>
|
||||
<string name="mod_action_unmark_as_featured">Avmarkera som utvald</string>
|
||||
<string name="mod_action_lock">Lås</string>
|
||||
<string name="mod_action_unlock">Lås upp</string>
|
||||
<string name="mod_action_remove">Ta bort</string>
|
||||
<string name="mod_action_mark_as_distinguished">Markera som distinguished</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Avmarkera som distinguished</string>
|
||||
<string name="report_list_title">Rapportlista</string>
|
||||
<string name="report_list_type_title">Rapportlistatyp</string>
|
||||
<string name="report_list_type_all">Alla</string>
|
||||
<string name="report_list_type_unresolved">Olöst</string>
|
||||
<string name="report_action_resolve">Lös</string>
|
||||
<string name="report_action_unresolve">Avlös</string>
|
||||
</resources>
|
@ -237,4 +237,18 @@
|
||||
rolovaní
|
||||
</string>
|
||||
<string name="action_quote">Citovať</string>
|
||||
<string name="mod_action_open_reports">Otvoriť prehľady</string>
|
||||
<string name="mod_action_mark_as_featured">Označiť ako odporúčané</string>
|
||||
<string name="mod_action_unmark_as_featured">Zrušiť označenie ako odporúčané</string>
|
||||
<string name="mod_action_lock">Uzamknúť</string>
|
||||
<string name="mod_action_unlock">Odomknúť</string>
|
||||
<string name="mod_action_remove">Odstrániť</string>
|
||||
<string name="mod_action_mark_as_distinguished">Označiť ako odlíšené</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Zrušiť označenie ako odlíšené</string>
|
||||
<string name="report_list_title">Zoznam prehľadov</string>
|
||||
<string name="report_list_type_title">Typ zoznamu prehľadov</string>
|
||||
<string name="report_list_type_all">Všetky</string>
|
||||
<string name="report_list_type_unresolved">Nevyriešené</string>
|
||||
<string name="report_action_resolve">Vyriešiť</string>
|
||||
<string name="report_action_unresolve">Nevyriešiť</string>
|
||||
</resources>
|
@ -241,4 +241,18 @@
|
||||
<string name="settings_mark_as_read_while_scrolling">Med pomikanjem označi objave kot prebrane
|
||||
</string>
|
||||
<string name="action_quote">Kvota</string>
|
||||
<string name="mod_action_open_reports">Odpri poročila</string>
|
||||
<string name="mod_action_mark_as_featured">Označi kot predstavljeno</string>
|
||||
<string name="mod_action_unmark_as_featured">Odznači kot predstavljeno</string>
|
||||
<string name="mod_action_lock">Zakleni</string>
|
||||
<string name="mod_action_unlock">Odkleni</string>
|
||||
<string name="mod_action_remove">Odstrani</string>
|
||||
<string name="mod_action_mark_as_distinguished">Označi kot prepoznavno</string>
|
||||
<string name="mod_action_unmark_as_distinguished">Odznači kot razločeno</string>
|
||||
<string name="report_list_title">Seznam poročil</string>
|
||||
<string name="report_list_type_title">Vrsta seznama poročil</string>
|
||||
<string name="report_list_type_all">Vse</string>
|
||||
<string name="report_list_type_unresolved">Nerazrešeno</string>
|
||||
<string name="report_action_resolve">Reši</string>
|
||||
<string name="report_action_unresolve">Prekliči razrešitev</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user