Add functionality for managing user-owned chat rooms: Implement getOwnRooms and deleteOwnRoom methods in ChatController and ChatService, add corresponding API routes in chatRouter, and enhance MultiChatDialog for displaying and deleting owned rooms with localized messages. Update i18n files for new features.
This commit is contained in:
@@ -14,6 +14,8 @@ class ChatController {
|
||||
this.getOneToOneMessageHistory = this.getOneToOneMessageHistory.bind(this);
|
||||
this.getRoomList = this.getRoomList.bind(this);
|
||||
this.getRoomCreateOptions = this.getRoomCreateOptions.bind(this);
|
||||
this.getOwnRooms = this.getOwnRooms.bind(this);
|
||||
this.deleteOwnRoom = this.deleteOwnRoom.bind(this);
|
||||
}
|
||||
|
||||
async getMessages(req, res) {
|
||||
@@ -185,6 +187,32 @@ class ChatController {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async getOwnRooms(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const rooms = await chatService.getOwnRooms(hashedUserId);
|
||||
res.status(200).json(rooms);
|
||||
} catch (error) {
|
||||
const status = error.message === 'user_not_found' ? 404 : 500;
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteOwnRoom(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const roomId = Number.parseInt(req.params.id, 10);
|
||||
if (!Number.isInteger(roomId) || roomId <= 0) {
|
||||
return res.status(400).json({ error: 'invalid_room_id' });
|
||||
}
|
||||
await chatService.deleteOwnRoom(hashedUserId, roomId);
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
const status = error.message === 'room_not_found_or_not_owner' || error.message === 'user_not_found' ? 404 : 500;
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatController;
|
||||
|
||||
@@ -16,5 +16,7 @@ router.post('/oneToOne/sendMessage', authenticate, chatController.sendOneToOneMe
|
||||
router.get('/oneToOne/messageHistory', authenticate, chatController.getOneToOneMessageHistory); // Neue Route zum Abrufen der Nachrichtengeschichte
|
||||
router.get('/rooms', chatController.getRoomList);
|
||||
router.get('/room-create-options', authenticate, chatController.getRoomCreateOptions);
|
||||
router.get('/my-rooms', authenticate, chatController.getOwnRooms);
|
||||
router.delete('/my-rooms/:id', authenticate, chatController.deleteOwnRoom);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import amqp from 'amqplib/callback_api.js';
|
||||
import User from '../models/community/user.js';
|
||||
import Room from '../models/chat/room.js';
|
||||
|
||||
const RABBITMQ_URL = 'amqp://localhost';
|
||||
const QUEUE = 'oneToOne_messages';
|
||||
@@ -169,6 +171,45 @@ class ChatService {
|
||||
roomTypes: interests.map((i) => ({ id: i.id, name: i.name }))
|
||||
};
|
||||
}
|
||||
|
||||
async getOwnRooms(hashedUserId) {
|
||||
const user = await User.findOne({
|
||||
where: { hashedId: hashedUserId },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (!user) {
|
||||
throw new Error('user_not_found');
|
||||
}
|
||||
|
||||
return Room.findAll({
|
||||
where: { ownerId: user.id },
|
||||
attributes: ['id', 'title', 'isPublic', 'roomTypeId', 'ownerId'],
|
||||
order: [['title', 'ASC']]
|
||||
});
|
||||
}
|
||||
|
||||
async deleteOwnRoom(hashedUserId, roomId) {
|
||||
const user = await User.findOne({
|
||||
where: { hashedId: hashedUserId },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (!user) {
|
||||
throw new Error('user_not_found');
|
||||
}
|
||||
|
||||
const deleted = await Room.destroy({
|
||||
where: {
|
||||
id: roomId,
|
||||
ownerId: user.id
|
||||
}
|
||||
});
|
||||
|
||||
if (!deleted) {
|
||||
throw new Error('room_not_found_or_not_owner');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export default new ChatService();
|
||||
|
||||
@@ -9,3 +9,12 @@ export const fetchRoomCreateOptions = async () => {
|
||||
const response = await apiClient.get("/api/chat/room-create-options");
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const fetchOwnRooms = async () => {
|
||||
const response = await apiClient.get("/api/chat/my-rooms");
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const deleteOwnRoom = async (roomId) => {
|
||||
await apiClient.delete(`/api/chat/my-rooms/${roomId}`);
|
||||
};
|
||||
|
||||
@@ -71,17 +71,17 @@
|
||||
{{ $t('chat.multichat.createRoom.labels.visibility') }}
|
||||
<select v-model="roomCreateForm.visibility">
|
||||
<option value="">{{ $t('chat.multichat.createRoom.options.none') }}</option>
|
||||
<option value="public">public</option>
|
||||
<option value="private">private</option>
|
||||
<option value="public">{{ $t('chat.multichat.createRoom.options.visibilityPublic') }}</option>
|
||||
<option value="private">{{ $t('chat.multichat.createRoom.options.visibilityPrivate') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
{{ $t('chat.multichat.createRoom.labels.gender') }}
|
||||
<select v-model="roomCreateForm.gender">
|
||||
<option value="">{{ $t('chat.multichat.createRoom.options.none') }}</option>
|
||||
<option value="m">m</option>
|
||||
<option value="f">f</option>
|
||||
<option value="any">any</option>
|
||||
<option value="m">{{ $t('chat.multichat.createRoom.options.genderMale') }}</option>
|
||||
<option value="f">{{ $t('chat.multichat.createRoom.options.genderFemale') }}</option>
|
||||
<option value="any">{{ $t('chat.multichat.createRoom.options.genderAny') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
@@ -135,6 +135,26 @@
|
||||
<div class="room-create-preview">
|
||||
{{ $t('chat.multichat.createRoom.commandPrefix') }}: <code>{{ buildRoomCreateCommandPreview() || '/cr <raumname>' }}</code>
|
||||
</div>
|
||||
<div class="owned-rooms-section">
|
||||
<div class="owned-rooms-title">{{ $t('chat.multichat.createRoom.ownedRooms.title') }}</div>
|
||||
<div class="owned-rooms-hint">{{ $t('chat.multichat.createRoom.ownedRooms.hint') }}</div>
|
||||
<div v-if="!ownRooms.length" class="owned-rooms-empty">
|
||||
{{ $t('chat.multichat.createRoom.ownedRooms.empty') }}
|
||||
</div>
|
||||
<div v-else class="owned-rooms-list">
|
||||
<div v-for="room in ownRooms" :key="`own-room-${room.id}`" class="owned-room-item">
|
||||
<div class="owned-room-main">
|
||||
<span class="owned-room-name">{{ room.title }}</span>
|
||||
<span class="owned-room-badge" :class="room.isPublic ? 'public' : 'private'">
|
||||
{{ room.isPublic ? $t('chat.multichat.createRoom.ownedRooms.public') : $t('chat.multichat.createRoom.ownedRooms.private') }}
|
||||
</span>
|
||||
</div>
|
||||
<button type="button" class="owned-room-delete-btn" @click="deleteOwnedRoom(room)">
|
||||
{{ $t('common.delete') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-list">
|
||||
<div class="user-list-header">Teilnehmer ({{ usersInRoom.length }})</div>
|
||||
@@ -199,7 +219,7 @@
|
||||
|
||||
<script>
|
||||
import DialogWidget from '@/components/DialogWidget.vue';
|
||||
import { fetchPublicRooms, fetchRoomCreateOptions } from '@/api/chatApi.js';
|
||||
import { fetchPublicRooms, fetchRoomCreateOptions, fetchOwnRooms } from '@/api/chatApi.js';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getChatWsUrl, getChatWsCandidates, getChatWsProtocols } from '@/services/chatWs.js';
|
||||
|
||||
@@ -293,6 +313,7 @@ export default {
|
||||
},
|
||||
roomCreateRights: [],
|
||||
roomCreateTypes: [],
|
||||
ownRooms: [],
|
||||
// Palette state
|
||||
paletteWidth: 420,
|
||||
paletteHeight: 220,
|
||||
@@ -375,12 +396,16 @@ export default {
|
||||
const candidates = [
|
||||
`chat.multichat.createRoom.rights.${raw}`,
|
||||
`navigation.${raw}`,
|
||||
`navigation.m-admin.${raw}`
|
||||
`navigation.m-administration.${raw}`
|
||||
];
|
||||
for (const key of candidates) {
|
||||
if (this.$te(key)) return this.$t(key);
|
||||
}
|
||||
return raw;
|
||||
return raw
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
},
|
||||
getRoomTypeLabel(roomType) {
|
||||
const raw = (roomType?.name || '').trim();
|
||||
@@ -399,6 +424,48 @@ export default {
|
||||
this.roomCreateTypes = [];
|
||||
}
|
||||
},
|
||||
async loadOwnRooms() {
|
||||
try {
|
||||
const rooms = await fetchOwnRooms();
|
||||
this.ownRooms = Array.isArray(rooms) ? rooms : [];
|
||||
} catch (e) {
|
||||
console.error('Failed loading own rooms', e);
|
||||
this.ownRooms = [];
|
||||
}
|
||||
},
|
||||
async deleteOwnedRoom(room) {
|
||||
const title = room?.title || '';
|
||||
const confirmed = window.confirm(this.$t('chat.multichat.createRoom.ownedRooms.confirmDelete', { room: title }));
|
||||
if (!confirmed) return;
|
||||
if (!this.transportConnected) {
|
||||
this.messages.push({
|
||||
id: Date.now(),
|
||||
user: 'System',
|
||||
text: this.$t('chat.multichat.createRoom.messages.noConnection')
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const payload = { type: 'message', message: `/dr ${title}` };
|
||||
if (this.debug) console.log('[Chat WS >>]', payload);
|
||||
this.sendWithToken(payload);
|
||||
this.messages.push({
|
||||
id: Date.now(),
|
||||
user: 'System',
|
||||
text: this.$t('chat.multichat.createRoom.ownedRooms.deleteSent', { room: title })
|
||||
});
|
||||
this.requestRoomRefreshAfterCreate();
|
||||
setTimeout(() => this.loadOwnRooms(), 700);
|
||||
setTimeout(() => this.loadOwnRooms(), 2000);
|
||||
} catch (e) {
|
||||
console.error('Failed deleting own room', e);
|
||||
this.messages.push({
|
||||
id: Date.now(),
|
||||
user: 'System',
|
||||
text: this.$t('chat.multichat.createRoom.ownedRooms.deleteError')
|
||||
});
|
||||
}
|
||||
},
|
||||
parseOptionalInteger(value) {
|
||||
if (value === null || value === undefined || value === '') return null;
|
||||
const num = Number(value);
|
||||
@@ -561,6 +628,7 @@ export default {
|
||||
this.showOptions = false;
|
||||
this.showRoomCreatePanel = false;
|
||||
this.loadRoomCreateOptions();
|
||||
this.loadOwnRooms();
|
||||
this.announcedRoomEnter = false;
|
||||
this.$refs.dialog.open();
|
||||
// Stelle die WS-Verbindung her, wenn der Dialog geöffnet wird
|
||||
@@ -1905,6 +1973,82 @@ export default {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.owned-rooms-section {
|
||||
margin-top: 0.9em;
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: 0.7em;
|
||||
}
|
||||
|
||||
.owned-rooms-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
|
||||
.owned-rooms-hint {
|
||||
font-size: 0.85em;
|
||||
color: #666;
|
||||
margin-bottom: 0.45em;
|
||||
}
|
||||
|
||||
.owned-rooms-empty {
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.owned-rooms-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35em;
|
||||
}
|
||||
|
||||
.owned-room-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.6em;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
padding: 0.35em 0.5em;
|
||||
}
|
||||
|
||||
.owned-room-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.owned-room-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.owned-room-badge {
|
||||
font-size: 0.75em;
|
||||
border-radius: 999px;
|
||||
padding: 0.1em 0.5em;
|
||||
border: 1px solid #bbb;
|
||||
}
|
||||
|
||||
.owned-room-badge.public {
|
||||
background: #e8f5e9;
|
||||
border-color: #81c784;
|
||||
}
|
||||
|
||||
.owned-room-badge.private {
|
||||
background: #fff3e0;
|
||||
border-color: #ffb74d;
|
||||
}
|
||||
|
||||
.owned-room-delete-btn {
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
padding: 0.2em 0.55em;
|
||||
}
|
||||
|
||||
.room-create-error {
|
||||
color: #b00020;
|
||||
font-size: 0.85em;
|
||||
|
||||
@@ -49,11 +49,11 @@
|
||||
"roomName": "Raumname",
|
||||
"visibility": "Sichtbarkeit",
|
||||
"gender": "Geschlecht",
|
||||
"minAge": "min_age",
|
||||
"maxAge": "max_age",
|
||||
"minAge": "Mindestalter",
|
||||
"maxAge": "Höchstalter",
|
||||
"password": "Passwort",
|
||||
"rightId": "right_id",
|
||||
"typeId": "type_id",
|
||||
"rightId": "Benötigtes Recht",
|
||||
"typeId": "Raumtyp",
|
||||
"friendsOnly": "friends_only=true"
|
||||
},
|
||||
"placeholders": {
|
||||
@@ -61,7 +61,12 @@
|
||||
"password": "ohne Leerzeichen"
|
||||
},
|
||||
"options": {
|
||||
"none": "(keine)"
|
||||
"none": "(keine)",
|
||||
"visibilityPublic": "Öffentlich",
|
||||
"visibilityPrivate": "Privat",
|
||||
"genderMale": "Männlich",
|
||||
"genderFemale": "Weiblich",
|
||||
"genderAny": "Alle / Keine Einschränkung"
|
||||
},
|
||||
"actions": {
|
||||
"create": "Raum erstellen",
|
||||
@@ -82,7 +87,30 @@
|
||||
"roomNameMissing": "Bitte einen Raumnamen angeben.",
|
||||
"sent": "Raum-Erstellung gesendet: {command}"
|
||||
},
|
||||
"rights": {},
|
||||
"ownedRooms": {
|
||||
"title": "Meine erstellten Räume",
|
||||
"hint": "Löschen per Daemon-Befehl: /dr <raumname> (Alias: /delete_room <raumname>)",
|
||||
"empty": "Du hast noch keine eigenen Räume.",
|
||||
"public": "public",
|
||||
"private": "private",
|
||||
"confirmDelete": "Soll der Raum \"{room}\" wirklich gelöscht werden?",
|
||||
"deleteSent": "Löschbefehl gesendet: /dr {room}",
|
||||
"deleteError": "Raum konnte nicht gelöscht werden."
|
||||
},
|
||||
"rights": {
|
||||
"mainadmin": "Hauptadministrator",
|
||||
"contactrequests": "Kontaktanfragen",
|
||||
"users": "Benutzer",
|
||||
"userrights": "Benutzerrechte",
|
||||
"forum": "Forum",
|
||||
"interests": "Interessen",
|
||||
"falukant": "Falukant",
|
||||
"minigames": "Minispiele",
|
||||
"match3": "Match3",
|
||||
"taxiTools": "Taxi-Tools",
|
||||
"chatrooms": "Chaträume",
|
||||
"servicesStatus": "Service-Status"
|
||||
},
|
||||
"types": {}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -49,11 +49,11 @@
|
||||
"roomName": "Room name",
|
||||
"visibility": "Visibility",
|
||||
"gender": "Gender",
|
||||
"minAge": "min_age",
|
||||
"maxAge": "max_age",
|
||||
"minAge": "Minimum age",
|
||||
"maxAge": "Maximum age",
|
||||
"password": "Password",
|
||||
"rightId": "right_id",
|
||||
"typeId": "type_id",
|
||||
"rightId": "Required right",
|
||||
"typeId": "Room type",
|
||||
"friendsOnly": "friends_only=true"
|
||||
},
|
||||
"placeholders": {
|
||||
@@ -61,7 +61,12 @@
|
||||
"password": "without spaces"
|
||||
},
|
||||
"options": {
|
||||
"none": "(none)"
|
||||
"none": "(none)",
|
||||
"visibilityPublic": "Public",
|
||||
"visibilityPrivate": "Private",
|
||||
"genderMale": "Male",
|
||||
"genderFemale": "Female",
|
||||
"genderAny": "Any / No restriction"
|
||||
},
|
||||
"actions": {
|
||||
"create": "Create room",
|
||||
@@ -82,7 +87,30 @@
|
||||
"roomNameMissing": "Please enter a room name.",
|
||||
"sent": "Room creation sent: {command}"
|
||||
},
|
||||
"rights": {},
|
||||
"ownedRooms": {
|
||||
"title": "My created rooms",
|
||||
"hint": "Delete via daemon command: /dr <roomname> (alias: /delete_room <roomname>)",
|
||||
"empty": "You have no own rooms yet.",
|
||||
"public": "public",
|
||||
"private": "private",
|
||||
"confirmDelete": "Do you really want to delete room \"{room}\"?",
|
||||
"deleteSent": "Delete command sent: /dr {room}",
|
||||
"deleteError": "Could not delete room."
|
||||
},
|
||||
"rights": {
|
||||
"mainadmin": "Main administrator",
|
||||
"contactrequests": "Contact requests",
|
||||
"users": "Users",
|
||||
"userrights": "User rights",
|
||||
"forum": "Forum",
|
||||
"interests": "Interests",
|
||||
"falukant": "Falukant",
|
||||
"minigames": "Mini games",
|
||||
"match3": "Match3",
|
||||
"taxiTools": "Taxi tools",
|
||||
"chatrooms": "Chat rooms",
|
||||
"servicesStatus": "Service status"
|
||||
},
|
||||
"types": {}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -48,11 +48,11 @@
|
||||
"roomName": "Nombre de la sala",
|
||||
"visibility": "Visibilidad",
|
||||
"gender": "Género",
|
||||
"minAge": "min_age",
|
||||
"maxAge": "max_age",
|
||||
"minAge": "Edad mínima",
|
||||
"maxAge": "Edad máxima",
|
||||
"password": "Contraseña",
|
||||
"rightId": "right_id",
|
||||
"typeId": "type_id",
|
||||
"rightId": "Permiso requerido",
|
||||
"typeId": "Tipo de sala",
|
||||
"friendsOnly": "friends_only=true"
|
||||
},
|
||||
"placeholders": {
|
||||
@@ -60,7 +60,12 @@
|
||||
"password": "sin espacios"
|
||||
},
|
||||
"options": {
|
||||
"none": "(ninguno)"
|
||||
"none": "(ninguno)",
|
||||
"visibilityPublic": "Pública",
|
||||
"visibilityPrivate": "Privada",
|
||||
"genderMale": "Masculino",
|
||||
"genderFemale": "Femenino",
|
||||
"genderAny": "Cualquiera / Sin restricción"
|
||||
},
|
||||
"actions": {
|
||||
"create": "Crear sala",
|
||||
@@ -81,7 +86,30 @@
|
||||
"roomNameMissing": "Introduce un nombre de sala.",
|
||||
"sent": "Creación de sala enviada: {command}"
|
||||
},
|
||||
"rights": {},
|
||||
"ownedRooms": {
|
||||
"title": "Mis salas creadas",
|
||||
"hint": "Eliminar con comando del daemon: /dr <sala> (alias: /delete_room <sala>)",
|
||||
"empty": "Aún no tienes salas propias.",
|
||||
"public": "public",
|
||||
"private": "private",
|
||||
"confirmDelete": "¿Seguro que quieres eliminar la sala \"{room}\"?",
|
||||
"deleteSent": "Comando de borrado enviado: /dr {room}",
|
||||
"deleteError": "No se pudo eliminar la sala."
|
||||
},
|
||||
"rights": {
|
||||
"mainadmin": "Administrador principal",
|
||||
"contactrequests": "Solicitudes de contacto",
|
||||
"users": "Usuarios",
|
||||
"userrights": "Permisos de usuario",
|
||||
"forum": "Foro",
|
||||
"interests": "Intereses",
|
||||
"falukant": "Falukant",
|
||||
"minigames": "Minijuegos",
|
||||
"match3": "Match3",
|
||||
"taxiTools": "Herramientas de Taxi",
|
||||
"chatrooms": "Salas de chat",
|
||||
"servicesStatus": "Estado de servicios"
|
||||
},
|
||||
"types": {}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user