2019-09-09 09:31:08 +02:00
< template >
2020-06-25 11:36:35 +02:00
< div >
2022-01-10 15:19:16 +01:00
< 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 } ) ,
} ,
] "
/ >
2020-06-25 11:36:35 +02:00
< section >
2020-11-30 10:24:11 +01:00
< b -message
title = "Error"
type = "is-danger"
v - for = "error in errors"
: key = "error"
>
2020-06-25 11:36:35 +02:00
{ { error } }
< / b - m e s s a g e >
< div class = "container" v-if ="report" >
< div class = "buttons" >
< b -button
v - if = "report.status !== ReportStatusEnum.RESOLVED"
@ click = "updateReport(ReportStatusEnum.RESOLVED)"
type = "is-primary"
> { { $t ( "Mark as resolved" ) } } < / b - b u t t o n
>
< b -button
v - if = "report.status !== ReportStatusEnum.OPEN"
@ click = "updateReport(ReportStatusEnum.OPEN)"
type = "is-success"
> { { $t ( "Reopen" ) } } < / b - b u t t o n
>
< b -button
v - if = "report.status !== ReportStatusEnum.CLOSED"
@ click = "updateReport(ReportStatusEnum.CLOSED)"
type = "is-danger"
> { { $t ( "Close" ) } } < / b - b u t t o n
>
< / div >
< div class = "table-container" >
< table class = "table is-striped is-fullwidth" >
< tbody >
2020-09-30 15:25:30 +02:00
< tr v-if ="report.reported.__typename === 'Group'" >
< 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 >
< tr v-else >
< td >
{ { $t ( "Reported identity" ) } }
< / td >
2020-06-25 11:36:35 +02:00
< td >
< router -link
: to = " {
name : RouteName . ADMIN _PROFILE ,
params : { id : report . reported . id } ,
} "
>
< img
v - if = "report.reported.avatar"
class = "image"
: src = "report.reported.avatar.url"
alt = ""
/ >
2020-09-30 15:25:30 +02:00
{ { displayNameAndUsername ( report . reported ) } }
2020-06-25 11:36:35 +02:00
< / r o u t e r - l i n k >
< / td >
< / tr >
< tr >
< td > { { $t ( "Reported by" ) } } < / td >
< td v-if ="report.reporter.type === ActorType.APPLICATION" >
{ { report . reporter . domain } }
< / td >
< td v-else >
< router -link
: to = " {
name : RouteName . ADMIN _PROFILE ,
params : { id : report . reporter . id } ,
} "
>
< img
v - if = "report.reporter.avatar"
class = "image"
: src = "report.reporter.avatar.url"
alt = ""
/ >
2020-09-30 15:25:30 +02:00
{ { displayNameAndUsername ( report . reporter ) } }
2020-06-25 11:36:35 +02:00
< / r o u t e r - l i n k >
< / td >
< / tr >
< tr >
< td > { { $t ( "Reported" ) } } < / td >
< td > { { report . insertedAt | formatDateTimeString } } < / td >
< / tr >
< tr v-if ="report.updatedAt !== report.insertedAt" >
< td > { { $t ( "Updated" ) } } < / td >
< td > { { report . updatedAt | formatDateTimeString } } < / td >
< / tr >
< tr >
< td > { { $t ( "Status" ) } } < / td >
< td >
2020-11-30 10:24:11 +01:00
< span v-if ="report.status === ReportStatusEnum.OPEN" > {{
$t ( "Open" )
} } < / span >
2020-06-25 11:36:35 +02:00
< 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 >
< tr v-if ="report.event && report.comments.length > 0" >
< td > { { $t ( "Event" ) } } < / td >
< td >
2020-11-30 10:24:11 +01:00
< router -link
: to = " {
name : RouteName . EVENT ,
params : { uuid : report . event . uuid } ,
} "
>
2020-06-25 11:36:35 +02:00
{ { report . event . title } }
< / r o u t e r - l i n k >
< span class = "is-pulled-right" >
<!-- < b -button - - >
<!-- tag = "router-link" -- >
<!-- type = "is-primary" -- >
<!-- : to = "{ name: RouteName.EDIT_EVENT, params: {eventId: report.event.uuid } }" -- >
<!-- icon - left = "pencil" -- >
<!-- size = "is-small" > { { $t ( 'Edit' ) } } < / b - b u t t o n > - - >
< b -button
type = "is-danger"
@ click = "confirmEventDelete()"
icon - left = "delete"
size = "is-small"
> { { $t ( "Delete" ) } } < / b - b u t t o n
>
< / span >
< / td >
< / tr >
< / tbody >
< / table >
< / div >
< div class = "box report-content" >
< p v -if = " report.content " v -html = " nl2br ( report.content ) " / >
< p v-else > {{ $ t ( " No comment " ) }} < / p >
< / div >
< div class = "box" v-if ="report.event && report.comments.length === 0" >
2020-11-30 10:24:11 +01:00
< router -link
: to = "{ name: RouteName.EVENT, params: { uuid: report.event.uuid } }"
>
2020-06-25 11:36:35 +02:00
< h3 class = "title" > { { report . event . title } } < / h3 >
< p v -html = " report.event.description " / >
< / r o u t e r - l i n k >
<!-- < b -button - - >
<!-- tag = "router-link" -- >
<!-- type = "is-primary" -- >
<!-- : to = "{ name: RouteName.EDIT_EVENT, params: {eventId: report.event.uuid } }" -- >
<!-- icon - left = "pencil" -- >
<!-- size = "is-small" > { { $t ( 'Edit' ) } } < / b - b u t t o n > - - >
< b -button
type = "is-danger"
@ click = "confirmEventDelete()"
icon - left = "delete"
size = "is-small"
> { { $t ( "Delete" ) } } < / b - b u t t o n
>
< / div >
2020-09-30 15:25:30 +02:00
< div v-if ="report.comments.length > 0" >
< ul v-for ="comment in report.comments" :key="comment.id" >
< li >
< div class = "box" v-if ="comment" >
< article class = "media" >
< div class = "media-left" >
2020-11-30 10:24:11 +01:00
< figure
class = "image is-48x48"
v - if = "comment.actor && comment.actor.avatar"
>
2020-09-30 15:25:30 +02:00
< img :src ="comment.actor.avatar.url" alt = "Image" / >
< / figure >
2020-11-30 10:24:11 +01:00
< b -icon
class = "media-left"
v - else
size = "is-large"
icon = "account-circle"
/ >
2020-06-25 11:36:35 +02:00
< / div >
2020-09-30 15:25:30 +02:00
< div class = "media-content" >
< div class = "content" >
< span v-if ="comment.actor" >
< strong > { { comment . actor . name } } < / strong >
< small > @ { { comment . actor . preferredUsername } } < / small >
< / span >
< span v-else > {{ $ t ( " Unknown actor " ) }} < / span >
< br / >
< p v -html = " comment.text " / >
< / div >
< b -button
type = "is-danger"
@ click = "confirmCommentDelete(comment)"
icon - left = "delete"
size = "is-small"
> { { $t ( "Delete" ) } } < / b - b u t t o n
>
< / div >
< / article >
< / div >
< / li >
< / ul >
< / div >
2019-11-15 18:36:47 +01:00
2020-06-25 11:36:35 +02:00
< h2 class = "title" v-if ="report.notes.length > 0" > {{ $ t ( " Notes " ) }} < / h2 >
2020-11-30 10:24:11 +01:00
< div
class = "box note"
v - for = "note in report.notes"
: id = "`note-${note.id}`"
: key = "note.id"
>
2020-06-25 11:36:35 +02:00
< p > { { note . content } } < / p >
2020-11-30 10:24:11 +01:00
< router -link
: to = " {
name : RouteName . ADMIN _PROFILE ,
params : { id : note . moderator . id } ,
} "
>
< img
alt
class = "image"
: src = "note.moderator.avatar.url"
v - if = "note.moderator.avatar"
/ >
2020-06-25 11:36:35 +02:00
@ { { note . moderator . preferredUsername } }
< / r o u t e r - l i n k >
< br / >
< small >
< a :href ="`#note-${note.id}`" v-if ="note.insertedAt" >
{ { note . insertedAt | formatDateTimeString } }
< / a >
< / small >
< / div >
2019-09-09 09:31:08 +02:00
2020-06-25 11:36:35 +02:00
< form @submit ="addNote()" >
< b -field : label = "$t('New note')" label -for = " newNoteInput " >
2020-11-30 10:24:11 +01:00
< b -input
type = "textarea"
v - model = "noteContent"
id = "newNoteInput"
> < / b - i n p u t >
2020-06-25 11:36:35 +02:00
< / b - f i e l d >
2020-11-30 10:24:11 +01:00
< b -button type = "submit" @click ="addNote" > {{
$t ( "Add a note" )
} } < / b - b u t t o n >
2020-06-25 11:36:35 +02:00
< / form >
< / div >
< / section >
< / div >
2019-09-09 09:31:08 +02:00
< / template >
< script lang = "ts" >
2020-02-18 08:57:00 +01:00
import { Component , Prop , Vue } from "vue-property-decorator" ;
import { CREATE _REPORT _NOTE , REPORT , UPDATE _REPORT } from "@/graphql/report" ;
2020-11-27 19:27:44 +01:00
import { IReport , IReportNote } from "@/types/report.model" ;
2020-02-18 08:57:00 +01:00
import { CURRENT _ACTOR _CLIENT } from "@/graphql/actor" ;
2020-11-27 19:27:44 +01:00
import { IPerson , displayNameAndUsername } from "@/types/actor" ;
2020-02-18 08:57:00 +01:00
import { DELETE _EVENT } from "@/graphql/event" ;
2021-05-12 18:13:39 +02:00
import uniq from "lodash/uniq" ;
2020-02-18 08:57:00 +01:00
import { nl2br } from "@/utils/html" ;
import { DELETE _COMMENT } from "@/graphql/comment" ;
import { IComment } from "@/types/comment.model" ;
2020-11-27 19:27:44 +01:00
import { ActorType , ReportStatusEnum } from "@/types/enums" ;
2020-02-18 08:57:00 +01:00
import RouteName from "../../router/name" ;
2021-05-12 18:10:07 +02:00
import { GraphQLError } from "graphql" ;
2021-06-11 14:21:27 +02:00
import { ApolloCache , FetchResult } from "@apollo/client/core" ;
2019-09-09 09:31:08 +02:00
@ Component ( {
apollo : {
report : {
query : REPORT ,
2020-08-27 11:53:24 +02:00
fetchPolicy : "cache-and-network" ,
2019-09-09 09:31:08 +02:00
variables ( ) {
return {
id : this . reportId ,
} ;
} ,
error ( { graphQLErrors } ) {
2021-05-12 18:10:07 +02:00
this . errors = uniq (
graphQLErrors . map ( ( { message } : GraphQLError ) => message )
) ;
2019-09-09 09:31:08 +02:00
} ,
} ,
2019-09-11 09:59:01 +02:00
currentActor : {
query : CURRENT _ACTOR _CLIENT ,
2019-09-09 09:31:08 +02:00
} ,
} ,
2019-12-03 11:29:51 +01:00
metaInfo ( ) {
return {
2020-02-18 08:57:00 +01:00
title : this . $t ( "Report" ) as string ,
titleTemplate : "%s | Mobilizon" ,
2019-12-03 11:29:51 +01:00
} ;
} ,
2019-09-09 09:31:08 +02:00
} )
export default class Report extends Vue {
@ Prop ( { required : true } ) reportId ! : number ;
2020-02-18 08:57:00 +01:00
2019-09-09 09:31:08 +02:00
report ! : IReport ;
2020-02-18 08:57:00 +01:00
2019-09-11 09:59:01 +02:00
currentActor ! : IPerson ;
2020-02-18 08:57:00 +01:00
2019-09-09 09:31:08 +02:00
errors : string [ ] = [ ] ;
ReportStatusEnum = ReportStatusEnum ;
2020-02-18 08:57:00 +01:00
2019-10-03 12:32:20 +02:00
RouteName = RouteName ;
2020-02-18 08:57:00 +01:00
2019-12-03 11:29:51 +01:00
ActorType = ActorType ;
2020-02-18 08:57:00 +01:00
2019-11-15 18:36:47 +01:00
nl2br = nl2br ;
2019-09-09 09:31:08 +02:00
2020-02-18 08:57:00 +01:00
noteContent = "" ;
2019-09-09 09:31:08 +02:00
2020-09-30 15:25:30 +02:00
displayNameAndUsername = displayNameAndUsername ;
addNote ( ) : void {
2019-09-09 09:31:08 +02:00
try {
this . $apollo . mutate < { createReportNote : IReportNote } > ( {
mutation : CREATE _REPORT _NOTE ,
variables : {
reportId : this . report . id ,
content : this . noteContent ,
} ,
2021-06-11 14:21:27 +02:00
update : (
store : ApolloCache < { createReportNote : IReportNote } > ,
{ data } : FetchResult
) => {
2019-09-09 09:31:08 +02:00
if ( data == null ) return ;
2020-02-18 08:57:00 +01:00
const cachedData = store . readQuery < { report : IReport } > ( {
query : REPORT ,
variables : { id : this . report . id } ,
} ) ;
2019-09-09 09:31:08 +02:00
if ( cachedData == null ) return ;
const { report } = cachedData ;
if ( report === null ) {
2020-11-30 10:24:11 +01:00
console . error (
"Cannot update event notes cache, because of null value."
) ;
2019-09-09 09:31:08 +02:00
return ;
}
const note = data . createReportNote ;
2019-09-11 09:59:01 +02:00
note . moderator = this . currentActor ;
2019-09-09 09:31:08 +02:00
report . notes = report . notes . concat ( [ note ] ) ;
2020-02-18 08:57:00 +01:00
store . writeQuery ( {
query : REPORT ,
variables : { id : this . report . id } ,
data : { report } ,
} ) ;
2019-09-09 09:31:08 +02:00
} ,
} ) ;
2020-02-18 08:57:00 +01:00
this . noteContent = "" ;
2019-09-09 09:31:08 +02:00
} catch ( error ) {
console . error ( error ) ;
}
}
2020-09-30 15:25:30 +02:00
confirmEventDelete ( ) : void {
2019-09-09 09:31:08 +02:00
this . $buefy . dialog . confirm ( {
2020-02-18 08:57:00 +01:00
title : this . $t ( "Deleting event" ) as string ,
message : this . $t (
2020-07-09 17:24:28 +02:00
"Are you sure you want to <b>delete</b> this event? This action cannot be undone. You may want to engage the discussion with the event creator or edit its event instead."
2020-02-18 08:57:00 +01:00
) as string ,
confirmText : this . $t ( "Delete Event" ) as string ,
type : "is-danger" ,
2019-09-09 09:31:08 +02:00
hasIcon : true ,
onConfirm : ( ) => this . deleteEvent ( ) ,
} ) ;
}
2020-09-30 15:25:30 +02:00
confirmCommentDelete ( comment : IComment ) : void {
2019-12-03 11:29:51 +01:00
this . $buefy . dialog . confirm ( {
2020-02-18 08:57:00 +01:00
title : this . $t ( "Deleting comment" ) as string ,
message : this . $t (
"Are you sure you want to <b>delete</b> this comment? This action cannot be undone."
) as string ,
confirmText : this . $t ( "Delete Comment" ) as string ,
type : "is-danger" ,
2019-12-03 11:29:51 +01:00
hasIcon : true ,
onConfirm : ( ) => this . deleteComment ( comment ) ,
} ) ;
}
2020-09-30 15:25:30 +02:00
async deleteEvent ( ) : Promise < void > {
2019-09-09 09:31:08 +02:00
if ( ! this . report . event || ! this . report . event . id ) return ;
const eventTitle = this . report . event . title ;
try {
await this . $apollo . mutate ( {
mutation : DELETE _EVENT ,
variables : {
eventId : this . report . event . id . toString ( ) ,
} ,
} ) ;
this . $buefy . notification . open ( {
2020-02-18 08:57:00 +01:00
message : this . $t ( "Event {eventTitle} deleted" , {
eventTitle ,
} ) as string ,
type : "is-success" ,
position : "is-bottom-right" ,
2019-09-09 09:31:08 +02:00
duration : 5000 ,
} ) ;
} catch ( error ) {
console . error ( error ) ;
}
}
2020-09-30 15:25:30 +02:00
async deleteComment ( comment : IComment ) : Promise < void > {
2019-12-03 11:29:51 +01:00
try {
await this . $apollo . mutate ( {
mutation : DELETE _COMMENT ,
variables : {
commentId : comment . id ,
} ,
} ) ;
2020-02-18 08:57:00 +01:00
this . $notifier . success ( this . $t ( "Comment deleted" ) as string ) ;
2019-12-03 11:29:51 +01:00
} catch ( error ) {
console . error ( error ) ;
}
}
2020-09-30 15:25:30 +02:00
async updateReport ( status : ReportStatusEnum ) : Promise < void > {
2019-09-09 09:31:08 +02:00
try {
2021-06-11 14:21:27 +02:00
await this . $apollo . mutate < { updateReportStatus : IReport } > ( {
2019-09-09 09:31:08 +02:00
mutation : UPDATE _REPORT ,
variables : {
reportId : this . report . id ,
status ,
} ,
2021-06-11 14:21:27 +02:00
update : (
store : ApolloCache < { updateReportStatus : IReport } > ,
{ data } : FetchResult
) => {
2019-09-09 09:31:08 +02:00
if ( data == null ) return ;
2020-02-18 08:57:00 +01:00
const reportCachedData = store . readQuery < { report : IReport } > ( {
query : REPORT ,
variables : { id : this . report . id } ,
} ) ;
2019-09-09 09:31:08 +02:00
if ( reportCachedData == null ) return ;
const { report } = reportCachedData ;
if ( report === null ) {
2020-11-30 10:24:11 +01:00
console . error (
"Cannot update event notes cache, because of null value."
) ;
2019-09-09 09:31:08 +02:00
return ;
}
2021-05-17 11:36:36 +02:00
const updatedReport = {
... report ,
status : data . updateReportStatus . status ,
} ;
2019-09-09 09:31:08 +02:00
2020-02-18 08:57:00 +01:00
store . writeQuery ( {
query : REPORT ,
variables : { id : this . report . id } ,
2021-05-17 11:36:36 +02:00
data : { report : updatedReport } ,
2020-02-18 08:57:00 +01:00
} ) ;
2019-09-09 09:31:08 +02:00
} ,
} ) ;
2019-11-15 18:36:47 +01:00
await this . $router . push ( { name : RouteName . REPORTS } ) ;
2019-09-09 09:31:08 +02:00
} catch ( error ) {
console . error ( error ) ;
}
}
}
< / script >
2019-11-15 18:36:47 +01:00
< style lang = "scss" scoped >
2020-02-18 08:57:00 +01:00
tbody td img . image ,
. note img . image {
display : inline ;
height : 1.5 em ;
vertical - align : text - bottom ;
}
2019-09-09 09:31:08 +02:00
2020-02-18 08:57:00 +01:00
. dialog . modal - card - foot {
justify - content : flex - end ;
}
2019-11-15 18:36:47 +01:00
2020-02-18 08:57:00 +01:00
. report - content {
border - left : 4 px solid $primary ;
}
2020-06-18 16:57:11 +02:00
. box a {
text - decoration : none ;
color : inherit ;
}
td > a {
text - decoration : none ;
}
2020-02-18 08:57:00 +01:00
< / style >