From 13f7a84cc14f23708e5e5aacde85ada6735e7ceb Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Wed, 21 Oct 2020 21:27:58 +0300 Subject: [PATCH 1/8] Create api function and module action for fetching single report --- src/api/reports.js | 9 +++++++++ src/store/modules/reports.js | 13 ++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/api/reports.js b/src/api/reports.js index abd371c1..4f41f956 100644 --- a/src/api/reports.js +++ b/src/api/reports.js @@ -24,6 +24,15 @@ export async function fetchReports(filter, page, pageSize, authHost, token) { }) } +export async function fetchSingleReport(id, authHost, token) { + return await request({ + baseURL: baseName(authHost), + url: `/api/pleroma/admin/reports/${id}`, + method: 'get', + headers: authHeaders(token) + }) +} + export async function createNote(content, reportID, authHost, token) { return await request({ baseURL: baseName(authHost), diff --git a/src/store/modules/reports.js b/src/store/modules/reports.js index 52eb3bc6..51f08219 100644 --- a/src/store/modules/reports.js +++ b/src/store/modules/reports.js @@ -1,4 +1,4 @@ -import { changeState, fetchReports, createNote, deleteNote } from '@/api/reports' +import { changeState, fetchReports, fetchSingleReport, createNote, deleteNote } from '@/api/reports' import { activateUsers, deactivateUsers, @@ -14,6 +14,7 @@ const reports = { loading: true, openReportsCount: 0, pageSize: 50, + singleReport: {}, stateFilter: '', totalReportsCount: 0 }, @@ -38,6 +39,9 @@ const reports = { }, SET_REPORTS_FILTER: (state, filter) => { state.stateFilter = filter + }, + SET_SINGLE_REPORT: (state, report) => { + state.singleReport = report } }, actions: { @@ -120,6 +124,13 @@ const reports = { commit('SET_PAGE', page) commit('SET_LOADING', false) }, + async FetchSingleReport({ commit, getters }, id) { + commit('SET_LOADING', true) + const { data } = await fetchSingleReport(id, getters.authHost, getters.token) + + commit('SET_SINGLE_REPORT', data) + commit('SET_LOADING', false) + }, async FetchOpenReportsCount({ commit, getters, state }) { commit('SET_LOADING', true) const { data } = await fetchReports('open', state.currentPage, state.pageSize, getters.authHost, getters.token) -- GitLab From 25e307442d87487b5b590c3b2c4bfd5e91f230e1 Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Wed, 21 Oct 2020 23:34:00 +0300 Subject: [PATCH 2/8] Make report ID in moderation log a link to the respective report --- src/router/index.js | 12 ++++++++++ src/views/moderation_log/LogEntryMessage.vue | 23 ++++++++++++++++++-- src/views/moderation_log/index.vue | 2 +- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/router/index.js b/src/router/index.js index b61e7b44..60be0f88 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -199,5 +199,17 @@ export const asyncRouterMap = [ ], hidden: true }, + { + path: '/reports/:id', + component: Layout, + children: [ + { + path: '', + name: 'ReportsShow', + component: () => import('@/views/reports/show') + } + ], + hidden: true + }, { path: '*', redirect: '/404', hidden: true } ] diff --git a/src/views/moderation_log/LogEntryMessage.vue b/src/views/moderation_log/LogEntryMessage.vue index d9cc8547..d19ada81 100644 --- a/src/views/moderation_log/LogEntryMessage.vue +++ b/src/views/moderation_log/LogEntryMessage.vue @@ -8,7 +8,16 @@ @{{ actor.nickname }} </span> </router-link> - <span>{{ logEntryMessage }}</span> + <span v-if="subject.type === 'report' && propertyExists(subject, 'id')"> + {{ logEntryMessageWithoutId[0] }} + <router-link + :to="{ name: 'ReportsShow', params: { id: subject.id }}" + class="router-link"> + <span style="font-weight: 600">#{{ subject.id }}</span> + </router-link> + {{ logEntryMessageWithoutId[1] }} + </span> + <span v-else>{{ logEntryMessage }}</span> </span> </template> @@ -24,11 +33,21 @@ export default { message: { type: String, required: true + }, + subject: { + type: [Object, Array], + required: false, + default: function() { + return {} + } } }, computed: { logEntryMessage() { - return this.message.split(this.actor.nickname)[1] + return this.actor.nickname ? this.message.split(this.actor.nickname)[1] : this.message + }, + logEntryMessageWithoutId() { + return this.logEntryMessage.split(`#${this.subject.id}`) } }, methods: { diff --git a/src/views/moderation_log/index.vue b/src/views/moderation_log/index.vue index af123e22..283bf4d2 100644 --- a/src/views/moderation_log/index.vue +++ b/src/views/moderation_log/index.vue @@ -43,7 +43,7 @@ v-for="(logEntry, index) in log" :key="index" :timestamp="normalizeTimestamp(logEntry.time)"> - <log-entry-message v-if="propertyExists(logEntry.data.actor, 'nickname')" :actor="logEntry.data.actor" :message="logEntry.message"/> + <log-entry-message v-if="propertyExists(logEntry.data.actor, 'nickname')" :actor="logEntry.data.actor" :message="logEntry.message" :subject="logEntry.data.subject"/> <span v-else>{{ logEntry.message }}</span> </el-timeline-item> </el-timeline> -- GitLab From 4501143b69a3e33c98e7fba4086447be69587ade Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Tue, 27 Oct 2020 01:12:38 +0300 Subject: [PATCH 3/8] Create show page for a single report --- src/views/reports/show.vue | 86 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/views/reports/show.vue diff --git a/src/views/reports/show.vue b/src/views/reports/show.vue new file mode 100644 index 00000000..e393afc0 --- /dev/null +++ b/src/views/reports/show.vue @@ -0,0 +1,86 @@ +<template> + <div v-if="!loading"> + <header class="report-page-header-container"> + <div class="report-page-header"> + <div v-if="propertyExists(report.account, 'nickname')" class="avatar-name-container"> + <h1 >{{ $t('reports.reportOn') }}</h1> + <el-avatar v-if="propertyExists(report.account, 'avatar')" :src="report.account.avatar" size="large" class="report-page-avatar"/> + <h1>{{ report.account.nickname }}</h1> + <a v-if="propertyExists(report.account, 'url')" :href="report.account.url" target="_blank"> + <i :title="$t('userProfile.openAccountInInstance')" class="el-icon-top-right"/> + </a> + </div> + <h1 v-else>{{ $t('reports.report') }}</h1> + <h4 v-if="propertyExists(report.account, 'id')" class="id">{{ $t('reports.id') }}: {{ report.id }}</h4> + </div> + <reboot-button/> + </header> + <el-card class="report"/> + </div> +</template> + +<script> +import RebootButton from '@/components/RebootButton' + +export default { + name: 'ReportsShow', + components: { RebootButton }, + computed: { + loading() { + return this.$store.state.reports.loading + }, + report() { + return this.$store.state.reports.singleReport + } + }, + mounted: function() { + this.$store.dispatch('NeedReboot') + this.$store.dispatch('GetNodeInfo') + this.$store.dispatch('FetchSingleReport', this.$route.params.id) + }, + methods: { + propertyExists(account, property, _secondProperty) { + if (_secondProperty) { + return account[property] && account[_secondProperty] + } + return account[property] + } + } +} +</script> + +<style rel='stylesheet/scss' lang='scss'> +.report-page-header { + display: flex; + flex-direction: column; + margin: 22px 15px 22px 20px; + padding: 0; + h1 { + display: inline; + margin: 0; + } + h4 { + margin-top: 10px; + } + .avatar-name-container { + display: flex; + align-items: center; + .el-icon-top-right { + font-size: 2em; + line-height: 36px; + color: #606266; + } + } + .id { + color: gray; + } + .report-page-avatar { + margin: 0 7px 0 12px; + } +} +.report-page-header-container { + align-items: center; + display: flex; + justify-content: space-between; +} +</style> -- GitLab From 1be1dbcefba8c00ca0135710524eae10d4ff7e3d Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Tue, 27 Oct 2020 01:46:51 +0300 Subject: [PATCH 4/8] Extract Report content into a separate component and reuse it in single report component --- src/views/reports/components/Report.vue | 149 +------------- .../reports/components/ReportContent.vue | 182 ++++++++++++++++++ src/views/reports/show.vue | 7 +- 3 files changed, 190 insertions(+), 148 deletions(-) create mode 100644 src/views/reports/components/ReportContent.vue diff --git a/src/views/reports/components/Report.vue b/src/views/reports/components/Report.vue index 7b2bbe7e..81847635 100644 --- a/src/views/reports/components/Report.vue +++ b/src/views/reports/components/Report.vue @@ -28,83 +28,7 @@ </div> </div> <el-divider class="divider"/> - <div class="report-account-container"> - <span class="report-row-key">{{ $t('reports.account') }}:</span> - <div class="report-account"> - <router-link - v-if="propertyExists(report.account, 'id')" - :to="{ name: 'UsersShow', params: { id: report.account.id }}" - class="router-link"> - <img - v-if="propertyExists(report.account, 'avatar')" - :src="report.account.avatar" - alt="avatar" - class="avatar-img"> - <span v-if="propertyExists(report.account, 'nickname')" class="report-account-name">{{ report.account.nickname }}</span> - <span v-else class="report-account-name deactivated">({{ $t('users.invalidNickname') }})</span> - </router-link> - <span v-else class="report-account-name deactivated">({{ $t('users.invalidNickname') }})</span> - <a v-if="propertyExists(report.account, 'url')" :href="report.account.url" target="_blank" class="account"> - {{ $t('userProfile.openAccountInInstance') }} - <i class="el-icon-top-right"/> - </a> - </div> - </div> - <div v-if="report.content && report.content.length > 0"> - <el-divider class="divider"/> - <span class="report-row-key">{{ $t('reports.content') }}: - <span>{{ report.content }}</span> - </span> - </div> - <el-divider class="divider"/> - <div :style="showStatuses(report.statuses) ? '' : 'margin-bottom:15px'" class="report-account-container"> - <span class="report-row-key">{{ $t('reports.actor') }}:</span> - <div class="report-account"> - <router-link - v-if="propertyExists(report.actor, 'id')" - :to="{ name: 'UsersShow', params: { id: report.actor.id }}" - class="router-link"> - <img - v-if="propertyExists(report.actor, 'avatar')" - :src="report.actor.avatar" - alt="avatar" - class="avatar-img"> - <span v-if="propertyExists(report.actor, 'nickname')" class="report-account-name">{{ report.actor.nickname }}</span> - <span v-else class="report-account-name deactivated">({{ $t('users.invalidNickname') }})</span> - </router-link> - <span v-else class="report-account-name deactivated">({{ $t('users.invalidNickname') }})</span> - <a v-if="propertyExists(report.actor, 'url')" :href="report.actor.url" target="_blank" class="account"> - {{ $t('userProfile.openAccountInInstance') }} - <i class="el-icon-top-right"/> - </a> - </div> - </div> - <div v-if="showStatuses(report.statuses)" class="reported-statuses"> - <el-collapse> - <el-collapse-item :title="getStatusesTitle(report.statuses)"> - <div v-for="status in report.statuses" :key="status.id"> - <status :status="status" :account="status.account.nickname ? status.account : report.account" :show-checkbox="false" :page="currentPage"/> - </div> - </el-collapse-item> - </el-collapse> - </div> - <div class="report-notes"> - <el-collapse> - <el-collapse-item :title="getNotesTitle(report.notes)"> - <note-card v-for="(note, index) in report.notes" :key="index" :note="note" :report="report"/> - </el-collapse-item> - </el-collapse> - <div class="report-note-form"> - <el-input - v-model="notes[report.id]" - :placeholder="$t('reports.leaveNote')" - type="textarea" - rows="2"/> - <div class="report-post-note"> - <el-button @click="handleNewNote(report.id)">{{ $t('reports.postNote') }}</el-button> - </div> - </div> - </div> + <report-content :report="report"/> </el-card> </el-timeline-item> </el-timeline> @@ -123,24 +47,18 @@ <script> import moment from 'moment' -import NoteCard from './NoteCard' -import Status from '@/components/Status' import ModerateUserDropdown from './ModerateUserDropdown' +import ReportContent from './ReportContent' export default { name: 'Report', - components: { Status, ModerateUserDropdown, NoteCard }, + components: { ModerateUserDropdown, ReportContent }, props: { reports: { type: Array, required: true } }, - data() { - return { - notes: {} - } - }, computed: { loading() { return this.$store.state.reports.loading @@ -172,16 +90,6 @@ export default { return 'primary' } }, - getStatusesTitle(statuses = []) { - return `Reported statuses: ${statuses.length} item(s)` - }, - getNotesTitle(notes = []) { - return `Notes: ${notes.length} item(s)` - }, - handleNewNote(reportID) { - this.$store.dispatch('CreateReportNote', { content: this.notes[reportID], reportID }) - this.notes[reportID] = '' - }, handlePageChange(page) { this.$store.dispatch('FetchReports', page) }, @@ -193,9 +101,6 @@ export default { return account[property] && account[_secondProperty] } return account[property] - }, - showStatuses(statuses = []) { - return statuses.length > 0 } } } @@ -206,25 +111,9 @@ export default { margin: 0; height: 17px; } - .account { - line-height: 26px; - font-size: 13px; - color: #606266; - } - .account:hover { - text-decoration: underline; - } - .avatar-img { - vertical-align: bottom; - width: 15px; - height: 15px; - } .divider { margin: 15px 0; } - .deactivated { - color: gray; - } .el-card__body { padding: 17px; } @@ -279,35 +168,9 @@ export default { height: 40px; } } - .report-account { - display: flex; - align-items: baseline; - justify-content: space-between; - flex-grow: 2; - } - .report-account-container { - display: flex; - align-items: baseline; - } - .report-account-name { - font-size: 15px; - font-weight: 500; - } - .report-row-key { - font-size: 14px; - font-weight: 500; - padding-right: 5px; - } .report-title { margin: 0; } - .report-note-form { - margin: 15px 0 0 0; - } - .report-post-note { - margin: 5px 0 0 0; - text-align: right; - } .reports-pagination { margin: 25px 0; text-align: center; @@ -316,12 +179,6 @@ export default { margin: 30px 45px 45px 19px; padding: 0px; } - .router-link { - text-decoration: none; - } - .reported-statuses { - margin-top: 15px; - } .submit-button { display: block; margin: 7px 0 17px auto; diff --git a/src/views/reports/components/ReportContent.vue b/src/views/reports/components/ReportContent.vue new file mode 100644 index 00000000..67cf16b4 --- /dev/null +++ b/src/views/reports/components/ReportContent.vue @@ -0,0 +1,182 @@ +<template> + <div> + <div class="report-account-container"> + <span class="report-row-key">{{ $t('reports.account') }}:</span> + <div class="report-account"> + <router-link + v-if="propertyExists(report.account, 'id')" + :to="{ name: 'UsersShow', params: { id: report.account.id }}" + class="router-link"> + <img + v-if="propertyExists(report.account, 'avatar')" + :src="report.account.avatar" + alt="avatar" + class="avatar-img"> + <span v-if="propertyExists(report.account, 'nickname')" class="report-account-name">{{ report.account.nickname }}</span> + <span v-else class="report-account-name deactivated">({{ $t('users.invalidNickname') }})</span> + </router-link> + <span v-else class="report-account-name deactivated">({{ $t('users.invalidNickname') }})</span> + <a v-if="propertyExists(report.account, 'url')" :href="report.account.url" target="_blank" class="account"> + {{ $t('userProfile.openAccountInInstance') }} + <i class="el-icon-top-right"/> + </a> + </div> + </div> + <div v-if="report.content && report.content.length > 0"> + <el-divider class="divider"/> + <span class="report-row-key">{{ $t('reports.content') }}: + <span>{{ report.content }}</span> + </span> + </div> + <el-divider class="divider"/> + <div :style="showStatuses(report.statuses) ? '' : 'margin-bottom:15px'" class="report-account-container"> + <span class="report-row-key">{{ $t('reports.actor') }}:</span> + <div class="report-account"> + <router-link + v-if="propertyExists(report.actor, 'id')" + :to="{ name: 'UsersShow', params: { id: report.actor.id }}" + class="router-link"> + <img + v-if="propertyExists(report.actor, 'avatar')" + :src="report.actor.avatar" + alt="avatar" + class="avatar-img"> + <span v-if="propertyExists(report.actor, 'nickname')" class="report-account-name">{{ report.actor.nickname }}</span> + <span v-else class="report-account-name deactivated">({{ $t('users.invalidNickname') }})</span> + </router-link> + <span v-else class="report-account-name deactivated">({{ $t('users.invalidNickname') }})</span> + <a v-if="propertyExists(report.actor, 'url')" :href="report.actor.url" target="_blank" class="account"> + {{ $t('userProfile.openAccountInInstance') }} + <i class="el-icon-top-right"/> + </a> + </div> + </div> + <div v-if="showStatuses(report.statuses)" class="reported-statuses"> + <el-collapse> + <el-collapse-item :title="getStatusesTitle(report.statuses)"> + <div v-for="status in report.statuses" :key="status.id"> + <status :status="status" :account="status.account.nickname ? status.account : report.account" :show-checkbox="false" :page="currentPage"/> // check why it's currentPage here + </div> + </el-collapse-item> + </el-collapse> + </div> + <div> + <el-collapse> + <el-collapse-item :title="getNotesTitle(report.notes)"> + <note-card v-for="(note, index) in report.notes" :key="index" :note="note" :report="report"/> + </el-collapse-item> + </el-collapse> + <div class="report-note-form"> + <el-input + v-model="notes[report.id]" + :placeholder="$t('reports.leaveNote')" + type="textarea" + rows="2"/> + <div class="report-post-note"> + <el-button @click="handleNewNote(report.id)">{{ $t('reports.postNote') }}</el-button> + </div> + </div> + </div> + </div> +</template> + +<script> +import NoteCard from './NoteCard' +import Status from '@/components/Status' + +export default { + name: 'ReportContent', + components: { NoteCard, Status }, + props: { + report: { + type: Object, + required: true + } + }, + data() { + return { + notes: {} + } + }, + computed: { + currentPage() { + return this.$store.state.reports.currentPage + } + }, + methods: { + getNotesTitle(notes = []) { + return `Notes: ${notes.length} item(s)` + }, + getStatusesTitle(statuses = []) { + return `Reported statuses: ${statuses.length} item(s)` + }, + handleNewNote(reportID) { + this.$store.dispatch('CreateReportNote', { content: this.notes[reportID], reportID }) + this.notes[reportID] = '' + }, + propertyExists(account, property, _secondProperty) { + if (_secondProperty) { + return account[property] && account[_secondProperty] + } + return account[property] + }, + showStatuses(statuses = []) { + return statuses.length > 0 + } + } +} +</script> + +<style rel='stylesheet/scss' lang='scss'> + .account { + line-height: 26px; + font-size: 13px; + color: #606266; + } + .account:hover { + text-decoration: underline; + } + .avatar-img { + vertical-align: bottom; + width: 15px; + height: 15px; + } + .deactivated { + color: gray; + } + .divider { + margin: 15px 0; + } + .report-account { + display: flex; + align-items: baseline; + justify-content: space-between; + flex-grow: 2; + } + .report-account-container { + display: flex; + align-items: baseline; + } + .report-account-name { + font-size: 15px; + font-weight: 500; + } + .report-note-form { + margin: 15px 0 0 0; + } + .report-post-note { + margin: 5px 0 0 0; + text-align: right; + } + .report-row-key { + font-size: 14px; + font-weight: 500; + padding-right: 5px; + } + .reported-statuses { + margin-top: 15px; + } + .router-link { + text-decoration: none; + } +</style> diff --git a/src/views/reports/show.vue b/src/views/reports/show.vue index e393afc0..676a518b 100644 --- a/src/views/reports/show.vue +++ b/src/views/reports/show.vue @@ -15,16 +15,19 @@ </div> <reboot-button/> </header> - <el-card class="report"/> + <el-card class="report"> + <report-content :report="report"/> + </el-card> </div> </template> <script> import RebootButton from '@/components/RebootButton' +import ReportContent from './components/ReportContent' export default { name: 'ReportsShow', - components: { RebootButton }, + components: { RebootButton, ReportContent }, computed: { loading() { return this.$store.state.reports.loading -- GitLab From 2fce51601fbac071610b335a8b31bef70eb1e7b6 Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Wed, 28 Oct 2020 14:40:28 +0300 Subject: [PATCH 5/8] Fix styles for report show page --- src/views/reports/show.vue | 65 ++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/views/reports/show.vue b/src/views/reports/show.vue index 676a518b..35468fbb 100644 --- a/src/views/reports/show.vue +++ b/src/views/reports/show.vue @@ -1,5 +1,5 @@ <template> - <div v-if="!loading"> + <div v-if="!loading" class="report-show-page-container"> <header class="report-page-header-container"> <div class="report-page-header"> <div v-if="propertyExists(report.account, 'nickname')" class="avatar-name-container"> @@ -11,10 +11,10 @@ </a> </div> <h1 v-else>{{ $t('reports.report') }}</h1> - <h4 v-if="propertyExists(report.account, 'id')" class="id">{{ $t('reports.id') }}: {{ report.id }}</h4> </div> <reboot-button/> </header> + <h4 v-if="propertyExists(report.account, 'id')" class="id">{{ $t('reports.id') }}: {{ report.id }}</h4> <el-card class="report"> <report-content :report="report"/> </el-card> @@ -53,37 +53,46 @@ export default { </script> <style rel='stylesheet/scss' lang='scss'> -.report-page-header { - display: flex; - flex-direction: column; - margin: 22px 15px 22px 20px; - padding: 0; - h1 { - display: inline; - margin: 0; +.report-show-page-container { + .id { + color: gray; + margin: 0 15px 22px 15px; } - h4 { - margin-top: 10px; + .report { + width: 1000px; + margin: auto; } - .avatar-name-container { + .report-page-header { display: flex; - align-items: center; - .el-icon-top-right { - font-size: 2em; - line-height: 36px; - color: #606266; + flex-direction: column; + margin: 10px 0; + padding: 0; + h1 { + display: inline; + margin: 0; + } + h4 { + margin-top: 10px; + } + .avatar-name-container { + display: flex; + align-items: center; + .el-icon-top-right { + font-size: 2em; + line-height: 36px; + color: #606266; + } + } + .report-page-avatar { + margin: 0 7px 0 12px; } } - .id { - color: gray; - } - .report-page-avatar { - margin: 0 7px 0 12px; + .report-page-header-container { + align-items: center; + display: flex; + justify-content: space-between; + margin: 0 15px; + padding: 0; } } -.report-page-header-container { - align-items: center; - display: flex; - justify-content: space-between; -} </style> -- GitLab From 07c3b12a5e4fecf7bd59f47ae67649b8252ad95d Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Fri, 30 Oct 2020 21:23:23 +0300 Subject: [PATCH 6/8] Add dropdown for changing report's state and moderating user --- src/store/modules/reports.js | 64 ++++++++++++++++--- .../components/ModerateUserDropdown.vue | 31 +++++++-- src/views/reports/components/Report.vue | 12 ++-- src/views/reports/show.vue | 48 +++++++++++++- 4 files changed, 134 insertions(+), 21 deletions(-) diff --git a/src/store/modules/reports.js b/src/store/modules/reports.js index 51f08219..c42fa894 100644 --- a/src/store/modules/reports.js +++ b/src/store/modules/reports.js @@ -59,6 +59,17 @@ const reports = { } dispatch('SuccessMessage') }, + async ActivateUserFromReportShow({ commit, dispatch, getters, state }, user) { + try { + await activateUsers([user.nickname], getters.authHost, getters.token) + } catch (_e) { + return + } finally { + const updatedReport = { ...state.singleReport, account: { ...user, deactivated: false }} + commit('SET_SINGLE_REPORT', updatedReport) + } + dispatch('SuccessMessage') + }, async AddTagFromReports({ commit, dispatch, getters, state }, { user, tag, reportId }) { try { await tagUser([user.nickname], [tag], getters.authHost, getters.token) @@ -73,16 +84,31 @@ const reports = { } dispatch('SuccessMessage') }, + async AddTagFromReportsFromReportShow({ commit, dispatch, getters, state }, { user, tag }) { + try { + await tagUser([user.nickname], [tag], getters.authHost, getters.token) + } catch (_e) { + return + } finally { + const updatedReport = { ...state.singleReport, account: { ...user, tags: [...user.tags, tag] }} + commit('SET_SINGLE_REPORT', updatedReport) + } + dispatch('SuccessMessage') + }, async ChangeReportState({ commit, dispatch, getters, state }, reportsData) { - changeState(reportsData, getters.authHost, getters.token) - - const updatedReports = state.fetchedReports.map(report => { - const updatedReportsIds = reportsData.map(({ id }) => id) - return updatedReportsIds.includes(report.id) ? { ...report, state: reportsData[0].state } : report - }) + try { + await changeState(reportsData, getters.authHost, getters.token) + } catch (_e) { + return + } finally { + const updatedReports = state.fetchedReports.map(report => { + const updatedReportsIds = reportsData.map(({ id }) => id) + return updatedReportsIds.includes(report.id) ? { ...report, state: reportsData[0].state } : report + }) - commit('SET_REPORTS', updatedReports) - dispatch('FetchOpenReportsCount') + commit('SET_REPORTS', updatedReports) + dispatch('FetchOpenReportsCount') + } }, ClearFetchedReports({ commit }) { commit('SET_REPORTS', []) @@ -101,6 +127,17 @@ const reports = { } dispatch('SuccessMessage') }, + async DeactivateUserFromReportShow({ commit, dispatch, getters, state }, user) { + try { + await deactivateUsers([user.nickname], getters.authHost, getters.token) + } catch (_e) { + return + } finally { + const updatedReport = { ...state.singleReport, account: { ...user, deactivated: true }} + commit('SET_SINGLE_REPORT', updatedReport) + } + dispatch('SuccessMessage') + }, async DeleteUserFromReports({ commit, dispatch, getters, state }, { user, reportId }) { try { await deleteUsers([user.nickname], getters.authHost, getters.token) @@ -152,6 +189,17 @@ const reports = { } dispatch('SuccessMessage') }, + async RemoveTagFromReportsFromReportShow({ commit, dispatch, getters, state }, { user, tag }) { + try { + await untagUser([user.nickname], [tag], getters.authHost, getters.token) + } catch (_e) { + return + } finally { + const updatedReport = { ...state.singleReport, account: { ...user, tags: user.tags.filter(userTag => userTag !== tag) }} + commit('SET_SINGLE_REPORT', updatedReport) + } + dispatch('SuccessMessage') + }, SetReportsFilter({ commit }, filter) { commit('SET_REPORTS_FILTER', filter) }, diff --git a/src/views/reports/components/ModerateUserDropdown.vue b/src/views/reports/components/ModerateUserDropdown.vue index cf01971a..9dfb58e4 100644 --- a/src/views/reports/components/ModerateUserDropdown.vue +++ b/src/views/reports/components/ModerateUserDropdown.vue @@ -1,6 +1,7 @@ <template> <el-dropdown :hide-on-click="false" trigger="click"> - <el-button :disabled="!account.id" plain size="small" icon="el-icon-files">{{ $t('reports.moderateUser') }} + <el-button :disabled="!account.id" :size="renderedFrom === 'showPage' ? 'medium' : 'small'" plain icon="el-icon-files"> + {{ $t('reports.moderateUser') }} <i class="el-icon-arrow-down el-icon--right"/> </el-button> <el-dropdown-menu slot="dropdown"> @@ -79,6 +80,10 @@ export default { reportId: { type: String, required: true + }, + renderedFrom: { + type: String, + required: true } }, computed: { @@ -111,9 +116,15 @@ export default { }) }, handleDeactivation(user) { - user.deactivated - ? this.$store.dispatch('ActivateUserFromReports', { user, reportId: this.reportId }) - : this.$store.dispatch('DeactivateUserFromReports', { user, reportId: this.reportId }) + if (this.renderedFrom === 'showPage') { + user.deactivated + ? this.$store.dispatch('ActivateUserFromReportShow', user) + : this.$store.dispatch('DeactivateUserFromReportShow', user) + } else if (this.renderedFrom === 'reportsPage') { + user.deactivated + ? this.$store.dispatch('ActivateUserFromReports', { user, reportId: this.reportId }) + : this.$store.dispatch('DeactivateUserFromReports', { user, reportId: this.reportId }) + } }, handleDeletion(user) { this.$confirm( @@ -135,9 +146,15 @@ export default { return this.$store.state.user.id !== id }, toggleTag(user, tag) { - user.tags.includes(tag) - ? this.$store.dispatch('RemoveTagFromReports', { user, tag, reportId: this.reportId }) - : this.$store.dispatch('AddTagFromReports', { user, tag, reportId: this.reportId }) + if (this.renderedFrom === 'showPage') { + user.tags.includes(tag) + ? this.$store.dispatch('RemoveTagFromReportsFromReportShow', { user, tag }) + : this.$store.dispatch('AddTagFromReportsFromReportShow', { user, tag }) + } else if (this.renderedFrom === 'reportsPage') { + user.tags.includes(tag) + ? this.$store.dispatch('RemoveTagFromReports', { user, tag, reportId: this.reportId }) + : this.$store.dispatch('AddTagFromReports', { user, tag, reportId: this.reportId }) + } } } } diff --git a/src/views/reports/components/Report.vue b/src/views/reports/components/Report.vue index 81847635..423aa439 100644 --- a/src/views/reports/components/Report.vue +++ b/src/views/reports/components/Report.vue @@ -24,7 +24,11 @@ <el-dropdown-item v-if="report.state !== 'closed'" @click.native="changeReportState('closed', report.id)">{{ $t('reports.close') }}</el-dropdown-item> </el-dropdown-menu> </el-dropdown> - <moderate-user-dropdown v-if="propertyExists(report.account, 'nickname')" :account="report.account" :report-id="report.id" /> + <moderate-user-dropdown + v-if="propertyExists(report.account, 'nickname')" + :account="report.account" + :report-id="report.id" + :rendered-from="'reportsPage'"/> </div> </div> <el-divider class="divider"/> @@ -74,12 +78,12 @@ export default { } }, methods: { - changeReportState(state, id) { - this.$store.dispatch('ChangeReportState', [{ state, id }]) - }, capitalizeFirstLetter(str) { return str.charAt(0).toUpperCase() + str.slice(1) }, + changeReportState(state, id) { + this.$store.dispatch('ChangeReportState', [{ state, id }]) + }, getStateType(state) { switch (state) { case 'closed': diff --git a/src/views/reports/show.vue b/src/views/reports/show.vue index 35468fbb..5f30f376 100644 --- a/src/views/reports/show.vue +++ b/src/views/reports/show.vue @@ -12,7 +12,23 @@ </div> <h1 v-else>{{ $t('reports.report') }}</h1> </div> - <reboot-button/> + <div> + <el-tag :type="getStateType(report.state)" class="report-tag">{{ capitalizeFirstLetter(report.state) }}</el-tag> + <el-dropdown trigger="click"> + <el-button plain icon="el-icon-edit" class="report-actions-button">{{ $t('reports.changeState') }}<i class="el-icon-arrow-down el-icon--right"/></el-button> + <el-dropdown-menu slot="dropdown"> + <el-dropdown-item v-if="report.state !== 'resolved'" @click.native="changeReportState('resolved', report.id)">{{ $t('reports.resolve') }}</el-dropdown-item> + <el-dropdown-item v-if="report.state !== 'open'" @click.native="changeReportState('open', report.id)">{{ $t('reports.reopen') }}</el-dropdown-item> + <el-dropdown-item v-if="report.state !== 'closed'" @click.native="changeReportState('closed', report.id)">{{ $t('reports.close') }}</el-dropdown-item> + </el-dropdown-menu> + </el-dropdown> + <moderate-user-dropdown + v-if="propertyExists(report.account, 'nickname')" + :account="report.account" + :report-id="report.id" + :rendered-from="'showPage'"/> + <reboot-button/> + </div> </header> <h4 v-if="propertyExists(report.account, 'id')" class="id">{{ $t('reports.id') }}: {{ report.id }}</h4> <el-card class="report"> @@ -22,12 +38,13 @@ </template> <script> +import ModerateUserDropdown from './components/ModerateUserDropdown' import RebootButton from '@/components/RebootButton' import ReportContent from './components/ReportContent' export default { name: 'ReportsShow', - components: { RebootButton, ReportContent }, + components: { ModerateUserDropdown, RebootButton, ReportContent }, computed: { loading() { return this.$store.state.reports.loading @@ -40,8 +57,26 @@ export default { this.$store.dispatch('NeedReboot') this.$store.dispatch('GetNodeInfo') this.$store.dispatch('FetchSingleReport', this.$route.params.id) + this.$store.dispatch('FetchTagPolicySetting') }, methods: { + capitalizeFirstLetter(str) { + return str.charAt(0).toUpperCase() + str.slice(1) + }, + async changeReportState(state, id) { + await this.$store.dispatch('ChangeReportState', [{ state, id }]) + this.$store.dispatch('FetchSingleReport', id) + }, + getStateType(state) { + switch (state) { + case 'closed': + return 'info' + case 'resolved': + return 'success' + default: + return 'primary' + } + }, propertyExists(account, property, _secondProperty) { if (_secondProperty) { return account[property] && account[_secondProperty] @@ -62,6 +97,9 @@ export default { width: 1000px; margin: auto; } + .report-actions-button { + margin: 3px 0 6px; + } .report-page-header { display: flex; flex-direction: column; @@ -94,5 +132,11 @@ export default { margin: 0 15px; padding: 0; } + .report-tag { + height: 36px; + line-height: 36px; + padding: 0 20px; + font-size: 14px; + } } </style> -- GitLab From 2145706095d0ba96ae0dd3dcb1f476909a2c68cf Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Fri, 30 Oct 2020 21:49:43 +0300 Subject: [PATCH 7/8] Fix moderate user dropdown width --- src/views/reports/components/ModerateUserDropdown.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/views/reports/components/ModerateUserDropdown.vue b/src/views/reports/components/ModerateUserDropdown.vue index 9dfb58e4..3d5618f1 100644 --- a/src/views/reports/components/ModerateUserDropdown.vue +++ b/src/views/reports/components/ModerateUserDropdown.vue @@ -4,7 +4,7 @@ {{ $t('reports.moderateUser') }} <i class="el-icon-arrow-down el-icon--right"/> </el-button> - <el-dropdown-menu slot="dropdown"> + <el-dropdown-menu slot="dropdown" class="moderate-user-dropdown"> <el-dropdown-item v-if="showDeactivatedButton(account)" @click.native="handleDeactivation(account)"> @@ -159,3 +159,9 @@ export default { } } </script> + +<style rel='stylesheet/scss' lang='scss'> +.moderate-user-dropdown { + width: 350px; +} +</style> -- GitLab From 43c524797d13e4db3661771807e99d5e1a44c87c Mon Sep 17 00:00:00 2001 From: Angelina Filippova <linakirsanova@gmail.com> Date: Fri, 30 Oct 2020 21:51:37 +0300 Subject: [PATCH 8/8] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 205b8203..0d2edbbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Evicting and banning objects from the MediaProxy cache is disabled if MediaProxy is disabled on the Settings tab. Add ability to enable MediaProxy and Invalidation from MediaProxy tab. - Allow to upload the custom Terms of Service and Instance Panel HTML pages via Admin API +- Add Report show page and link Moderation log references to the respective reports ### Changed -- GitLab