forked from Mirrors/elk
feat: Report posts (#2184)
This commit is contained in:
parent
5ea09d323f
commit
34aca66fef
8 changed files with 462 additions and 70 deletions
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { mastodon } from 'masto'
|
import type { mastodon } from 'masto'
|
||||||
|
import { toggleFollowAccount, useRelationship } from '~~/composables/masto/relationship'
|
||||||
|
|
||||||
const { account, command, context, ...props } = defineProps<{
|
const { account, command, context, ...props } = defineProps<{
|
||||||
account: mastodon.v1.Account
|
account: mastodon.v1.Account
|
||||||
|
@ -14,26 +15,6 @@ const enable = $computed(() => !isSelf && currentUser.value)
|
||||||
const relationship = $computed(() => props.relationship || useRelationship(account).value)
|
const relationship = $computed(() => props.relationship || useRelationship(account).value)
|
||||||
|
|
||||||
const { client } = $(useMasto())
|
const { client } = $(useMasto())
|
||||||
async function toggleFollow() {
|
|
||||||
if (relationship!.following) {
|
|
||||||
if (await openConfirmDialog({
|
|
||||||
title: t('confirm.unfollow.title'),
|
|
||||||
confirm: t('confirm.unfollow.confirm'),
|
|
||||||
cancel: t('confirm.unfollow.cancel'),
|
|
||||||
}) !== 'confirm')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
relationship!.following = !relationship!.following
|
|
||||||
try {
|
|
||||||
const newRel = await client.v1.accounts[relationship!.following ? 'follow' : 'unfollow'](account.id)
|
|
||||||
Object.assign(relationship!, newRel)
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
// TODO error handling
|
|
||||||
relationship!.following = !relationship!.following
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unblock() {
|
async function unblock() {
|
||||||
relationship!.blocking = false
|
relationship!.blocking = false
|
||||||
|
@ -67,7 +48,7 @@ useCommand({
|
||||||
visible: () => command && enable,
|
visible: () => command && enable,
|
||||||
name: () => `${relationship?.following ? t('account.unfollow') : t('account.follow')} ${getShortHandle(account)}`,
|
name: () => `${relationship?.following ? t('account.unfollow') : t('account.follow')} ${getShortHandle(account)}`,
|
||||||
icon: 'i-ri:star-line',
|
icon: 'i-ri:star-line',
|
||||||
onActivate: () => toggleFollow(),
|
onActivate: () => toggleFollowAccount(relationship!, account),
|
||||||
})
|
})
|
||||||
|
|
||||||
const buttonStyle = $computed(() => {
|
const buttonStyle = $computed(() => {
|
||||||
|
@ -95,7 +76,7 @@ const buttonStyle = $computed(() => {
|
||||||
rounded-full flex="~ gap2 center" font-500 min-w-30 h-fit px3 py1
|
rounded-full flex="~ gap2 center" font-500 min-w-30 h-fit px3 py1
|
||||||
:class="buttonStyle"
|
:class="buttonStyle"
|
||||||
:hover="!relationship?.blocking && !relationship?.muting && relationship?.following ? 'border-red text-red' : 'bg-base border-primary text-primary'"
|
:hover="!relationship?.blocking && !relationship?.muting && relationship?.following ? 'border-red text-red' : 'bg-base border-primary text-primary'"
|
||||||
@click="relationship?.blocking ? unblock() : relationship?.muting ? unmute() : toggleFollow()"
|
@click="relationship?.blocking ? unblock() : relationship?.muting ? unmute() : toggleFollowAccount(relationship!, account)"
|
||||||
>
|
>
|
||||||
<template v-if="relationship?.blocking">
|
<template v-if="relationship?.blocking">
|
||||||
<span elk-group-hover="hidden">{{ $t('account.blocking') }}</span>
|
<span elk-group-hover="hidden">{{ $t('account.blocking') }}</span>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { mastodon } from 'masto'
|
import type { mastodon } from 'masto'
|
||||||
|
import { toggleBlockAccount, toggleBlockDomain, toggleMuteAccount } from '~~/composables/masto/relationship'
|
||||||
|
|
||||||
const { account } = defineProps<{
|
const { account } = defineProps<{
|
||||||
account: mastodon.v1.Account
|
account: mastodon.v1.Account
|
||||||
|
@ -18,46 +19,6 @@ const { t } = useI18n()
|
||||||
const { client } = $(useMasto())
|
const { client } = $(useMasto())
|
||||||
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
|
||||||
|
|
||||||
async function toggleMute() {
|
|
||||||
if (!relationship!.muting && await openConfirmDialog({
|
|
||||||
title: t('confirm.mute_account.title', [account.acct]),
|
|
||||||
confirm: t('confirm.mute_account.confirm'),
|
|
||||||
cancel: t('confirm.mute_account.cancel'),
|
|
||||||
}) !== 'confirm')
|
|
||||||
return
|
|
||||||
|
|
||||||
relationship!.muting = !relationship!.muting
|
|
||||||
relationship = relationship!.muting
|
|
||||||
? await client.v1.accounts.mute(account.id, {
|
|
||||||
// TODO support more options
|
|
||||||
})
|
|
||||||
: await client.v1.accounts.unmute(account.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function toggleBlockUser() {
|
|
||||||
if (!relationship!.blocking && await openConfirmDialog({
|
|
||||||
title: t('confirm.block_account.title', [account.acct]),
|
|
||||||
confirm: t('confirm.block_account.confirm'),
|
|
||||||
cancel: t('confirm.block_account.cancel'),
|
|
||||||
}) !== 'confirm')
|
|
||||||
return
|
|
||||||
|
|
||||||
relationship!.blocking = !relationship!.blocking
|
|
||||||
relationship = await client.v1.accounts[relationship!.blocking ? 'block' : 'unblock'](account.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function toggleBlockDomain() {
|
|
||||||
if (!relationship!.domainBlocking && await openConfirmDialog({
|
|
||||||
title: t('confirm.block_domain.title', [getServerName(account)]),
|
|
||||||
confirm: t('confirm.block_domain.confirm'),
|
|
||||||
cancel: t('confirm.block_domain.cancel'),
|
|
||||||
}) !== 'confirm')
|
|
||||||
return
|
|
||||||
|
|
||||||
relationship!.domainBlocking = !relationship!.domainBlocking
|
|
||||||
await client.v1.domainBlocks[relationship!.domainBlocking ? 'block' : 'unblock'](getServerName(account))
|
|
||||||
}
|
|
||||||
|
|
||||||
async function toggleReblogs() {
|
async function toggleReblogs() {
|
||||||
if (!relationship!.showingReblogs && await openConfirmDialog({
|
if (!relationship!.showingReblogs && await openConfirmDialog({
|
||||||
title: t('confirm.show_reblogs.title', [account.acct]),
|
title: t('confirm.show_reblogs.title', [account.acct]),
|
||||||
|
@ -149,16 +110,16 @@ async function removeUserNote() {
|
||||||
<CommonDropdownItem
|
<CommonDropdownItem
|
||||||
v-if="!relationship?.muting"
|
v-if="!relationship?.muting"
|
||||||
:text="$t('menu.mute_account', [`@${account.acct}`])"
|
:text="$t('menu.mute_account', [`@${account.acct}`])"
|
||||||
icon="i-ri:volume-up-fill"
|
icon="i-ri:volume-mute-line"
|
||||||
:command="command"
|
:command="command"
|
||||||
@click="toggleMute()"
|
@click="toggleMuteAccount (relationship!, account)"
|
||||||
/>
|
/>
|
||||||
<CommonDropdownItem
|
<CommonDropdownItem
|
||||||
v-else
|
v-else
|
||||||
:text="$t('menu.unmute_account', [`@${account.acct}`])"
|
:text="$t('menu.unmute_account', [`@${account.acct}`])"
|
||||||
icon="i-ri:volume-mute-line"
|
icon="i-ri:volume-up-fill"
|
||||||
:command="command"
|
:command="command"
|
||||||
@click="toggleMute()"
|
@click="toggleMuteAccount (relationship!, account)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CommonDropdownItem
|
<CommonDropdownItem
|
||||||
|
@ -166,14 +127,14 @@ async function removeUserNote() {
|
||||||
:text="$t('menu.block_account', [`@${account.acct}`])"
|
:text="$t('menu.block_account', [`@${account.acct}`])"
|
||||||
icon="i-ri:forbid-2-line"
|
icon="i-ri:forbid-2-line"
|
||||||
:command="command"
|
:command="command"
|
||||||
@click="toggleBlockUser()"
|
@click="toggleBlockAccount (relationship!, account)"
|
||||||
/>
|
/>
|
||||||
<CommonDropdownItem
|
<CommonDropdownItem
|
||||||
v-else
|
v-else
|
||||||
:text="$t('menu.unblock_account', [`@${account.acct}`])"
|
:text="$t('menu.unblock_account', [`@${account.acct}`])"
|
||||||
icon="i-ri:checkbox-circle-line"
|
icon="i-ri:checkbox-circle-line"
|
||||||
:command="command"
|
:command="command"
|
||||||
@click="toggleBlockUser()"
|
@click="toggleBlockAccount (relationship!, account)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template v-if="getServerName(account) !== currentServer">
|
<template v-if="getServerName(account) !== currentServer">
|
||||||
|
@ -182,16 +143,23 @@ async function removeUserNote() {
|
||||||
:text="$t('menu.block_domain', [getServerName(account)])"
|
:text="$t('menu.block_domain', [getServerName(account)])"
|
||||||
icon="i-ri:shut-down-line"
|
icon="i-ri:shut-down-line"
|
||||||
:command="command"
|
:command="command"
|
||||||
@click="toggleBlockDomain()"
|
@click="toggleBlockDomain(relationship!, account)"
|
||||||
/>
|
/>
|
||||||
<CommonDropdownItem
|
<CommonDropdownItem
|
||||||
v-else
|
v-else
|
||||||
:text="$t('menu.unblock_domain', [getServerName(account)])"
|
:text="$t('menu.unblock_domain', [getServerName(account)])"
|
||||||
icon="i-ri:restart-line"
|
icon="i-ri:restart-line"
|
||||||
:command="command"
|
:command="command"
|
||||||
@click="toggleBlockDomain()"
|
@click="toggleBlockDomain(relationship!, account)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<CommonDropdownItem
|
||||||
|
:text="$t('menu.report_account', [`@${account.acct}`])"
|
||||||
|
icon="i-ri:flag-2-line"
|
||||||
|
:command="command"
|
||||||
|
@click="openReportDialog(account)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
isMediaPreviewOpen,
|
isMediaPreviewOpen,
|
||||||
isPreviewHelpOpen,
|
isPreviewHelpOpen,
|
||||||
isPublishDialogOpen,
|
isPublishDialogOpen,
|
||||||
|
isReportDialogOpen,
|
||||||
isSigninDialogOpen,
|
isSigninDialogOpen,
|
||||||
} from '~/composables/dialog'
|
} from '~/composables/dialog'
|
||||||
|
|
||||||
|
@ -102,5 +103,8 @@ function handleFavouritedBoostedByClose() {
|
||||||
<ModalDialog v-model="isKeyboardShortcutsDialogOpen" max-w-full sm:max-w-140 md:max-w-170 lg:max-w-220 md:min-w-160>
|
<ModalDialog v-model="isKeyboardShortcutsDialogOpen" max-w-full sm:max-w-140 md:max-w-170 lg:max-w-220 md:min-w-160>
|
||||||
<MagickeysKeyboardShortcuts @close="closeKeyboardShortcuts()" />
|
<MagickeysKeyboardShortcuts @close="closeKeyboardShortcuts()" />
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
|
<ModalDialog v-model="isReportDialogOpen" keep-alive max-w-175>
|
||||||
|
<ReportModal v-if="reportAccount" :account="reportAccount" :status="reportStatus" @close="closeReportDialog()" />
|
||||||
|
</ModalDialog>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
266
components/report/ReportModal.vue
Normal file
266
components/report/ReportModal.vue
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
<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>
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { mastodon } from 'masto'
|
import type { mastodon } from 'masto'
|
||||||
|
import { toggleBlockAccount, toggleMuteAccount, useRelationship } from '~~/composables/masto/relationship'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
status: mastodon.v1.Status
|
status: mastodon.v1.Status
|
||||||
|
@ -260,6 +261,60 @@ function showFavoritedAndBoostedBy() {
|
||||||
:command="command"
|
:command="command"
|
||||||
@click="mentionUser(status.account)"
|
@click="mentionUser(status.account)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<CommonDropdownItem
|
||||||
|
v-if="!useRelationship(status.account).value?.muting"
|
||||||
|
:text="$t('menu.mute_account', [`@${status.account.acct}`])"
|
||||||
|
icon="i-ri:volume-mute-line"
|
||||||
|
:command="command"
|
||||||
|
@click="toggleMuteAccount(useRelationship(status.account).value!, status.account)"
|
||||||
|
/>
|
||||||
|
<CommonDropdownItem
|
||||||
|
v-else
|
||||||
|
:text="$t('menu.unmute_account', [`@${status.account.acct}`])"
|
||||||
|
icon="i-ri:volume-up-fill"
|
||||||
|
:command="command"
|
||||||
|
@click="toggleMuteAccount(useRelationship(status.account).value!, status.account)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CommonDropdownItem
|
||||||
|
v-if="!useRelationship(status.account).value?.blocking"
|
||||||
|
:text="$t('menu.block_account', [`@${status.account.acct}`])"
|
||||||
|
icon="i-ri:forbid-2-line"
|
||||||
|
:command="command"
|
||||||
|
@click="toggleBlockAccount(useRelationship(status.account).value!, status.account)"
|
||||||
|
/>
|
||||||
|
<CommonDropdownItem
|
||||||
|
v-else
|
||||||
|
:text="$t('menu.unblock_account', [`@${status.account.acct}`])"
|
||||||
|
icon="i-ri:checkbox-circle-line"
|
||||||
|
:command="command"
|
||||||
|
@click="toggleBlockAccount(useRelationship(status.account).value!, status.account)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template v-if="getServerName(status.account) && getServerName(status.account) !== currentServer">
|
||||||
|
<CommonDropdownItem
|
||||||
|
v-if="!useRelationship(status.account).value?.domainBlocking"
|
||||||
|
:text="$t('menu.block_domain', [getServerName(status.account)])"
|
||||||
|
icon="i-ri:shut-down-line"
|
||||||
|
:command="command"
|
||||||
|
@click="toggleBlockDomain(useRelationship(status.account).value!, status.account)"
|
||||||
|
/>
|
||||||
|
<CommonDropdownItem
|
||||||
|
v-else
|
||||||
|
:text="$t('menu.unblock_domain', [getServerName(status.account)])"
|
||||||
|
icon="i-ri:restart-line"
|
||||||
|
:command="command"
|
||||||
|
@click="toggleBlockDomain(useRelationship(status.account).value!, status.account)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<CommonDropdownItem
|
||||||
|
:text="$t('menu.report_account', [`@${status.account.acct}`])"
|
||||||
|
icon="i-ri:flag-2-line"
|
||||||
|
:command="command"
|
||||||
|
@click="openReportDialog(status.account, status)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,6 +12,9 @@ export const mediaPreviewIndex = ref(0)
|
||||||
export const statusEdit = ref<mastodon.v1.StatusEdit>()
|
export const statusEdit = ref<mastodon.v1.StatusEdit>()
|
||||||
export const dialogDraftKey = ref<string>()
|
export const dialogDraftKey = ref<string>()
|
||||||
|
|
||||||
|
export const reportAccount = ref<mastodon.v1.Account>()
|
||||||
|
export const reportStatus = ref<mastodon.v1.Status>()
|
||||||
|
|
||||||
export const commandPanelInput = ref('')
|
export const commandPanelInput = ref('')
|
||||||
|
|
||||||
export const isFirstVisit = useLocalStorage(STORAGE_KEY_FIRST_VISIT, !process.mock)
|
export const isFirstVisit = useLocalStorage(STORAGE_KEY_FIRST_VISIT, !process.mock)
|
||||||
|
@ -26,6 +29,7 @@ export const isCommandPanelOpen = ref(false)
|
||||||
export const isConfirmDialogOpen = ref(false)
|
export const isConfirmDialogOpen = ref(false)
|
||||||
export const isErrorDialogOpen = ref(false)
|
export const isErrorDialogOpen = ref(false)
|
||||||
export const isFavouritedBoostedByDialogOpen = ref(false)
|
export const isFavouritedBoostedByDialogOpen = ref(false)
|
||||||
|
export const isReportDialogOpen = ref(false)
|
||||||
|
|
||||||
export const lastPublishDialogStatus = ref<mastodon.v1.Status | null>(null)
|
export const lastPublishDialogStatus = ref<mastodon.v1.Status | null>(null)
|
||||||
|
|
||||||
|
@ -148,3 +152,13 @@ export function toggleKeyboardShortcuts() {
|
||||||
export function closeKeyboardShortcuts() {
|
export function closeKeyboardShortcuts() {
|
||||||
isKeyboardShortcutsDialogOpen.value = false
|
isKeyboardShortcutsDialogOpen.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function openReportDialog(account: mastodon.v1.Account, status?: mastodon.v1.Status) {
|
||||||
|
reportAccount.value = account
|
||||||
|
reportStatus.value = status
|
||||||
|
isReportDialogOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeReportDialog() {
|
||||||
|
isReportDialogOpen.value = false
|
||||||
|
}
|
||||||
|
|
|
@ -31,3 +31,68 @@ async function fetchRelationships() {
|
||||||
for (let i = 0; i < requested.length; i++)
|
for (let i = 0; i < requested.length; i++)
|
||||||
requested[i][1].value = relationships[i]
|
requested[i][1].value = relationships[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function toggleFollowAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
||||||
|
const { client } = $(useMasto())
|
||||||
|
const i18n = useNuxtApp().$i18n
|
||||||
|
|
||||||
|
if (relationship!.following) {
|
||||||
|
if (await openConfirmDialog({
|
||||||
|
title: i18n.t('confirm.unfollow.title'),
|
||||||
|
confirm: i18n.t('confirm.unfollow.confirm'),
|
||||||
|
cancel: i18n.t('confirm.unfollow.cancel'),
|
||||||
|
}) !== 'confirm')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
relationship!.following = !relationship!.following
|
||||||
|
relationship = await client.v1.accounts[relationship!.following ? 'follow' : 'unfollow'](account.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function toggleMuteAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
||||||
|
const { client } = $(useMasto())
|
||||||
|
const i18n = useNuxtApp().$i18n
|
||||||
|
|
||||||
|
if (!relationship!.muting && await openConfirmDialog({
|
||||||
|
title: i18n.t('confirm.mute_account.title', [account.acct]),
|
||||||
|
confirm: i18n.t('confirm.mute_account.confirm'),
|
||||||
|
cancel: i18n.t('confirm.mute_account.cancel'),
|
||||||
|
}) !== 'confirm')
|
||||||
|
return
|
||||||
|
|
||||||
|
relationship!.muting = !relationship!.muting
|
||||||
|
relationship = relationship!.muting
|
||||||
|
? await client.v1.accounts.mute(account.id, {
|
||||||
|
// TODO support more options
|
||||||
|
})
|
||||||
|
: await client.v1.accounts.unmute(account.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function toggleBlockAccount(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
||||||
|
const { client } = $(useMasto())
|
||||||
|
const i18n = useNuxtApp().$i18n
|
||||||
|
|
||||||
|
if (!relationship!.blocking && await openConfirmDialog({
|
||||||
|
title: i18n.t('confirm.block_account.title', [account.acct]),
|
||||||
|
confirm: i18n.t('confirm.block_account.confirm'),
|
||||||
|
cancel: i18n.t('confirm.block_account.cancel'),
|
||||||
|
}) !== 'confirm')
|
||||||
|
return
|
||||||
|
|
||||||
|
relationship!.blocking = !relationship!.blocking
|
||||||
|
relationship = await client.v1.accounts[relationship!.blocking ? 'block' : 'unblock'](account.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function toggleBlockDomain(relationship: mastodon.v1.Relationship, account: mastodon.v1.Account) {
|
||||||
|
const { client } = $(useMasto())
|
||||||
|
const i18n = useNuxtApp().$i18n
|
||||||
|
|
||||||
|
if (!relationship!.domainBlocking && await openConfirmDialog({
|
||||||
|
title: i18n.t('confirm.block_domain.title', [getServerName(account)]),
|
||||||
|
confirm: i18n.t('confirm.block_domain.confirm'),
|
||||||
|
cancel: i18n.t('confirm.block_domain.cancel'),
|
||||||
|
}) !== 'confirm')
|
||||||
|
return
|
||||||
|
|
||||||
|
relationship!.domainBlocking = !relationship!.domainBlocking
|
||||||
|
await client.v1.domainBlocks[relationship!.domainBlocking ? 'block' : 'unblock'](getServerName(account))
|
||||||
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"compose": "Compose",
|
"compose": "Compose",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
|
"done": "Done",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"enter_app": "Enter App",
|
"enter_app": "Enter App",
|
||||||
"favourite": "Favorite",
|
"favourite": "Favorite",
|
||||||
|
@ -243,6 +244,7 @@
|
||||||
"open_in_original_site": "Open in original site",
|
"open_in_original_site": "Open in original site",
|
||||||
"pin_on_profile": "Pin on profile",
|
"pin_on_profile": "Pin on profile",
|
||||||
"remove_personal_note": "Remove personal note from {0}",
|
"remove_personal_note": "Remove personal note from {0}",
|
||||||
|
"report_account": "Report {0}",
|
||||||
"share_post": "Share this post",
|
"share_post": "Share this post",
|
||||||
"show_favourited_and_boosted_by": "Show who favorited and boosted",
|
"show_favourited_and_boosted_by": "Show who favorited and boosted",
|
||||||
"show_reblogs": "Show boosts from {0}",
|
"show_reblogs": "Show boosts from {0}",
|
||||||
|
@ -254,6 +256,7 @@
|
||||||
"translate_post": "Translate post",
|
"translate_post": "Translate post",
|
||||||
"unblock_account": "Unblock {0}",
|
"unblock_account": "Unblock {0}",
|
||||||
"unblock_domain": "Unblock domain {0}",
|
"unblock_domain": "Unblock domain {0}",
|
||||||
|
"unfollow_account": "Unfollow {0}",
|
||||||
"unmute_account": "Unmute {0}",
|
"unmute_account": "Unmute {0}",
|
||||||
"unmute_conversation": "Unmute this post",
|
"unmute_conversation": "Unmute this post",
|
||||||
"unpin_on_profile": "Unpin on profile"
|
"unpin_on_profile": "Unpin on profile"
|
||||||
|
@ -353,6 +356,42 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"report": {
|
||||||
|
"additional_comments": "Additional comments",
|
||||||
|
"another_server": "The user you're reporting is from another server",
|
||||||
|
"anything_else": "Is there anything else you think we should know?",
|
||||||
|
"block_desc": "You will no longer see any posts from this user. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
|
||||||
|
"dontlike": "I don't like it",
|
||||||
|
"dontlike_desc": "It is not something you want to see",
|
||||||
|
"forward": "Yes, forward this report to {0}",
|
||||||
|
"forward_question": "Do you want to send an anonymized copy of this report to that server as well?",
|
||||||
|
"further_actions": {
|
||||||
|
"limit": {
|
||||||
|
"description": "Here are your options for controlling what you see:",
|
||||||
|
"title": "Don't want to see this?"
|
||||||
|
},
|
||||||
|
"report": {
|
||||||
|
"description": "While we review this, here are the actions you can take:",
|
||||||
|
"title": "Thanks for reporting, we'll look into this."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"limiting": "Limiting {0}",
|
||||||
|
"mute_desc": "You will no longer see any posts from this user. They can still follow you and see your posts. They will not know that they are muted.",
|
||||||
|
"other": "It's something else",
|
||||||
|
"other_desc": "The issue does not fit into other categories",
|
||||||
|
"reporting": "Reporting {0}",
|
||||||
|
"select_many": "Select all that apply:",
|
||||||
|
"select_one": "Choose the best match:",
|
||||||
|
"select_posts": "Are there any posts that back up this report?",
|
||||||
|
"select_posts_other": "Are there any other posts that back up this report?",
|
||||||
|
"spam": "It's spam",
|
||||||
|
"spam_desc": "Malicious links, fake engagement, or repetitive replies",
|
||||||
|
"submit": "Submit Report",
|
||||||
|
"unfollow_desc": "You will no longer see posts from this user in your home feed. You may still see posts from them elsewhere.",
|
||||||
|
"violation": "It violates one or more of the server rules",
|
||||||
|
"whats_wrong_account": "Tell us what's wrong with this account",
|
||||||
|
"whats_wrong_post": "Tell us what's wrong with this post"
|
||||||
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"search_desc": "Search for people & hashtags",
|
"search_desc": "Search for people & hashtags",
|
||||||
"search_empty": "Could not find anything for these search terms"
|
"search_empty": "Could not find anything for these search terms"
|
||||||
|
|
Loading…
Reference in a new issue