mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2026-03-22 18:25:29 +00:00
413 lines
12 KiB
Vue
413 lines
12 KiB
Vue
<template>
|
|
<div class="content-menu" @click.stop.prevent>
|
|
<dropdown :placement="placement" offset="5">
|
|
<template #default="{ toggleMenu }">
|
|
<slot name="button" :toggleMenu="toggleMenu">
|
|
<os-button
|
|
data-test="content-menu-button"
|
|
variant="primary"
|
|
appearance="outline"
|
|
size="sm"
|
|
circle
|
|
:aria-label="$t('actions.menu')"
|
|
@click.prevent="toggleMenu()"
|
|
>
|
|
<template #icon>
|
|
<os-icon :icon="icons.ellipsisV" />
|
|
</template>
|
|
</os-button>
|
|
</slot>
|
|
</template>
|
|
<template #popover="{ toggleMenu }">
|
|
<div class="content-menu-popover">
|
|
<ds-menu :routes="routes">
|
|
<template #menuitem="item">
|
|
<ds-menu-item
|
|
:route="item.route"
|
|
:parents="item.parents"
|
|
@click.stop.prevent="openItem(item.route, toggleMenu)"
|
|
>
|
|
<os-icon :icon="item.route.icon" />
|
|
{{ item.route.label }}
|
|
</ds-menu-item>
|
|
</template>
|
|
</ds-menu>
|
|
</div>
|
|
</template>
|
|
</dropdown>
|
|
<confirm-modal
|
|
v-if="showConfirmModal"
|
|
:modalData="currentModalData"
|
|
@close="showConfirmModal = false"
|
|
/>
|
|
<report-modal
|
|
v-if="showReportModal"
|
|
:name="getResourceName()"
|
|
:type="resourceType"
|
|
:id="resource.id"
|
|
@close="showReportModal = false"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { OsButton, OsIcon } from '@ocelot-social/ui'
|
|
import { iconRegistry } from '~/utils/iconRegistry'
|
|
import Dropdown from '~/components/Dropdown'
|
|
import ConfirmModal from '~/components/Modal/ConfirmModal'
|
|
import ReportModal from '~/components/Modal/ReportModal'
|
|
import { reviewMutation } from '~/graphql/Moderation.js'
|
|
import PinnedPostsMixin from '~/mixins/pinnedPosts'
|
|
|
|
export default {
|
|
name: 'ContentMenu',
|
|
components: {
|
|
ConfirmModal,
|
|
Dropdown,
|
|
OsButton,
|
|
OsIcon,
|
|
ReportModal,
|
|
},
|
|
mixins: [PinnedPostsMixin],
|
|
props: {
|
|
placement: { type: String, default: 'top-end' },
|
|
resource: { type: Object, required: true },
|
|
isOwner: { type: Boolean, default: false },
|
|
resourceType: {
|
|
type: String,
|
|
required: true,
|
|
validator: (value) => {
|
|
return value.match(/(contribution|comment|organization|user)/)
|
|
},
|
|
},
|
|
modalsData: {
|
|
type: Object,
|
|
required: false,
|
|
default: () => {
|
|
return {}
|
|
},
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
showConfirmModal: false,
|
|
showReportModal: false,
|
|
currentModalData: null,
|
|
}
|
|
},
|
|
created() {
|
|
this.icons = iconRegistry
|
|
},
|
|
computed: {
|
|
routes() {
|
|
const routes = []
|
|
|
|
if (this.resourceType === 'contribution') {
|
|
if (this.isOwner) {
|
|
routes.push({
|
|
label: this.$t(`post.menu.edit`),
|
|
name: 'post-edit-id',
|
|
params: {
|
|
id: this.resource.id,
|
|
},
|
|
icon: this.icons.edit,
|
|
})
|
|
routes.push({
|
|
label: this.$t(`post.menu.delete`),
|
|
callback: () => {
|
|
this.openModal('confirm', 'delete')
|
|
},
|
|
icon: this.icons.trash,
|
|
})
|
|
}
|
|
|
|
if (this.isAdmin && (!this.resource.group || this.resource.group.groupType === 'public')) {
|
|
if (!this.resource.pinnedBy && this.canBePinned) {
|
|
routes.push({
|
|
label: this.$t(`post.menu.pin`),
|
|
callback: () => {
|
|
this.$emit('pinPost', this.resource)
|
|
},
|
|
icon: this.icons.mapPin,
|
|
})
|
|
} else {
|
|
if (this.resource.pinnedBy) {
|
|
routes.push({
|
|
label: this.$t(`post.menu.unpin`),
|
|
callback: () => {
|
|
this.$emit('unpinPost', this.resource)
|
|
},
|
|
icon: this.icons.unlink,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.isAdmin) {
|
|
routes.push({
|
|
label: this.$t(`post.menu.push`),
|
|
callback: () => {
|
|
this.$emit('pushPost', this.resource)
|
|
},
|
|
icon: this.icons.arrowUp,
|
|
})
|
|
}
|
|
|
|
if (this.isAdmin && this.resource.sortDate !== this.resource.createdAt) {
|
|
routes.push({
|
|
label: this.$t(`post.menu.unpush`),
|
|
callback: () => {
|
|
this.$emit('unpushPost', this.resource)
|
|
},
|
|
icon: this.icons.unlink,
|
|
})
|
|
}
|
|
|
|
if (this.resource.isObservedByMe) {
|
|
routes.push({
|
|
label: this.$t(`post.menu.unobserve`),
|
|
callback: () => {
|
|
this.$emit('toggleObservePost', this.resource.id, false)
|
|
},
|
|
icon: this.icons.bellSlashed,
|
|
})
|
|
} else {
|
|
routes.push({
|
|
label: this.$t(`post.menu.observe`),
|
|
callback: () => {
|
|
this.$emit('toggleObservePost', this.resource.id, true)
|
|
},
|
|
icon: this.icons.bell,
|
|
})
|
|
}
|
|
}
|
|
|
|
if (this.isOwner && this.resourceType === 'comment') {
|
|
routes.push({
|
|
label: this.$t(`comment.menu.edit`),
|
|
callback: () => {
|
|
this.$emit('editComment')
|
|
},
|
|
icon: this.icons.edit,
|
|
})
|
|
routes.push({
|
|
label: this.$t(`comment.menu.delete`),
|
|
callback: () => {
|
|
this.openModal('confirm', 'delete')
|
|
},
|
|
icon: this.icons.trash,
|
|
})
|
|
}
|
|
|
|
if (!this.isOwner) {
|
|
routes.push({
|
|
label: this.$t(`report.${this.resourceType}.title`),
|
|
callback: () => {
|
|
this.openModal('report')
|
|
},
|
|
icon: this.icons.flag,
|
|
})
|
|
}
|
|
|
|
if (!this.isOwner && this.isModerator) {
|
|
if (!this.resource.disabled) {
|
|
routes.push({
|
|
label: this.$t(`disable.${this.resourceType}.title`),
|
|
callback: () => {
|
|
this.openModal('confirm', 'disable')
|
|
},
|
|
icon: this.icons.eyeSlash,
|
|
})
|
|
} else {
|
|
routes.push({
|
|
label: this.$t(`release.${this.resourceType}.title`),
|
|
callback: () => {
|
|
this.openModal('confirm', 'release')
|
|
},
|
|
icon: this.icons.eye,
|
|
})
|
|
}
|
|
}
|
|
|
|
if (this.resourceType === 'user') {
|
|
if (this.isOwner) {
|
|
routes.push({
|
|
label: this.$t(`settings.name`),
|
|
path: '/settings',
|
|
icon: this.icons.edit,
|
|
})
|
|
} else {
|
|
if (this.resource.isMuted) {
|
|
routes.push({
|
|
label: this.$t(`settings.muted-users.unmute`),
|
|
callback: () => {
|
|
this.$emit('unmute', this.resource)
|
|
},
|
|
icon: this.icons.microphone,
|
|
})
|
|
} else {
|
|
routes.push({
|
|
label: this.$t(`settings.muted-users.mute`),
|
|
callback: () => {
|
|
this.$emit('mute', this.resource)
|
|
},
|
|
icon: this.icons.microphoneSlash,
|
|
})
|
|
}
|
|
if (this.resource.isBlocked) {
|
|
routes.push({
|
|
label: this.$t(`settings.blocked-users.unblock`),
|
|
callback: () => {
|
|
this.$emit('unblock', this.resource)
|
|
},
|
|
icon: this.icons.userPlus,
|
|
})
|
|
} else {
|
|
routes.push({
|
|
label: this.$t(`settings.blocked-users.block`),
|
|
callback: () => {
|
|
this.$emit('block', this.resource)
|
|
},
|
|
icon: this.icons.userTimes,
|
|
})
|
|
}
|
|
if (this.isAdmin === true) {
|
|
routes.push({
|
|
label: this.$t(`settings.deleteUserAccount.name`),
|
|
callback: () => {
|
|
this.$emit('delete', this.resource)
|
|
},
|
|
icon: this.icons.trash,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
if (
|
|
this.resourceType === 'contribution' &&
|
|
this.resource.group &&
|
|
['admin', 'owner'].includes(this.resource.group.myRole) &&
|
|
(this.canBeGroupPinned || this.resource.groupPinned)
|
|
) {
|
|
routes.push({
|
|
label: this.resource.groupPinned
|
|
? this.$t(`post.menu.groupUnpin`)
|
|
: this.$t(`post.menu.groupPin`),
|
|
callback: () => {
|
|
this.$emit(this.resource.groupPinned ? 'unpinGroupPost' : 'pinGroupPost', this.resource)
|
|
},
|
|
icon: this.resource.groupPinned ? this.icons.unlink : this.icons.mapPin,
|
|
})
|
|
}
|
|
|
|
return routes
|
|
},
|
|
isModerator() {
|
|
return this.$store.getters['auth/isModerator']
|
|
},
|
|
isAdmin() {
|
|
return this.$store.getters['auth/isAdmin']
|
|
},
|
|
canBePinned() {
|
|
return (
|
|
this.maxPinnedPosts === 1 ||
|
|
(this.maxPinnedPosts > 1 && this.currentlyPinnedPosts < this.maxPinnedPosts)
|
|
)
|
|
},
|
|
canBeGroupPinned() {
|
|
const maxGroupPinnedPosts = this.$env.MAX_GROUP_PINNED_POSTS
|
|
return (
|
|
maxGroupPinnedPosts === 1 ||
|
|
(maxGroupPinnedPosts > 1 &&
|
|
this.resource.group &&
|
|
this.resource.group.currentlyPinnedPostsCount < maxGroupPinnedPosts)
|
|
)
|
|
},
|
|
},
|
|
methods: {
|
|
openItem(route, toggleMenu) {
|
|
if (route.callback) {
|
|
route.callback()
|
|
} else {
|
|
this.$router.push(route)
|
|
}
|
|
toggleMenu()
|
|
},
|
|
openModal(dialog, modalDataName = null) {
|
|
if (dialog === 'report') {
|
|
this.showReportModal = true
|
|
return
|
|
}
|
|
let modalData = {}
|
|
if (modalDataName) {
|
|
if (modalDataName === 'disable' || modalDataName === 'release') {
|
|
modalData = this.reviewModalData(modalDataName)
|
|
} else {
|
|
modalData = this.modalsData[modalDataName] || {}
|
|
}
|
|
}
|
|
this.currentModalData = modalData
|
|
this.showConfirmModal = true
|
|
},
|
|
reviewModalData(action) {
|
|
const disable = action === 'disable'
|
|
const name = this.getResourceName()
|
|
return {
|
|
titleIdent: `${action}.${this.resourceType}.title`,
|
|
messageIdent: `${action}.${this.resourceType}.message`,
|
|
messageParams: { name: this.$filters.truncate(name, 30) },
|
|
buttons: {
|
|
confirm: {
|
|
danger: true,
|
|
icon: this.icons.exclamationCircle,
|
|
textIdent: `${action}.submit`,
|
|
callback: async () => {
|
|
try {
|
|
await this.$apollo.mutate({
|
|
mutation: reviewMutation(),
|
|
variables: { resourceId: this.resource.id, disable, closed: false },
|
|
})
|
|
this.$toast.success(this.$t(`${action}.success`))
|
|
this.$set(this.resource, 'disabled', disable)
|
|
} catch (err) {
|
|
this.$toast.error(err.message)
|
|
throw err
|
|
}
|
|
},
|
|
},
|
|
cancel: {
|
|
icon: this.icons.close,
|
|
textIdent: `${action}.cancel`,
|
|
callback: () => {},
|
|
},
|
|
},
|
|
}
|
|
},
|
|
getResourceName() {
|
|
switch (this.resourceType) {
|
|
case 'user':
|
|
case 'organization':
|
|
return this.resource.name || ''
|
|
case 'contribution':
|
|
return this.resource.title || ''
|
|
case 'comment':
|
|
return this.resource.author?.name || ''
|
|
default:
|
|
return ''
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.content-menu-popover {
|
|
nav {
|
|
margin-top: -$space-xx-small;
|
|
margin-bottom: -$space-xx-small;
|
|
margin-left: -$space-x-small;
|
|
margin-right: -$space-x-small;
|
|
}
|
|
}
|
|
</style>
|