forked from AkkomaGang/admin-fe
Merge branch 'feature/combine-reports-with-one-topic' into 'master'
Combine reports with one topic and add pagination See merge request pleroma/admin-fe!52
This commit is contained in:
commit
253fa33235
26 changed files with 1414 additions and 782 deletions
|
@ -11,18 +11,41 @@ const reports = [
|
|||
{ created_at: '2019-05-18T13:01:33.000Z', account: { acct: 'nick', display_name: 'Nick Keys', tags: [] }, actor: { acct: 'admin' }, state: 'closed', id: '4', content: '', statuses: [] }
|
||||
]
|
||||
|
||||
export async function fetchReports(limit, max_id, authHost, token) {
|
||||
const paginatedReports = max_id.length > 0 ? reports.slice(5) : reports.slice(0, 5)
|
||||
return Promise.resolve({ data: { reports: paginatedReports }})
|
||||
const groupedReports = [
|
||||
{ account: { avatar: 'http://localhost:4000/images/avi.png', confirmation_pending: false, deactivated: false, display_name: 'leo', id: '9oG0YghgBi94EATI9I', local: true, nickname: 'leo', roles: { admin: false, moderator: false }, tags: [] },
|
||||
actors: [{ acct: 'admin', avatar: 'http://localhost:4000/images/avi.png', deactivated: false, display_name: 'admin', id: '9oFz4pTauG0cnJ581w', local: true, nickname: 'admin', roles: { admin: false, moderator: false }, tags: [], url: 'http://localhost:4000/users/admin', username: 'admin' }],
|
||||
date: '2019-11-23T12:56:11.969772Z',
|
||||
reports: [
|
||||
{ created_at: '2019-05-21T21:35:33.000Z', account: { acct: 'benj', display_name: 'Benjamin Fame', tags: [] }, actor: { acct: 'admin' }, state: 'open', id: '2', content: 'This is a report', statuses: [] },
|
||||
{ created_at: '2019-05-20T22:45:33.000Z', account: { acct: 'alice', display_name: 'Alice Pool', tags: [] }, actor: { acct: 'admin2' }, state: 'resolved', id: '7', content: 'Please block this user', statuses: [
|
||||
{ account: { display_name: 'Alice Pool', avatar: '' }, visibility: 'public', sensitive: false, id: '11', content: 'Hey!', url: '', created_at: '2019-05-10T21:35:33.000Z' },
|
||||
{ account: { display_name: 'Alice Pool', avatar: '' }, visibility: 'unlisted', sensitive: true, id: '10', content: 'Bye!', url: '', created_at: '2019-05-10T21:00:33.000Z' }
|
||||
] }
|
||||
],
|
||||
status: {
|
||||
account: { acct: 'leo' },
|
||||
content: 'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis',
|
||||
created_at: '2019-11-23T12:55:20.000Z',
|
||||
id: '9pFoQO69piu7cUDnJg',
|
||||
url: 'http://localhost:4000/notice/9pFoQO69piu7cUDnJg',
|
||||
visibility: 'unlisted',
|
||||
sensitive: true
|
||||
},
|
||||
status_deleted: false
|
||||
}
|
||||
]
|
||||
|
||||
export async function fetchReports(filter, page, pageSize, authHost, token) {
|
||||
return filter.length > 0
|
||||
? Promise.resolve({ data: { reports: reports.filter(report => report.state === filter) }})
|
||||
: Promise.resolve({ data: { reports }})
|
||||
}
|
||||
|
||||
export async function filterReports(filter, limit, max_id, authHost, token) {
|
||||
const filteredReports = reports.filter(report => report.state === filter)
|
||||
const paginatedReports = max_id.length > 0 ? filteredReports.slice(5) : filteredReports.slice(0, 5)
|
||||
return Promise.resolve({ data: { reports: paginatedReports }})
|
||||
export async function fetchGroupedReports(authHost, token) {
|
||||
return Promise.resolve({ data: { reports: groupedReports }})
|
||||
}
|
||||
|
||||
export async function changeState(state, id, authHost, token) {
|
||||
export async function changeState(reportsData, authHost, token) {
|
||||
return Promise.resolve({ data: '' })
|
||||
}
|
||||
|
||||
|
|
7
src/api/__mocks__/status.js
Normal file
7
src/api/__mocks__/status.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export async function changeStatusScope(id, sensitive, visibility, authHost, token) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
export async function deleteStatus(id, authHost, token) {
|
||||
return Promise.resolve()
|
||||
}
|
|
@ -2,48 +2,32 @@ import request from '@/utils/request'
|
|||
import { getToken } from '@/utils/auth'
|
||||
import { baseName } from './utils'
|
||||
|
||||
export async function changeState(state, id, authHost, token) {
|
||||
export async function changeState(reports, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/reports`,
|
||||
method: 'patch',
|
||||
headers: authHeaders(token),
|
||||
data: { reports: [{ id, state }] }
|
||||
data: { reports }
|
||||
})
|
||||
}
|
||||
|
||||
export async function changeStatusScope(id, sensitive, visibility, authHost, token) {
|
||||
export async function fetchReports(filter, page, pageSize, authHost, token) {
|
||||
const url = filter.length > 0
|
||||
? `/api/pleroma/admin/reports?state=${filter}&page=${page}&page_size=${pageSize}`
|
||||
: `/api/pleroma/admin/reports?page=${page}&page_size=${pageSize}`
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/statuses/${id}`,
|
||||
method: 'put',
|
||||
headers: authHeaders(token),
|
||||
data: { sensitive, visibility }
|
||||
})
|
||||
}
|
||||
|
||||
export async function deleteStatus(id, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/statuses/${id}`,
|
||||
method: 'delete',
|
||||
headers: authHeaders(token)
|
||||
})
|
||||
}
|
||||
|
||||
export async function fetchReports(limit, max_id, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/reports?limit=${limit}&max_id=${max_id}`,
|
||||
url,
|
||||
method: 'get',
|
||||
headers: authHeaders(token)
|
||||
})
|
||||
}
|
||||
|
||||
export async function filterReports(filter, limit, max_id, authHost, token) {
|
||||
export async function fetchGroupedReports(authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/reports?state=${filter}&limit=${limit}&max_id=${max_id}`,
|
||||
url: `/api/pleroma/admin/grouped_reports`,
|
||||
method: 'get',
|
||||
headers: authHeaders(token)
|
||||
})
|
||||
|
|
24
src/api/status.js
Normal file
24
src/api/status.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import request from '@/utils/request'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { baseName } from './utils'
|
||||
|
||||
export async function changeStatusScope(id, sensitive, visibility, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/statuses/${id}`,
|
||||
method: 'put',
|
||||
headers: authHeaders(token),
|
||||
data: { sensitive, visibility }
|
||||
})
|
||||
}
|
||||
|
||||
export async function deleteStatus(id, authHost, token) {
|
||||
return await request({
|
||||
baseURL: baseName(authHost),
|
||||
url: `/api/pleroma/admin/statuses/${id}`,
|
||||
method: 'delete',
|
||||
headers: authHeaders(token)
|
||||
})
|
||||
}
|
||||
|
||||
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
|
|
@ -253,6 +253,7 @@ export default {
|
|||
},
|
||||
reports: {
|
||||
reports: 'Reports',
|
||||
groupedReports: 'Grouped reports',
|
||||
reply: 'Reply',
|
||||
from: 'From',
|
||||
showNotes: 'Show notes',
|
||||
|
@ -264,19 +265,32 @@ export default {
|
|||
deleteCompleted: 'Delete comleted',
|
||||
deleteCanceled: 'Delete canceled',
|
||||
noNotes: 'No notes to display',
|
||||
changeState: 'Change report state',
|
||||
changeState: "Change report's state",
|
||||
changeAllReports: 'Change all reports',
|
||||
changeScope: 'Change scope',
|
||||
moderateUser: 'Moderate user',
|
||||
resolve: 'Resolve',
|
||||
reopen: 'Reopen',
|
||||
close: 'Close',
|
||||
resolveAll: 'Resolve all',
|
||||
reopenAll: 'Reopen all',
|
||||
closeAll: 'Close all',
|
||||
addSensitive: 'Add Sensitive flag',
|
||||
removeSensitive: 'Remove Sensitive flag',
|
||||
public: 'Make status public',
|
||||
private: 'Make status private',
|
||||
unlisted: 'Make status unlisted',
|
||||
sensitive: 'Sensitive',
|
||||
deleteStatus: 'Delete status'
|
||||
deleteStatus: 'Delete status',
|
||||
reportOn: 'Report on',
|
||||
reportsOn: 'Reports on',
|
||||
id: 'ID',
|
||||
account: 'Account',
|
||||
actor: 'Actor',
|
||||
actors: 'Actors',
|
||||
content: 'Content',
|
||||
reportedStatus: 'Reported status',
|
||||
statusDeleted: 'This status has been deleted'
|
||||
},
|
||||
reportsFilter: {
|
||||
inputPlaceholder: 'Select filter',
|
||||
|
|
|
@ -8,6 +8,7 @@ import permission from './modules/permission'
|
|||
import relays from './modules/relays'
|
||||
import reports from './modules/reports'
|
||||
import settings from './modules/settings'
|
||||
import status from './modules/status'
|
||||
import tagsView from './modules/tagsView'
|
||||
import user from './modules/user'
|
||||
import userProfile from './modules/userProfile'
|
||||
|
@ -27,6 +28,7 @@ const store = new Vuex.Store({
|
|||
relays,
|
||||
reports,
|
||||
settings,
|
||||
status,
|
||||
tagsView,
|
||||
user,
|
||||
userProfile,
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { changeState, changeStatusScope, deleteStatus, fetchReports, filterReports } from '@/api/reports'
|
||||
import { changeState, fetchReports, fetchGroupedReports } from '@/api/reports'
|
||||
|
||||
const reports = {
|
||||
state: {
|
||||
fetchedReports: [],
|
||||
idOfLastReport: '',
|
||||
page_limit: 5,
|
||||
fetchedGroupedReports: [],
|
||||
totalReportsCount: 0,
|
||||
currentPage: 1,
|
||||
pageSize: 50,
|
||||
groupReports: false,
|
||||
stateFilter: '',
|
||||
loading: true
|
||||
},
|
||||
|
@ -15,67 +18,67 @@ const reports = {
|
|||
SET_LOADING: (state, status) => {
|
||||
state.loading = status
|
||||
},
|
||||
SET_PAGE: (state, page) => {
|
||||
state.currentPage = page
|
||||
},
|
||||
SET_REPORTS: (state, reports) => {
|
||||
state.fetchedReports = reports
|
||||
},
|
||||
SET_GROUPED_REPORTS: (state, reports) => {
|
||||
state.fetchedGroupedReports = reports
|
||||
},
|
||||
SET_REPORTS_COUNT: (state, total) => {
|
||||
state.totalReportsCount = total
|
||||
},
|
||||
SET_REPORTS_FILTER: (state, filter) => {
|
||||
state.stateFilter = filter
|
||||
},
|
||||
SET_REPORTS_GROUPING: (state) => {
|
||||
state.groupReports = !state.groupReports
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async ChangeReportState({ commit, getters, state }, { reportState, reportId }) {
|
||||
changeState(reportState, reportId, getters.authHost, getters.token)
|
||||
async ChangeReportState({ commit, getters, state }, reportsData) {
|
||||
changeState(reportsData, getters.authHost, getters.token)
|
||||
|
||||
const updatedReports = state.fetchedReports.map(report => {
|
||||
return report.id === reportId ? { ...report, state: reportState } : report
|
||||
const updatedReportsIds = reportsData.map(({ id }) => id)
|
||||
return updatedReportsIds.includes(report.id) ? { ...report, state: reportsData[0].state } : report
|
||||
})
|
||||
|
||||
const updatedGroupedReports = state.fetchedGroupedReports.map(group => {
|
||||
const updatedReportsIds = reportsData.map(({ id }) => id)
|
||||
const updatedReports = group.reports.map(report => updatedReportsIds.includes(report.id) ? { ...report, state: reportsData[0].state } : report)
|
||||
return { ...group, reports: updatedReports }
|
||||
})
|
||||
|
||||
commit('SET_REPORTS', updatedReports)
|
||||
},
|
||||
async ChangeStatusScope({ commit, getters, state }, { statusId, isSensitive, visibility, reportId }) {
|
||||
const { data } = await changeStatusScope(statusId, isSensitive, visibility, getters.authHost, getters.token)
|
||||
const updatedReports = state.fetchedReports.map(report => {
|
||||
if (report.id === reportId) {
|
||||
const statuses = report.statuses.map(status => status.id === statusId ? data : status)
|
||||
return { ...report, statuses }
|
||||
} else {
|
||||
return report
|
||||
}
|
||||
})
|
||||
commit('SET_REPORTS', updatedReports)
|
||||
commit('SET_GROUPED_REPORTS', updatedGroupedReports)
|
||||
},
|
||||
ClearFetchedReports({ commit }) {
|
||||
commit('SET_REPORTS', [])
|
||||
commit('SET_LAST_REPORT_ID', '')
|
||||
},
|
||||
async DeleteStatus({ commit, getters, state }, { statusId, reportId }) {
|
||||
deleteStatus(statusId, getters.authHost, getters.token)
|
||||
const updatedReports = state.fetchedReports.map(report => {
|
||||
if (report.id === reportId) {
|
||||
const statuses = report.statuses.filter(status => status.id !== statusId)
|
||||
return { ...report, statuses }
|
||||
} else {
|
||||
return report
|
||||
}
|
||||
})
|
||||
commit('SET_REPORTS', updatedReports)
|
||||
},
|
||||
async FetchReports({ commit, getters, state }) {
|
||||
async FetchReports({ commit, getters, state }, page) {
|
||||
commit('SET_LOADING', true)
|
||||
const { data } = await fetchReports(state.stateFilter, page, state.pageSize, getters.authHost, getters.token)
|
||||
|
||||
const response = 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)
|
||||
commit('SET_REPORTS', data.reports)
|
||||
commit('SET_REPORTS_COUNT', data.total)
|
||||
commit('SET_PAGE', page)
|
||||
commit('SET_LOADING', false)
|
||||
},
|
||||
async FetchGroupedReports({ commit, getters }) {
|
||||
commit('SET_LOADING', true)
|
||||
const { data } = await fetchGroupedReports(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_GROUPED_REPORTS', data.reports)
|
||||
commit('SET_LOADING', false)
|
||||
},
|
||||
SetFilter({ commit }, filter) {
|
||||
commit('SET_REPORTS_FILTER', filter)
|
||||
},
|
||||
ToggleReportsGrouping({ commit }) {
|
||||
commit('SET_REPORTS_GROUPING')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
src/store/modules/status.js
Normal file
28
src/store/modules/status.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { changeStatusScope, deleteStatus } from '@/api/status'
|
||||
|
||||
const status = {
|
||||
actions: {
|
||||
async ChangeStatusScope({ dispatch, getters }, { statusId, isSensitive, visibility, reportCurrentPage, userId, godmode }) {
|
||||
await changeStatusScope(statusId, isSensitive, visibility, getters.authHost, getters.token)
|
||||
if (reportCurrentPage !== 0) { // called from Reports
|
||||
dispatch('FetchReports', reportCurrentPage)
|
||||
} else if (userId.length > 0) { // called from User profile
|
||||
dispatch('FetchUserStatuses', { userId, godmode })
|
||||
} else { // called from GroupedReports
|
||||
dispatch('FetchGroupedReports')
|
||||
}
|
||||
},
|
||||
async DeleteStatus({ dispatch, getters }, { statusId, reportCurrentPage, userId, godmode }) {
|
||||
await deleteStatus(statusId, getters.authHost, getters.token)
|
||||
if (reportCurrentPage !== 0) {
|
||||
dispatch('FetchReports', reportCurrentPage)
|
||||
} else if (userId.length > 0) {
|
||||
dispatch('FetchUserStatuses', { userId, godmode })
|
||||
} else {
|
||||
dispatch('FetchGroupedReports')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default status
|
|
@ -2,33 +2,42 @@ import { fetchUser, fetchUserStatuses } from '@/api/users'
|
|||
|
||||
const userProfile = {
|
||||
state: {
|
||||
statuses: [],
|
||||
statusesLoading: true,
|
||||
user: {},
|
||||
loading: true,
|
||||
statuses: []
|
||||
userProfileLoading: true
|
||||
},
|
||||
mutations: {
|
||||
SET_STATUSES: (state, statuses) => {
|
||||
state.statuses = statuses
|
||||
},
|
||||
SET_STATUSES_LOADING: (state, status) => {
|
||||
state.statusesLoading = status
|
||||
},
|
||||
SET_USER: (state, user) => {
|
||||
state.user = user
|
||||
},
|
||||
SET_LOADING: (state, status) => {
|
||||
state.loading = status
|
||||
},
|
||||
SET_STATUSES: (state, statuses) => {
|
||||
state.statuses = statuses
|
||||
SET_USER_PROFILE_LOADING: (state, status) => {
|
||||
state.userProfileLoading = status
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async FetchData({ commit, getters }, { id, godmode }) {
|
||||
commit('SET_LOADING', true)
|
||||
|
||||
const [userResponse, statusesResponse] = await Promise.all([
|
||||
fetchUser(id, getters.authHost, getters.token),
|
||||
fetchUserStatuses(id, getters.authHost, godmode, getters.token)
|
||||
])
|
||||
async FetchUserProfile({ commit, dispatch, getters }, { userId, godmode }) {
|
||||
commit('SET_USER_PROFILE_LOADING', true)
|
||||
|
||||
const userResponse = await fetchUser(userId, getters.authHost, getters.token)
|
||||
commit('SET_USER', userResponse.data)
|
||||
commit('SET_STATUSES', statusesResponse.data)
|
||||
commit('SET_LOADING', false)
|
||||
commit('SET_USER_PROFILE_LOADING', false)
|
||||
|
||||
dispatch('FetchUserStatuses', { userId, godmode })
|
||||
},
|
||||
async FetchUserStatuses({ commit, getters }, { userId, godmode }) {
|
||||
commit('SET_STATUSES_LOADING', true)
|
||||
|
||||
const statuses = await fetchUserStatuses(userId, getters.authHost, godmode, getters.token)
|
||||
|
||||
commit('SET_STATUSES', statuses.data)
|
||||
commit('SET_STATUSES_LOADING', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
143
src/views/reports/components/GroupedReport.vue
Normal file
143
src/views/reports/components/GroupedReport.vue
Normal file
|
@ -0,0 +1,143 @@
|
|||
<template>
|
||||
<el-timeline class="timeline">
|
||||
<el-timeline-item
|
||||
v-for="groupedReport in groupedReports"
|
||||
:key="groupedReport.id"
|
||||
:timestamp="parseTimestamp(groupedReport.date)"
|
||||
placement="top"
|
||||
class="timeline-item-container">
|
||||
<el-card class="grouped-report">
|
||||
<div class="header-container">
|
||||
<div>
|
||||
<h3 class="report-title">{{ $t('reports.reportsOn') }} {{ groupedReport.account.display_name }}</h3>
|
||||
</div>
|
||||
<div>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-edit">{{ $t('reports.changeAllReports') }}<i class="el-icon-arrow-down el-icon--right"/></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="changeAllReports('resolved', groupedReport.reports)">{{ $t('reports.resolveAll') }}</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="changeAllReports('open', groupedReport.reports)">{{ $t('reports.reopenAll') }}</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="changeAllReports('closed', groupedReport.reports)">{{ $t('reports.closeAll') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<moderate-user-dropdown :account="groupedReport.account"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">{{ $t('reports.account') }}:</span>
|
||||
<img
|
||||
:src="groupedReport.account.avatar"
|
||||
alt="avatar"
|
||||
class="avatar-img">
|
||||
<a :href="groupedReport.account.url" target="_blank">
|
||||
<span>{{ groupedReport.account.nickname }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">{{ $t('reports.actors') }}:</span>
|
||||
<span v-for="(actor, index) in groupedReport.actors" :key="actor.id">
|
||||
<a :href="actor.url" target="_blank">
|
||||
{{ actor.acct }}<span v-if="index < groupedReport.actors.length - 1">, </span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="groupedReport.status">
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">{{ $t('reports.reportedStatus') }}:</span>
|
||||
<status :status="groupedReport.status" class="reported-status"/>
|
||||
</div>
|
||||
<div v-if="groupedReport.reports">
|
||||
<el-collapse>
|
||||
<el-collapse-item :title="$t('reports.reports')">
|
||||
<report-card :reports="groupedReport.reports"/>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import ModerateUserDropdown from './ModerateUserDropdown'
|
||||
import ReportCard from './ReportCard'
|
||||
import Status from '../../status/Status'
|
||||
|
||||
export default {
|
||||
name: 'Report',
|
||||
components: { ModerateUserDropdown, ReportCard, Status },
|
||||
props: {
|
||||
groupedReports: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeAllReports(reportState, groupOfReports) {
|
||||
const reportsData = groupOfReports.map(report => {
|
||||
return { id: report.id, state: reportState }
|
||||
})
|
||||
this.$store.dispatch('ChangeReportState', reportsData)
|
||||
},
|
||||
parseTimestamp(timestamp) {
|
||||
return moment(timestamp).format('L HH:mm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</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>
|
86
src/views/reports/components/ModerateUserDropdown.vue
Normal file
86
src/views/reports/components/ModerateUserDropdown.vue
Normal file
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-files">{{ $t('reports.moderateUser') }}
|
||||
<i class="el-icon-arrow-down el-icon--right"/>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-if="showDeactivatedButton(account)"
|
||||
@click.native="handleDeactivation(account)">
|
||||
{{ account.deactivated ? $t('users.activateAccount') : $t('users.deactivateAccount') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="showDeactivatedButton(account.id)"
|
||||
@click.native="handleDeletion(account.id)">
|
||||
{{ $t('users.deleteAccount') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:divided="true"
|
||||
:class="{ 'active-tag': account.tags.includes('force_nsfw') }"
|
||||
@click.native="toggleTag(account, 'force_nsfw')">
|
||||
{{ $t('users.forceNsfw') }}
|
||||
<i v-if="account.tags.includes('force_nsfw')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:class="{ 'active-tag': account.tags.includes('strip_media') }"
|
||||
@click.native="toggleTag(account, 'strip_media')">
|
||||
{{ $t('users.stripMedia') }}
|
||||
<i v-if="account.tags.includes('strip_media')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:class="{ 'active-tag': account.tags.includes('force_unlisted') }"
|
||||
@click.native="toggleTag(account, 'force_unlisted')">
|
||||
{{ $t('users.forceUnlisted') }}
|
||||
<i v-if="account.tags.includes('force_unlisted')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:class="{ 'active-tag': account.tags.includes('sandbox') }"
|
||||
@click.native="toggleTag(account, 'sandbox')">
|
||||
{{ $t('users.sandbox') }}
|
||||
<i v-if="account.tags.includes('sandbox')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="account.local"
|
||||
:class="{ 'active-tag': account.tags.includes('disable_remote_subscription') }"
|
||||
@click.native="toggleTag(account, 'disable_remote_subscription')">
|
||||
{{ $t('users.disableRemoteSubscription') }}
|
||||
<i v-if="account.tags.includes('disable_remote_subscription')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="account.local"
|
||||
:class="{ 'active-tag': account.tags.includes('disable_any_subscription') }"
|
||||
@click.native="toggleTag(account, 'disable_any_subscription')">
|
||||
{{ $t('users.disableAnySubscription') }}
|
||||
<i v-if="account.tags.includes('disable_any_subscription')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ModerateUserDropdown',
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleDeactivation({ nickname }) {
|
||||
this.$store.dispatch('ToggleUserActivation', nickname)
|
||||
},
|
||||
handleDeletion(user) {
|
||||
this.$store.dispatch('DeleteUser', user)
|
||||
},
|
||||
showDeactivatedButton(id) {
|
||||
return this.$store.state.user.id !== id
|
||||
},
|
||||
toggleTag(user, tag) {
|
||||
user.tags.includes(tag)
|
||||
? this.$store.dispatch('RemoveTag', { users: [user], tag })
|
||||
: this.$store.dispatch('AddTag', { users: [user], tag })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
250
src/views/reports/components/Report.vue
Normal file
250
src/views/reports/components/Report.vue
Normal file
|
@ -0,0 +1,250 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-timeline class="timeline">
|
||||
<el-timeline-item
|
||||
v-for="report in reports"
|
||||
:timestamp="parseTimestamp(report.created_at)"
|
||||
:key="report.id"
|
||||
placement="top"
|
||||
class="timeline-item-container">
|
||||
<el-card>
|
||||
<div class="header-container">
|
||||
<div>
|
||||
<h3 class="report-title">{{ $t('reports.reportOn') }} {{ report.account.display_name }}</h3>
|
||||
<h5 class="id">{{ $t('reports.id') }}: {{ report.id }}</h5>
|
||||
</div>
|
||||
<div>
|
||||
<el-tag :type="getStateType(report.state)" size="large">{{ capitalizeFirstLetter(report.state) }}</el-tag>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-edit">{{ $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 :account="report.account"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">{{ $t('reports.account') }}:</span>
|
||||
<img
|
||||
:src="report.account.avatar"
|
||||
alt="avatar"
|
||||
class="avatar-img">
|
||||
<a :href="report.account.url" target="_blank" class="account">
|
||||
<span>{{ report.account.acct }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="report.content.length > 0">
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">{{ $t('reports.content') }}:
|
||||
<span>{{ report.content }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">{{ $t('reports.actor') }}:</span>
|
||||
<img
|
||||
:src="report.actor.avatar"
|
||||
alt="avatar"
|
||||
class="avatar-img">
|
||||
<a :href="report.actor.url" target="_blank" class="account">
|
||||
<span>{{ report.actor.acct }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="report.statuses.length > 0" class="statuses">
|
||||
<el-collapse>
|
||||
<el-collapse-item :title="getStatusesTitle(report.statuses)">
|
||||
<div v-for="status in report.statuses" :key="status.id">
|
||||
<status :status="status" :page="currentPage"/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!loading" class="reports-pagination">
|
||||
<el-pagination
|
||||
:total="totalReportsCount"
|
||||
:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import Status from '../../status/Status'
|
||||
import ModerateUserDropdown from './ModerateUserDropdown'
|
||||
|
||||
export default {
|
||||
name: 'Report',
|
||||
components: { Status, ModerateUserDropdown },
|
||||
props: {
|
||||
reports: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
loading() {
|
||||
return this.$store.state.reports.loading
|
||||
},
|
||||
pageSize() {
|
||||
return this.$store.state.reports.pageSize
|
||||
},
|
||||
totalReportsCount() {
|
||||
return this.$store.state.reports.totalReportsCount
|
||||
},
|
||||
currentPage() {
|
||||
return this.$store.state.reports.currentPage
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeReportState(state, id) {
|
||||
this.$store.dispatch('ChangeReportState', [{ state, id }])
|
||||
},
|
||||
capitalizeFirstLetter(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
getStateType(state) {
|
||||
switch (state) {
|
||||
case 'closed':
|
||||
return 'info'
|
||||
case 'resolved':
|
||||
return 'success'
|
||||
default:
|
||||
return 'primary'
|
||||
}
|
||||
},
|
||||
getStatusesTitle(statuses) {
|
||||
return `Reported statuses: ${statuses.length} item(s)`
|
||||
},
|
||||
handlePageChange(page) {
|
||||
this.$store.dispatch('FetchReports', page)
|
||||
},
|
||||
parseTimestamp(timestamp) {
|
||||
return moment(timestamp).format('L HH:mm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss'>
|
||||
.account {
|
||||
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-collapse {
|
||||
border-bottom: none;
|
||||
}
|
||||
.el-collapse-item__header {
|
||||
height: 46px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.el-collapse-item__content {
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
.el-icon-arrow-right {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.el-icon-close {
|
||||
padding: 10px 5px 10px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
h4 {
|
||||
margin: 0;
|
||||
height: 17px;
|
||||
}
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
height: 40px;
|
||||
}
|
||||
.id {
|
||||
color: gray;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.line {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border: 0.5px solid #EBEEF5;
|
||||
margin: 15px 0 15px;
|
||||
}
|
||||
.new-note {
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 17px;
|
||||
margin: 13px 0 7px;
|
||||
}
|
||||
}
|
||||
.note {
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0,.1);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.no-notes {
|
||||
font-style: italic;
|
||||
color: gray;
|
||||
}
|
||||
.report-row-key {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.report-row-key {
|
||||
font-size: 14px;
|
||||
}
|
||||
.report-title {
|
||||
margin: 0;
|
||||
}
|
||||
.reports-pagination {
|
||||
margin: 25px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.statuses {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.submit-button {
|
||||
display: block;
|
||||
margin: 7px 0 17px auto;
|
||||
}
|
||||
.timestamp {
|
||||
margin: 0;
|
||||
font-style: italic;
|
||||
color: gray;
|
||||
}
|
||||
@media
|
||||
only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
.timeline-item-container {
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 80px;
|
||||
}
|
||||
.id {
|
||||
margin: 6px 0 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
130
src/views/reports/components/ReportCard.vue
Normal file
130
src/views/reports/components/ReportCard.vue
Normal file
|
@ -0,0 +1,130 @@
|
|||
<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>
|
||||
<el-tag :type="getStateType(report.state)" size="large">{{ capitalizeFirstLetter(report.state) }}</el-tag>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-edit">{{ $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>
|
||||
</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: {
|
||||
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':
|
||||
return 'info'
|
||||
case 'resolved':
|
||||
return 'success'
|
||||
default:
|
||||
return 'primary'
|
||||
}
|
||||
},
|
||||
parseTimestamp(timestamp) {
|
||||
return moment(timestamp).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss'>
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.el-icon-arrow-right {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.report-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
height: 40px;
|
||||
}
|
||||
.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-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 80px;
|
||||
}
|
||||
.report-actor-container {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.report-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -44,7 +44,7 @@ export default {
|
|||
toggleFilters() {
|
||||
this.$store.dispatch('SetFilter', this.$data.filter)
|
||||
this.$store.dispatch('ClearFetchedReports')
|
||||
this.$store.dispatch('FetchReports')
|
||||
this.$store.dispatch('FetchReports', 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
<template>
|
||||
<el-collapse-item :title="getStatusesTitle(report.statuses)">
|
||||
<el-card v-for="status in report.statuses" :key="status.id" class="status-card">
|
||||
<div slot="header">
|
||||
<div class="status-header">
|
||||
<div class="status-account-container">
|
||||
<div class="status-account">
|
||||
<img :src="status.account.avatar" class="status-avatar-img">
|
||||
<h3 class="status-account-name">{{ status.account.display_name }}</h3>
|
||||
</div>
|
||||
<a :href="status.account.url" target="_blank" class="account">
|
||||
@{{ status.account.acct }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="status-actions">
|
||||
<el-tag v-if="status.sensitive" type="warning" size="large">{{ $t('reports.sensitive') }}</el-tag>
|
||||
<el-tag size="large">{{ capitalizeFirstLetter(status.visibility) }}</el-tag>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-edit" class="status-actions-button">
|
||||
{{ $t('reports.changeScope') }}<i class="el-icon-arrow-down el-icon--right"/>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-if="!status.sensitive"
|
||||
@click.native="changeStatus(status.id, true, status.visibility, report.id)">
|
||||
{{ $t('reports.addSensitive') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.sensitive"
|
||||
@click.native="changeStatus(status.id, false, status.visibility, report.id)">
|
||||
{{ $t('reports.removeSensitive') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'public'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'public', report.id)">
|
||||
{{ $t('reports.public') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'private'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'private', report.id)">
|
||||
{{ $t('reports.private') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'unlisted'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'unlisted', report.id)">
|
||||
{{ $t('reports.unlisted') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.native="deleteStatus(status.id, report.id)">
|
||||
{{ $t('reports.deleteStatus') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-body">
|
||||
<span class="status-content" v-html="status.content"/>
|
||||
<a :href="status.url" target="_blank" class="account">
|
||||
{{ parseTimestamp(status.created_at) }}
|
||||
</a>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-collapse-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
name: 'Statuses',
|
||||
props: {
|
||||
report: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
capitalizeFirstLetter(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
changeStatus(statusId, isSensitive, visibility, reportId) {
|
||||
this.$store.dispatch('ChangeStatusScope', { statusId, isSensitive, visibility, reportId })
|
||||
},
|
||||
deleteStatus(statusId, reportId) {
|
||||
this.$confirm('Are you sure you want to delete this status?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$store.dispatch('DeleteStatus', { statusId, reportId })
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: 'Delete completed'
|
||||
})
|
||||
}).catch(() => {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: 'Delete canceled'
|
||||
})
|
||||
})
|
||||
},
|
||||
getStatusesTitle(statuses) {
|
||||
return `Reported statuses: ${statuses.length} item(s)`
|
||||
},
|
||||
parseTimestamp(timestamp) {
|
||||
return moment(timestamp).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss'>
|
||||
.account {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.status-account {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.status-avatar-img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.status-account-name {
|
||||
margin: 0;
|
||||
height: 22px;
|
||||
}
|
||||
.status-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.status-content {
|
||||
font-size: 15px;
|
||||
}
|
||||
.status-card {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.status-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@media
|
||||
only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
.el-message {
|
||||
min-width: 80%;
|
||||
}
|
||||
.el-message-box {
|
||||
width: 80%;
|
||||
}
|
||||
.status-card {
|
||||
.el-card__header {
|
||||
padding: 10px 17px
|
||||
}
|
||||
.el-tag {
|
||||
margin: 3px 4px 3px 0;
|
||||
}
|
||||
.status-account-container {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.status-actions-button {
|
||||
margin: 3px 0 3px;
|
||||
}
|
||||
.status-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.status-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,271 +0,0 @@
|
|||
<template>
|
||||
<el-timeline-item :timestamp="parseTimestamp(report.created_at)" placement="top" class="timeline-item-container">
|
||||
<el-card>
|
||||
<div class="header-container">
|
||||
<div>
|
||||
<h3 class="report-title">Report on {{ report.account.display_name }}</h3>
|
||||
<h5 class="id">ID: {{ report.id }}</h5>
|
||||
</div>
|
||||
<div>
|
||||
<el-tag :type="getStateType(report.state)" size="large">{{ capitalizeFirstLetter(report.state) }}</el-tag>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-edit">{{ $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>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-files">{{ $t('reports.moderateUser') }}<i class="el-icon-arrow-down el-icon--right"/></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-if="showDeactivatedButton(report.account)"
|
||||
@click.native="toggleActivation(report.account)">
|
||||
{{ report.account.deactivated ? $t('users.activateAccount') : $t('users.deactivateAccount') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="showDeactivatedButton(report.account.id)"
|
||||
@click.native="handleDeletion(report.account.id)">
|
||||
{{ $t('users.deleteAccount') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:divided="true"
|
||||
:class="{ 'active-tag': report.account.tags.includes('force_nsfw') }"
|
||||
@click.native="toggleTag(report.account, 'force_nsfw')">
|
||||
{{ $t('users.forceNsfw') }}
|
||||
<i v-if="report.account.tags.includes('force_nsfw')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:class="{ 'active-tag': report.account.tags.includes('strip_media') }"
|
||||
@click.native="toggleTag(report.account, 'strip_media')">
|
||||
{{ $t('users.stripMedia') }}
|
||||
<i v-if="report.account.tags.includes('strip_media')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:class="{ 'active-tag': report.account.tags.includes('force_unlisted') }"
|
||||
@click.native="toggleTag(report.account, 'force_unlisted')">
|
||||
{{ $t('users.forceUnlisted') }}
|
||||
<i v-if="report.account.tags.includes('force_unlisted')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:class="{ 'active-tag': report.account.tags.includes('sandbox') }"
|
||||
@click.native="toggleTag(report.account, 'sandbox')">
|
||||
{{ $t('users.sandbox') }}
|
||||
<i v-if="report.account.tags.includes('sandbox')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="report.account.local"
|
||||
:class="{ 'active-tag': report.account.tags.includes('disable_remote_subscription') }"
|
||||
@click.native="toggleTag(report.account, 'disable_remote_subscription')">
|
||||
{{ $t('users.disableRemoteSubscription') }}
|
||||
<i v-if="report.account.tags.includes('disable_remote_subscription')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="report.account.local"
|
||||
:class="{ 'active-tag': report.account.tags.includes('disable_any_subscription') }"
|
||||
@click.native="toggleTag(report.account, 'disable_any_subscription')">
|
||||
{{ $t('users.disableAnySubscription') }}
|
||||
<i v-if="report.account.tags.includes('disable_any_subscription')" class="el-icon-check"/>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">Account:</span>
|
||||
<img
|
||||
:src="report.account.avatar"
|
||||
alt="avatar"
|
||||
class="avatar-img">
|
||||
<a :href="report.account.url" target="_blank" class="account">
|
||||
<span class="report-row-value">{{ report.account.acct }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="report.content.length > 0">
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">Content:
|
||||
<span class="report-row-value">{{ report.content }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="line"/>
|
||||
<span class="report-row-key">Actor:</span>
|
||||
<img
|
||||
:src="report.actor.avatar"
|
||||
alt="avatar"
|
||||
class="avatar-img">
|
||||
<a :href="report.actor.url" target="_blank" class="account">
|
||||
<span class="report-row-value">{{ report.actor.acct }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="report.statuses.length > 0" class="statuses">
|
||||
<el-collapse>
|
||||
<statuses :report="report"/>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import Statuses from './Statuses'
|
||||
|
||||
export default {
|
||||
name: 'TimelineItem',
|
||||
components: { Statuses },
|
||||
props: {
|
||||
report: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeReportState(reportState, reportId) {
|
||||
this.$store.dispatch('ChangeReportState', { reportState, reportId })
|
||||
},
|
||||
capitalizeFirstLetter(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
getStateType(state) {
|
||||
switch (state) {
|
||||
case 'closed':
|
||||
return 'info'
|
||||
case 'resolved':
|
||||
return 'success'
|
||||
default:
|
||||
return 'primary'
|
||||
}
|
||||
},
|
||||
handleDeletion(user) {
|
||||
this.$store.dispatch('DeleteUsers', [user])
|
||||
},
|
||||
parseTimestamp(timestamp) {
|
||||
return moment(timestamp).format('L HH:mm')
|
||||
},
|
||||
showDeactivatedButton(id) {
|
||||
return this.$store.state.user.id !== id
|
||||
},
|
||||
toggleActivation(user) {
|
||||
user.deactivated
|
||||
? this.$store.dispatch('ActivateUsers', [user])
|
||||
: this.$store.dispatch('DeactivateUsers', [user])
|
||||
},
|
||||
toggleTag(user, tag) {
|
||||
user.tags.includes(tag)
|
||||
? this.$store.dispatch('RemoveTag', { users: [user], tag })
|
||||
: this.$store.dispatch('AddTag', { users: [user], tag })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss'>
|
||||
.account {
|
||||
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-collapse {
|
||||
border-bottom: none;
|
||||
}
|
||||
.el-collapse-item__header {
|
||||
height: 46px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.el-collapse-item__content {
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
.el-icon-arrow-right {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.el-icon-close {
|
||||
padding: 10px 5px 10px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
h4 {
|
||||
margin: 0;
|
||||
height: 17px;
|
||||
}
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
height: 40px;
|
||||
}
|
||||
.id {
|
||||
color: gray;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.line {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border: 0.5px solid #EBEEF5;
|
||||
margin: 15px 0 15px;
|
||||
}
|
||||
.new-note {
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 17px;
|
||||
margin: 13px 0 7px;
|
||||
}
|
||||
}
|
||||
.note {
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0,.1);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.no-notes {
|
||||
font-style: italic;
|
||||
color: gray;
|
||||
}
|
||||
.report-row-key {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.report-row-key {
|
||||
font-size: 14px;
|
||||
}
|
||||
.report-title {
|
||||
margin: 0;
|
||||
}
|
||||
.statuses {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.submit-button {
|
||||
display: block;
|
||||
margin: 7px 0 17px auto;
|
||||
}
|
||||
.timestamp {
|
||||
margin: 0;
|
||||
font-style: italic;
|
||||
color: gray;
|
||||
}
|
||||
@media
|
||||
only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
.timeline-item-container {
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 80px;
|
||||
}
|
||||
.id {
|
||||
margin: 6px 0 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,13 +1,22 @@
|
|||
<template>
|
||||
<div class="reports-container">
|
||||
<h1>{{ $t('reports.reports') }}</h1>
|
||||
<h1 v-if="groupReports">
|
||||
{{ $t('reports.groupedReports') }}
|
||||
<span class="report-count">({{ normalizedReportsCount }})</span>
|
||||
</h1>
|
||||
<h1 v-else>
|
||||
{{ $t('reports.reports') }}
|
||||
<span class="report-count">({{ normalizedReportsCount }})</span>
|
||||
</h1>
|
||||
<div class="filter-container">
|
||||
<reports-filter/>
|
||||
<reports-filter v-if="!groupReports"/>
|
||||
<el-checkbox v-model="groupReports" class="group-reports-checkbox">
|
||||
Group reports by statuses
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<div class="block">
|
||||
<el-timeline class="timeline">
|
||||
<timeline-item v-loading="loading" v-for="report in reports" :report="report" :key="report.id"/>
|
||||
</el-timeline>
|
||||
<grouped-report v-loading="loading" v-if="groupReports" :grouped-reports="groupedReports"/>
|
||||
<report v-loading="loading" v-else :reports="reports"/>
|
||||
<div v-if="reports.length === 0" class="no-reports-message">
|
||||
<p>There are no reports to display</p>
|
||||
</div>
|
||||
|
@ -16,34 +25,44 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import TimelineItem from './components/TimelineItem'
|
||||
import GroupedReport from './components/GroupedReport'
|
||||
import numeral from 'numeral'
|
||||
import Report from './components/Report'
|
||||
import ReportsFilter from './components/ReportsFilter'
|
||||
|
||||
export default {
|
||||
components: { TimelineItem, ReportsFilter },
|
||||
components: { GroupedReport, Report, ReportsFilter },
|
||||
computed: {
|
||||
groupedReports() {
|
||||
return this.$store.state.reports.fetchedGroupedReports
|
||||
},
|
||||
groupReports: {
|
||||
get() {
|
||||
return this.$store.state.reports.groupReports
|
||||
},
|
||||
set() {
|
||||
this.toggleReportsGrouping()
|
||||
}
|
||||
},
|
||||
loading() {
|
||||
return this.$store.state.users.loading
|
||||
return this.$store.state.reports.loading
|
||||
},
|
||||
normalizedReportsCount() {
|
||||
return this.groupReports
|
||||
? numeral(this.$store.state.reports.fetchedGroupedReports.length).format('0a')
|
||||
: numeral(this.$store.state.reports.totalReportsCount).format('0a')
|
||||
},
|
||||
reports() {
|
||||
return this.$store.state.reports.fetchedReports
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('FetchReports')
|
||||
},
|
||||
created() {
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('scroll', this.handleScroll)
|
||||
this.$store.dispatch('FetchReports', 1)
|
||||
this.$store.dispatch('FetchGroupedReports')
|
||||
},
|
||||
methods: {
|
||||
handleScroll(reports) {
|
||||
const bottomOfWindow = document.documentElement.scrollHeight - document.documentElement.scrollTop === document.documentElement.clientHeight
|
||||
if (bottomOfWindow) {
|
||||
this.$store.dispatch('FetchReports')
|
||||
}
|
||||
toggleReportsGrouping() {
|
||||
this.$store.dispatch('ToggleReportsGrouping')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,16 +75,24 @@ export default {
|
|||
padding: 0px;
|
||||
}
|
||||
.filter-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 22px 15px 22px 15px;
|
||||
padding-bottom: 0
|
||||
}
|
||||
.group-reports-checkbox {
|
||||
margin-top: 10px;
|
||||
}
|
||||
h1 {
|
||||
margin: 22px 0 0 15px;
|
||||
}
|
||||
.no-reports-message {
|
||||
color: gray;
|
||||
margin-left: 19px
|
||||
|
||||
}
|
||||
.report-count {
|
||||
color: gray;
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
@media
|
||||
|
@ -78,9 +105,9 @@ only screen and (max-width: 760px),
|
|||
.filter-container {
|
||||
margin: 0 10px
|
||||
}
|
||||
.timeline {
|
||||
margin: 20px 20px 20px 18px
|
||||
}
|
||||
}
|
||||
#app > div > div.main-container > section > div > div.block > ul {
|
||||
margin: 45px 45px 5px 19px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
269
src/views/status/Status.vue
Normal file
269
src/views/status/Status.vue
Normal file
|
@ -0,0 +1,269 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-card v-if="!status.deleted" class="status-card">
|
||||
<div slot="header">
|
||||
<div class="status-header">
|
||||
<div class="status-account-container">
|
||||
<div class="status-account">
|
||||
<img :src="status.account.avatar" class="status-avatar-img">
|
||||
<h3 class="status-account-name">{{ status.account.display_name }}</h3>
|
||||
</div>
|
||||
<a :href="status.account.url" target="_blank" class="account">
|
||||
@{{ status.account.acct }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="status-actions">
|
||||
<el-tag v-if="status.sensitive" type="warning" size="large">{{ $t('reports.sensitive') }}</el-tag>
|
||||
<el-tag size="large">{{ capitalizeFirstLetter(status.visibility) }}</el-tag>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button plain size="small" icon="el-icon-edit" class="status-actions-button">
|
||||
{{ $t('reports.changeScope') }}<i class="el-icon-arrow-down el-icon--right"/>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-if="!status.sensitive"
|
||||
@click.native="changeStatus(status.id, true, status.visibility)">
|
||||
{{ $t('reports.addSensitive') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.sensitive"
|
||||
@click.native="changeStatus(status.id, false, status.visibility)">
|
||||
{{ $t('reports.removeSensitive') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'public'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'public')">
|
||||
{{ $t('reports.public') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'private'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'private')">
|
||||
{{ $t('reports.private') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="status.visibility !== 'unlisted'"
|
||||
@click.native="changeStatus(status.id, status.sensitive, 'unlisted')">
|
||||
{{ $t('reports.unlisted') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.native="deleteStatus(status.id)">
|
||||
{{ $t('reports.deleteStatus') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-body">
|
||||
<div v-if="status.spoiler_text">
|
||||
<strong>{{ status.spoiler_text }}</strong>
|
||||
<el-button v-if="!showHiddenStatus" size="mini" class="show-more-button" @click="showHiddenStatus = true">Show more</el-button>
|
||||
<el-button v-if="showHiddenStatus" size="mini" class="show-more-button" @click="showHiddenStatus = false">Show less</el-button>
|
||||
<div v-if="showHiddenStatus">
|
||||
<span class="status-content" v-html="status.content"/>
|
||||
<div v-if="status.poll" class="poll">
|
||||
<ul>
|
||||
<li v-for="(option, index) in status.poll.options" :key="index">
|
||||
{{ option.title }}
|
||||
<el-progress :percentage="optionPercent(status.poll, option)" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-for="(attachment, index) in status.media_attachments" :key="index" class="image">
|
||||
<img :src="attachment.preview_url">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!status.spoiler_text">
|
||||
<span class="status-content" v-html="status.content"/>
|
||||
<div v-if="status.poll" class="poll">
|
||||
<ul>
|
||||
<li v-for="(option, index) in status.poll.options" :key="index">
|
||||
{{ option.title }}
|
||||
<el-progress :percentage="optionPercent(status.poll, option)" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-for="(attachment, index) in status.media_attachments" :key="index" class="image">
|
||||
<img :src="attachment.preview_url">
|
||||
</div>
|
||||
</div>
|
||||
<a :href="status.url" target="_blank" class="account">
|
||||
{{ parseTimestamp(status.created_at) }}
|
||||
</a>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card v-else class="status-card">
|
||||
<div slot="header">
|
||||
<div class="status-header">
|
||||
<div class="status-account-container">
|
||||
<div class="status-account">
|
||||
<h4 class="status-deleted">{{ $t('reports.statusDeleted') }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-body">
|
||||
<span v-if="status.content" class="status-content" v-html="status.content"/>
|
||||
<span v-else class="status-without-content">no content</span>
|
||||
</div>
|
||||
<a v-if="status.created_at" :href="status.url" target="_blank" class="account">
|
||||
{{ parseTimestamp(status.created_at) }}
|
||||
</a>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
name: 'Status',
|
||||
props: {
|
||||
status: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
},
|
||||
userId: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
godmode: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showHiddenStatus: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
capitalizeFirstLetter(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
changeStatus(statusId, isSensitive, visibility) {
|
||||
this.$store.dispatch('ChangeStatusScope', { statusId, isSensitive, visibility, reportCurrentPage: this.page, userId: this.userId, godmode: this.godmode })
|
||||
},
|
||||
deleteStatus(statusId) {
|
||||
this.$confirm('Are you sure you want to delete this status?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$store.dispatch('DeleteStatus', { statusId, reportCurrentPage: this.page, userId: this.userId, godmode: this.godmode })
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: 'Delete completed'
|
||||
})
|
||||
}).catch(() => {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: 'Delete canceled'
|
||||
})
|
||||
})
|
||||
},
|
||||
optionPercent(poll, pollOption) {
|
||||
const allVotes = poll.options.reduce((acc, option) => (acc + option.votes_count), 0)
|
||||
if (allVotes === 0) {
|
||||
return 0
|
||||
}
|
||||
return +(pollOption.votes_count / allVotes * 100).toFixed(1)
|
||||
},
|
||||
parseTimestamp(timestamp) {
|
||||
return moment(timestamp).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss'>
|
||||
.account {
|
||||
text-decoration: underline;
|
||||
line-height: 26px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.image {
|
||||
width: 20%;
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.show-more-button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.status-account {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.status-avatar-img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.status-account-name {
|
||||
margin: 0;
|
||||
height: 22px;
|
||||
}
|
||||
.status-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.status-content {
|
||||
font-size: 15px;
|
||||
line-height: 26px;
|
||||
}
|
||||
.status-card {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.status-deleted {
|
||||
font-style: italic;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.status-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.status-without-content {
|
||||
font-style: italic;
|
||||
}
|
||||
@media
|
||||
only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
.el-message {
|
||||
min-width: 80%;
|
||||
}
|
||||
.el-message-box {
|
||||
width: 80%;
|
||||
}
|
||||
.status-card {
|
||||
.el-card__header {
|
||||
padding: 10px 17px;
|
||||
}
|
||||
.el-tag {
|
||||
margin: 3px 4px 3px 0;
|
||||
}
|
||||
.status-account-container {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.status-actions-button {
|
||||
margin: 3px 0 3px;
|
||||
}
|
||||
.status-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.status-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<main v-if="!loading">
|
||||
<main v-if="!userProfileLoading">
|
||||
<header>
|
||||
<el-avatar :src="user.avatar" size="large" />
|
||||
<h1>{{ user.display_name }}</h1>
|
||||
|
@ -71,23 +71,9 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
<el-col :span="18">
|
||||
<el-timeline class="statuses">
|
||||
<el-timeline-item v-for="status in statuses" :timestamp="createdAtLocaleString(status.created_at)" :key="status.id">
|
||||
<el-card>
|
||||
<strong v-if="status.spoiler_text">{{ status.spoiler_text }}</strong>
|
||||
<p v-if="status.content" v-html="status.content" />
|
||||
<div v-if="status.poll" class="poll">
|
||||
<ul>
|
||||
<li v-for="(option, index) in status.poll.options" :key="index">
|
||||
{{ option.title }}
|
||||
<el-progress :percentage="optionPercent(status.poll, option)" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-for="(attachment, index) in status.media_attachments" :key="index" class="image">
|
||||
<img :src="attachment.preview_url">
|
||||
</div>
|
||||
</el-card>
|
||||
<el-timeline v-if="!statusesLoading" class="statuses">
|
||||
<el-timeline-item v-for="status in statuses" :key="status.id">
|
||||
<status :status="status" :user-id="user.id" :godmode="showPrivate"/>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-col>
|
||||
|
@ -96,45 +82,36 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Status from '../status/Status'
|
||||
|
||||
export default {
|
||||
name: 'UsersShow',
|
||||
components: { Status },
|
||||
data() {
|
||||
return {
|
||||
showPrivate: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
loading() {
|
||||
return this.$store.state.userProfile.loading
|
||||
statuses() {
|
||||
return this.$store.state.userProfile.statuses
|
||||
},
|
||||
statusesLoading() {
|
||||
return this.$store.state.userProfile.statusesLoading
|
||||
},
|
||||
user() {
|
||||
return this.$store.state.userProfile.user
|
||||
},
|
||||
statuses() {
|
||||
return this.$store.state.userProfile.statuses
|
||||
userProfileLoading() {
|
||||
return this.$store.state.userProfile.userProfileLoading
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.$store.dispatch('FetchData', { id: this.$route.params.id, godmode: false })
|
||||
this.$store.dispatch('FetchUserProfile', { userId: this.$route.params.id, godmode: false })
|
||||
},
|
||||
methods: {
|
||||
optionPercent(poll, pollOption) {
|
||||
const allVotes = poll.options.reduce((acc, option) => (acc + option.votes_count), 0)
|
||||
if (allVotes === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return +(pollOption.votes_count / allVotes * 100).toFixed(1)
|
||||
},
|
||||
createdAtLocaleString(createdAt) {
|
||||
const date = new Date(createdAt)
|
||||
|
||||
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
|
||||
},
|
||||
onTogglePrivate() {
|
||||
console.log(this.showPrivate)
|
||||
|
||||
this.$store.dispatch('FetchData', { id: this.$route.params.id, godmode: this.showPrivate })
|
||||
this.$store.dispatch('FetchUserProfile', { userId: this.$route.params.id, godmode: this.showPrivate })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
47
test/views/reports/groupedReport.test.js
Normal file
47
test/views/reports/groupedReport.test.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
import Vuex from 'vuex'
|
||||
import { mount, createLocalVue, config } from '@vue/test-utils'
|
||||
import Element from 'element-ui'
|
||||
import GroupedReport from '@/views/reports/components/GroupedReport'
|
||||
import storeConfig from './store.conf'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
config.mocks["$t"] = () => {}
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
||||
jest.mock('@/api/reports')
|
||||
|
||||
describe('Grouped report', () => {
|
||||
let store
|
||||
|
||||
beforeEach(async() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
store.dispatch('FetchGroupedReports')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('changes state of all reports in a group', async (done) => {
|
||||
const groupedReports = store.state.reports.fetchedGroupedReports
|
||||
const wrapper = mount(GroupedReport, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
groupedReports
|
||||
}
|
||||
})
|
||||
|
||||
expect(groupedReports[0].reports[0].state).toBe('open')
|
||||
expect(groupedReports[0].reports[1].state).toBe('resolved')
|
||||
|
||||
const button = wrapper.find(`.grouped-report .el-dropdown-menu__item:nth-child(3)`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(store.state.reports.fetchedGroupedReports[0].reports[0].state).toBe('closed')
|
||||
expect(store.state.reports.fetchedGroupedReports[0].reports[1].state).toBe('closed')
|
||||
done()
|
||||
})
|
||||
})
|
|
@ -31,22 +31,7 @@ describe('Reports', () => {
|
|||
|
||||
await flushPromises()
|
||||
const initialReports = store.state.reports.fetchedReports.length
|
||||
expect(initialReports).toEqual(5)
|
||||
done()
|
||||
})
|
||||
|
||||
it('loads more reports on scroll', async (done) => {
|
||||
const wrapper = mount(Reports, {
|
||||
store,
|
||||
localVue
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
|
||||
window.dispatchEvent(new CustomEvent('scroll', { detail: 2000 }))
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(7)
|
||||
expect(initialReports).toEqual(7)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
|
75
test/views/reports/report.test.js
Normal file
75
test/views/reports/report.test.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
import Vuex from 'vuex'
|
||||
import { mount, createLocalVue, config } from '@vue/test-utils'
|
||||
import Element from 'element-ui'
|
||||
import Report from '@/views/reports/components/Report'
|
||||
import storeConfig from './store.conf'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
config.mocks["$t"] = () => {}
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
||||
jest.mock('@/api/reports')
|
||||
|
||||
describe('Report in a timeline', () => {
|
||||
let store
|
||||
|
||||
beforeEach(async() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('changes report state from open to resolved', async (done) => {
|
||||
const reports = store.state.reports.fetchedReports
|
||||
const wrapper = mount(Report, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
reports
|
||||
}
|
||||
})
|
||||
expect(reports[0].state).toBe('open')
|
||||
|
||||
const button = wrapper.find(`li.el-timeline-item:nth-child(1) li.el-dropdown-menu__item:nth-child(1)`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[0].state).toBe('resolved')
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes report state from open to closed', async (done) => {
|
||||
const reports = store.state.reports.fetchedReports
|
||||
const wrapper = mount(Report, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
reports
|
||||
}
|
||||
})
|
||||
expect(reports[3].state).toBe('open')
|
||||
|
||||
const button = wrapper.find(`li.el-timeline-item:nth-child(4) li.el-dropdown-menu__item:nth-child(2)`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[3].state).toBe('closed')
|
||||
done()
|
||||
})
|
||||
|
||||
it('shows statuses', () => {
|
||||
const reports = store.state.reports.fetchedReports
|
||||
const wrapper = mount(Report, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
reports
|
||||
}
|
||||
})
|
||||
|
||||
const statuses = wrapper.findAll(`.status-card`)
|
||||
expect(statuses.length).toEqual(2)
|
||||
})
|
||||
})
|
|
@ -24,11 +24,11 @@ describe('Reports filter', () => {
|
|||
})
|
||||
|
||||
it('shows open reports when "Open" filter is applied', async (done) => {
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(7)
|
||||
|
||||
store.dispatch('SetFilter', 'open')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
store.dispatch('FetchReports')
|
||||
store.dispatch('FetchReports', 1)
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(2)
|
||||
|
||||
|
@ -36,7 +36,7 @@ describe('Reports filter', () => {
|
|||
})
|
||||
|
||||
it('shows resolved reports when "Resolved" filter is applied', async (done) => {
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(7)
|
||||
|
||||
store.dispatch('SetFilter', 'resolved')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
|
@ -48,7 +48,7 @@ describe('Reports filter', () => {
|
|||
})
|
||||
|
||||
it('shows closed reports when "Closed" filter is applied', async (done) => {
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(7)
|
||||
|
||||
store.dispatch('SetFilter', 'closed')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
|
@ -60,7 +60,7 @@ describe('Reports filter', () => {
|
|||
})
|
||||
|
||||
it('shows all users after removing filters', async (done) => {
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(7)
|
||||
|
||||
store.dispatch('SetFilter', 'open')
|
||||
store.dispatch('ClearFetchedReports')
|
||||
|
@ -72,7 +72,7 @@ describe('Reports filter', () => {
|
|||
store.dispatch('ClearFetchedReports')
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(5)
|
||||
expect(store.state.reports.fetchedReports.length).toEqual(7)
|
||||
|
||||
done()
|
||||
})
|
||||
|
|
151
test/views/reports/status.test.js
Normal file
151
test/views/reports/status.test.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
import Vuex from 'vuex'
|
||||
import { mount, createLocalVue, config } from '@vue/test-utils'
|
||||
import Element from 'element-ui'
|
||||
import Status from '@/views/status/Status'
|
||||
import storeConfig from './store.conf'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
config.mocks["$t"] = () => {}
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
||||
jest.mock('@/api/reports')
|
||||
jest.mock('@/api/status')
|
||||
|
||||
describe('Status in reports', () => {
|
||||
let store
|
||||
|
||||
beforeEach(async() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
store.dispatch('FetchReports', 1)
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('adds sensitive flag to a status', async (done) => {
|
||||
const status = store.state.reports.fetchedReports[4].statuses[0]
|
||||
const wrapper = mount(Status, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
status,
|
||||
page: 1,
|
||||
userId: '7',
|
||||
godmode: false
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const changeStatusStub = jest.fn()
|
||||
wrapper.setMethods({ changeStatus: changeStatusStub })
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(1)`)
|
||||
button.trigger('click')
|
||||
|
||||
expect(wrapper.vm.changeStatus).toHaveBeenCalled()
|
||||
expect(wrapper.vm.changeStatus).toHaveBeenCalledWith('11', true, 'public')
|
||||
done()
|
||||
})
|
||||
|
||||
it('removes sensitive flag to a status', async (done) => {
|
||||
const status = store.state.reports.fetchedReports[4].statuses[1]
|
||||
const wrapper = mount(Status, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
status,
|
||||
page: 1,
|
||||
userId: '7',
|
||||
godmode: false
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const changeStatusStub = jest.fn()
|
||||
wrapper.setMethods({ changeStatus: changeStatusStub })
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(1)`)
|
||||
button.trigger('click')
|
||||
|
||||
expect(wrapper.vm.changeStatus).toHaveBeenCalled()
|
||||
expect(wrapper.vm.changeStatus).toHaveBeenCalledWith('10', false, 'unlisted')
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes status visibility from public to unlisted', async (done) => {
|
||||
const status = store.state.reports.fetchedReports[4].statuses[0]
|
||||
const wrapper = mount(Status, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
status,
|
||||
page: 1,
|
||||
userId: '7',
|
||||
godmode: false
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const changeStatusStub = jest.fn()
|
||||
wrapper.setMethods({ changeStatus: changeStatusStub })
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(3)`)
|
||||
button.trigger('click')
|
||||
|
||||
expect(wrapper.vm.changeStatus).toHaveBeenCalled()
|
||||
expect(wrapper.vm.changeStatus).toHaveBeenCalledWith('11', false, 'unlisted')
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes status visibility from unlisted to private', async (done) => {
|
||||
const status = store.state.reports.fetchedReports[4].statuses[1]
|
||||
const wrapper = mount(Status, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
status,
|
||||
page: 1,
|
||||
userId: '7',
|
||||
godmode: false
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const changeStatusStub = jest.fn()
|
||||
wrapper.setMethods({ changeStatus: changeStatusStub })
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(3)`)
|
||||
button.trigger('click')
|
||||
|
||||
expect(wrapper.vm.changeStatus).toHaveBeenCalled()
|
||||
expect(wrapper.vm.changeStatus).toHaveBeenCalledWith('10', true, 'private')
|
||||
done()
|
||||
})
|
||||
|
||||
it('deletes a status', async (done) => {
|
||||
const status = store.state.reports.fetchedReports[4].statuses[1]
|
||||
const wrapper = mount(Status, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
status,
|
||||
page: 1,
|
||||
userId: '7',
|
||||
godmode: false
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const deleteStatusStub = jest.fn()
|
||||
wrapper.setMethods({ deleteStatus: deleteStatusStub })
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(4)`)
|
||||
button.trigger('click')
|
||||
|
||||
expect(wrapper.vm.deleteStatus).toHaveBeenCalled()
|
||||
expect(wrapper.vm.deleteStatus).toHaveBeenCalledWith('10')
|
||||
done()
|
||||
})
|
||||
})
|
|
@ -2,6 +2,7 @@ import app from '@/store/modules/app'
|
|||
import user from '@/store/modules/user'
|
||||
import users from '@/store/modules/users'
|
||||
import reports from '@/store/modules/reports'
|
||||
import status from '@/store/modules/status'
|
||||
import getters from '@/store/getters'
|
||||
|
||||
export default {
|
||||
|
@ -9,7 +10,8 @@ export default {
|
|||
app,
|
||||
user,
|
||||
users,
|
||||
reports
|
||||
reports,
|
||||
status
|
||||
},
|
||||
getters
|
||||
}
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
import Vuex from 'vuex'
|
||||
import { mount, createLocalVue, config } from '@vue/test-utils'
|
||||
import Element from 'element-ui'
|
||||
import TimelineItem from '@/views/reports/components/TimelineItem'
|
||||
import storeConfig from './store.conf'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
config.mocks["$t"] = () => {}
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Element)
|
||||
|
||||
jest.mock('@/api/reports')
|
||||
|
||||
describe('Report in a timeline', () => {
|
||||
let store
|
||||
|
||||
beforeEach(async() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
store.dispatch('FetchReports')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('changes report state from open to resolved', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[0]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.state).toBe('open')
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(${1})`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[0].state).toBe('resolved')
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes report state from open to closed', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[3]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.state).toBe('open')
|
||||
|
||||
const button = wrapper.find(`li.el-dropdown-menu__item:nth-child(${2})`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[3].state).toBe('closed')
|
||||
done()
|
||||
})
|
||||
|
||||
it('shows statuses', () => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
|
||||
const statuses = wrapper.findAll(`.status-card`)
|
||||
expect(statuses.length).toEqual(2)
|
||||
})
|
||||
|
||||
it('adds sensitive flag to a status', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.statuses[0].sensitive).toBe(false)
|
||||
|
||||
const button = wrapper.find(`.status-card li.el-dropdown-menu__item`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses[0].sensitive).toEqual(true)
|
||||
done()
|
||||
})
|
||||
|
||||
it('removes sensitive flag to a status', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.statuses[1].sensitive).toBe(true)
|
||||
|
||||
const button = wrapper.find(`.status-card:nth-child(${2}) li.el-dropdown-menu__item`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses[1].sensitive).toEqual(false)
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes status visibility from public to unlisted', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.statuses[0].visibility).toBe('public')
|
||||
|
||||
const button = wrapper.find(`.status-card li.el-dropdown-menu__item:nth-child(${3})`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses[0].visibility).toEqual('unlisted')
|
||||
done()
|
||||
})
|
||||
|
||||
it('changes status visibility from unlisted to private', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
const wrapper = mount(TimelineItem, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
report: report
|
||||
}
|
||||
})
|
||||
expect(report.statuses[1].visibility).toBe('unlisted')
|
||||
|
||||
const button = wrapper.find(`.status-card:nth-child(${2}) li.el-dropdown-menu__item:nth-child(${3})`)
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses[1].visibility).toEqual('private')
|
||||
done()
|
||||
})
|
||||
|
||||
it('deletes a status', async (done) => {
|
||||
const report = store.state.reports.fetchedReports[4]
|
||||
expect(report.statuses.length).toEqual(2)
|
||||
|
||||
store.dispatch('DeleteStatus', { statusId: '11', reportId: '7'})
|
||||
await flushPromises()
|
||||
expect(store.state.reports.fetchedReports[4].statuses.length).toEqual(1)
|
||||
done()
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue