Projects / snek / src / snek / templates /Â dialog_invite.html
git clone https://molodetz.nl/retoor/snek.git
Raw source file available here .
<dialog id="invite-dialog">
<div class="dialog-backdrop">
<div class="dialog-box">
<div class="dialog-title"><h2>Invite User</h2></div>
<div class="dialog-content">
<div class="dialog-form">
<input type="text" id="invite-username-input" class="dialog-input" placeholder="Username" autocomplete="off">
<div id="invite-user-suggestions" class="dialog-suggestions"></div>
</div>
</div>
<div class="dialog-actions">
<button class="dialog-button secondary btn-cancel">Cancel</button>
<button class="dialog-button primary btn-invite">Invite</button>
</div>
</div>
</div>
</dialog>
<script>
class InviteDialog {
constructor() {
this.dialog = document.getElementById('invite-dialog');
this.usernameInput = document.getElementById('invite-username-input');
this.suggestions = document.getElementById('invite-user-suggestions');
this.inviteButton = this.dialog.querySelector('.btn-invite');
this.cancelButton = this.dialog.querySelector('.btn-cancel');
this.channelUid = null;
this._searchTimeout = null;
this._initEventListeners();
}
_initEventListeners() {
this.cancelButton.addEventListener('click', () => this.close());
this.inviteButton.addEventListener('click', () => this.invite());
this.usernameInput.addEventListener('keyup', (e) => {
if (e.key === 'Enter') {
this.invite();
} else {
this._debounceSearch();
}
});
}
_debounceSearch() {
clearTimeout(this._searchTimeout);
this._searchTimeout = setTimeout(() => this._search(), 300);
}
async _search() {
const query = this.usernameInput.value.trim();
if (query.length < 2) {
this.suggestions.innerHTML = '';
return;
}
try {
const users = await window.app.rpc.searchUser(query);
this.suggestions.innerHTML = '';
users.forEach(username => {
const div = document.createElement('div');
div.className = 'dialog-suggestion-item';
div.textContent = username;
div.addEventListener('click', () => {
this.usernameInput.value = username;
this.suggestions.innerHTML = '';
});
this.suggestions.appendChild(div);
});
} catch (err) {
this.suggestions.innerHTML = '';
}
}
show(channelUid) {
this.channelUid = channelUid;
this.usernameInput.value = '';
this.suggestions.innerHTML = '';
this.dialog.showModal();
this.usernameInput.focus();
}
close() {
this.dialog.close();
}
async invite() {
const username = this.usernameInput.value.trim();
if (!username) {
this.usernameInput.classList.add('error');
return;
}
this.usernameInput.classList.remove('error');
this.inviteButton.disabled = true;
try {
const result = await window.app.rpc.inviteUser(this.channelUid, username);
if (result && result.success) {
this.close();
} else if (result && result.error) {
this.usernameInput.classList.add('error');
this.usernameInput.title = result.error;
}
} catch (err) {
this.usernameInput.classList.add('error');
this.usernameInput.title = 'Failed to invite user';
} finally {
this.inviteButton.disabled = false;
}
}
}
const inviteDialog = new InviteDialog();
function showInvite() {
inviteDialog.show('{{ channel.uid.value }}');
}
</script>