forked from Mirrors/elk
266 lines
8.3 KiB
Vue
266 lines
8.3 KiB
Vue
<script setup lang="ts">
|
|
import type { mastodon } from 'masto'
|
|
import { toggleBlockAccount, toggleFollowAccount, toggleMuteAccount, useRelationship } from '~~/composables/masto/relationship'
|
|
|
|
const { account, status } = defineProps<{
|
|
account: mastodon.v1.Account
|
|
status?: mastodon.v1.Status
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(event: 'close'): void
|
|
}>()
|
|
|
|
const { client } = useMasto()
|
|
|
|
const step = ref('selectCategory')
|
|
const serverRules = ref((await client.value.v2.instance.fetch()).rules || [])
|
|
const reportReason = ref('')
|
|
const selectedRuleIds = ref([])
|
|
const availableStatuses = ref(status ? [status] : [])
|
|
const selectedStatusIds = ref(status ? [status.id] : [])
|
|
const additionalComments = ref('')
|
|
const forwardReport = ref(false)
|
|
|
|
const dismissButton = ref<HTMLDivElement>()
|
|
|
|
loadStatuses() // Load statuses asynchronously ahead of time
|
|
|
|
function categoryChosen() {
|
|
step.value = reportReason.value === 'dontlike' ? 'furtherActions' : 'selectStatuses'
|
|
resetModal()
|
|
}
|
|
|
|
async function loadStatuses() {
|
|
if (status) {
|
|
// Load the 5 statuses before and after the reported status
|
|
const prevStatuses = await client.value.v1.accounts.listStatuses(account.id, {
|
|
maxId: status.id,
|
|
limit: 5,
|
|
})
|
|
const nextStatuses = await client.value.v1.accounts.listStatuses(account.id, {
|
|
minId: status.id,
|
|
limit: 5,
|
|
})
|
|
availableStatuses.value = availableStatuses.value.concat(prevStatuses)
|
|
availableStatuses.value = availableStatuses.value.concat(nextStatuses)
|
|
}
|
|
else {
|
|
// Reporting an account directly
|
|
// Load the 10 most recent statuses
|
|
const mostRecentStatuses = await client.value.v1.accounts.listStatuses(account.id, {
|
|
limit: 10,
|
|
})
|
|
availableStatuses.value = mostRecentStatuses
|
|
}
|
|
availableStatuses.value.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
|
}
|
|
|
|
async function submitReport() {
|
|
await client.value.v1.reports.create({
|
|
accountId: account.id,
|
|
statusIds: selectedStatusIds.value,
|
|
comment: additionalComments.value,
|
|
forward: forwardReport.value,
|
|
category: reportReason.value === 'spam' ? 'spam' : reportReason.value === 'violation' ? 'violation' : 'other',
|
|
ruleIds: reportReason.value === 'violation' ? selectedRuleIds.value : null,
|
|
})
|
|
step.value = 'furtherActions'
|
|
resetModal()
|
|
}
|
|
|
|
function unfollow() {
|
|
emit('close')
|
|
toggleFollowAccount(useRelationship(account).value!, account)
|
|
}
|
|
|
|
function mute() {
|
|
emit('close')
|
|
toggleMuteAccount(useRelationship(account).value!, account)
|
|
}
|
|
|
|
function block() {
|
|
emit('close')
|
|
toggleBlockAccount(useRelationship(account).value!, account)
|
|
}
|
|
|
|
function resetModal() {
|
|
// TODO: extract this scroll/reset logic into ModalDialog element
|
|
dismissButton.value?.scrollIntoView() // scroll to top
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div my-8 px-3 sm:px-8 flex="~ col gap-4" relative>
|
|
<h2 mxa text-xl>
|
|
<i18n-t :keypath="reportReason === 'dontlike' ? 'report.limiting' : 'report.reporting'">
|
|
<b text-primary>@{{ account.acct }}</b>
|
|
</i18n-t>
|
|
</h2>
|
|
<button ref="dismissButton" btn-action-icon absolute top--8 right-0 m1 aria-label="Close" @click="emit('close')">
|
|
<div i-ri:close-line />
|
|
</button>
|
|
|
|
<template v-if="step === 'selectCategory'">
|
|
<h1 mxa text-4xl mb4>
|
|
{{ status ? $t('report.whats_wrong_post') : $t('report.whats_wrong_account') }}
|
|
</h1>
|
|
<p text-xl>
|
|
{{ $t('report.select_one') }}
|
|
</p>
|
|
|
|
<div>
|
|
<input id="dontlike" v-model="reportReason" type="radio" value="dontlike">
|
|
<label pl-2 for="dontlike" font-bold>{{ $t('report.dontlike') }}</label>
|
|
<p pl-6>
|
|
{{ $t('report.dontlike_desc') }}
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<input id="spam" v-model="reportReason" type="radio" value="spam">
|
|
<label pl-2 for="spam" font-bold>{{ $t('report.spam') }}</label>
|
|
<p pl-6>
|
|
{{ $t('report.spam_desc') }}
|
|
</p>
|
|
</div>
|
|
|
|
<div v-if="serverRules.length > 0">
|
|
<input id="violation" v-model="reportReason" type="radio" value="violation">
|
|
<label pl-2 for="violation" font-bold>{{ $t('report.violation') }}</label>
|
|
<p v-if="reportReason === 'violation'" pl-6 pt-2 text-primary font-bold>
|
|
{{ $t('report.select_many') }}
|
|
</p>
|
|
<ul pl-6>
|
|
<li v-for="rule in serverRules" :key="rule.id" pt-2>
|
|
<input
|
|
:id="rule.id"
|
|
v-model="selectedRuleIds"
|
|
type="checkbox"
|
|
:value="rule.id"
|
|
:disabled="reportReason !== 'violation'"
|
|
>
|
|
<label pl-2 :for="rule.id">{{ rule.text }}</label>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<input id="other" v-model="reportReason" type="radio" value="other">
|
|
<label pl-2 for="other" font-bold>{{ $t('report.other') }}</label>
|
|
<p pl-6>
|
|
{{ $t('report.other_desc') }}
|
|
</p>
|
|
</div>
|
|
|
|
<div v-if="reportReason && reportReason !== 'dontlike'">
|
|
<h3 mt-8 mb-4 font-bold>
|
|
{{ $t('report.anything_else') }}
|
|
</h3>
|
|
<textarea v-model="additionalComments" w-full h-20 p-3 border :placeholder="$t('report.additional_comments')" />
|
|
<div v-if="getServerName(account) && getServerName(account) !== currentServer">
|
|
<h3 mt-8 mb-2 font-bold>
|
|
{{ $t('report.another_server') }}
|
|
</h3>
|
|
<p pb-1>
|
|
{{ $t('report.forward_question') }}
|
|
</p>
|
|
<input id="forward" v-model="forwardReport" type="checkbox" value="rule.id">
|
|
<label pl-2 for="forward"><b>{{ $t('report.forward', [getServerName(account)]) }}</b></label>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
btn-solid mxa mt-10
|
|
:disabled="!reportReason || (reportReason === 'violation' && selectedRuleIds.length < 1)"
|
|
@click="categoryChosen()"
|
|
>
|
|
{{ $t('action.next') }}
|
|
</button>
|
|
</template>
|
|
|
|
<template v-else-if="step === 'selectStatuses'">
|
|
<h1 mxa text-4xl mb4>
|
|
{{ status ? $t('report.select_posts_other') : $t('report.select_posts') }}
|
|
</h1>
|
|
<p text-primary font-bold>
|
|
{{ $t('report.select_many') }}
|
|
</p>
|
|
<table>
|
|
<tr v-for="availableStatus in availableStatuses" :key="availableStatus.id">
|
|
<td>
|
|
<input
|
|
:id="availableStatus.id"
|
|
v-model="selectedStatusIds"
|
|
type="checkbox"
|
|
:value="availableStatus.id"
|
|
>
|
|
</td>
|
|
<td>
|
|
<label :for="availableStatus.id">
|
|
<StatusCard :status="availableStatus" :actions="false" pointer-events-none />
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<button
|
|
btn-solid mxa mt-5
|
|
@click="submitReport()"
|
|
>
|
|
{{ $t('report.submit') }}
|
|
</button>
|
|
</template>
|
|
|
|
<template v-else-if="step === 'furtherActions'">
|
|
<h1 mxa text-4xl mb4>
|
|
{{ reportReason === 'dontlike' ? $t('report.further_actions.limit.title') : $t('report.further_actions.report.title') }}
|
|
</h1>
|
|
<p text-xl>
|
|
{{ reportReason === 'dontlike' ? $t('report.further_actions.limit.description') : $t('report.further_actions.report.description') }}
|
|
</p>
|
|
|
|
<div v-if="useRelationship(account).value?.following">
|
|
<button btn-outline mxa mt-4 mb-2 @click="unfollow()">
|
|
<i18n-t keypath="menu.unfollow_account">
|
|
<b>@{{ account.acct }}</b>
|
|
</i18n-t>
|
|
</button><br>
|
|
{{ $t('report.unfollow_desc') }}
|
|
</div>
|
|
<div v-if="!useRelationship(account).value?.muting">
|
|
<button btn-outline mxa mt-4 mb-2 @click="mute()">
|
|
<i18n-t keypath="menu.mute_account">
|
|
<b>@{{ account.acct }}</b>
|
|
</i18n-t>
|
|
</button><br>
|
|
{{ $t('report.mute_desc') }}
|
|
</div>
|
|
<div v-if="!useRelationship(account).value?.blocking">
|
|
<button btn-outline mxa mt-4 mb-2 @click="block()">
|
|
<i18n-t keypath="menu.block_account">
|
|
<b>@{{ account.acct }}</b>
|
|
</i18n-t>
|
|
</button><br>
|
|
{{ $t('report.block_desc') }}
|
|
</div>
|
|
<button btn-solid mxa mt-10 @click="emit('close')">
|
|
{{ $t('action.done') }}
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
tr {
|
|
border-bottom-width: 1px;
|
|
}
|
|
|
|
tr:last-child {
|
|
border: none;
|
|
}
|
|
|
|
td {
|
|
padding-top: 10px;
|
|
padding-bottom: 10px;
|
|
}
|
|
</style>
|