feat(socialnetwork): enhance folder and video management with user visibility options

- Added functionality to manage selected users for adult folders and erotic videos, allowing for more granular visibility control.
- Introduced new endpoints and methods in the SocialNetworkController and SocialNetworkService to handle selected users.
- Updated the frontend components to include input fields for selected users in CreateFolderDialog, EditImageDialog, and EroticPicturesView.
- Enhanced the routing to support fetching erotic folders and videos by username, improving user experience in profile views.
This commit is contained in:
Torsten Schulz (local)
2026-03-27 16:56:45 +01:00
parent 3b29273f90
commit 5556beffef
13 changed files with 1081 additions and 36 deletions

View File

@@ -20,15 +20,16 @@
:isLastItem="true"
:depth="0"
:parentsWithChildren="[false]"
:noActionItems="isForeignView"
@edit-folder="openEditFolderDialog"
@delete-folder="deleteFolder"
/>
</ul>
<button @click="openCreateFolderDialog">{{ $t('socialnetwork.gallery.create_folder') }}</button>
<button v-if="!isForeignView" @click="openCreateFolderDialog">{{ $t('socialnetwork.gallery.create_folder') }}</button>
</div>
<div class="content">
<div class="upload-section surface-card">
<div v-if="!isForeignView" class="upload-section surface-card">
<div class="upload-header" @click="toggleUploadSection">
<span><i class="icon-upload-toggle">{{ isUploadVisible ? '&#9650;' : '&#9660;' }}</i></span>
<h3>{{ $t('socialnetwork.erotic.uploadTitle') }}</h3>
@@ -72,6 +73,15 @@
</template>
</multiselect>
</div>
<div v-if="requiresSelectedUsers" class="form-group">
<label for="selectedUsers">{{ $t('socialnetwork.gallery.visibility.selected-users') }}</label>
<input
id="selectedUsers"
v-model="selectedUsernamesText"
type="text"
placeholder="anna, bert, clara"
/>
</div>
<button type="submit" class="upload-button">
{{ $t('socialnetwork.gallery.upload.upload_button') }}
@@ -127,6 +137,7 @@ import FolderItem from '../../components/FolderItem.vue';
import 'vue-multiselect/dist/vue-multiselect.min.css';
import { EventBus } from '@/utils/eventBus.js';
import { showApiError, showSuccess } from '@/utils/feedback.js';
import { mapGetters } from 'vuex';
export default {
components: {
@@ -143,22 +154,31 @@ export default {
isUploadVisible: true,
visibilityOptions: [],
selectedVisibilities: [],
selectedUsernamesText: '',
imagePreview: null,
reportTarget: { type: null, id: null },
reportReason: 'other',
reportNote: '',
viewUsername: '',
};
},
computed: {
...mapGetters(['user']),
reportReasonOptions() {
return ['suspected_minor', 'non_consensual', 'violence', 'harassment', 'spam', 'other'].map(value => ({
value,
label: this.$t(`socialnetwork.erotic.reportReasons.${value}`)
}));
},
isForeignView() {
return Boolean(this.viewUsername && this.viewUsername !== this.user?.username);
},
requiresSelectedUsers() {
return this.selectedVisibilities.some(option => option?.description === 'selected-users');
}
},
async mounted() {
await this.loadFolders();
await this.initializeView();
await this.loadImageVisibilities();
if (this.folders) {
this.selectFolder(this.folders);
@@ -169,8 +189,14 @@ export default {
EventBus.off('folderCreated', this.loadFolders);
},
methods: {
async initializeView() {
this.viewUsername = String(this.$route.query.username || '').trim();
await this.loadFolders();
},
async loadFolders() {
const response = await apiClient.get('/api/socialnetwork/erotic/folders');
const response = this.isForeignView
? await apiClient.get(`/api/socialnetwork/profile/erotic/folders/${this.viewUsername}`)
: await apiClient.get('/api/socialnetwork/erotic/folders');
this.folders = response.data;
},
async loadImageVisibilities() {
@@ -223,6 +249,9 @@ export default {
formData.append('folderId', this.selectedFolder.id);
formData.append('title', this.imageTitle);
formData.append('visibility', JSON.stringify(this.selectedVisibilities.map((v) => v.id)));
formData.append('selectedUsers', JSON.stringify(
this.selectedUsernamesText.split(',').map(value => value.trim()).filter(Boolean)
));
await apiClient.post('/api/socialnetwork/erotic/images', formData, {
headers: {
@@ -234,6 +263,7 @@ export default {
this.fileToUpload = null;
this.imagePreview = null;
this.selectedVisibilities = this.visibilityOptions.filter(option => option.description === 'adults');
this.selectedUsernamesText = '';
},
async fetchImage(image) {
if (image.isModeratedHidden) {
@@ -250,6 +280,10 @@ export default {
this.isUploadVisible = !this.isUploadVisible;
},
openImageDialog(image) {
if (this.isForeignView) {
this.$root.$refs.showImageDialog.open(image);
return;
}
this.$root.$refs.editImageDialog.open(image);
},
startReport(type, id) {
@@ -280,6 +314,7 @@ export default {
const response = await apiClient.put(`/api/socialnetwork/erotic/images/${updatedImage.id}`, {
title: updatedImage.title,
visibilities: updatedImage.visibilities,
selectedUsers: updatedImage.selectedUsers || [],
});
this.images = response.data.map((image) => ({
...image,
@@ -301,6 +336,16 @@ export default {
// Separate delete flow for adult folders is intentionally not enabled yet.
},
},
watch: {
'$route.query.username': {
async handler() {
await this.initializeView();
if (this.folders) {
await this.selectFolder(this.folders);
}
}
}
}
};
</script>