forked from potsda.mn/mobilizon
Merge branch 'search-fixes' into 'main'
Fix event card background color behind picture Closes #1174, #1172 et #1171 See merge request framasoft/mobilizon!1301
This commit is contained in:
commit
3b7f0f8acf
|
@ -142,7 +142,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item-active {
|
.dropdown-item-active {
|
||||||
@apply bg-white text-black;
|
@apply bg-white dark:bg-zinc-700 dark:text-zinc-100 text-black;
|
||||||
}
|
}
|
||||||
.dropdown-button {
|
.dropdown-button {
|
||||||
@apply inline-flex gap-1;
|
@apply inline-flex gap-1;
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<form
|
<form
|
||||||
class=""
|
|
||||||
v-if="isAbleToComment"
|
v-if="isAbleToComment"
|
||||||
@submit.prevent="createCommentForEvent(newComment)"
|
@submit.prevent="createCommentForEvent(newComment)"
|
||||||
@keyup.ctrl.enter="createCommentForEvent(newComment)"
|
class="mt-2"
|
||||||
>
|
>
|
||||||
<o-notification
|
<o-notification
|
||||||
v-if="isEventOrganiser && !areCommentsClosed"
|
v-if="isEventOrganiser && !areCommentsClosed"
|
||||||
|
@ -26,6 +25,7 @@
|
||||||
mode="comment"
|
mode="comment"
|
||||||
v-model="newComment.text"
|
v-model="newComment.text"
|
||||||
:aria-label="t('Comment body')"
|
:aria-label="t('Comment body')"
|
||||||
|
@submit="createCommentForEvent(newComment)"
|
||||||
/>
|
/>
|
||||||
<p class="" v-if="emptyCommentError">
|
<p class="" v-if="emptyCommentError">
|
||||||
{{ t("Comment text can't be empty") }}
|
{{ t("Comment text can't be empty") }}
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
<p v-if="commentsLoading" class="text-center">
|
<p v-if="commentsLoading" class="text-center">
|
||||||
{{ t("Loading comments…") }}
|
{{ t("Loading comments…") }}
|
||||||
</p>
|
</p>
|
||||||
<transition-group tag="div" name="comment-empty-list" v-else>
|
<transition-group tag="div" name="comment-empty-list" v-else class="mt-2">
|
||||||
<transition-group
|
<transition-group
|
||||||
key="list"
|
key="list"
|
||||||
name="comment-list"
|
name="comment-list"
|
||||||
|
@ -61,18 +61,17 @@
|
||||||
class="comment-list"
|
class="comment-list"
|
||||||
tag="ul"
|
tag="ul"
|
||||||
>
|
>
|
||||||
<comment
|
<event-comment
|
||||||
class="root-comment"
|
class="root-comment my-2"
|
||||||
:comment="comment"
|
:comment="comment"
|
||||||
:event="event"
|
:event="event"
|
||||||
:currentActor="currentActor"
|
:currentActor="currentActor"
|
||||||
v-for="comment in filteredOrderedComments"
|
v-for="comment in filteredOrderedComments"
|
||||||
:key="comment.id"
|
:key="comment.id"
|
||||||
@create-comment="createCommentForEvent"
|
@create-comment="createCommentForEvent"
|
||||||
@delete-comment="
|
@delete-comment="commentToDelete => deleteComment({
|
||||||
deleteComment({
|
commentId: commentToDelete.id as string,
|
||||||
commentId: comment.id as string,
|
originCommentId: commentToDelete.originComment?.id,
|
||||||
originCommentId: comment.originComment?.id,
|
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
@ -85,7 +84,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Comment from "@/components/Comment/EventComment.vue";
|
import EventComment from "@/components/Comment/EventComment.vue";
|
||||||
import IdentityPickerWrapper from "@/views/Account/IdentityPickerWrapper.vue";
|
import IdentityPickerWrapper from "@/views/Account/IdentityPickerWrapper.vue";
|
||||||
import { CommentModeration } from "@/types/enums";
|
import { CommentModeration } from "@/types/enums";
|
||||||
import { CommentModel, IComment } from "../../types/comment.model";
|
import { CommentModel, IComment } from "../../types/comment.model";
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
'bg-violet-1': commentSelected,
|
'bg-violet-1': commentSelected,
|
||||||
'shadow-none': !rootComment,
|
'shadow-none': !rootComment,
|
||||||
}"
|
}"
|
||||||
class="mbz-card p-2"
|
class="bg-white dark:bg-zinc-900 rounded p-2"
|
||||||
>
|
>
|
||||||
<article :id="commentId" dir="auto">
|
<article :id="commentId" dir="auto" class="mbz-comment">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="flex items-center gap-1" v-if="actorComment">
|
<div class="flex items-center gap-1" v-if="actorComment">
|
||||||
|
@ -36,9 +36,9 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a v-else :href="commentURL">
|
<p v-else :href="commentURL">
|
||||||
<span>{{ t("[deleted]") }}</span>
|
<span>{{ t("[deleted]") }}</span>
|
||||||
</a>
|
</p>
|
||||||
<a :href="commentURL">
|
<a :href="commentURL">
|
||||||
<small v-if="comment.updatedAt">{{
|
<small v-if="comment.updatedAt">{{
|
||||||
formatDistanceToNow(new Date(comment.updatedAt), {
|
formatDistanceToNow(new Date(comment.updatedAt), {
|
||||||
|
@ -47,19 +47,6 @@
|
||||||
})
|
})
|
||||||
}}</small>
|
}}</small>
|
||||||
</a>
|
</a>
|
||||||
<div v-if="!comment.deletedAt" class="flex">
|
|
||||||
<button
|
|
||||||
v-if="actorComment?.id === currentActor?.id"
|
|
||||||
@click="deleteComment"
|
|
||||||
>
|
|
||||||
<Delete :size="16" />
|
|
||||||
<span class="sr-only">{{ t("Delete") }}</span>
|
|
||||||
</button>
|
|
||||||
<button @click="reportModal">
|
|
||||||
<Alert :size="16" />
|
|
||||||
<span class="sr-only">{{ t("Report") }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!comment.deletedAt"
|
v-if="!comment.deletedAt"
|
||||||
|
@ -68,11 +55,53 @@
|
||||||
:lang="comment.language"
|
:lang="comment.language"
|
||||||
/>
|
/>
|
||||||
<div v-else>{{ t("[This comment has been deleted]") }}</div>
|
<div v-else>{{ t("[This comment has been deleted]") }}</div>
|
||||||
|
<nav class="flex gap-1 mt-1" v-if="!comment.deletedAt">
|
||||||
|
<button
|
||||||
|
class="cursor-pointer flex hover:bg-zinc-300 dark:hover:bg-zinc-600 rounded p-1"
|
||||||
|
v-if="
|
||||||
|
currentActor?.id &&
|
||||||
|
event.options.commentModeration !== CommentModeration.CLOSED &&
|
||||||
|
!comment.deletedAt
|
||||||
|
"
|
||||||
|
@click="createReplyToComment()"
|
||||||
|
>
|
||||||
|
<Reply />
|
||||||
|
<span>{{ t("Reply") }}</span>
|
||||||
|
</button>
|
||||||
|
<o-dropdown aria-role="list">
|
||||||
|
<template #trigger>
|
||||||
|
<button
|
||||||
|
class="cursor-pointer flex hover:bg-zinc-300 dark:hover:bg-zinc-600 rounded p-1"
|
||||||
|
>
|
||||||
|
<DotsHorizontal />
|
||||||
|
<span class="sr-only">{{ t("More options") }}</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<o-dropdown-item
|
||||||
|
aria-role="listitem"
|
||||||
|
v-if="actorComment?.id === currentActor?.id"
|
||||||
|
>
|
||||||
|
<button class="flex items-center gap-1" @click="deleteComment">
|
||||||
|
<Delete :size="16" />
|
||||||
|
<span>{{ t("Delete") }}</span>
|
||||||
|
</button>
|
||||||
|
</o-dropdown-item>
|
||||||
|
<o-dropdown-item aria-role="listitem">
|
||||||
|
<button
|
||||||
|
@click="isReportModalActive = true"
|
||||||
|
class="flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<Alert :size="16" />
|
||||||
|
<span>{{ t("Report") }}</span>
|
||||||
|
</button>
|
||||||
|
</o-dropdown-item>
|
||||||
|
</o-dropdown>
|
||||||
|
</nav>
|
||||||
<div class="" v-if="comment.totalReplies">
|
<div class="" v-if="comment.totalReplies">
|
||||||
<p
|
<button
|
||||||
v-if="!showReplies"
|
v-if="!showReplies"
|
||||||
@click="showReplies = true"
|
@click="showReplies = true"
|
||||||
class="flex cursor-pointer"
|
class="flex cursor-pointer hover:bg-zinc-300 dark:hover:bg-zinc-600 rounded p-1"
|
||||||
>
|
>
|
||||||
<ChevronDown />
|
<ChevronDown />
|
||||||
<span>{{
|
<span>{{
|
||||||
|
@ -84,28 +113,16 @@
|
||||||
comment.totalReplies
|
comment.totalReplies
|
||||||
)
|
)
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</button>
|
||||||
<p
|
<button
|
||||||
v-else-if="comment.totalReplies && showReplies"
|
v-else-if="comment.totalReplies && showReplies"
|
||||||
@click="showReplies = false"
|
@click="showReplies = false"
|
||||||
class="flex cursor-pointer"
|
class="flex cursor-pointer hover:bg-zinc-300 dark:hover:bg-zinc-600 rounded p-1"
|
||||||
>
|
>
|
||||||
<ChevronUp />
|
<ChevronUp />
|
||||||
<span>{{ t("Hide replies") }}</span>
|
<span>{{ t("Hide replies") }}</span>
|
||||||
</p>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<nav
|
|
||||||
v-if="
|
|
||||||
currentActor?.id &&
|
|
||||||
event.options.commentModeration !== CommentModeration.CLOSED &&
|
|
||||||
!comment.deletedAt
|
|
||||||
"
|
|
||||||
@click="createReplyToComment()"
|
|
||||||
class="flex gap-1 cursor-pointer"
|
|
||||||
>
|
|
||||||
<Reply />
|
|
||||||
<span>{{ t("Reply") }}</span>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<form
|
<form
|
||||||
|
@ -137,6 +154,7 @@
|
||||||
:current-actor="currentActor"
|
:current-actor="currentActor"
|
||||||
:aria-label="t('Comment body')"
|
:aria-label="t('Comment body')"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
|
@submit="replyToComment"
|
||||||
/>
|
/>
|
||||||
<o-button
|
<o-button
|
||||||
:disabled="newComment.text.trim().length === 0"
|
:disabled="newComment.text.trim().length === 0"
|
||||||
|
@ -149,29 +167,37 @@
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</form>
|
</form>
|
||||||
<div>
|
<transition-group
|
||||||
<div>
|
name="comment-replies"
|
||||||
<div @click="showReplies = false" />
|
v-if="showReplies"
|
||||||
</div>
|
tag="ul"
|
||||||
<transition-group
|
class="flex flex-col gap-2"
|
||||||
name="comment-replies"
|
>
|
||||||
v-if="showReplies"
|
<EventComment
|
||||||
tag="ul"
|
v-for="reply in comment.replies"
|
||||||
class="flex flex-col gap-2"
|
:key="reply.id"
|
||||||
>
|
:comment="reply"
|
||||||
<Comment
|
:event="event"
|
||||||
v-for="reply in comment.replies"
|
:currentActor="currentActor"
|
||||||
:key="reply.id"
|
:rootComment="false"
|
||||||
:comment="reply"
|
@create-comment="emit('create-comment', $event)"
|
||||||
:event="event"
|
@delete-comment="emit('delete-comment', $event)"
|
||||||
:currentActor="currentActor"
|
@report-comment="emit('report-comment', $event)"
|
||||||
:rootComment="false"
|
class="ml-2"
|
||||||
@create-comment="emit('create-comment', $event)"
|
/>
|
||||||
@delete-comment="emit('delete-comment', $event)"
|
</transition-group>
|
||||||
@report-comment="emit('report-comment', $event)"
|
<o-modal
|
||||||
/>
|
v-model:active="isReportModalActive"
|
||||||
</transition-group>
|
has-modal-card
|
||||||
</div>
|
ref="reportModal"
|
||||||
|
:close-button-aria-label="t('Close')"
|
||||||
|
>
|
||||||
|
<ReportModal
|
||||||
|
:on-confirm="reportComment"
|
||||||
|
:title="t('Report this comment')"
|
||||||
|
:outside-domain="comment.actor?.domain"
|
||||||
|
/>
|
||||||
|
</o-modal>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -197,8 +223,13 @@ import Delete from "vue-material-design-icons/Delete.vue";
|
||||||
import Alert from "vue-material-design-icons/Alert.vue";
|
import Alert from "vue-material-design-icons/Alert.vue";
|
||||||
import ChevronUp from "vue-material-design-icons/ChevronUp.vue";
|
import ChevronUp from "vue-material-design-icons/ChevronUp.vue";
|
||||||
import ChevronDown from "vue-material-design-icons/ChevronDown.vue";
|
import ChevronDown from "vue-material-design-icons/ChevronDown.vue";
|
||||||
|
import DotsHorizontal from "vue-material-design-icons/DotsHorizontal.vue";
|
||||||
import Reply from "vue-material-design-icons/Reply.vue";
|
import Reply from "vue-material-design-icons/Reply.vue";
|
||||||
import type { Locale } from "date-fns";
|
import type { Locale } from "date-fns";
|
||||||
|
import ReportModal from "@/components/Report/ReportModal.vue";
|
||||||
|
import { useCreateReport } from "@/composition/apollo/report";
|
||||||
|
import { Snackbar } from "@/plugins/snackbar";
|
||||||
|
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||||
|
|
||||||
const Editor = defineAsyncComponent(
|
const Editor = defineAsyncComponent(
|
||||||
() => import("@/components/TextEditor.vue")
|
() => import("@/components/TextEditor.vue")
|
||||||
|
@ -214,26 +245,20 @@ const props = withDefaults(
|
||||||
{ rootComment: true }
|
{ rootComment: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits<{
|
||||||
"create-comment",
|
(e: "create-comment", comment: IComment): void;
|
||||||
"delete-comment",
|
(e: "delete-comment", comment: IComment): void;
|
||||||
"report-comment",
|
(e: "report-comment", comment: IComment): void;
|
||||||
]);
|
}>();
|
||||||
|
|
||||||
const commentEditor = ref<typeof EditorComponent | null>(null);
|
const commentEditor = ref<typeof EditorComponent | null>(null);
|
||||||
|
|
||||||
// Hack because Vue only exports it's own interface.
|
|
||||||
// See https://github.com/kaorun343/vue-property-decorator/issues/257
|
|
||||||
// @Ref() readonly commentEditor!: EditorComponent & {
|
|
||||||
// replyToComment: (comment: IComment) => void;
|
|
||||||
// focus: () => void;
|
|
||||||
// };
|
|
||||||
|
|
||||||
const newComment = ref<IComment>(new CommentModel());
|
const newComment = ref<IComment>(new CommentModel());
|
||||||
const replyTo = ref(false);
|
const replyTo = ref(false);
|
||||||
const showReplies = ref(false);
|
const showReplies = ref(false);
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n({ useScope: "global" });
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
const isReportModalActive = ref(false);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (route?.hash.includes(`#comment-${props.comment.uuid}`)) {
|
if (route?.hash.includes(`#comment-${props.comment.uuid}`)) {
|
||||||
|
@ -310,219 +335,57 @@ const reportModal = (): void => {
|
||||||
// });
|
// });
|
||||||
};
|
};
|
||||||
|
|
||||||
// const reportComment = async (
|
const {
|
||||||
// content: string,
|
mutate: createReportMutation,
|
||||||
// forward: boolean
|
onError: onCreateReportError,
|
||||||
// ): Promise<void> => {
|
onDone: oneCreateReportDone,
|
||||||
// try {
|
} = useCreateReport();
|
||||||
// if (!props.comment.actor) return;
|
|
||||||
|
|
||||||
// const { onError, onDone } = useMutation(CREATE_REPORT, () => ({
|
const reportComment = async (
|
||||||
// variables: {
|
content: string,
|
||||||
// eventId: props.event.id,
|
forward: boolean
|
||||||
// reportedId: props.comment.actor?.id,
|
): Promise<void> => {
|
||||||
// commentsIds: [props.comment.id],
|
if (!props.comment.actor) return;
|
||||||
// content,
|
createReportMutation({
|
||||||
// forward,
|
eventId: props.event.id,
|
||||||
// },
|
reportedId: props.comment.actor?.id ?? "",
|
||||||
// }));
|
commentsIds: [props.comment.id ?? ""],
|
||||||
|
content,
|
||||||
|
forward,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const snackbar = inject<Snackbar>("snackbar");
|
||||||
|
const { oruga } = useProgrammatic();
|
||||||
|
|
||||||
|
onCreateReportError((e) => {
|
||||||
|
isReportModalActive.value = false;
|
||||||
|
if (e.message) {
|
||||||
|
snackbar?.open({
|
||||||
|
message: e.message,
|
||||||
|
variant: "danger",
|
||||||
|
position: "bottom",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
oneCreateReportDone(() => {
|
||||||
|
isReportModalActive.value = false;
|
||||||
|
oruga.notification.open({
|
||||||
|
message: t("Comment from {'@'}{username} reported", {
|
||||||
|
username: props.comment.actor?.preferredUsername,
|
||||||
|
}),
|
||||||
|
variant: "success",
|
||||||
|
position: "bottom-right",
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// // this.$buefy.notification.open({
|
|
||||||
// // message: this.t("Comment from @{username} reported", {
|
|
||||||
// // username: this.comment.actor.preferredUsername,
|
|
||||||
// // }) as string,
|
|
||||||
// // type: "is-success",
|
|
||||||
// // position: "is-bottom-right",
|
|
||||||
// // duration: 5000,
|
|
||||||
// // });
|
|
||||||
// } catch (e: any) {
|
|
||||||
// if (e.message) {
|
|
||||||
// // Snackbar.open({
|
|
||||||
// // message: e.message,
|
|
||||||
// // type: "is-danger",
|
|
||||||
// // position: "is-bottom",
|
|
||||||
// // });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
const actorComment = computed(() => props.comment.actor);
|
const actorComment = computed(() => props.comment.actor);
|
||||||
const dateFnsLocale = inject<Locale>("dateFnsLocale");
|
const dateFnsLocale = inject<Locale>("dateFnsLocale");
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style>
|
||||||
@use "@/styles/_mixins" as *;
|
article.mbz-comment .mention.h-card {
|
||||||
form.reply {
|
@apply inline-block border border-zinc-600 dark:border-zinc-300 rounded py-0.5 px-1;
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first-line {
|
|
||||||
margin-bottom: 3px;
|
|
||||||
|
|
||||||
* {
|
|
||||||
padding: 0 5px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong.organizer {
|
|
||||||
border-radius: 12px;
|
|
||||||
color: white;
|
|
||||||
padding: 0 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// & > small {
|
|
||||||
// @include margin-left(0.3rem);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-line {
|
|
||||||
display: flex;
|
|
||||||
max-width: calc(80rem - 64px);
|
|
||||||
|
|
||||||
.editor {
|
|
||||||
flex: 1;
|
|
||||||
// @include padding-right(10px);
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a.comment-link {
|
|
||||||
text-decoration: none;
|
|
||||||
// @include margin-left(5px);
|
|
||||||
color: text;
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
small {
|
|
||||||
&:hover {
|
|
||||||
color: hsl(0, 0%, 21%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-element {
|
|
||||||
padding: 0.25rem;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
&.announcement {
|
|
||||||
small {
|
|
||||||
color: hsl(0, 0%, 21%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
.reply-btn,
|
|
||||||
small,
|
|
||||||
span,
|
|
||||||
strong,
|
|
||||||
a.comment-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// .media-left {
|
|
||||||
// @include margin-right(5px);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
.root-comment .replies {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.left {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
// @include margin-right(10px);
|
|
||||||
|
|
||||||
.vertical-border {
|
|
||||||
width: 3px;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.05);
|
|
||||||
margin: 10px calc(1rem + 1px);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .media-content {
|
|
||||||
overflow-x: initial;
|
|
||||||
.content {
|
|
||||||
text-align: start;
|
|
||||||
.editor-line {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icons {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.media:hover .media-content .icons {
|
|
||||||
display: inline;
|
|
||||||
|
|
||||||
button {
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.load-replies {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
& > p > span {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.level-item.reply-btn {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
article {
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-replies {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-replies-enter-active,
|
|
||||||
.comment-replies-leave-active,
|
|
||||||
.comment-replies-move {
|
|
||||||
transition: 500ms cubic-bezier(0.59, 0.12, 0.34, 0.95);
|
|
||||||
transition-property: opacity, transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-replies-enter {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(50px) scaleY(0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-replies-enter-to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0) scaleY(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-replies-leave-active {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-replies-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scaleY(0);
|
|
||||||
transform-origin: center top;
|
|
||||||
}
|
|
||||||
|
|
||||||
// .reply-action .icon {
|
|
||||||
// @include padding-right(0.4rem);
|
|
||||||
// }
|
|
||||||
|
|
||||||
.visually-hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
22
js/src/components/Editor/RichEditorKeyboardSubmit.ts
Normal file
22
js/src/components/Editor/RichEditorKeyboardSubmit.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Extension } from "@tiptap/vue-3";
|
||||||
|
|
||||||
|
export interface RichEditorKeyboardSubmitOptions {
|
||||||
|
submit: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Extension.create<RichEditorKeyboardSubmitOptions>({
|
||||||
|
name: "RichEditorKeyboardSubmit",
|
||||||
|
addOptions() {
|
||||||
|
return {
|
||||||
|
submit: () => ({}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
addKeyboardShortcuts() {
|
||||||
|
return {
|
||||||
|
"Ctrl-Enter": () => {
|
||||||
|
this.options.submit();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
|
@ -9,7 +9,7 @@
|
||||||
:isInternal="isInternal"
|
:isInternal="isInternal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-secondary rounded-lg"
|
class="rounded-lg"
|
||||||
:class="{ 'sm:w-full sm:max-w-[20rem]': mode === 'row' }"
|
:class="{ 'sm:w-full sm:max-w-[20rem]': mode === 'row' }"
|
||||||
>
|
>
|
||||||
<figure class="block relative pt-40">
|
<figure class="block relative pt-40">
|
||||||
|
|
|
@ -1,23 +1,35 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dark:bg-zinc-700 p-2 rounded" v-if="report">
|
<div
|
||||||
<div class="flex gap-1">
|
class="bg-mbz-yellow-alt-50 hover:bg-mbz-yellow-alt-100 dark:bg-zinc-700 hover:dark:bg-zinc-600 rounded"
|
||||||
<figure class="" v-if="report.reported.avatar">
|
v-if="report"
|
||||||
<img
|
>
|
||||||
alt=""
|
<div class="flex justify-between gap-1 border-b p-2">
|
||||||
:src="report.reported.avatar.url"
|
<div class="flex gap-1">
|
||||||
class="rounded-full"
|
<figure class="" v-if="report.reported.avatar">
|
||||||
width="48"
|
<img
|
||||||
height="48"
|
alt=""
|
||||||
/>
|
:src="report.reported.avatar.url"
|
||||||
</figure>
|
class="rounded-full"
|
||||||
<AccountCircle v-else :size="48" />
|
width="24"
|
||||||
<div class="">
|
height="24"
|
||||||
<p class="" v-if="report.reported.name">{{ report.reported.name }}</p>
|
/>
|
||||||
<p class="">@{{ usernameWithDomain(report.reported) }}</p>
|
</figure>
|
||||||
|
<AccountCircle v-else :size="24" />
|
||||||
|
<div class="">
|
||||||
|
<p class="" v-if="report.reported.name">{{ report.reported.name }}</p>
|
||||||
|
<p class="text-zinc-700 dark:text-zinc-100 text-sm">
|
||||||
|
@{{ usernameWithDomain(report.reported) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p v-if="report.reported.suspended" class="text-red-700 font-bold">
|
||||||
|
{{ t("Suspended") }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="reported_by">
|
<div class="p-2">
|
||||||
<div class="">
|
<div class="">
|
||||||
<span v-if="report.reporter.type === ActorType.APPLICATION">
|
<span v-if="report.reporter.type === ActorType.APPLICATION">
|
||||||
{{
|
{{
|
||||||
|
@ -26,6 +38,14 @@
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
report.reporter.preferredUsername === 'anonymous' &&
|
||||||
|
!report.reporter.domain
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ t("Reported by someone anonymously") }}
|
||||||
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{
|
{{
|
||||||
t("Reported by {reporter}", {
|
t("Reported by {reporter}", {
|
||||||
|
|
|
@ -90,6 +90,7 @@ import { computed, ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { IComment } from "../../types/comment.model";
|
import { IComment } from "../../types/comment.model";
|
||||||
import { usernameWithDomain } from "@/types/actor";
|
import { usernameWithDomain } from "@/types/actor";
|
||||||
|
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
|
@ -208,6 +208,7 @@ import Gapcursor from "@tiptap/extension-gapcursor";
|
||||||
import History from "@tiptap/extension-history";
|
import History from "@tiptap/extension-history";
|
||||||
import { IActor, IPerson, usernameWithDomain } from "../types/actor";
|
import { IActor, IPerson, usernameWithDomain } from "../types/actor";
|
||||||
import CustomImage from "./Editor/Image";
|
import CustomImage from "./Editor/Image";
|
||||||
|
import RichEditorKeyboardSubmit from "./Editor/RichEditorKeyboardSubmit";
|
||||||
import { UPLOAD_MEDIA } from "../graphql/upload";
|
import { UPLOAD_MEDIA } from "../graphql/upload";
|
||||||
import { listenFileUpload } from "../utils/upload";
|
import { listenFileUpload } from "../utils/upload";
|
||||||
import Mention from "@tiptap/extension-mention";
|
import Mention from "@tiptap/extension-mention";
|
||||||
|
@ -252,7 +253,7 @@ const props = withDefaults(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue"]);
|
const emit = defineEmits(["update:modelValue", "submit"]);
|
||||||
|
|
||||||
const isDescriptionMode = computed((): boolean => {
|
const isDescriptionMode = computed((): boolean => {
|
||||||
return props.mode === "description" || isBasicMode.value;
|
return props.mode === "description" || isBasicMode.value;
|
||||||
|
@ -297,7 +298,7 @@ const editor = useEditor({
|
||||||
"aria-label": props.ariaLabel ?? "",
|
"aria-label": props.ariaLabel ?? "",
|
||||||
role: "textbox",
|
role: "textbox",
|
||||||
class:
|
class:
|
||||||
"prose dark:prose-invert prose-sm sm:prose lg:prose-lg xl:prose-xl m-5 focus:outline-none !max-w-full",
|
"prose dark:prose-invert prose-sm lg:prose-lg xl:prose-xl bg-zinc-50 dark:bg-zinc-700 focus:outline-none !max-w-full",
|
||||||
},
|
},
|
||||||
transformPastedHTML: transformPastedHTML,
|
transformPastedHTML: transformPastedHTML,
|
||||||
},
|
},
|
||||||
|
@ -323,6 +324,9 @@ const editor = useEditor({
|
||||||
Link.configure({
|
Link.configure({
|
||||||
HTMLAttributes: { target: "_blank", rel: "noopener noreferrer ugc" },
|
HTMLAttributes: { target: "_blank", rel: "noopener noreferrer ugc" },
|
||||||
}),
|
}),
|
||||||
|
RichEditorKeyboardSubmit.configure({
|
||||||
|
submit: () => emit("submit"),
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
injectCSS: false,
|
injectCSS: false,
|
||||||
content: props.modelValue,
|
content: props.modelValue,
|
||||||
|
@ -480,9 +484,7 @@ onBeforeUnmount(() => {
|
||||||
div.ProseMirror {
|
div.ProseMirror {
|
||||||
min-height: 2.5rem;
|
min-height: 2.5rem;
|
||||||
box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1);
|
box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1);
|
||||||
background-color: white;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: #363636;
|
|
||||||
border: 1px solid #dbdbdb;
|
border: 1px solid #dbdbdb;
|
||||||
padding: 12px 6px;
|
padding: 12px 6px;
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ export function useAnonymousReportsConfig() {
|
||||||
}>(ANONYMOUS_REPORTS_CONFIG, undefined, { fetchPolicy: "cache-only" });
|
}>(ANONYMOUS_REPORTS_CONFIG, undefined, { fetchPolicy: "cache-only" });
|
||||||
|
|
||||||
const anonymousReportsConfig = computed(
|
const anonymousReportsConfig = computed(
|
||||||
() => configResult.value?.config?.anonymous?.participation
|
() => configResult.value?.config?.anonymous?.reports
|
||||||
);
|
);
|
||||||
return { anonymousReportsConfig, error, loading };
|
return { anonymousReportsConfig, error, loading };
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,11 @@ export const REPORTS = gql`
|
||||||
id
|
id
|
||||||
reported {
|
reported {
|
||||||
...ActorFragment
|
...ActorFragment
|
||||||
|
suspended
|
||||||
}
|
}
|
||||||
reporter {
|
reporter {
|
||||||
...ActorFragment
|
...ActorFragment
|
||||||
|
suspended
|
||||||
}
|
}
|
||||||
event {
|
event {
|
||||||
id
|
id
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
"Close": "Close",
|
"Close": "Close",
|
||||||
"Closed": "Closed",
|
"Closed": "Closed",
|
||||||
"Comment deleted": "Comment deleted",
|
"Comment deleted": "Comment deleted",
|
||||||
"Comment from @{username} reported": "Comment from @{username} reported",
|
"Comment from {'@'}{username} reported": "Comment from {'@'}{username} reported",
|
||||||
"Comments": "Comments",
|
"Comments": "Comments",
|
||||||
"Confirm my participation": "Confirm my participation",
|
"Confirm my participation": "Confirm my participation",
|
||||||
"Confirm my particpation": "Confirm my particpation",
|
"Confirm my particpation": "Confirm my particpation",
|
||||||
|
|
|
@ -192,7 +192,7 @@
|
||||||
"Closed": "Fermé",
|
"Closed": "Fermé",
|
||||||
"Comment body": "Corps du commentaire",
|
"Comment body": "Corps du commentaire",
|
||||||
"Comment deleted": "Commentaire supprimé",
|
"Comment deleted": "Commentaire supprimé",
|
||||||
"Comment from @{username} reported": "Commentaire de @{username} signalé",
|
"Comment from {'@'}{username} reported": "Commentaire de {'@'}{username} signalé",
|
||||||
"Comment text can't be empty": "Le texte du commentaire ne peut être vide",
|
"Comment text can't be empty": "Le texte du commentaire ne peut être vide",
|
||||||
"Comments": "Commentaires",
|
"Comments": "Commentaires",
|
||||||
"Comments are closed for everybody else.": "Les commentaires sont fermés pour tou·te·s les autres.",
|
"Comments are closed for everybody else.": "Les commentaires sont fermés pour tou·te·s les autres.",
|
||||||
|
|
|
@ -123,7 +123,7 @@ const { result: membershipsResult } = useQuery<{
|
||||||
() => ({ enabled: currentActor.value?.id !== undefined })
|
() => ({ enabled: currentActor.value?.id !== undefined })
|
||||||
);
|
);
|
||||||
const memberships = computed(
|
const memberships = computed(
|
||||||
() => membershipsResult.value?.person.memberships.elements
|
() => membershipsResult.value?.person.memberships?.elements
|
||||||
);
|
);
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
|
@ -543,7 +543,7 @@
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.GROUP_EVENTS,
|
name: RouteName.GROUP_EVENTS,
|
||||||
params: { preferredUsername: usernameWithDomain(group) },
|
params: { preferredUsername: usernameWithDomain(group) },
|
||||||
query: { future: false },
|
query: { showPassedEvents: true },
|
||||||
}"
|
}"
|
||||||
>{{ t("View past events") }}</o-button
|
>{{ t("View past events") }}</o-button
|
||||||
>
|
>
|
||||||
|
@ -559,7 +559,9 @@
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.GROUP_EVENTS,
|
name: RouteName.GROUP_EVENTS,
|
||||||
params: { preferredUsername: usernameWithDomain(group) },
|
params: { preferredUsername: usernameWithDomain(group) },
|
||||||
query: { future: organizedEvents.elements.length > 0 },
|
query: {
|
||||||
|
showPassedEvents: organizedEvents.elements.length === 0,
|
||||||
|
},
|
||||||
}"
|
}"
|
||||||
>{{ t("View all events") }}</o-button
|
>{{ t("View all events") }}</o-button
|
||||||
>
|
>
|
||||||
|
@ -696,7 +698,7 @@ const {
|
||||||
group,
|
group,
|
||||||
loading: groupLoading,
|
loading: groupLoading,
|
||||||
refetch: refetchGroup,
|
refetch: refetchGroup,
|
||||||
} = useGroup(props.preferredUsername);
|
} = useGroup(props.preferredUsername, { afterDateTime: new Date() });
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { t } = useI18n({ useScope: "global" });
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
@ -913,36 +915,42 @@ const toggleFollowNotify = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const reportGroup = async (content: string, forward: boolean) => {
|
const {
|
||||||
isReportModalActive.value = false;
|
mutate: createReportMutation,
|
||||||
reportModalRef.value.close();
|
onError: onCreateReportError,
|
||||||
|
onDone: onCreateReportDone,
|
||||||
|
} = useCreateReport();
|
||||||
|
|
||||||
const {
|
const reportGroup = (content: string, forward: boolean) => {
|
||||||
mutate: createReportMutation,
|
isReportModalActive.value = false;
|
||||||
onError: onCreateReportError,
|
console.debug("report group", {
|
||||||
onDone: oneCreateReportDone,
|
reportedId: group.value?.id ?? "",
|
||||||
} = useCreateReport();
|
content,
|
||||||
|
forward,
|
||||||
|
});
|
||||||
|
|
||||||
createReportMutation({
|
createReportMutation({
|
||||||
reportedId: group.value?.id ?? "",
|
reportedId: group.value?.id ?? "",
|
||||||
content,
|
content,
|
||||||
forward,
|
forward,
|
||||||
});
|
});
|
||||||
|
|
||||||
oneCreateReportDone(() => {
|
|
||||||
notifier?.success(t("Group {groupTitle} reported", { groupTitle }));
|
|
||||||
});
|
|
||||||
|
|
||||||
onCreateReportError((error: any) => {
|
|
||||||
console.error(error);
|
|
||||||
notifier?.error(
|
|
||||||
t("Error while reporting group {groupTitle}", {
|
|
||||||
groupTitle,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onCreateReportDone(() => {
|
||||||
|
notifier?.success(
|
||||||
|
t("Group {groupTitle} reported", { groupTitle: groupTitle.value })
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
onCreateReportError((error: any) => {
|
||||||
|
console.error(error);
|
||||||
|
notifier?.error(
|
||||||
|
t("Error while reporting group {groupTitle}", {
|
||||||
|
groupTitle: groupTitle.value,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const triggerShare = (): void => {
|
const triggerShare = (): void => {
|
||||||
if (navigator.share) {
|
if (navigator.share) {
|
||||||
navigator
|
navigator
|
||||||
|
@ -1030,8 +1038,8 @@ const physicalAddress = computed((): Address | null => {
|
||||||
return new Address(group.value?.physicalAddress);
|
return new Address(group.value?.physicalAddress);
|
||||||
});
|
});
|
||||||
|
|
||||||
const ableToReport = computed((): boolean | undefined => {
|
const ableToReport = computed((): boolean => {
|
||||||
return anonymousReportsConfig.value?.allowed;
|
return anonymousReportsConfig.value?.allowed === true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const organizedEvents = computed((): Paginate<IEvent> => {
|
const organizedEvents = computed((): Paginate<IEvent> => {
|
||||||
|
|
|
@ -14,365 +14,404 @@
|
||||||
/>
|
/>
|
||||||
<section v-if="actionLogs.total > 0 && actionLogs.elements.length > 0">
|
<section v-if="actionLogs.total > 0 && actionLogs.elements.length > 0">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="log in actionLogs.elements" :key="log.id">
|
<li
|
||||||
<div class="box">
|
v-for="log in actionLogs.elements"
|
||||||
<img
|
:key="log.id"
|
||||||
class="image"
|
class="bg-mbz-yellow-alt-50 hover:bg-mbz-yellow-alt-100 dark:bg-zinc-700 hover:dark:bg-zinc-600 rounded p-2 my-1"
|
||||||
:src="log.actor.avatar.url"
|
>
|
||||||
:alt="log.actor.avatar.alt || ''"
|
<div class="flex gap-1">
|
||||||
v-if="log.actor.avatar"
|
<div class="flex gap-1">
|
||||||
/>
|
<figure class="" v-if="log.actor?.avatar">
|
||||||
<i18n-t
|
<img
|
||||||
v-if="log.action === ActionLogAction.REPORT_UPDATE_CLOSED"
|
alt=""
|
||||||
tag="span"
|
:src="log.actor.avatar?.url"
|
||||||
keypath="{moderator} closed {report}"
|
class="rounded-full"
|
||||||
>
|
width="36"
|
||||||
<template #moderator>
|
height="36"
|
||||||
<router-link
|
/>
|
||||||
:to="{
|
</figure>
|
||||||
name: RouteName.ADMIN_PROFILE,
|
<AccountCircle v-else :size="36" />
|
||||||
params: { id: log.actor.id },
|
</div>
|
||||||
}"
|
<div>
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #report>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.REPORT,
|
|
||||||
params: { reportId: log.object.id },
|
|
||||||
}"
|
|
||||||
>{{
|
|
||||||
$t("report #{report_number}", {
|
|
||||||
report_number: log.object.id,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_OPENED"
|
|
||||||
tag="span"
|
|
||||||
keypath="{moderator} reopened {report}"
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #report>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.REPORT,
|
|
||||||
params: { reportId: log.object.id },
|
|
||||||
}"
|
|
||||||
>{{
|
|
||||||
$t("report #{report_number}", {
|
|
||||||
report_number: log.object.id,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_RESOLVED"
|
|
||||||
tag="span"
|
|
||||||
keypath="{moderator} marked {report} as resolved"
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #report>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.REPORT,
|
|
||||||
params: { reportId: log.object.id },
|
|
||||||
}"
|
|
||||||
>{{
|
|
||||||
$t("report #{report_number}", {
|
|
||||||
report_number: log.object.id,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="log.action === ActionLogAction.NOTE_CREATION"
|
|
||||||
tag="span"
|
|
||||||
keypath="{moderator} added a note on {report}"
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #report>
|
|
||||||
<router-link
|
|
||||||
v-if="log.object.report"
|
|
||||||
:to="{
|
|
||||||
name: RouteName.REPORT,
|
|
||||||
params: { reportId: log.object.report.id },
|
|
||||||
}"
|
|
||||||
>{{
|
|
||||||
$t("report #{report_number}", {
|
|
||||||
report_number: log.object.report.id,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</router-link>
|
|
||||||
<span v-else>{{ $t("a non-existent report") }}</span>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="log.action === ActionLogAction.EVENT_DELETION"
|
|
||||||
tag="span"
|
|
||||||
keypath='{moderator} deleted an event named "{title}"'
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #title>
|
|
||||||
<b>{{ log.object.title }}</b>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="
|
|
||||||
log.action === ActionLogAction.ACTOR_SUSPENSION &&
|
|
||||||
log.object.__typename == 'Person'
|
|
||||||
"
|
|
||||||
tag="span"
|
|
||||||
keypath="{moderator} suspended profile {profile}"
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #profile>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.object.id },
|
|
||||||
}"
|
|
||||||
>{{ displayNameAndUsername(log.object) }}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="
|
|
||||||
log.action === ActionLogAction.ACTOR_UNSUSPENSION &&
|
|
||||||
log.object.__typename == 'Person'
|
|
||||||
"
|
|
||||||
tag="span"
|
|
||||||
keypath="{moderator} has unsuspended profile {profile}"
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #profile>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.object.id },
|
|
||||||
}"
|
|
||||||
>{{ displayNameAndUsername(log.object) }}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="
|
|
||||||
log.action === ActionLogAction.ACTOR_SUSPENSION &&
|
|
||||||
log.object.__typename == 'Group'
|
|
||||||
"
|
|
||||||
tag="span"
|
|
||||||
keypath="{moderator} suspended group {profile}"
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #profile>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_GROUP_PROFILE,
|
|
||||||
params: { id: log.object.id },
|
|
||||||
}"
|
|
||||||
>{{ displayNameAndUsername(log.object) }}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="
|
|
||||||
log.action === ActionLogAction.ACTOR_UNSUSPENSION &&
|
|
||||||
log.object.__typename == 'Group'
|
|
||||||
"
|
|
||||||
tag="span"
|
|
||||||
keypath="{moderator} has unsuspended group {profile}"
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #profile>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_GROUP_PROFILE,
|
|
||||||
params: { id: log.object.id },
|
|
||||||
}"
|
|
||||||
>{{ displayNameAndUsername(log.object) }}
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<i18n-t
|
|
||||||
v-else-if="log.action === ActionLogAction.USER_DELETION"
|
|
||||||
tag="span"
|
|
||||||
keypath="{moderator} has deleted user {user}"
|
|
||||||
>
|
|
||||||
<template #moderator>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.actor.id },
|
|
||||||
}"
|
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #user>
|
|
||||||
<router-link
|
|
||||||
v-if="log.object.confirmedAt"
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_USER_PROFILE,
|
|
||||||
params: { id: log.object.id },
|
|
||||||
}"
|
|
||||||
>{{ log.object.email }}
|
|
||||||
</router-link>
|
|
||||||
<b v-else>{{ log.object.email }}</b>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<span
|
|
||||||
v-else-if="
|
|
||||||
log.action === ActionLogAction.COMMENT_DELETION &&
|
|
||||||
log.object.event
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<i18n-t
|
<i18n-t
|
||||||
|
v-if="log.action === ActionLogAction.REPORT_UPDATE_CLOSED"
|
||||||
tag="span"
|
tag="span"
|
||||||
keypath="{moderator} has deleted a comment from {author} under the event {event}"
|
keypath="{moderator} closed {report}"
|
||||||
>
|
>
|
||||||
<template #moderator>
|
<template #moderator>
|
||||||
<router-link
|
<router-link
|
||||||
|
class="underline"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.ADMIN_PROFILE,
|
name: RouteName.ADMIN_PROFILE,
|
||||||
params: { id: log.actor.id },
|
params: { id: log.actor.id },
|
||||||
}"
|
}"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
>{{ displayName(log.actor) }}</router-link
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<template #event>
|
<template #report>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="log.object.event && log.object.event.uuid"
|
class="underline"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.EVENT,
|
name: RouteName.REPORT,
|
||||||
params: { uuid: log.object.event.uuid },
|
params: { reportId: log.object.id },
|
||||||
}"
|
}"
|
||||||
>{{ log.object.event.title }}
|
>{{
|
||||||
</router-link>
|
$t("report #{report_number}", {
|
||||||
<b v-else>{{ log.object.event.title }}</b>
|
report_number: log.object.id,
|
||||||
</template>
|
})
|
||||||
<template #author>
|
}}
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.ADMIN_PROFILE,
|
|
||||||
params: { id: log.object.actor.id },
|
|
||||||
}"
|
|
||||||
>{{ displayNameAndUsername(log.object.actor) }}
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
<pre v-html="log.object.text" />
|
|
||||||
</span>
|
|
||||||
<span v-else-if="log.action === ActionLogAction.COMMENT_DELETION">
|
|
||||||
<i18n-t
|
<i18n-t
|
||||||
|
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_OPENED"
|
||||||
tag="span"
|
tag="span"
|
||||||
keypath="{moderator} has deleted a comment from {author}"
|
keypath="{moderator} reopened {report}"
|
||||||
>
|
>
|
||||||
<template #moderator>
|
<template #moderator>
|
||||||
<router-link
|
<router-link
|
||||||
|
class="underline"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.ADMIN_PROFILE,
|
name: RouteName.ADMIN_PROFILE,
|
||||||
params: { id: log.actor.id },
|
params: { id: log.actor.id },
|
||||||
}"
|
}"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
>{{ displayName(log.actor) }}</router-link
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<template #author>
|
<template #report>
|
||||||
<router-link
|
<router-link
|
||||||
|
class="underline"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.ADMIN_PROFILE,
|
name: RouteName.REPORT,
|
||||||
params: { id: log.object.actor.id },
|
params: { reportId: log.object.id },
|
||||||
}"
|
}"
|
||||||
>{{ displayNameAndUsername(log.object.actor) }}
|
>{{
|
||||||
|
$t("report #{report_number}", {
|
||||||
|
report_number: log.object.id,
|
||||||
|
})
|
||||||
|
}}
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
<pre v-html="log.object.text" />
|
<i18n-t
|
||||||
</span>
|
v-else-if="
|
||||||
<i18n-t
|
log.action === ActionLogAction.REPORT_UPDATE_RESOLVED
|
||||||
v-else
|
"
|
||||||
tag="span"
|
tag="span"
|
||||||
keypath="{moderator} has done an unknown action"
|
keypath="{moderator} marked {report} as resolved"
|
||||||
>
|
>
|
||||||
<template #moderator>
|
<template #moderator>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
class="underline"
|
||||||
name: RouteName.ADMIN_PROFILE,
|
:to="{
|
||||||
params: { id: log.actor.id },
|
name: RouteName.ADMIN_PROFILE,
|
||||||
}"
|
params: { id: log.actor.id },
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #report>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.REPORT,
|
||||||
|
params: { reportId: log.object.id },
|
||||||
|
}"
|
||||||
|
>{{
|
||||||
|
$t("report #{report_number}", {
|
||||||
|
report_number: log.object.id,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t
|
||||||
|
v-else-if="log.action === ActionLogAction.NOTE_CREATION"
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} added a note on {report}"
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #report>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
v-if="log.object.report"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.REPORT,
|
||||||
|
params: { reportId: log.object.report.id },
|
||||||
|
}"
|
||||||
|
>{{
|
||||||
|
$t("report #{report_number}", {
|
||||||
|
report_number: log.object.report.id,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</router-link>
|
||||||
|
<span v-else>{{ $t("a non-existent report") }}</span>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t
|
||||||
|
v-else-if="log.action === ActionLogAction.EVENT_DELETION"
|
||||||
|
tag="span"
|
||||||
|
keypath='{moderator} deleted an event named "{title}"'
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
<b>{{ log.object.title }}</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t
|
||||||
|
v-else-if="
|
||||||
|
log.action === ActionLogAction.ACTOR_SUSPENSION &&
|
||||||
|
log.object.__typename == 'Person'
|
||||||
|
"
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} suspended profile {profile}"
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #profile>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.object.id },
|
||||||
|
}"
|
||||||
|
>{{ displayNameAndUsername(log.object) }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t
|
||||||
|
v-else-if="
|
||||||
|
log.action === ActionLogAction.ACTOR_UNSUSPENSION &&
|
||||||
|
log.object.__typename == 'Person'
|
||||||
|
"
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} has unsuspended profile {profile}"
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #profile>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.object.id },
|
||||||
|
}"
|
||||||
|
>{{ displayNameAndUsername(log.object) }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t
|
||||||
|
v-else-if="
|
||||||
|
log.action === ActionLogAction.ACTOR_SUSPENSION &&
|
||||||
|
log.object.__typename == 'Group'
|
||||||
|
"
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} suspended group {profile}"
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #profile>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_GROUP_PROFILE,
|
||||||
|
params: { id: log.object.id },
|
||||||
|
}"
|
||||||
|
>{{ displayNameAndUsername(log.object) }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t
|
||||||
|
v-else-if="
|
||||||
|
log.action === ActionLogAction.ACTOR_UNSUSPENSION &&
|
||||||
|
log.object.__typename == 'Group'
|
||||||
|
"
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} has unsuspended group {profile}"
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #profile>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_GROUP_PROFILE,
|
||||||
|
params: { id: log.object.id },
|
||||||
|
}"
|
||||||
|
>{{ displayNameAndUsername(log.object) }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t
|
||||||
|
v-else-if="log.action === ActionLogAction.USER_DELETION"
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} has deleted user {user}"
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #user>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
v-if="log.object.confirmedAt"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_USER_PROFILE,
|
||||||
|
params: { id: log.object.id },
|
||||||
|
}"
|
||||||
|
>{{ log.object.email }}
|
||||||
|
</router-link>
|
||||||
|
<b v-else>{{ log.object.email }}</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<span
|
||||||
|
v-else-if="
|
||||||
|
log.action === ActionLogAction.COMMENT_DELETION &&
|
||||||
|
log.object.event
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<i18n-t
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} has deleted a comment from {author} under the event {event}"
|
||||||
>
|
>
|
||||||
</template>
|
<template #moderator>
|
||||||
</i18n-t>
|
<router-link
|
||||||
<br />
|
class="underline"
|
||||||
<small>{{ formatDateTimeString(log.insertedAt) }}</small>
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #event>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
v-if="log.object.event && log.object.event.uuid"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.EVENT,
|
||||||
|
params: { uuid: log.object.event.uuid },
|
||||||
|
}"
|
||||||
|
>{{ log.object.event.title }}
|
||||||
|
</router-link>
|
||||||
|
<b v-else>{{ log.object.event.title }}</b>
|
||||||
|
</template>
|
||||||
|
<template #author>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.object.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayNameAndUsername(log.object.actor) }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<pre v-html="log.object.text" />
|
||||||
|
</span>
|
||||||
|
<span v-else-if="log.action === ActionLogAction.COMMENT_DELETION">
|
||||||
|
<i18n-t
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} has deleted a comment from {author}"
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #author>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.object.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayNameAndUsername(log.object.actor) }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<pre v-html="log.object.text" />
|
||||||
|
</span>
|
||||||
|
<i18n-t
|
||||||
|
v-else
|
||||||
|
tag="span"
|
||||||
|
keypath="{moderator} has done an unknown action"
|
||||||
|
>
|
||||||
|
<template #moderator>
|
||||||
|
<router-link
|
||||||
|
class="underline"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.ADMIN_PROFILE,
|
||||||
|
params: { id: log.actor.id },
|
||||||
|
}"
|
||||||
|
>{{ displayName(log.actor) }}</router-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<br />
|
||||||
|
<small>{{ formatDateTimeString(log.insertedAt) }}</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -399,7 +438,7 @@ import { IActionLog } from "@/types/report.model";
|
||||||
import { LOGS } from "@/graphql/report";
|
import { LOGS } from "@/graphql/report";
|
||||||
import { ActionLogAction } from "@/types/enums";
|
import { ActionLogAction } from "@/types/enums";
|
||||||
import RouteName from "../../router/name";
|
import RouteName from "../../router/name";
|
||||||
import { displayNameAndUsername } from "../../types/actor";
|
import { displayNameAndUsername, displayName } from "../../types/actor";
|
||||||
import { Paginate } from "@/types/paginate";
|
import { Paginate } from "@/types/paginate";
|
||||||
import { useQuery } from "@vue/apollo-composable";
|
import { useQuery } from "@vue/apollo-composable";
|
||||||
import { integerTransformer, useRouteQuery } from "vue-use-route-query";
|
import { integerTransformer, useRouteQuery } from "vue-use-route-query";
|
||||||
|
@ -407,6 +446,7 @@ import { useHead } from "@vueuse/head";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { formatDateTimeString } from "@/filters/datetime";
|
import { formatDateTimeString } from "@/filters/datetime";
|
||||||
|
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
|
||||||
|
|
||||||
const LOGS_PER_PAGE = 10;
|
const LOGS_PER_PAGE = 10;
|
||||||
|
|
||||||
|
@ -436,10 +476,6 @@ img.image {
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
section ul li {
|
section ul li {
|
||||||
margin: 0.5rem auto;
|
margin: 0.5rem auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,10 @@ const { result: reportsResult } = useQuery<{ reports: Paginate<IReport> }>(
|
||||||
status: status.value,
|
status: status.value,
|
||||||
limit: REPORT_PAGE_LIMIT,
|
limit: REPORT_PAGE_LIMIT,
|
||||||
domain: filterDomain.value,
|
domain: filterDomain.value,
|
||||||
})
|
}),
|
||||||
|
{
|
||||||
|
fetchPolicy: "cache-and-network",
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const reports = computed(
|
const reports = computed(
|
||||||
|
|
|
@ -136,8 +136,9 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="report.event && report.comments.length > 0">
|
<tr v-if="report.event && report.comments.length > 0">
|
||||||
<td>{{ t("Event") }}</td>
|
<td>{{ t("Event") }}</td>
|
||||||
<td>
|
<td class="flex gap-2 items-center">
|
||||||
<router-link
|
<router-link
|
||||||
|
class="underline"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.EVENT,
|
name: RouteName.EVENT,
|
||||||
params: { uuid: report.event.uuid },
|
params: { uuid: report.event.uuid },
|
||||||
|
@ -145,41 +146,32 @@
|
||||||
>
|
>
|
||||||
{{ report.event.title }}
|
{{ report.event.title }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<span>
|
<o-button
|
||||||
<!-- <o-button-->
|
variant="danger"
|
||||||
<!-- tag="router-link"-->
|
@click="confirmEventDelete()"
|
||||||
<!-- variant="primary"-->
|
icon-left="delete"
|
||||||
<!-- :to="{ name: RouteName.EDIT_EVENT, params: {eventId: report.event.uuid } }"-->
|
>{{ t("Delete") }}</o-button
|
||||||
<!-- icon-left="pencil"-->
|
>
|
||||||
<!-- size="small">{{ t('Edit') }}</o-button>-->
|
|
||||||
<o-button
|
|
||||||
variant="danger"
|
|
||||||
@click="confirmEventDelete()"
|
|
||||||
icon-left="delete"
|
|
||||||
size="small"
|
|
||||||
>{{ t("Delete") }}</o-button
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section class="bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3">
|
||||||
<h2>{{ t("Report reason") }}</h2>
|
<h2 class="mb-1">{{ t("Report reason") }}</h2>
|
||||||
<div class="dark:bg-zinc-700 p-2 rounded my-2">
|
<div class="">
|
||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<figure class="" v-if="report.reported.avatar">
|
<figure class="" v-if="report.reported.avatar">
|
||||||
<img
|
<img
|
||||||
alt=""
|
alt=""
|
||||||
:src="report.reported.avatar.url"
|
:src="report.reported.avatar.url"
|
||||||
class="rounded-full"
|
class="rounded-full"
|
||||||
width="48"
|
width="36"
|
||||||
height="48"
|
height="36"
|
||||||
/>
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
<AccountCircle v-else :size="48" />
|
<AccountCircle v-else :size="36" />
|
||||||
<div class="">
|
<div class="">
|
||||||
<p class="" v-if="report.reported.name">
|
<p class="" v-if="report.reported.name">
|
||||||
{{ report.reported.name }}
|
{{ report.reported.name }}
|
||||||
|
@ -196,15 +188,12 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="" v-if="report.event && report.comments.length === 0">
|
<section
|
||||||
<h2>{{ t("Reported content") }}</h2>
|
class="bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3"
|
||||||
<EventCard :event="report.event" mode="row" class="my-2" />
|
v-if="report.event && report.comments.length === 0"
|
||||||
<!-- <o-button-->
|
>
|
||||||
<!-- tag="router-link"-->
|
<h2 class="mb-1">{{ t("Reported content") }}</h2>
|
||||||
<!-- variant="primary"-->
|
<EventCard :event="report.event" mode="row" class="my-2 max-w-4xl" />
|
||||||
<!-- :to="{ name: RouteName.EDIT_EVENT, params: {eventId: report.event.uuid } }"-->
|
|
||||||
<!-- icon-left="pencil"-->
|
|
||||||
<!-- size="small">{{ t('Edit') }}</o-button>-->
|
|
||||||
<o-button
|
<o-button
|
||||||
variant="danger"
|
variant="danger"
|
||||||
@click="confirmEventDelete()"
|
@click="confirmEventDelete()"
|
||||||
|
@ -214,50 +203,52 @@
|
||||||
>
|
>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div v-if="report.comments.length > 0">
|
<section
|
||||||
|
class="bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3"
|
||||||
|
v-if="report.comments.length > 0"
|
||||||
|
>
|
||||||
|
<h2 class="mb-1">{{ t("Reported content") }}</h2>
|
||||||
<ul v-for="comment in report.comments" :key="comment.id">
|
<ul v-for="comment in report.comments" :key="comment.id">
|
||||||
<li>
|
<li>
|
||||||
<div class="" v-if="comment">
|
<div class="" v-if="comment">
|
||||||
<article class="flex gap-1">
|
<article>
|
||||||
<div class="">
|
<div class="flex gap-1">
|
||||||
<figure class="" v-if="comment.actor && comment.actor.avatar">
|
<figure class="" v-if="comment.actor?.avatar">
|
||||||
<img
|
<img
|
||||||
:src="comment.actor.avatar.url"
|
|
||||||
alt=""
|
alt=""
|
||||||
width="48"
|
:src="comment.actor.avatar?.url"
|
||||||
height="48"
|
class="rounded-full"
|
||||||
|
width="36"
|
||||||
|
height="36"
|
||||||
/>
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
<AccountCircle :size="48" v-else />
|
<AccountCircle v-else :size="36" />
|
||||||
</div>
|
<div>
|
||||||
<div class="">
|
<div v-if="comment.actor">
|
||||||
<div class="prose dark:prose-invert">
|
<p>{{ comment.actor.name }}</p>
|
||||||
<span v-if="comment.actor">
|
<p>@{{ comment.actor.preferredUsername }}</p>
|
||||||
<strong>{{ comment.actor.name }}</strong>
|
</div>
|
||||||
<small>@{{ comment.actor.preferredUsername }}</small>
|
|
||||||
</span>
|
|
||||||
<span v-else>{{ t("Unknown actor") }}</span>
|
<span v-else>{{ t("Unknown actor") }}</span>
|
||||||
<br />
|
|
||||||
<p v-html="comment.text" />
|
|
||||||
</div>
|
</div>
|
||||||
<o-button
|
|
||||||
variant="danger"
|
|
||||||
@click="confirmCommentDelete(comment)"
|
|
||||||
icon-left="delete"
|
|
||||||
size="small"
|
|
||||||
>{{ t("Delete") }}</o-button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="prose dark:prose-invert" v-html="comment.text" />
|
||||||
|
<o-button
|
||||||
|
variant="danger"
|
||||||
|
@click="confirmCommentDelete(comment)"
|
||||||
|
icon-left="delete"
|
||||||
|
size="small"
|
||||||
|
>{{ t("Delete") }}</o-button
|
||||||
|
>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section class="bg-white dark:bg-zinc-700 rounded px-2 pt-1 pb-2 my-3">
|
||||||
<h2>{{ t("Notes") }}</h2>
|
<h2 class="mb-1">{{ t("Notes") }}</h2>
|
||||||
<div
|
<div
|
||||||
class="box note"
|
class=""
|
||||||
v-for="note in report.notes"
|
v-for="note in report.notes"
|
||||||
:id="`note-${note.id}`"
|
:id="`note-${note.id}`"
|
||||||
:key="note.id"
|
:key="note.id"
|
||||||
|
@ -536,8 +527,4 @@ tbody td img.image,
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
td > a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -529,6 +529,16 @@
|
||||||
class="my-4"
|
class="my-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<o-notification v-else-if="searchLoading === false" variant="info">
|
||||||
|
<p>{{ t("No events found") }}</p>
|
||||||
|
<p v-if="searchIsUrl && !currentUser?.id">
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"Only registered users may fetch remote events from their URL."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</o-notification>
|
||||||
<o-pagination
|
<o-pagination
|
||||||
v-if="
|
v-if="
|
||||||
(searchEvents && searchEvents?.total > EVENT_PAGE_LIMIT) ||
|
(searchEvents && searchEvents?.total > EVENT_PAGE_LIMIT) ||
|
||||||
|
@ -544,16 +554,6 @@
|
||||||
:aria-page-label="t('Page')"
|
:aria-page-label="t('Page')"
|
||||||
:aria-current-label="t('Current page')"
|
:aria-current-label="t('Current page')"
|
||||||
/>
|
/>
|
||||||
<o-notification v-else-if="searchLoading === false" variant="info">
|
|
||||||
<p>{{ t("No events found") }}</p>
|
|
||||||
<p v-if="searchIsUrl && !currentUser?.id">
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"Only registered users may fetch remote events from their URL."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
</o-notification>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="contentType === ContentType.EVENTS">
|
<template v-else-if="contentType === ContentType.EVENTS">
|
||||||
<template v-if="searchEvents && searchEvents.total > 0">
|
<template v-if="searchEvents && searchEvents.total > 0">
|
||||||
|
|
|
@ -1272,16 +1272,11 @@ defmodule Mobilizon.Events do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @spec events_for_search_query(String.t()) :: Ecto.Query.t()
|
|
||||||
# defp events_for_search_query("") do
|
|
||||||
# Event
|
|
||||||
# |> join: rank in fragment("")
|
|
||||||
# end
|
|
||||||
|
|
||||||
defp events_for_search_query(search_string) do
|
defp events_for_search_query(search_string) do
|
||||||
from(event in Event,
|
Event
|
||||||
join: id_and_rank in matching_event_ids_and_ranks(search_string),
|
|> distinct(:id)
|
||||||
on: id_and_rank.id == event.id
|
|> join(:inner, [e], id_and_rank in matching_event_ids_and_ranks(search_string),
|
||||||
|
on: id_and_rank.id == e.id
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue