diff --git a/static/galene.css b/static/galene.css
index 82fc007..16fd0c9 100644
--- a/static/galene.css
+++ b/static/galene.css
@@ -1337,3 +1337,7 @@ header .collapse:hover {
margin-left: 10px;
margin-right: 10px;
}
+
+#invite-dialog {
+ background-color: #eee;
+}
diff --git a/static/galene.html b/static/galene.html
index 8ffe699..5b93974 100644
--- a/static/galene.html
+++ b/static/galene.html
@@ -281,6 +281,21 @@
+
+
diff --git a/static/galene.js b/static/galene.js
index d094f80..ad64240 100644
--- a/static/galene.js
+++ b/static/galene.js
@@ -2094,6 +2094,88 @@ function stringCompare(a, b) {
return 0
}
+/**
+ * @param {string} v
+ */
+function dateFromInput(v) {
+ let d = new Date(v);
+ if(d.toString() === 'Invalid Date')
+ throw new Error('Invalid date');
+ return d;
+}
+
+/**
+ * @param {Date} d
+ */
+function dateToInput(d) {
+ let dd = new Date(d);
+ dd.setMinutes(dd.getMinutes() - dd.getTimezoneOffset());
+ return dd.toISOString().slice(0, -1);
+}
+
+function inviteMenu() {
+ let d = /** @type {HTMLDialogElement} */
+ (document.getElementById('invite-dialog'));
+ if(!('HTMLDialogElement' in window) || !d.showModal) {
+ makeToken();
+ return;
+ }
+ d.returnValue = '';
+ let c = getButtonElement('invite-cancel');
+ c.onclick = function(e) { d.close('cancel'); };
+ let u = getInputElement('invite-username');
+ u.value = '';
+ let now = new Date();
+ now.setMilliseconds(0);
+ now.setSeconds(0);
+ let nb = getInputElement('invite-not-before');
+ nb.min = dateToInput(now);
+ let ex = getInputElement('invite-expires');
+ let expires = new Date(now);
+ expires.setDate(expires.getDate() + 2);
+ ex.min = dateToInput(expires);
+ ex.value = dateToInput(expires);
+ d.showModal();
+}
+
+document.getElementById('invite-dialog').onclose = function(e) {
+ if(!(this instanceof HTMLDialogElement))
+ throw new Error('Unexpected type for this');
+ let dialog = /** @type {HTMLDialogElement} */(this);
+ if(dialog.returnValue !== 'invite')
+ return;
+ let u = getInputElement('invite-username');
+ let username = u.value.trim() || null;
+ let nb = getInputElement('invite-not-before');
+ let notBefore = null;
+ if(nb.value) {
+ try {
+ notBefore = dateFromInput(nb.value);
+ } catch(e) {
+ displayError(`Couldn't parse ${nb.value}: ${e}`);
+ return;
+ }
+ }
+ let ex = getInputElement('invite-expires');
+ let expires = null;
+ if(ex.value) {
+ try {
+ expires = dateFromInput(ex.value);
+ } catch(e) {
+ displayError(`Couldn't parse ${nb.value}: ${e}`);
+ return;
+ }
+ }
+ let template = {}
+ if(username)
+ template.username = username;
+ if(notBefore)
+ template['not-before'] = notBefore;
+ if(expires)
+ template.expires = expires;
+ makeToken(template);
+};
+
/**
* @param {HTMLElement} elt
*/
@@ -2122,7 +2204,7 @@ function userMenu(elt) {
if(serverConnection.version !== "1" &&
serverConnection.permissions.indexOf('token') >= 0) {
items.push({label: 'Invite user', onClick: () => {
- makeToken();
+ inviteMenu();
}});
}
if(serverConnection.permissions.indexOf('present') >= 0 && canFile())