feat: add email invitation functionality for guests with support for multiple addresses
All checks were successful
Build & Push Docker Image / build (push) Successful in 4m21s
All checks were successful
Build & Push Docker Image / build (push) Successful in 4m21s
This commit is contained in:
@@ -51,6 +51,7 @@ export default function RoomDetail() {
|
||||
// Federation invite state
|
||||
const [showFedInvite, setShowFedInvite] = useState(false);
|
||||
const [fedAddress, setFedAddress] = useState('');
|
||||
const [fedEmails, setFedEmails] = useState('');
|
||||
const [fedMessage, setFedMessage] = useState('');
|
||||
const [fedSending, setFedSending] = useState(false);
|
||||
|
||||
@@ -266,25 +267,51 @@ export default function RoomDetail() {
|
||||
|
||||
const handleFedInvite = async (e) => {
|
||||
e.preventDefault();
|
||||
// Accept @user@domain or user@domain — must have a domain part
|
||||
const normalized = fedAddress.startsWith('@') ? fedAddress.slice(1) : fedAddress;
|
||||
if (!normalized.includes('@') || normalized.endsWith('@')) {
|
||||
const hasAddress = fedAddress.trim().length > 0;
|
||||
const hasEmails = fedEmails.trim().length > 0;
|
||||
|
||||
if (!hasAddress && !hasEmails) {
|
||||
toast.error(t('federation.addressHint'));
|
||||
return;
|
||||
}
|
||||
|
||||
setFedSending(true);
|
||||
try {
|
||||
await api.post('/federation/invite', {
|
||||
room_uid: uid,
|
||||
to: fedAddress,
|
||||
message: fedMessage || undefined,
|
||||
});
|
||||
toast.success(t('federation.sent'));
|
||||
if (hasAddress) {
|
||||
// Federation address mode
|
||||
const normalized = fedAddress.startsWith('@') ? fedAddress.slice(1) : fedAddress;
|
||||
if (!normalized.includes('@') || normalized.endsWith('@')) {
|
||||
toast.error(t('federation.addressHint'));
|
||||
setFedSending(false);
|
||||
return;
|
||||
}
|
||||
await api.post('/federation/invite', {
|
||||
room_uid: uid,
|
||||
to: fedAddress,
|
||||
message: fedMessage || undefined,
|
||||
});
|
||||
toast.success(t('federation.sent'));
|
||||
} else {
|
||||
// Email mode
|
||||
const emailList = fedEmails.split(',').map(e => e.trim()).filter(Boolean);
|
||||
if (emailList.length === 0) {
|
||||
toast.error(t('federation.emailHint'));
|
||||
setFedSending(false);
|
||||
return;
|
||||
}
|
||||
await api.post('/rooms/invite-email', {
|
||||
room_uid: uid,
|
||||
emails: emailList,
|
||||
message: fedMessage || undefined,
|
||||
});
|
||||
toast.success(t('federation.emailSent'));
|
||||
}
|
||||
setShowFedInvite(false);
|
||||
setFedAddress('');
|
||||
setFedEmails('');
|
||||
setFedMessage('');
|
||||
} catch (err) {
|
||||
toast.error(err.response?.data?.error || t('federation.sendFailed'));
|
||||
toast.error(err.response?.data?.error || t(hasAddress ? 'federation.sendFailed' : 'federation.emailSendFailed'));
|
||||
} finally {
|
||||
setFedSending(false);
|
||||
}
|
||||
@@ -857,13 +884,33 @@ export default function RoomDetail() {
|
||||
<input
|
||||
type="text"
|
||||
value={fedAddress}
|
||||
onChange={e => setFedAddress(e.target.value)}
|
||||
onChange={e => { setFedAddress(e.target.value); if (e.target.value) setFedEmails(''); }}
|
||||
className="input-field"
|
||||
placeholder={t('federation.addressPlaceholder')}
|
||||
required
|
||||
disabled={fedEmails.trim().length > 0}
|
||||
/>
|
||||
<p className="text-xs text-th-text-s mt-1">{t('federation.addressHint')}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 my-2">
|
||||
<div className="flex-1 border-t border-th-border" />
|
||||
<span className="text-xs text-th-text-s uppercase">{t('common.or')}</span>
|
||||
<div className="flex-1 border-t border-th-border" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-th-text mb-1.5">{t('federation.emailLabel')}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={fedEmails}
|
||||
onChange={e => { setFedEmails(e.target.value); if (e.target.value) setFedAddress(''); }}
|
||||
className="input-field"
|
||||
placeholder={t('federation.emailPlaceholder')}
|
||||
disabled={fedAddress.trim().length > 0}
|
||||
/>
|
||||
<p className="text-xs text-th-text-s mt-1">{t('federation.emailHint')}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-th-text mb-1.5">{t('federation.messageLabel')}</label>
|
||||
<textarea
|
||||
@@ -878,7 +925,7 @@ export default function RoomDetail() {
|
||||
<button type="button" onClick={() => setShowFedInvite(false)} className="btn-secondary flex-1">
|
||||
{t('common.cancel')}
|
||||
</button>
|
||||
<button type="submit" disabled={fedSending} className="btn-primary flex-1">
|
||||
<button type="submit" disabled={fedSending || (!fedAddress.trim() && !fedEmails.trim())} className="btn-primary flex-1">
|
||||
{fedSending ? <Loader2 size={16} className="animate-spin" /> : <Send size={16} />}
|
||||
{t('federation.send')}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user