From 303b0d1903224887ff7ccd0b764e0dd4f63870f8 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 02:08:07 +0100 Subject: [PATCH 01/11] Provide a list of Mastodon servers from joinmastodon.org --- src/index.pug | 8 +++++-- src/script/index.js | 56 ++++++++++++++++++++++++++++++++++++++++++--- src/style/main.scss | 1 + 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/index.pug b/src/index.pug index a85c79c..03d816c 100644 --- a/src/index.pug +++ b/src/index.pug @@ -28,7 +28,11 @@ html(lang="en") label(for="text") Post text textarea#text(rows=6, name="text", required) section - label(for="instance") Mastodon instance URL + label(for="choose_instance") Choose your Mastodon instance + select#choose_instance() + option(value="") -- Choose an instance -- + section + label(for="instance") Or type the instance URL input#instance(type="url", name="instance", placeholder="https://", required) section.remember @@ -43,4 +47,4 @@ html(lang="en") section a(href="https://github.com/NickKaramoff/toot") This project on GitHub - script(src="index.js") \ No newline at end of file + script(src="index.js") diff --git a/src/script/index.js b/src/script/index.js index e0a697c..3387cba 100644 --- a/src/script/index.js +++ b/src/script/index.js @@ -8,6 +8,8 @@ function normalizeUrl(url) { return url; } +const instance = document.getElementById('instance'); +const choose_instance = document.getElementById('choose_instance'); var prefillInstance = window.localStorage.getItem('mastodon_instance'); var paramPairs = window.location.search.substr(1).split('&'); @@ -24,10 +26,58 @@ for (var i = 0; i < paramPairsLength; i++) { delete i delete paramPair -if (prefillInstance != null) { - document.getElementById('instance').value = normalizeUrl(prefillInstance); +function add_instance(text, disabled, selected) { + const opt = document.createElement('option'); + opt.innerText = text; + if (disabled) { + opt.setAttribute('disabled', true); + } + if (selected) { + opt.setAttribute('selected', true); + } + choose_instance.appendChild(opt); } +if (prefillInstance != null) { + const url = normalizeUrl(prefillInstance); + instance.value = url; + add_instance(url.slice(8, url.length - 1), false, true); +} + +choose_instance.addEventListener('focus', function (e) { + if (choose_instance.children.length < 3) { + if (choose_instance.children.length > 1) { + choose_instance.children[1].remove(); + } + add_instance("... loading ...", true); + fetch('https://api.joinmastodon.org/servers') + .then(response => { + if (!response.ok) { + throw new Error('Failure response from joinmastodon.'); + } + return response.json(); + }) + .then(servers => { + choose_instance.children[1].remove(); + const domains = servers.map(obj => obj.domain); + domains.sort(); + for (const domain of domains) { + add_instance(domain); + } + }) + .catch(error => { + choose_instance.children[1].remove(); + add_instance("--LOADING FAILED--", true); + console.error( + 'Failed to fetch servers list from joinmastodon.', error); + }); + } +}) + +choose_instance.addEventListener('change', function (e) { + instance.value = `https://${choose_instance.value}`; +}); + document .getElementById('form') .addEventListener('submit', function (e) { @@ -42,4 +92,4 @@ document var shareUrl = instance + "share?text=" + encodeURIComponent(text); window.open(shareUrl, '_blank', 'noopener,noreferrer') -}) + }) diff --git a/src/style/main.scss b/src/style/main.scss index 556d920..e3eeccd 100644 --- a/src/style/main.scss +++ b/src/style/main.scss @@ -100,6 +100,7 @@ main { transition: background-color 300ms ease, border 300ms ease; } + select, textarea, input[type=url] { color: inherit; From 06f7c372b5bd2c3650afbe8a0b61289ba5a9e22d Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 08:27:27 +0100 Subject: [PATCH 02/11] Extract the domain by properly parsing the instance URL --- src/script/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/script/index.js b/src/script/index.js index 3387cba..fdd609c 100644 --- a/src/script/index.js +++ b/src/script/index.js @@ -38,10 +38,16 @@ function add_instance(text, disabled, selected) { choose_instance.appendChild(opt); } +function parseUrl(url) { + const parser = document.createElement('a'); + parser.href = url; + return parser; +} + if (prefillInstance != null) { const url = normalizeUrl(prefillInstance); instance.value = url; - add_instance(url.slice(8, url.length - 1), false, true); + add_instance(parseUrl(url).hostname, false, true); } choose_instance.addEventListener('focus', function (e) { From e27cb3f123dfb6ecc277cfb7320c97c2500a1ab6 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 08:39:45 +0100 Subject: [PATCH 03/11] Don't remove the prefilled entry when loading the instances --- src/script/index.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/script/index.js b/src/script/index.js index fdd609c..7465075 100644 --- a/src/script/index.js +++ b/src/script/index.js @@ -38,6 +38,19 @@ function add_instance(text, disabled, selected) { choose_instance.appendChild(opt); } +function add_loading_instance() { + add_instance("... loading ...", true); +} + +function remove_loading_instance() { + const last_index = choose_instance.children.length - 1; + const last_entry = choose_instance.children[last_index]; + + if (last_entry.innerText === "... loading ...") { + last_entry.remove(); + } +} + function parseUrl(url) { const parser = document.createElement('a'); parser.href = url; @@ -52,10 +65,8 @@ if (prefillInstance != null) { choose_instance.addEventListener('focus', function (e) { if (choose_instance.children.length < 3) { - if (choose_instance.children.length > 1) { - choose_instance.children[1].remove(); - } - add_instance("... loading ...", true); + remove_loading_instance(); + add_loading_instance(); fetch('https://api.joinmastodon.org/servers') .then(response => { if (!response.ok) { @@ -64,7 +75,7 @@ choose_instance.addEventListener('focus', function (e) { return response.json(); }) .then(servers => { - choose_instance.children[1].remove(); + remove_loading_instance(); const domains = servers.map(obj => obj.domain); domains.sort(); for (const domain of domains) { @@ -72,7 +83,7 @@ choose_instance.addEventListener('focus', function (e) { } }) .catch(error => { - choose_instance.children[1].remove(); + remove_loading_instance(); add_instance("--LOADING FAILED--", true); console.error( 'Failed to fetch servers list from joinmastodon.', error); From 70d484d8a16a730a28edd693a3e9f4bf978b0353 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 08:42:23 +0100 Subject: [PATCH 04/11] Construct the URL using normalizeUrl Co-authored-by: Nikita Karamov --- src/script/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/index.js b/src/script/index.js index 7465075..880b8c8 100644 --- a/src/script/index.js +++ b/src/script/index.js @@ -92,7 +92,7 @@ choose_instance.addEventListener('focus', function (e) { }) choose_instance.addEventListener('change', function (e) { - instance.value = `https://${choose_instance.value}`; + instance.value = normalizeUrl(choose_instance.value); }); document From e8b9390343b17ccba75dc663e56dfe3aa2d90dac Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 09:07:17 +0100 Subject: [PATCH 05/11] Include the prefilled domain in the list, in the correct sort order --- src/script/index.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/script/index.js b/src/script/index.js index 880b8c8..2561e86 100644 --- a/src/script/index.js +++ b/src/script/index.js @@ -26,7 +26,7 @@ for (var i = 0; i < paramPairsLength; i++) { delete i delete paramPair -function add_instance(text, disabled, selected) { +function add_instance(text, disabled, selected, value) { const opt = document.createElement('option'); opt.innerText = text; if (disabled) { @@ -35,6 +35,9 @@ function add_instance(text, disabled, selected) { if (selected) { opt.setAttribute('selected', true); } + if (value !== undefined) { + opt.value = value; + } choose_instance.appendChild(opt); } @@ -75,16 +78,23 @@ choose_instance.addEventListener('focus', function (e) { return response.json(); }) .then(servers => { - remove_loading_instance(); + const chosen_instance = choose_instance.value; const domains = servers.map(obj => obj.domain); + if (domains.indexOf(chosen_instance) === -1) { + domains.push(chosen_instance); + } domains.sort(); + + choose_instance.innerHTML = ""; + add_instance("-- Choose an instance --", false, false, "") for (const domain of domains) { - add_instance(domain); + const selected = (domain === chosen_instance); + add_instance(domain, false, selected); } }) .catch(error => { - remove_loading_instance(); - add_instance("--LOADING FAILED--", true); + choose_instance.innerHTML = ""; + add_instance("-- LOADING FAILED! --", true, false, ""); console.error( 'Failed to fetch servers list from joinmastodon.', error); }); From 4d36c872caa1998c5f4af57e315dc5b0006b88b4 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 09:09:35 +0100 Subject: [PATCH 06/11] Make instance list appearance consistent with other controls --- src/style/main.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/style/main.scss b/src/style/main.scss index e3eeccd..14ae01e 100644 --- a/src/style/main.scss +++ b/src/style/main.scss @@ -71,6 +71,7 @@ main { display: inline-block; } + select, textarea, input { font-size: 1rem; From 7850cece523515e4a0d70059804bf633402ab738 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 09:28:46 +0100 Subject: [PATCH 07/11] Use XMLHttpRequest (for IE 11 compatibility) --- src/script/index.js | 61 +++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/script/index.js b/src/script/index.js index 2561e86..4bfc09e 100644 --- a/src/script/index.js +++ b/src/script/index.js @@ -60,6 +60,35 @@ function parseUrl(url) { return parser; } +function instances_loading_error() { + choose_instance.innerHTML = ""; + add_instance("-- LOADING FAILED! --", true, false, ""); + console.error('Failed to fetch servers list from joinmastodon.'); +} + +function instances_loaded() { + if (this.status !== 200) { + instances_loading_error(); + return; + } + + const servers = JSON.parse(this.responseText); + + const chosen_instance = choose_instance.value; + const domains = servers.map(obj => obj.domain); + if (domains.indexOf(chosen_instance) === -1) { + domains.push(chosen_instance); + } + domains.sort(); + + choose_instance.innerHTML = ""; + add_instance("-- Choose an instance --", false, false, "") + for (const domain of domains) { + const selected = (domain === chosen_instance); + add_instance(domain, false, selected); + } +} + if (prefillInstance != null) { const url = normalizeUrl(prefillInstance); instance.value = url; @@ -70,34 +99,12 @@ choose_instance.addEventListener('focus', function (e) { if (choose_instance.children.length < 3) { remove_loading_instance(); add_loading_instance(); - fetch('https://api.joinmastodon.org/servers') - .then(response => { - if (!response.ok) { - throw new Error('Failure response from joinmastodon.'); - } - return response.json(); - }) - .then(servers => { - const chosen_instance = choose_instance.value; - const domains = servers.map(obj => obj.domain); - if (domains.indexOf(chosen_instance) === -1) { - domains.push(chosen_instance); - } - domains.sort(); - choose_instance.innerHTML = ""; - add_instance("-- Choose an instance --", false, false, "") - for (const domain of domains) { - const selected = (domain === chosen_instance); - add_instance(domain, false, selected); - } - }) - .catch(error => { - choose_instance.innerHTML = ""; - add_instance("-- LOADING FAILED! --", true, false, ""); - console.error( - 'Failed to fetch servers list from joinmastodon.', error); - }); + const req = new XMLHttpRequest(); + req.addEventListener('load', instances_loaded); + req.addEventListener('error', instances_loading_error); + req.open('GET', 'https://api.joinmastodon.org/servers'); + req.send(); } }) From d4bf9e6456e4ba7561cecfd6547015d46a923da8 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 10:03:42 +0100 Subject: [PATCH 08/11] Use an input with associatied datalist instead of select, for instances list --- src/index.pug | 7 ++--- src/script/index.js | 64 +++++++-------------------------------------- 2 files changed, 11 insertions(+), 60 deletions(-) diff --git a/src/index.pug b/src/index.pug index 03d816c..9ea8041 100644 --- a/src/index.pug +++ b/src/index.pug @@ -28,12 +28,9 @@ html(lang="en") label(for="text") Post text textarea#text(rows=6, name="text", required) section + datalist#instances_list label(for="choose_instance") Choose your Mastodon instance - select#choose_instance() - option(value="") -- Choose an instance -- - section - label(for="instance") Or type the instance URL - input#instance(type="url", name="instance", placeholder="https://", required) + input#instance(type="url", name="instance", placeholder="https://", list="instances_list", required) section.remember input#remember(type="checkbox", name="remember") diff --git a/src/script/index.js b/src/script/index.js index 4bfc09e..f042627 100644 --- a/src/script/index.js +++ b/src/script/index.js @@ -9,12 +9,12 @@ function normalizeUrl(url) { } const instance = document.getElementById('instance'); -const choose_instance = document.getElementById('choose_instance'); +const instances_list = document.getElementById('instances_list'); + var prefillInstance = window.localStorage.getItem('mastodon_instance'); var paramPairs = window.location.search.substr(1).split('&'); var paramPairsLength = paramPairs.length; - for (var i = 0; i < paramPairsLength; i++) { var paramPair = paramPairs[i].split('='); if (paramPair[0] === 'text') { @@ -26,43 +26,7 @@ for (var i = 0; i < paramPairsLength; i++) { delete i delete paramPair -function add_instance(text, disabled, selected, value) { - const opt = document.createElement('option'); - opt.innerText = text; - if (disabled) { - opt.setAttribute('disabled', true); - } - if (selected) { - opt.setAttribute('selected', true); - } - if (value !== undefined) { - opt.value = value; - } - choose_instance.appendChild(opt); -} - -function add_loading_instance() { - add_instance("... loading ...", true); -} - -function remove_loading_instance() { - const last_index = choose_instance.children.length - 1; - const last_entry = choose_instance.children[last_index]; - - if (last_entry.innerText === "... loading ...") { - last_entry.remove(); - } -} - -function parseUrl(url) { - const parser = document.createElement('a'); - parser.href = url; - return parser; -} - function instances_loading_error() { - choose_instance.innerHTML = ""; - add_instance("-- LOADING FAILED! --", true, false, ""); console.error('Failed to fetch servers list from joinmastodon.'); } @@ -74,32 +38,26 @@ function instances_loaded() { const servers = JSON.parse(this.responseText); - const chosen_instance = choose_instance.value; + const chosen_instance = instance.value; const domains = servers.map(obj => obj.domain); if (domains.indexOf(chosen_instance) === -1) { domains.push(chosen_instance); } domains.sort(); - choose_instance.innerHTML = ""; - add_instance("-- Choose an instance --", false, false, "") for (const domain of domains) { - const selected = (domain === chosen_instance); - add_instance(domain, false, selected); + const opt = document.createElement('option'); + opt.value = normalizeUrl(domain); + instances_list.appendChild(opt); } } if (prefillInstance != null) { - const url = normalizeUrl(prefillInstance); - instance.value = url; - add_instance(parseUrl(url).hostname, false, true); + instance.value = normalizeUrl(prefillInstance); } -choose_instance.addEventListener('focus', function (e) { - if (choose_instance.children.length < 3) { - remove_loading_instance(); - add_loading_instance(); - +instance.addEventListener('focus', function (e) { + if (instances_list.children.length === 0) { const req = new XMLHttpRequest(); req.addEventListener('load', instances_loaded); req.addEventListener('error', instances_loading_error); @@ -108,10 +66,6 @@ choose_instance.addEventListener('focus', function (e) { } }) -choose_instance.addEventListener('change', function (e) { - instance.value = normalizeUrl(choose_instance.value); -}); - document .getElementById('form') .addEventListener('submit', function (e) { From 3cc3c96a3ef3ab34e7f5b140ae251e630f24d3ac Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 10:59:25 +0100 Subject: [PATCH 09/11] Avoid adding empty entries to the donain list, when nothing is chosen. Co-authored-by: Nikita Karamov --- src/script/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/index.js b/src/script/index.js index f042627..a2c3993 100644 --- a/src/script/index.js +++ b/src/script/index.js @@ -40,7 +40,7 @@ function instances_loaded() { const chosen_instance = instance.value; const domains = servers.map(obj => obj.domain); - if (domains.indexOf(chosen_instance) === -1) { + if (chosen_instance && domains.indexOf(chosen_instance) === -1) { domains.push(chosen_instance); } domains.sort(); From 315b2c6ee62bdddfa3260f65af44c5274c5be617 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 10:59:49 +0100 Subject: [PATCH 10/11] Remove styling for select since we are not using it Co-authored-by: Nikita Karamov --- src/style/main.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/style/main.scss b/src/style/main.scss index 14ae01e..e3eeccd 100644 --- a/src/style/main.scss +++ b/src/style/main.scss @@ -71,7 +71,6 @@ main { display: inline-block; } - select, textarea, input { font-size: 1rem; From dd4fb48516b215e16791207d7cb5fcc5edb1a43b Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Thu, 24 Sep 2020 10:59:56 +0100 Subject: [PATCH 11/11] Remove styling for select since we are not using it Co-authored-by: Nikita Karamov --- src/style/main.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/style/main.scss b/src/style/main.scss index e3eeccd..556d920 100644 --- a/src/style/main.scss +++ b/src/style/main.scss @@ -100,7 +100,6 @@ main { transition: background-color 300ms ease, border 300ms ease; } - select, textarea, input[type=url] { color: inherit;