2022-08-26 14:08:58 +00:00
< template >
< breadcrumbs -nav
v - if = "report"
: links = " [
{
name : RouteName . MODERATION ,
text : t ( 'Moderation' ) ,
} ,
{
name : RouteName . REPORTS ,
text : t ( 'Reports' ) ,
} ,
{
name : RouteName . REPORT ,
params : { id : report . id } ,
text : t ( 'Report #{reportNumber}' , { reportNumber : report . id } ) ,
} ,
] "
/ >
< o -notification
title = "Error"
variant = "danger"
v - for = "error in errors"
: key = "error"
>
{ { error } }
< / o - n o t i f i c a t i o n >
< div class = "container mx-auto" v-if ="report" >
< div class = "flex flex-wrap gap-2 my-2" >
< o -button
v - if = "report.status !== ReportStatusEnum.RESOLVED"
@ click = "updateReport(ReportStatusEnum.RESOLVED)"
variant = "primary"
> { { t ( "Mark as resolved" ) } } < / o - b u t t o n
>
< o -button
v - if = "report.status !== ReportStatusEnum.OPEN"
@ click = "updateReport(ReportStatusEnum.OPEN)"
variant = "success"
> { { t ( "Reopen" ) } } < / o - b u t t o n
>
< o -button
v - if = "report.status !== ReportStatusEnum.CLOSED"
@ click = "updateReport(ReportStatusEnum.CLOSED)"
variant = "danger"
> { { t ( "Close" ) } } < / o - b u t t o n
>
2023-01-31 18:35:29 +00:00
< o -button
2023-02-16 13:50:12 +00:00
v - if = "antispamEnabled"
2023-01-31 18:35:29 +00:00
outlined
@ click = "reportToAntispam(true)"
variant = "text"
class = "!text-mbz-danger"
> { { t ( "Report as spam" ) } } < / o - b u t t o n
>
< o -button
2023-02-16 13:50:12 +00:00
v - if = "antispamEnabled"
2023-01-31 18:35:29 +00:00
outlined
@ click = "reportToAntispam(false)"
variant = "text"
class = "!text-mbz-success"
> { { t ( "Report as ham" ) } } < / o - b u t t o n
>
2022-08-26 14:08:58 +00:00
< / div >
< section class = "w-full" >
< table class = "table w-full" >
< tbody >
2023-08-31 17:04:05 +00:00
< tr v-if ="report.reported?.type === ActorType.GROUP" >
2022-08-26 14:08:58 +00:00
< td > { { t ( "Reported group" ) } } < / td >
< td >
< router -link
: to = " {
name : RouteName . ADMIN _GROUP _PROFILE ,
params : { id : report . reported . id } ,
} "
>
< img
v - if = "report.reported.avatar"
class = "image"
: src = "report.reported.avatar.url"
alt = ""
/ >
{ { displayNameAndUsername ( report . reported ) } }
< / r o u t e r - l i n k >
< / td >
< / tr >
2023-08-31 17:04:05 +00:00
< tr v -else -if = " report.reported ? .type = = = ActorType.PERSON " >
2022-08-26 14:08:58 +00:00
< td >
{ { t ( "Reported identity" ) } }
< / td >
2023-08-31 15:08:55 +00:00
< td class = "flex items-center justify-between pr-6" >
2022-08-26 14:08:58 +00:00
< router -link
: to = " {
name : RouteName . ADMIN _PROFILE ,
params : { id : report . reported . id } ,
} "
2023-08-31 16:39:04 +00:00
class = "inline-flex gap-1"
2022-08-26 14:08:58 +00:00
>
< img
v - if = "report.reported.avatar"
2023-08-31 16:39:04 +00:00
class = "image rounded-full"
2022-08-26 14:08:58 +00:00
: src = "report.reported.avatar.url"
alt = ""
/ >
2023-08-31 16:39:04 +00:00
< template v-if ="report.reported.suspended" >
< i18n -t keypath = "{profileName} (suspended)" >
< template # profileName >
{ { displayNameAndUsername ( report . reported ) } }
< / template >
< / i 1 8 n - t >
< / template >
< template v-else > {{
displayNameAndUsername ( report . reported )
} } < / template >
2022-08-26 14:08:58 +00:00
< / r o u t e r - l i n k >
2023-08-31 15:08:55 +00:00
< o -button
2023-08-31 16:39:04 +00:00
v - if = "report.reported.domain && !report.reported.suspended"
2023-08-31 15:08:55 +00:00
variant = "danger"
@ click = "suspendProfile(report.reported.id as string)"
icon - left = "delete"
2023-08-31 16:39:04 +00:00
size = "small"
2023-08-31 15:08:55 +00:00
> { { t ( "Suspend the profile" ) } } < / o - b u t t o n
>
< o -button
2023-08-31 16:39:04 +00:00
v - else - if = "
( report . reported as IPerson ) . user &&
! ( ( report . reported as IPerson ) . user as IUser ) . disabled
"
2023-08-31 15:08:55 +00:00
variant = "danger"
@ click = "suspendUser((report.reported as IPerson).user as IUser)"
icon - left = "delete"
2023-08-31 16:39:04 +00:00
size = "small"
2023-08-31 15:08:55 +00:00
> { { t ( "Suspend the account" ) } } < / o - b u t t o n
>
2022-08-26 14:08:58 +00:00
< / td >
< / tr >
2023-08-31 17:04:05 +00:00
< tr v-else >
< td >
{ { t ( "Reported identity" ) } }
< / td >
< td >
{ { t ( "Unknown actor" ) } }
< / td >
< / tr >
2022-08-26 14:08:58 +00:00
< tr >
< td > { { t ( "Reported by" ) } } < / td >
2023-08-31 17:04:05 +00:00
< td v-if ="report.reporter?.type === ActorType.APPLICATION" >
2022-08-26 14:08:58 +00:00
{ { report . reporter . domain } }
< / td >
2023-08-31 17:04:05 +00:00
< td v -else -if = " report.reporter ? .type = = = ActorType.PERSON " >
2022-08-26 14:08:58 +00:00
< router -link
: to = " {
name : RouteName . ADMIN _PROFILE ,
params : { id : report . reporter . id } ,
} "
>
< img
v - if = "report.reporter.avatar"
2023-08-31 16:39:04 +00:00
class = "image rounded-full"
2022-08-26 14:08:58 +00:00
: src = "report.reporter.avatar.url"
alt = ""
/ >
{ { displayNameAndUsername ( report . reporter ) } }
< / r o u t e r - l i n k >
< / td >
2023-08-31 17:04:05 +00:00
< td v-else >
{ { t ( "Unknown actor" ) } }
< / td >
2022-08-26 14:08:58 +00:00
< / tr >
< tr >
2023-08-31 17:04:05 +00:00
< td > { { t ( "Reported at" ) } } < / td >
2022-08-26 14:08:58 +00:00
< td > { { formatDateTimeString ( report . insertedAt ) } } < / td >
< / tr >
< tr v-if ="report.updatedAt !== report.insertedAt" >
2023-08-31 17:04:05 +00:00
< td > { { t ( "Updated at" ) } } < / td >
2022-08-26 14:08:58 +00:00
< td > { { formatDateTimeString ( report . updatedAt ) } } < / td >
< / tr >
< tr >
< td > { { t ( "Status" ) } } < / td >
< td >
< span v-if ="report.status === ReportStatusEnum.OPEN" > {{
t ( "Open" )
} } < / span >
< span v -else -if = " report.status = = = ReportStatusEnum.CLOSED " >
{ { t ( "Closed" ) } }
< / span >
< span v -else -if = " report.status = = = ReportStatusEnum.RESOLVED " >
{ { t ( "Resolved" ) } }
< / span >
< span v-else > {{ t ( " Unknown " ) }} < / span >
< / td >
< / tr >
< / tbody >
< / table >
< / section >
2022-10-25 17:01:43 +00:00
< section class = "bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3" >
< h2 class = "mb-1" > { { t ( "Report reason" ) } } < / h2 >
< div class = "" >
2022-08-26 14:08:58 +00:00
< div class = "flex gap-1" >
2023-08-31 17:04:05 +00:00
< figure class = "" v-if ="report.reported?.avatar" >
2022-08-26 14:08:58 +00:00
< img
alt = ""
: src = "report.reported.avatar.url"
class = "rounded-full"
2022-10-25 17:01:43 +00:00
width = "36"
height = "36"
2022-08-26 14:08:58 +00:00
/ >
< / figure >
2022-10-25 17:01:43 +00:00
< AccountCircle v -else :size ="36" / >
2023-08-31 17:04:05 +00:00
< div class = "" v-if ="report.reported" >
< p class = "" v-if ="report.reported?.name" >
2022-08-26 14:08:58 +00:00
{ { report . reported . name } }
< / p >
< p class = "" > @ { { usernameWithDomain ( report . reported ) } } < / p >
< / div >
2023-08-31 17:04:05 +00:00
< p v-else > {{ t ( " Unknown actor " ) }} < / p >
2022-08-26 14:08:58 +00:00
< / div >
2023-12-06 10:05:56 +00:00
< div class = "prose dark:prose-invert" v-if ="report.content" >
{ { report . content } }
< / div >
2022-08-26 14:08:58 +00:00
< p v-else > {{ t ( " No comment " ) }} < / p >
< / div >
< / section >
2022-10-25 17:01:43 +00:00
< section
class = "bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3"
2023-06-05 16:32:29 +00:00
v - if = "
report . events &&
report . events ? . length > 0 &&
report . comments . length === 0
"
2022-10-25 17:01:43 +00:00
>
< h2 class = "mb-1" > { { t ( "Reported content" ) } } < / h2 >
2023-08-31 12:37:54 +00:00
< ul >
< li v-for ="event in report.events" :key="event.id" >
< EventCard :event ="event" mode = "row" class = "my-2 max-w-4xl" / >
< o -button
variant = "danger"
@ click = "confirmEventDelete(event)"
icon - left = "delete"
> < template v-if ="isOnlyReportedContent" > {{
t ( "Delete event and resolve report" )
} } < / t e m p l a t e
> < template v-else > {{ t ( " Delete event " ) }} < / template > < / o -button
>
< / li >
< / ul >
2022-08-26 14:08:58 +00:00
< / section >
2022-10-25 17:01:43 +00:00
< section
class = "bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3"
v - if = "report.comments.length > 0"
>
< h2 class = "mb-1" > { { t ( "Reported content" ) } } < / h2 >
2022-08-26 14:08:58 +00:00
< ul v-for ="comment in report.comments" :key="comment.id" >
< li >
2023-10-17 14:41:31 +00:00
< template v-if ="comment.conversation && comment.event" >
< i18n -t keypath = "Comment from an event announcement" tag = "p" >
< template # eventTitle >
< router -link
: to = " {
name : RouteName . EVENT ,
params : { uuid : comment . event ? . uuid } ,
} "
>
< b > { { comment . event ? . title } } < / b >
< / r o u t e r - l i n k >
< / template >
< / i 1 8 n - t >
< DiscussionComment
: modelValue = "comment"
: current - actor = "currentActor as IPerson"
: readOnly = "true"
/ >
< / template >
< template v -else -if = " comment.conversation " >
< i18n -t keypath = "Comment from a private conversation" tag = "p" >
< template # eventTitle >
< router -link
: to = " {
name : RouteName . EVENT ,
params : { uuid : comment . event ? . uuid } ,
} "
>
< b > { { comment . event ? . title } } < / b >
< / r o u t e r - l i n k >
< / template >
< / i 1 8 n - t >
< DiscussionComment
: modelValue = "comment"
: current - actor = "currentActor as IPerson"
: readOnly = "true"
/ >
< / template >
< template v-else >
< i18n -t keypath = "Comment under event {eventTitle}" tag = "p" >
< template # eventTitle >
< router -link
: to = " {
name : RouteName . EVENT ,
params : { uuid : comment . event ? . uuid } ,
} "
>
< b > { { comment . event ? . title } } < / b >
< / r o u t e r - l i n k >
< / template >
< / i 1 8 n - t >
< EventComment
: root - comment = "true"
: comment = "comment"
: event = "comment.event as IEvent"
: current - actor = "currentActor as IPerson"
: readOnly = "true"
/ >
< / template >
2023-08-31 12:37:54 +00:00
< o -button
v - if = "!comment.deletedAt"
variant = "danger"
@ click = "confirmCommentDelete(comment)"
icon - left = "delete"
> < template v-if ="isOnlyReportedContent" > {{
t ( "Delete comment and resolve report" )
} } < / t e m p l a t e
> < template v-else > {{ t ( " Delete comment " ) }} < / template > < / o -button
>
2022-08-26 14:08:58 +00:00
< / li >
< / ul >
2022-10-25 17:01:43 +00:00
< / section >
2022-08-26 14:08:58 +00:00
2023-08-31 12:37:54 +00:00
< section
class = "bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3"
v - if = "
report . events &&
report . events ? . length === 0 &&
report . comments . length === 0
"
>
< EmptyContent inline center icon = "alert-circle" >
{ { t ( "No content found" ) } }
< template # desc >
{ { t ( "Maybe the content was removed by the author or a moderator" ) } }
< / template >
< / EmptyContent >
< / section >
2022-10-25 17:01:43 +00:00
< section class = "bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3" >
< h2 class = "mb-1" > { { t ( "Notes" ) } } < / h2 >
2022-08-26 14:08:58 +00:00
< div
2022-10-25 17:01:43 +00:00
class = ""
2022-08-26 14:08:58 +00:00
v - for = "note in report.notes"
: id = "`note-${note.id}`"
: key = "note.id"
>
< p > { { note . content } } < / p >
< router -link
: to = " {
name : RouteName . ADMIN _PROFILE ,
params : { id : note . moderator . id } ,
} "
>
< img
alt = ""
class = "rounded-full"
: src = "note.moderator.avatar.url"
v - if = "note.moderator.avatar"
/ >
@ { { note . moderator . preferredUsername } }
< / r o u t e r - l i n k >
< br / >
< small >
< a :href ="`#note-${note.id}`" v-if ="note.insertedAt" >
{ { formatDateTimeString ( note . insertedAt ) } }
< / a >
< / small >
< / div >
< form
@ submit = "
createReportNoteMutation ( {
reportId : report ? . id ,
content : noteContent ,
} )
"
>
< o -field : label = "t('New note')" label -for = " newNoteInput " >
< o -input
type = "textarea"
v - model = "noteContent"
2023-12-14 09:50:38 +00:00
expanded
2022-08-26 14:08:58 +00:00
id = "newNoteInput"
> < / o - i n p u t >
< / o - f i e l d >
< o -button class = "mt-2" type = "submit" > { { t ( "Add a note" ) } } < / o - b u t t o n >
< / form >
< / section >
< / div >
< / template >
< script lang = "ts" setup >
import { CREATE _REPORT _NOTE , REPORT , UPDATE _REPORT } from "@/graphql/report" ;
import { IReport , IReportNote } from "@/types/report.model" ;
2023-08-31 12:37:54 +00:00
import {
IPerson ,
displayNameAndUsername ,
usernameWithDomain ,
} from "@/types/actor" ;
2022-08-26 14:08:58 +00:00
import { DELETE _EVENT } from "@/graphql/event" ;
import uniq from "lodash/uniq" ;
import { DELETE _COMMENT } from "@/graphql/comment" ;
import { IComment } from "@/types/comment.model" ;
2023-01-31 18:35:29 +00:00
import { ActorType , AntiSpamFeedback , ReportStatusEnum } from "@/types/enums" ;
2022-08-26 14:08:58 +00:00
import RouteName from "@/router/name" ;
import { GraphQLError } from "graphql" ;
import { ApolloCache , FetchResult } from "@apollo/client/core" ;
2023-08-31 15:08:55 +00:00
import { useLazyQuery , useMutation , useQuery } from "@vue/apollo-composable" ;
2022-08-26 14:08:58 +00:00
import { useCurrentActorClient } from "@/composition/apollo/actor" ;
2023-11-21 15:04:09 +00:00
import { useHead } from "@unhead/vue" ;
2022-08-26 14:08:58 +00:00
import { useI18n } from "vue-i18n" ;
import { useRouter } from "vue-router" ;
import { ref , computed , inject } from "vue" ;
import { formatDateTimeString } from "@/filters/datetime" ;
import AccountCircle from "vue-material-design-icons/AccountCircle.vue" ;
import { Dialog } from "@/plugins/dialog" ;
import { Notifier } from "@/plugins/notifier" ;
import EventCard from "@/components/Event/EventCard.vue" ;
2023-01-31 18:35:29 +00:00
import { useFeatures } from "@/composition/apollo/config" ;
2023-06-05 16:32:29 +00:00
import { IEvent } from "@/types/event.model" ;
2023-08-31 12:37:54 +00:00
import EmptyContent from "@/components/Utils/EmptyContent.vue" ;
import EventComment from "@/components/Comment/EventComment.vue" ;
2023-10-17 14:41:31 +00:00
import DiscussionComment from "@/components/Discussion/DiscussionComment.vue" ;
2023-08-31 15:08:55 +00:00
import { SUSPEND _PROFILE } from "@/graphql/actor" ;
import { GET _USER , SUSPEND _USER } from "@/graphql/user" ;
import { IUser } from "@/types/current-user.model" ;
2022-08-26 14:08:58 +00:00
const router = useRouter ( ) ;
const props = defineProps < { reportId : string } > ( ) ;
const { currentActor } = useCurrentActorClient ( ) ;
2023-01-31 18:35:29 +00:00
const { features } = useFeatures ( ) ;
const antispamEnabled = computed ( ( ) => features . value ? . antispam ) ;
2022-08-26 14:08:58 +00:00
const { result : reportResult , onError : onReportQueryError } = useQuery < {
report : IReport ;
} > ( REPORT , ( ) => ( {
id : props . reportId ,
} ) ) ;
const report = computed ( ( ) => reportResult . value ? . report ) ;
onReportQueryError ( ( { graphQLErrors } ) => {
errors . value = uniq (
graphQLErrors . map ( ( { message } : GraphQLError ) => message )
) ;
} ) ;
const { t } = useI18n ( { useScope : "global" } ) ;
useHead ( {
title : computed ( ( ) => t ( "Report" ) ) ,
} ) ;
const notifier = inject < Notifier > ( "notifier" ) ;
const errors = ref < string [ ] > ( [ ] ) ;
const noteContent = ref ( "" ) ;
2023-08-31 12:37:54 +00:00
const reportedContent = computed ( ( ) => {
return [ ... ( report . value ? . events ? ? [ ] ) , ... ( report . value ? . comments ? ? [ ] ) ] ;
} ) ;
const isOnlyReportedContent = computed (
( ) => reportedContent . value . length === 1
) ;
2022-08-26 14:08:58 +00:00
const {
mutate : createReportNoteMutation ,
onDone : createReportNoteMutationDone ,
onError : createReportNoteMutationError ,
} = useMutation < {
createReportNote : IReportNote ;
} > ( CREATE _REPORT _NOTE , ( ) => ( {
update : (
store : ApolloCache < { createReportNote : IReportNote } > ,
{ data } : FetchResult
) => {
if ( data == null ) return ;
const cachedData = store . readQuery < { report : IReport } > ( {
query : REPORT ,
variables : { id : report . value ? . id } ,
} ) ;
if ( cachedData == null ) return ;
const { report : cachedReport } = cachedData ;
if ( cachedReport === null ) {
console . error ( "Cannot update event notes cache, because of null value." ) ;
return ;
}
const note = data . createReportNote ;
note . moderator = currentActor . value ;
cachedReport . notes = cachedReport . notes . concat ( [ note ] ) ;
store . writeQuery ( {
query : REPORT ,
variables : { id : report . value ? . id } ,
data : { report } ,
} ) ;
} ,
} ) ) ;
createReportNoteMutationDone ( ( ) => {
noteContent . value = "" ;
} ) ;
createReportNoteMutationError ( ( error ) => {
console . error ( error ) ;
} ) ;
const dialog = inject < Dialog > ( "dialog" ) ;
2023-08-31 12:37:54 +00:00
const addResolveReportPart = computed ( ( ) => {
if ( isOnlyReportedContent . value ) {
return "<p>" + t ( "This will also resolve the report." ) + "</p>" ;
}
return "" ;
} ) ;
2023-06-05 16:32:29 +00:00
const confirmEventDelete = ( event : IEvent ) : void => {
2022-08-26 14:08:58 +00:00
dialog ? . confirm ( {
title : t ( "Deleting event" ) ,
2023-08-31 12:37:54 +00:00
message :
t (
"Are you sure you want to <b>delete</b> this event? <b>This action cannot be undone</b>. You may want to engage the discussion with the event creator and ask them to edit their event instead."
) + addResolveReportPart . value ,
confirmText : isOnlyReportedContent . value
? t ( "Delete event and resolve report" )
: t ( "Delete event" ) ,
2022-08-26 14:08:58 +00:00
variant : "danger" ,
hasIcon : true ,
2023-06-05 16:32:29 +00:00
onConfirm : ( ) => deleteEvent ( event ) ,
2022-08-26 14:08:58 +00:00
} ) ;
} ;
const confirmCommentDelete = ( comment : IComment ) : void => {
dialog ? . confirm ( {
title : t ( "Deleting comment" ) ,
2023-08-31 12:37:54 +00:00
message :
t (
"Are you sure you want to <b>delete</b> this comment? <b>This action cannot be undone</b>."
) + addResolveReportPart . value ,
confirmText : isOnlyReportedContent . value
? t ( "Delete comment and resolve report" )
: t ( "Delete comment" ) ,
2022-08-26 14:08:58 +00:00
variant : "danger" ,
hasIcon : true ,
onConfirm : ( ) => deleteCommentMutation ( { commentId : comment . id } ) ,
} ) ;
} ;
const {
mutate : deleteEventMutation ,
onDone : deleteEventMutationDone ,
onError : deleteEventMutationError ,
2023-08-31 12:37:54 +00:00
} = useMutation < { deleteEvent : { id : string } } > ( DELETE _EVENT , ( ) => ( {
update : (
store : ApolloCache < { deleteEvent : { id : string } } > ,
{ data } : FetchResult
) => {
if ( data == null ) return ;
const reportCachedData = store . readQuery < { report : IReport } > ( {
query : REPORT ,
variables : { id : report . value ? . id } ,
} ) ;
if ( reportCachedData == null ) return ;
const { report : cachedReport } = reportCachedData ;
if ( cachedReport === null ) {
console . error (
"Cannot update report events cache, because of null value."
) ;
return ;
}
const updatedReport = {
... cachedReport ,
events : cachedReport . events ? . filter (
( cachedEvent ) => cachedEvent . id !== data . deleteEvent . id
) ,
} ;
store . writeQuery ( {
query : REPORT ,
variables : { id : report . value ? . id } ,
data : { report : updatedReport } ,
} ) ;
} ,
} ) ) ;
deleteEventMutationDone ( async ( ) => {
if ( reportedContent . value . length === 0 ) {
await updateReport ( ReportStatusEnum . RESOLVED ) ;
notifier ? . success ( t ( "Event deleted and report resolved" ) ) ;
} else {
notifier ? . success ( t ( "Event deleted" ) ) ;
}
2022-08-26 14:08:58 +00:00
} ) ;
deleteEventMutationError ( ( error ) => {
console . error ( error ) ;
} ) ;
2023-06-05 16:32:29 +00:00
const deleteEvent = async ( event : IEvent ) : Promise < void > => {
if ( ! event ? . id ) return ;
2022-08-26 14:08:58 +00:00
2023-06-05 16:32:29 +00:00
deleteEventMutation (
{ eventId : event . id } ,
{ context : { eventTitle : event . title } }
) ;
2022-08-26 14:08:58 +00:00
} ;
const {
mutate : deleteCommentMutation ,
onDone : deleteCommentMutationDone ,
onError : deleteCommentMutationError ,
2023-08-31 12:37:54 +00:00
} = useMutation < { deleteComment : { id : string } } > ( DELETE _COMMENT , ( ) => ( {
update : (
store : ApolloCache < { deleteComment : { id : string } } > ,
{ data } : FetchResult
) => {
if ( data == null ) return ;
const reportCachedData = store . readQuery < { report : IReport } > ( {
query : REPORT ,
variables : { id : report . value ? . id } ,
} ) ;
if ( reportCachedData == null ) return ;
const { report : cachedReport } = reportCachedData ;
if ( cachedReport === null ) {
console . error (
"Cannot update report comments cache, because of null value."
) ;
return ;
}
const updatedReport = {
... cachedReport ,
comments : cachedReport . comments . filter (
( cachedComment ) => cachedComment . id !== data . deleteComment . id
) ,
} ;
store . writeQuery ( {
query : REPORT ,
variables : { id : report . value ? . id } ,
data : { report : updatedReport } ,
} ) ;
} ,
} ) ) ;
2022-08-26 14:08:58 +00:00
2023-08-31 12:37:54 +00:00
deleteCommentMutationDone ( async ( ) => {
if ( reportedContent . value . length === 0 ) {
await updateReport ( ReportStatusEnum . RESOLVED ) ;
notifier ? . success ( t ( "Comment deleted and report resolved" ) ) ;
} else {
notifier ? . success ( t ( "Comment deleted" ) ) ;
}
2022-08-26 14:08:58 +00:00
} ) ;
deleteCommentMutationError ( ( error ) => {
console . error ( error ) ;
} ) ;
const {
mutate : updateReportMutation ,
onDone : onUpdateReportMutation ,
onError : onUpdateReportError ,
2023-01-31 18:35:29 +00:00
} = useMutation <
Record < string , any > ,
{
reportId : string ;
status : ReportStatusEnum ;
antispamFeedback ? : AntiSpamFeedback ;
}
> ( UPDATE _REPORT , ( ) => ( {
2022-08-26 14:08:58 +00:00
update : (
store : ApolloCache < { updateReportStatus : IReport } > ,
{ data } : FetchResult
) => {
if ( data == null ) return ;
const reportCachedData = store . readQuery < { report : IReport } > ( {
query : REPORT ,
variables : { id : report . value ? . id } ,
} ) ;
if ( reportCachedData == null ) return ;
const { report : cachedReport } = reportCachedData ;
if ( cachedReport === null ) {
console . error ( "Cannot update event notes cache, because of null value." ) ;
return ;
}
const updatedReport = {
... cachedReport ,
status : data . updateReportStatus . status ,
} ;
store . writeQuery ( {
query : REPORT ,
variables : { id : report . value ? . id } ,
data : { report : updatedReport } ,
} ) ;
} ,
} ) ) ;
2023-08-31 12:37:54 +00:00
onUpdateReportMutation ( async ( ) => {
await router . push ( { name : RouteName . REPORTS } ) ;
2022-08-26 14:08:58 +00:00
} ) ;
onUpdateReportError ( ( error ) => {
console . error ( error ) ;
} ) ;
const updateReport = async ( status : ReportStatusEnum ) : Promise < void > => {
2023-01-31 18:35:29 +00:00
if ( report . value ) {
updateReportMutation ( {
reportId : report . value ? . id ,
status ,
} ) ;
}
} ;
const reportToAntispam = ( spam : boolean ) => {
dialog ? . confirm ( {
title : spam ? t ( "Report as undetected spam" ) : t ( "Report as ham" ) ,
message : t (
"The report contents (eventual comments and event) and the reported profile details will be transmitted to Akismet."
) ,
confirmText : t ( "Submit to Akismet" ) ,
variant : "warning" ,
hasIcon : true ,
onConfirm : ( ) => {
if ( report . value ) {
updateReportMutation ( {
reportId : report . value . id ,
status : report . value . status ,
antispamFeedback : spam ? AntiSpamFeedback . SPAM : AntiSpamFeedback . HAM ,
} ) ;
}
} ,
2022-08-26 14:08:58 +00:00
} ) ;
} ;
2023-08-31 15:08:55 +00:00
const { mutate : doSuspendProfile , onDone : onSuspendProfileDone } = useMutation <
{
suspendProfile : { id : string } ;
} ,
{ id : string }
> ( SUSPEND _PROFILE ) ;
const { mutate : doSuspendUser , onDone : onSuspendUserDone } = useMutation <
{ suspendProfile : { id : string } } ,
{ userId : string }
> ( SUSPEND _USER ) ;
2023-10-17 14:41:31 +00:00
const { load : loadUserLazyQuery } = useLazyQuery <
{ user : IUser } ,
{ id : string }
> ( GET _USER ) ;
2023-08-31 15:08:55 +00:00
const suspendProfile = async ( actorId : string ) : Promise < void > => {
dialog ? . confirm ( {
title : t ( "Suspend the profile?" ) ,
message :
t (
"Do you really want to suspend this profile? All of the profiles content will be deleted."
) +
` <p><b> ` +
t ( "There will be no way to restore the profile's data!" ) +
` </b></p> ` ,
confirmText : t ( "Suspend the profile" ) ,
cancelText : t ( "Cancel" ) ,
variant : "danger" ,
onConfirm : async ( ) => {
doSuspendProfile ( {
id : actorId ,
} ) ;
return router . push ( { name : RouteName . USERS } ) ;
} ,
} ) ;
} ;
const userSuspendedProfilesMessages = ( user : IUser ) => {
return (
t ( "The following user's profiles will be deleted, with all their data:" ) +
` <ul class="list-disc pl-3"> ` +
user . actors
. map ( ( person ) => ` <li> ${ displayNameAndUsername ( person ) } </li> ` )
. join ( "" ) +
` </ul><b> `
) ;
} ;
const cachedReportedUser = ref < IUser | undefined > ( ) ;
const suspendUser = async ( user : IUser ) : Promise < void > => {
try {
if ( ! cachedReportedUser . value ) {
2023-10-17 14:41:31 +00:00
try {
const result = await loadUserLazyQuery ( GET _USER , { id : user . id } ) ;
if ( ! result ) return ;
cachedReportedUser . value = result . user ;
} catch ( e ) {
return ;
}
2023-08-31 15:08:55 +00:00
}
dialog ? . confirm ( {
title : t ( "Suspend the account?" ) ,
message :
t ( "Do you really want to suspend the account « {emailAccount} » ?" , {
emailAccount : cachedReportedUser . value . email ,
} ) +
" " +
userSuspendedProfilesMessages ( cachedReportedUser . value ) +
"<b>" +
t ( "There will be no way to restore the user's data!" ) +
` </b> ` ,
confirmText : t ( "Suspend the account" ) ,
cancelText : t ( "Cancel" ) ,
variant : "danger" ,
onConfirm : async ( ) => {
doSuspendUser ( {
userId : user . id ,
} ) ;
return router . push ( { name : RouteName . USERS } ) ;
} ,
} ) ;
} catch ( e ) {
console . error ( e ) ;
}
} ;
onSuspendUserDone ( async ( ) => {
await router . push ( { name : RouteName . REPORTS } ) ;
notifier ? . success ( t ( "User suspended and report resolved" ) ) ;
} ) ;
onSuspendProfileDone ( async ( ) => {
await router . push ( { name : RouteName . REPORTS } ) ;
notifier ? . success ( t ( "Profile suspended and report resolved" ) ) ;
} ) ;
2022-08-26 14:08:58 +00:00
< / script >
< style lang = "scss" scoped >
tbody td img . image ,
. note img . image {
display : inline ;
height : 1.5 em ;
vertical - align : text - bottom ;
}
. dialog . modal - card - foot {
justify - content : flex - end ;
}
. box a {
text - decoration : none ;
color : inherit ;
}
< / style >