diff --git a/src/store/modules/reports.js b/src/store/modules/reports.js index 8ff3a90973e7a8d4da6e25bd5bd10d1481b5c98a..b532184704e0672cf47d3eda1a6df2aaacd85ca3 100644 --- a/src/store/modules/reports.js +++ b/src/store/modules/reports.js @@ -3,8 +3,8 @@ import { changeState, changeStatusScope, deleteStatus, fetchReports, filterRepor const reports = { state: { fetchedReports: [], + groupReports: true, idOfLastReport: '', - page_limit: 5, stateFilter: '', loading: true }, @@ -20,6 +20,9 @@ const reports = { }, SET_REPORTS_FILTER: (state, filter) => { state.stateFilter = filter + }, + SET_REPORTS_GROUPING: (state) => { + state.groupReports = !state.groupReports } }, actions: { @@ -42,20 +45,18 @@ const reports = { }, async FetchReports({ commit, getters, state }) { commit('SET_LOADING', true) - - const response = state.stateFilter.length === 0 + const { data } = state.stateFilter.length === 0 ? await fetchReports(state.page_limit, state.idOfLastReport, getters.authHost, getters.token) : await filterReports(state.stateFilter, state.page_limit, state.idOfLastReport, getters.authHost, getters.token) - const reports = state.fetchedReports.concat(response.data.reports) - const id = reports.length > 0 ? reports[reports.length - 1].id : state.idOfLastReport - - commit('SET_REPORTS', reports) - commit('SET_LAST_REPORT_ID', id) + commit('SET_REPORTS', data.reports) commit('SET_LOADING', false) }, SetFilter({ commit }, filter) { commit('SET_REPORTS_FILTER', filter) + }, + ToggleReportsGrouping({ commit }) { + commit('SET_REPORTS_GROUPING') } } } diff --git a/src/views/reports/components/GroupedReport.vue b/src/views/reports/components/GroupedReport.vue new file mode 100644 index 0000000000000000000000000000000000000000..ef2dc6ceb2987ef946b8ee2fbe41496ff82e0e2c --- /dev/null +++ b/src/views/reports/components/GroupedReport.vue @@ -0,0 +1,120 @@ +<template> + <el-card> + <div class="header-container"> + <div> + <h3 class="report-title">{{ $t('reports.reportsOn') }} {{ group.account.display_name }}</h3> + </div> + <div> + <moderate-user-dropdown :account="group.account"/> + </div> + </div> + <div> + <div class="line"/> + <span class="report-row-key">{{ $t('reports.account') }}:</span> + <img + :src="group.account.avatar" + alt="avatar" + class="avatar-img"> + <a :href="group.account.url" target="_blank"> + <span>{{ group.account.acct }}</span> + </a> + </div> + <div> + <div class="line"/> + <span class="report-row-key">{{ $t('reports.actors') }}:</span> + <span v-for="actor in group.actors" :key="actor.id"> + <a :href="actor.url" target="_blank"> + <span>{{ actor.acct }}, </span> + </a> + </span> + </div> + <div v-if="group.status"> + <div class="line"/> + <span class="report-row-key">{{ $t('reports.reportedStatus') }}:</span> + <statuses :statuses="group.status" class="reported-status"/> + </div> + <div v-if="group.reports"> + <div class="line"/> + <el-collapse> + <el-collapse-item :title="$t('reports.reports')"> + <report-card :reports="group.reports"/> + </el-collapse-item> + </el-collapse> + </div> + </el-card> +</template> + +<script> +import ModerateUserDropdown from './ModerateUserDropdown' +import ReportCard from './ReportCard' +import Statuses from './Statuses' + +export default { + name: 'Report', + components: { ModerateUserDropdown, ReportCard, Statuses }, + props: { + group: { + type: Object, + required: true + } + }, + methods: { + changeMultipleReportsState(reportState, groupOfReports) { + // this.$store.dispatch('ChangeReportState', { reportState, reportId }) + } + } +} +</script> + +<style rel='stylesheet/scss' lang='scss'> + a { + text-decoration: underline; + } + .avatar-img { + vertical-align: bottom; + width: 15px; + height: 15px; + margin-left: 5px; + } + .el-card__body { + padding: 17px; + } + .el-card__header { + background-color: #FAFAFA; + padding: 10px 20px; + } + .el-icon-arrow-right { + margin-right: 6px; + } + .header-container { + display: flex; + justify-content: space-between; + align-items: baseline; + height: 40px; + } + .line { + width: 100%; + height: 0; + border: 0.5px solid #EBEEF5; + margin: 15px 0 15px; + } + .report-title { + margin: 0; + } + .report-row-key { + font-size: 14px; + font-weight: 500; + } + .reported-status { + margin-top: 15px; + } + @media + only screen and (max-width: 760px), + (min-device-width: 768px) and (max-device-width: 1024px) { + .header-container { + display: flex; + flex-direction: column; + height: 80px; + } + } +</style> diff --git a/src/views/reports/components/ReportCard.vue b/src/views/reports/components/ReportCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..62fa1945d077ddd92627ea2e7a54c5a189e4228b --- /dev/null +++ b/src/views/reports/components/ReportCard.vue @@ -0,0 +1,89 @@ +<template> + <div> + <el-card v-for="report in reports" :key="report.id" class="report-card"> + <div slot="header"> + <div class="report-header"> + <div class="report-actor-container"> + <div class="report-actor"> + <img :src="report.actor.avatar" class="report-avatar-img"> + <h3 class="report-actor-name">{{ report.actor.display_name }}</h3> + </div> + <a :href="report.actor.url" target="_blank"> + @{{ report.actor.acct }} + </a> + </div> + </div> + </div> + <div class="report-body"> + <span class="report-content" v-html="report.content"/> + {{ parseTimestamp(report.created_at) }} + </div> + </el-card> + </div> +</template> + +<script> +import moment from 'moment' + +export default { + name: 'Statuses', + props: { + reports: { + type: Array, + required: true + } + }, + methods: { + parseTimestamp(timestamp) { + return moment(timestamp).format('YYYY-MM-DD HH:mm') + } + } +} +</script> + +<style rel='stylesheet/scss' lang='scss'> + a { + text-decoration: underline; + } + .report-actor { + display: flex; + align-items: center; + } + .report-actor-name { + margin: 0; + height: 22px; + } + .report-avatar-img { + width: 15px; + height: 15px; + margin-right: 5px; + } + .report-body { + display: flex; + flex-direction: column; + } + .report-card { + margin-bottom: 15px; + } + .report-content { + font-size: 15px; + } + .report-header { + display: flex; + justify-content: space-between; + } + @media + only screen and (max-width: 760px), + (min-device-width: 768px) and (max-device-width: 1024px) { + .el-card__header { + padding: 10px 17px; + } + .report-actor-container { + margin-bottom: 5px; + } + .report-header { + display: flex; + flex-direction: column; + } + } +</style> diff --git a/src/views/reports/index.vue b/src/views/reports/index.vue index 928e2b2fb4e8f0a8d15f6288da425c8819f2ae4b..f35ab2e33a766b0b52bb5f98bcbb4e9b9f840f92 100644 --- a/src/views/reports/index.vue +++ b/src/views/reports/index.vue @@ -3,12 +3,15 @@ <h1>{{ $t('reports.reports') }}</h1> <div class="filter-container"> <reports-filter/> - <el-checkbox v-model="groupReports" class="group-reports-checkbox" @change="toggleReportsGrouping"> + <el-checkbox v-model="groupReports" class="group-reports-checkbox"> Group reports by statuses </el-checkbox> </div> <div class="block"> - <el-timeline class="timeline"> + <el-timeline v-if="groupReports" class="timeline"> + <grouped-report v-loading="loading" v-for="group in groups" :group="group" :key="group.id"/> + </el-timeline> + <el-timeline v-else class="timeline"> <report v-loading="loading" v-for="report in reports" :report="report" :key="report.id"/> </el-timeline> <div v-if="reports.length === 0" class="no-reports-message"> @@ -29,14 +32,181 @@ </template> <script> +import GroupedReport from './components/GroupedReport' import Report from './components/Report' import ReportsFilter from './components/ReportsFilter' export default { - components: { Report, ReportsFilter }, + components: { GroupedReport, Report, ReportsFilter }, computed: { - groupReports() { - return this.$store.state.reports.groupReports + groups() { + return [{ + date: '19-07-2019', + account: { + 'acct': 'user', + 'avatar': 'https://pleroma.example.org/images/avi.png', + 'avatar_static': 'https://pleroma.example.org/images/avi.png', + 'bot': false, + 'created_at': '2019-04-23T17:32:04.000Z', + 'display_name': 'User', + 'emojis': [], + 'fields': [], + 'followers_count': 1, + 'following_count': 1, + 'header': 'https://pleroma.example.org/images/banner.png', + 'header_static': 'https://pleroma.example.org/images/banner.png', + 'id': '9i6dAJqSGSKMzLG2Lo', + 'locked': false, + 'note': '', + 'pleroma': { + 'confirmation_pending': false, + 'hide_favorites': true, + 'hide_followers': false, + 'hide_follows': false, + 'is_admin': false, + 'is_moderator': false, + 'relationship': {}, + 'tags': [] + }, + 'source': { + 'note': '', + 'pleroma': {}, + 'sensitive': false + }, + 'tags': ['force_unlisted'], + 'statuses_count': 3, + 'url': 'https://pleroma.example.org/users/user', + 'username': 'user' + }, + actors: [ + { 'acct': 'lain', 'url': 'https://pleroma.example.org/users/lain' }, + { 'acct': 'linafilippova1', 'url': 'https://pleroma.example.org/users/linafilippova1' } + ], + reports: [ + { + 'actor': { + 'acct': 'lain', + 'avatar': 'https://pleroma.example.org/images/avi.png', + 'display_name': 'Roger Braun', + 'url': 'https://pleroma.example.org/users/lain' + }, + 'content': 'Please delete it', + 'created_at': '2019-04-29T19:48:15.000Z', + 'id': '9iJGOv1j8hxuw19bcm', + 'state': 'open' + }, + { 'actor': { + 'acct': 'linafilippova1', + 'avatar': 'https://pleroma.example.org/images/avi.png', + 'display_name': 'Lina Filippova', + 'url': 'https://pleroma.example.org/users/linafilippova1' + }, + 'content': 'This is an assault', + 'created_at': '2019-03-01T19:48:15.000Z', + 'id': '9iJGOv1alksjdf3r', + 'state': 'resolve' + } + ], + status: [{ + 'account': { + 'acct': 'user', + 'avatar': 'https://pleroma.example.org/images/avi.png', + 'avatar_static': 'https://pleroma.example.org/images/avi.png', + 'bot': false, + 'created_at': '2019-04-23T17:32:04.000Z', + 'display_name': 'User', + 'emojis': [], + 'fields': [], + 'followers_count': 1, + 'following_count': 1, + 'header': 'https://pleroma.example.org/images/banner.png', + 'header_static': 'https://pleroma.example.org/images/banner.png', + 'id': '9i6dAJqSGSKMzLG2Lo', + 'locked': false, + 'note': '', + 'pleroma': { + 'confirmation_pending': false, + 'hide_favorites': true, + 'hide_followers': false, + 'hide_follows': false, + 'is_admin': false, + 'is_moderator': false, + 'relationship': {}, + 'tags': [] + }, + 'source': { + 'note': '', + 'pleroma': {}, + 'sensitive': false + }, + 'tags': ['force_unlisted'], + 'statuses_count': 3, + 'url': 'https://pleroma.example.org/users/user', + 'username': 'user' + }, + 'application': { + 'name': 'Web', + 'website': null + }, + 'bookmarked': false, + 'card': null, + 'content': '<span class=\"h-card\"><a data-user=\"9hEkA5JsvAdlSrocam\" class=\"u-url mention\" href=\"https://pleroma.example.org/users/lain\">@<span>lain</span></a></span> click on my link <a href=\"https://www.google.com/\">https://www.google.com/</a>', + 'created_at': '2019-04-23T19:15:47.000Z', + 'emojis': [], + 'favourited': false, + 'favourites_count': 0, + 'id': '9i6mQ9uVrrOmOime8m', + 'in_reply_to_account_id': null, + 'in_reply_to_id': null, + 'language': null, + 'media_attachments': [], + 'mentions': [ + { + 'acct': 'lain', + 'id': '9hEkA5JsvAdlSrocam', + 'url': 'https://pleroma.example.org/users/lain', + 'username': 'lain' + }, + { + 'acct': 'user', + 'id': '9i6dAJqSGSKMzLG2Lo', + 'url': 'https://pleroma.example.org/users/user', + 'username': 'user' + } + ], + 'muted': false, + 'pinned': false, + 'pleroma': { + 'content': { + 'text/plain': '@lain click on my link https://www.google.com/' + }, + 'conversation_id': 28, + 'in_reply_to_account_acct': null, + 'local': true, + 'spoiler_text': { + 'text/plain': '' + } + }, + 'reblog': null, + 'reblogged': false, + 'reblogs_count': 0, + 'replies_count': 0, + 'sensitive': false, + 'spoiler_text': '', + 'tags': [], + 'uri': 'https://pleroma.example.org/objects/8717b90f-8e09-4b58-97b0-e3305472b396', + 'url': 'https://pleroma.example.org/notice/9i6mQ9uVrrOmOime8m', + 'visibility': 'direct' + }] + }] + }, + groupReports: { + get() { + return this.$store.state.reports.groupReports + }, + set() { + this.toggleReportsGrouping() + } }, loading() { return this.$store.state.reports.loading @@ -68,7 +238,7 @@ export default { this.$store.dispatch('FetchReports', { page }) }, toggleReportsGrouping() { - + this.$store.dispatch('ToggleReportsGrouping') } // handleScroll(reports) { // const bottomOfWindow = document.documentElement.scrollHeight - document.documentElement.scrollTop === document.documentElement.clientHeight