- Added topic and forum IDs to moderation report queries for better context. - Updated admin interface to include links to open reported forum messages. - Implemented methods to handle opening target messages directly from the moderation reports view. - Enhanced internationalization support for new UI elements in both German and English. - Improved scrolling behavior to focus on specific messages when navigating to them.
239 lines
7.0 KiB
Vue
239 lines
7.0 KiB
Vue
<template>
|
|
<div class="forum-topic-view">
|
|
<section class="forum-topic-hero surface-card">
|
|
<div>
|
|
<div class="forum-topic-back link" @click="openForum()">{{ $t('socialnetwork.forum.title') }} {{ forumName }}</div>
|
|
<h2 v-if="forumTopic">{{ forumTopic }}</h2>
|
|
<p>{{ $t('socialnetwork.forum.topicIntro') }}</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="forum-topic-messages">
|
|
<ul class="messages">
|
|
<li v-for="message in messages" :key="message.id" :id="`msg-${message.id}`" class="surface-card">
|
|
<div v-html="sanitizedMessage(message)"></div>
|
|
<div class="footer">
|
|
<span class="footer-left">
|
|
<span class="link" @click="openProfile(message.lastMessageUser.hashedId)">
|
|
{{ message.lastMessageUser.username }}
|
|
</span>
|
|
<button type="button" class="report-btn" @click="reportMessage(message)">
|
|
{{ $t('socialnetwork.forum.reportAction') }}
|
|
</button>
|
|
</span>
|
|
<span>{{ new Date(message.createdAt).toLocaleString() }}</span>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<div class="editor-container surface-card">
|
|
<EditorContent v-if="editor" :editor="editor" class="editor" />
|
|
</div>
|
|
<button @click="saveNewMessage">{{ $t('socialnetwork.forum.createNewMesssage') }}</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { Editor, EditorContent } from '@tiptap/vue-3'
|
|
import StarterKit from '@tiptap/starter-kit'
|
|
import apiClient from '../../utils/axios'
|
|
import DOMPurify from 'dompurify'
|
|
|
|
export default {
|
|
name: 'ForumTopicView',
|
|
components: {
|
|
EditorContent,
|
|
},
|
|
data() {
|
|
return {
|
|
forumTopicId: '',
|
|
forumTopic: null,
|
|
forumName: null,
|
|
forumId: 0,
|
|
messages: [],
|
|
editor: null,
|
|
}
|
|
},
|
|
mounted() {
|
|
this.forumTopicId = this.$route.params.id;
|
|
this.loadForumTopic();
|
|
|
|
this.editor = new Editor({
|
|
extensions: [StarterKit],
|
|
content: '',
|
|
editable: true,
|
|
editorProps: { attributes: { class: 'pm-root' } },
|
|
});
|
|
},
|
|
beforeUnmount() {
|
|
if (this.editor) {
|
|
this.editor.destroy();
|
|
}
|
|
},
|
|
methods: {
|
|
async loadForumTopic() {
|
|
try {
|
|
const response = await apiClient.get(`/api/forum/topic/${this.forumTopicId}`);
|
|
this.setContent(response.data);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
},
|
|
setContent(data) {
|
|
this.forumTopic = data.title;
|
|
this.forumName = data.forum.name;
|
|
this.forumId = data.forum.id;
|
|
this.messages = data.messages;
|
|
this.$nextTick(() => this.scrollToHashIfPresent());
|
|
},
|
|
async openProfile(id) {
|
|
this.$root.$refs.userProfileDialog.userId = id;
|
|
this.$root.$refs.userProfileDialog.open();
|
|
},
|
|
async saveNewMessage() {
|
|
const content = this.editor ? this.editor.getHTML() : '';
|
|
if (!content.trim()) return;
|
|
|
|
try {
|
|
const url = `/api/forum/topic/${this.forumTopicId}/message`;
|
|
const response = await apiClient.post(url, { content });
|
|
this.editor.commands.clearContent();
|
|
this.setContent(response.data);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
},
|
|
openForum() {
|
|
this.$router.push(`/socialnetwork/forum/${this.forumId}`);
|
|
},
|
|
sanitizedMessage(message) {
|
|
return DOMPurify.sanitize(message.text);
|
|
},
|
|
async reportMessage(message) {
|
|
const reason = window.prompt(this.$t('socialnetwork.forum.reportPrompt'));
|
|
if (reason == null) return;
|
|
const trimmed = String(reason || '').trim();
|
|
if (trimmed.length < 3) {
|
|
this.$root.$refs.messageDialog?.open(
|
|
this.$t('socialnetwork.forum.reportReasonTooShort'),
|
|
this.$t('error.title')
|
|
);
|
|
return;
|
|
}
|
|
try {
|
|
await apiClient.post('/api/moderation/reports', {
|
|
targetType: 'forum_message',
|
|
targetId: message.id,
|
|
reason: trimmed,
|
|
details: '',
|
|
});
|
|
this.$root.$refs.messageDialog?.open(
|
|
this.$t('socialnetwork.forum.reportSubmitted'),
|
|
this.$t('message.title')
|
|
);
|
|
} catch (error) {
|
|
console.error('Error creating moderation report:', error);
|
|
this.$root.$refs.messageDialog?.open(
|
|
this.$t('socialnetwork.forum.reportError'),
|
|
this.$t('error.title')
|
|
);
|
|
}
|
|
},
|
|
scrollToHashIfPresent() {
|
|
const hash = this.$route?.hash || '';
|
|
if (!hash || hash.length < 2) return;
|
|
const el = document.querySelector(hash);
|
|
if (el && typeof el.scrollIntoView === 'function') {
|
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.forum-topic-view {
|
|
max-width: 860px;
|
|
margin: 0 auto;
|
|
padding-bottom: 24px;
|
|
}
|
|
|
|
.forum-topic-hero {
|
|
padding: 24px 26px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.forum-topic-back {
|
|
margin-bottom: 10px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.forum-topic-hero p {
|
|
margin: 0;
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.messages {
|
|
list-style-type: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.messages > li {
|
|
margin-bottom: 0.75em;
|
|
padding: 1rem 1.1rem;
|
|
}
|
|
|
|
.messages > li > .footer {
|
|
color: var(--color-text-muted);
|
|
font-size: 0.8em;
|
|
margin-top: 0.5em;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.footer-left {
|
|
flex: 1;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.messages > li > .footer > span:last-child {
|
|
text-align: right;
|
|
}
|
|
|
|
.report-btn {
|
|
min-height: auto;
|
|
padding: 2px 8px;
|
|
font-size: 0.75rem;
|
|
border-radius: 999px;
|
|
background: rgba(255, 255, 255, 0.78);
|
|
}
|
|
|
|
.editor-container {
|
|
margin-top: 1rem;
|
|
padding: 0;
|
|
min-height: 260px;
|
|
background-color: white;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.editor {
|
|
min-height: 260px;
|
|
outline: none;
|
|
cursor: text;
|
|
}
|
|
.editor :deep(.ProseMirror) {
|
|
min-height: 260px;
|
|
outline: none;
|
|
padding: 14px;
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
}
|
|
.editor :deep(.ProseMirror p) { margin: 0 0 .6rem; }
|
|
.editor :deep(.ProseMirror p:first-child) { margin-top: 0; }
|
|
.editor :deep(.ProseMirror-focused) { outline: 2px solid rgba(100,150,255,.35); }
|
|
</style>
|