feat(logging): implement centralized logging system and replace console errors with structured logs
feat(federation): add room sync and deletion notification endpoints for federated instances fix(federation): handle room deletion and update settings during sync process feat(federation): enhance FederatedRoomCard and FederatedRoomDetail components to display deleted rooms i18n: add translations for room deletion messages in English and German
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Globe, Trash2, ExternalLink, Hash, Users, Video, VideoOff } from 'lucide-react';
|
||||
import { Globe, Trash2, ExternalLink, Hash, Users, Video, VideoOff, AlertTriangle } from 'lucide-react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
import api from '../services/api';
|
||||
@@ -8,8 +8,11 @@ export default function FederatedRoomCard({ room, onRemove }) {
|
||||
const { t } = useLanguage();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isDeleted = room.deleted === 1 || room.deleted === true;
|
||||
|
||||
const handleJoin = (e) => {
|
||||
e.stopPropagation();
|
||||
if (isDeleted) return;
|
||||
window.open(room.join_url, '_blank');
|
||||
};
|
||||
|
||||
@@ -28,7 +31,7 @@ export default function FederatedRoomCard({ room, onRemove }) {
|
||||
const recordingOn = room.allow_recording === 1 || room.allow_recording === true;
|
||||
|
||||
return (
|
||||
<div className="card-hover group p-5 cursor-pointer" onClick={() => navigate(`/federation/rooms/${room.id}`)}>
|
||||
<div className={`card-hover group p-5 cursor-pointer ${isDeleted ? 'opacity-60' : ''}`} onClick={() => navigate(`/federation/rooms/${room.id}`)}>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -36,9 +39,16 @@ export default function FederatedRoomCard({ room, onRemove }) {
|
||||
<h3 className="text-base font-semibold text-th-text truncate group-hover:text-th-accent transition-colors">
|
||||
{room.room_name}
|
||||
</h3>
|
||||
<span className="flex-shrink-0 px-2 py-0.5 bg-th-accent/15 text-th-accent rounded-full text-xs font-medium">
|
||||
{t('federation.federated')}
|
||||
</span>
|
||||
{isDeleted ? (
|
||||
<span className="flex-shrink-0 px-2 py-0.5 bg-red-500/15 text-red-500 rounded-full text-xs font-medium flex items-center gap-1">
|
||||
<AlertTriangle size={10} />
|
||||
{t('federation.roomDeleted')}
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex-shrink-0 px-2 py-0.5 bg-th-accent/15 text-th-accent rounded-full text-xs font-medium">
|
||||
{t('federation.federated')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-th-text-s mt-0.5 truncate">
|
||||
{t('federation.from')}: <span className="font-medium">{room.from_user}</span>
|
||||
@@ -79,17 +89,21 @@ export default function FederatedRoomCard({ room, onRemove }) {
|
||||
</div>
|
||||
|
||||
{/* Read-only notice */}
|
||||
<p className="text-xs text-th-text-s mb-4 italic">{t('federation.readOnlyNotice')}</p>
|
||||
<p className="text-xs text-th-text-s mb-4 italic">
|
||||
{isDeleted ? t('federation.roomDeletedNotice') : t('federation.readOnlyNotice')}
|
||||
</p>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-2 pt-3 border-t border-th-border">
|
||||
<button
|
||||
onClick={handleJoin}
|
||||
className="btn-primary text-xs py-1.5 px-3 flex-1"
|
||||
>
|
||||
<ExternalLink size={14} />
|
||||
{t('federation.joinMeeting')}
|
||||
</button>
|
||||
{!isDeleted && (
|
||||
<button
|
||||
onClick={handleJoin}
|
||||
className="btn-primary text-xs py-1.5 px-3 flex-1"
|
||||
>
|
||||
<ExternalLink size={14} />
|
||||
{t('federation.joinMeeting')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={handleRemove}
|
||||
className="btn-ghost text-xs py-1.5 px-2 text-th-error hover:text-th-error"
|
||||
|
||||
@@ -383,6 +383,8 @@
|
||||
"recordingOnHint": "Meetings in diesem Raum können aufgezeichnet werden",
|
||||
"recordingOffHint": "Meetings in diesem Raum werden nicht aufgezeichnet",
|
||||
"roomDetails": "Raumdetails",
|
||||
"joinUrl": "Beitritts-URL"
|
||||
"joinUrl": "Beitritts-URL",
|
||||
"roomDeleted": "Gelöscht",
|
||||
"roomDeletedNotice": "Dieser Raum wurde vom Besitzer auf der Ursprungsinstanz gelöscht und ist nicht mehr verfügbar."
|
||||
}
|
||||
}
|
||||
@@ -383,6 +383,8 @@
|
||||
"recordingOnHint": "Meetings in this room may be recorded",
|
||||
"recordingOffHint": "Meetings in this room will not be recorded",
|
||||
"roomDetails": "Room Details",
|
||||
"joinUrl": "Join URL"
|
||||
"joinUrl": "Join URL",
|
||||
"roomDeleted": "Deleted",
|
||||
"roomDeletedNotice": "This room has been deleted by the owner on the origin instance and is no longer available."
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
ArrowLeft, Globe, ExternalLink, Trash2, Hash, Users,
|
||||
Video, VideoOff, Loader2, Link2,
|
||||
Video, VideoOff, Loader2, Link2, AlertTriangle,
|
||||
} from 'lucide-react';
|
||||
import api from '../services/api';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
@@ -66,6 +66,7 @@ export default function FederatedRoomDetail() {
|
||||
if (!room) return null;
|
||||
|
||||
const recordingOn = room.allow_recording === 1 || room.allow_recording === true;
|
||||
const isDeleted = room.deleted === 1 || room.deleted === true;
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto">
|
||||
@@ -78,19 +79,38 @@ export default function FederatedRoomDetail() {
|
||||
{t('federation.backToDashboard')}
|
||||
</button>
|
||||
|
||||
{/* Deleted banner */}
|
||||
{isDeleted && (
|
||||
<div className="card p-4 mb-4 border-red-500/30 bg-red-500/10">
|
||||
<div className="flex items-center gap-3">
|
||||
<AlertTriangle size={20} className="text-red-500 flex-shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-red-500">{t('federation.roomDeleted')}</p>
|
||||
<p className="text-xs text-th-text-s mt-0.5">{t('federation.roomDeletedNotice')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Header */}
|
||||
<div className="card p-6 mb-4">
|
||||
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4">
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<div className="w-10 h-10 rounded-lg bg-th-accent/15 flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||
<Globe size={20} className="text-th-accent" />
|
||||
<div className={`w-10 h-10 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5 ${isDeleted ? 'bg-red-500/15' : 'bg-th-accent/15'}`}>
|
||||
{isDeleted ? <AlertTriangle size={20} className="text-red-500" /> : <Globe size={20} className="text-th-accent" />}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<h1 className="text-xl font-bold text-th-text truncate">{room.room_name}</h1>
|
||||
<span className="px-2 py-0.5 bg-th-accent/15 text-th-accent rounded-full text-xs font-medium">
|
||||
{t('federation.federated')}
|
||||
</span>
|
||||
{isDeleted ? (
|
||||
<span className="px-2 py-0.5 bg-red-500/15 text-red-500 rounded-full text-xs font-medium">
|
||||
{t('federation.roomDeleted')}
|
||||
</span>
|
||||
) : (
|
||||
<span className="px-2 py-0.5 bg-th-accent/15 text-th-accent rounded-full text-xs font-medium">
|
||||
{t('federation.federated')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-th-text-s mt-1">
|
||||
{t('federation.from')}: <span className="font-medium text-th-text">{room.from_user}</span>
|
||||
@@ -99,13 +119,15 @@ export default function FederatedRoomDetail() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 flex-shrink-0">
|
||||
<button
|
||||
onClick={handleJoin}
|
||||
className="btn-primary"
|
||||
>
|
||||
<ExternalLink size={16} />
|
||||
{t('federation.joinMeeting')}
|
||||
</button>
|
||||
{!isDeleted && (
|
||||
<button
|
||||
onClick={handleJoin}
|
||||
className="btn-primary"
|
||||
>
|
||||
<ExternalLink size={16} />
|
||||
{t('federation.joinMeeting')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={handleRemove}
|
||||
disabled={removing}
|
||||
@@ -176,7 +198,7 @@ export default function FederatedRoomDetail() {
|
||||
|
||||
{/* Read-only notice */}
|
||||
<p className="text-xs text-th-text-s mt-4 text-center italic">
|
||||
{t('federation.readOnlyNotice')}
|
||||
{isDeleted ? t('federation.roomDeletedNotice') : t('federation.readOnlyNotice')}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user