Rewrite the instance selector
This commit adds a new '/api/instances' endpoint that returns the domains of the available Mastodon instances. This also changes the input field, making it accept a host rather than the full domain.
This commit is contained in:
parent
6031d959ea
commit
d3fb510bbb
6 changed files with 87 additions and 69 deletions
40
lib/main.js
40
lib/main.js
|
@ -26,13 +26,11 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const INSTANCE_LIST_URL = "https://api.joinmastodon.org/servers";
|
|
||||||
const LOCAL_STORAGE_KEY = "recentInstances";
|
const LOCAL_STORAGE_KEY = "recentInstances";
|
||||||
const RECENT_INSTANCES_SIZE = 5;
|
const RECENT_INSTANCES_SIZE = 5;
|
||||||
|
|
||||||
const $form = document.querySelector("#js-s2f-form");
|
const $form = document.querySelector("#js-s2f-form");
|
||||||
const $instance = document.querySelector("#instance");
|
const $instance = document.querySelector("#instance");
|
||||||
const $instanceDatalist = document.querySelector("#instanceDatalist");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds missing "https://" and ending slash to the URL
|
* Adds missing "https://" and ending slash to the URL
|
||||||
|
@ -50,43 +48,6 @@ function normalizeUrl(url) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoadInstancesError() {
|
|
||||||
console.error("Couldn't load instance list");
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLoadInstancesSuccess() {
|
|
||||||
if (this.status >= 400) {
|
|
||||||
return onLoadInstancesError();
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentInstance = $instance.value;
|
|
||||||
const instanceDomains = JSON.parse(this.responseText).map(
|
|
||||||
(index) => index.domain,
|
|
||||||
);
|
|
||||||
if (currentInstance && !instanceDomains.includes(currentInstance)) {
|
|
||||||
instanceDomains.push(currentInstance);
|
|
||||||
}
|
|
||||||
instanceDomains.sort();
|
|
||||||
|
|
||||||
for (const instanceDomain of instanceDomains) {
|
|
||||||
const $option = document.createElement("option");
|
|
||||||
$option.value = normalizeUrl(instanceDomain);
|
|
||||||
$instanceDatalist.append($option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadInstances() {
|
|
||||||
if ($instanceDatalist.children.length === 0) {
|
|
||||||
const request = new XMLHttpRequest();
|
|
||||||
|
|
||||||
request.addEventListener("load", onLoadInstancesSuccess);
|
|
||||||
request.addEventListener("error", onLoadInstancesError);
|
|
||||||
|
|
||||||
request.open("GET", INSTANCE_LIST_URL);
|
|
||||||
request.send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRecentInstances() {
|
function getRecentInstances() {
|
||||||
const storedValue = window.localStorage.getItem(LOCAL_STORAGE_KEY);
|
const storedValue = window.localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||||
if (!storedValue) return [];
|
if (!storedValue) return [];
|
||||||
|
@ -136,5 +97,4 @@ if (prefillInstance != undefined) {
|
||||||
$instance.value = normalizeUrl(prefillInstance);
|
$instance.value = normalizeUrl(prefillInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
$instance.addEventListener("focus", loadInstances);
|
|
||||||
$form.addEventListener("submit", onFormSubmit);
|
$form.addEventListener("submit", onFormSubmit);
|
||||||
|
|
61
src/components/instance-select.astro
Normal file
61
src/components/instance-select.astro
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
let instances;
|
||||||
|
try {
|
||||||
|
const response = await fetch(new URL("/api/instances", Astro.url));
|
||||||
|
instances = await response.json();
|
||||||
|
} catch {
|
||||||
|
console.error("Couln't fetch instances");
|
||||||
|
instances = [];
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
<datalist id="instanceDatalist">
|
||||||
|
{instances.map((instance) => <option value="{instance}" />)}
|
||||||
|
</datalist>
|
||||||
|
<label>
|
||||||
|
Mastodon, Pleroma, or GNU Social instance
|
||||||
|
<div class="instance-input">
|
||||||
|
<span id="https-label">https://</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="instance"
|
||||||
|
id="instance"
|
||||||
|
placeholder="mastodon.social"
|
||||||
|
list="instanceDatalist"
|
||||||
|
required
|
||||||
|
aria-describedby="https-label"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="remember">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="remember"
|
||||||
|
name="remember"
|
||||||
|
/>
|
||||||
|
Remember my instance on this device
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.instance-input {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: stretch;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 1%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
16
src/pages/api/instances.ts
Normal file
16
src/pages/api/instances.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { APIRoute } from "astro";
|
||||||
|
|
||||||
|
export const get: APIRoute = async () => {
|
||||||
|
const response = await fetch("https://api.joinmastodon.org/servers");
|
||||||
|
const instances = await response.json();
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify(instances.map((instance) => instance.domain)),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Cache-Control": "s-maxage=86400, max-age=86400, public",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
|
@ -4,10 +4,10 @@ export const post: APIRoute = async ({ redirect, request }) => {
|
||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
|
|
||||||
const text = (formData.get("text") as string) || "";
|
const text = (formData.get("text") as string) || "";
|
||||||
const instanceURL =
|
const instanceDomain =
|
||||||
(formData.get("instance") as string) || "https://mastodon.social";
|
(formData.get("instance") as string) || "mastodon.social";
|
||||||
|
|
||||||
const publishUrl = new URL("/share", instanceURL);
|
const publishUrl = new URL("/share", `https://${instanceDomain}/`);
|
||||||
publishUrl.search = new URLSearchParams({
|
publishUrl.search = new URLSearchParams({
|
||||||
text,
|
text,
|
||||||
}).toString();
|
}).toString();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
---
|
---
|
||||||
|
import InstanceSelect from "../components/instance-select.astro";
|
||||||
import "../styles/main.scss";
|
import "../styles/main.scss";
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -22,9 +23,7 @@ import "../styles/main.scss";
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
-->
|
--><!DOCTYPE html>
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
@ -37,7 +36,7 @@ import "../styles/main.scss";
|
||||||
</title>
|
</title>
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="Share₂Fedi is a share page for Mastodon, Pleroma, and GNU Social. Type in your post text and the instance URL and click ‘Publish!’"
|
content="Share₂Fedi is a share page for Mastodon, Pleroma, and GNU Social. Type in your post text and the instance URL and click ‘Publish!’"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="canonical"
|
rel="canonical"
|
||||||
|
@ -107,27 +106,7 @@ import "../styles/main.scss";
|
||||||
></textarea>
|
></textarea>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<datalist id="instanceDatalist"></datalist>
|
<InstanceSelect />
|
||||||
<label>
|
|
||||||
Choose your Mastodon, Pleroma, or GNU Social instance
|
|
||||||
<input
|
|
||||||
type="url"
|
|
||||||
name="instance"
|
|
||||||
id="instance"
|
|
||||||
placeholder="https://"
|
|
||||||
list="instanceDatalist"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="remember">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="remember"
|
|
||||||
name="remember"
|
|
||||||
/>
|
|
||||||
Remember my instance on this device
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
--s2f-accent-color-light: #5d8379;
|
--s2f-accent-color-light: #5d8379;
|
||||||
--s2f-accent-color-contrast: #005e4e;
|
--s2f-accent-color-contrast: #005e4e;
|
||||||
--s2f-border-color: #ccc;
|
--s2f-border-color: #ccc;
|
||||||
|
--s2f-input-group-bg-color: #eee;
|
||||||
--s2f-input-bg-color: #fff;
|
--s2f-input-bg-color: #fff;
|
||||||
--s2f-input-text-color: #000;
|
--s2f-input-text-color: #000;
|
||||||
--s2f-button-text-color: #fff;
|
--s2f-button-text-color: #fff;
|
||||||
|
@ -103,7 +104,7 @@ textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="url"],
|
input[type="text"],
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--s2f-input-text-color);
|
color: var(--s2f-input-text-color);
|
||||||
|
@ -137,6 +138,7 @@ input[type="submit"] {
|
||||||
--s2f-accent-color-light: #619587;
|
--s2f-accent-color-light: #619587;
|
||||||
--s2f-accent-color-contrast: #a8f7e2;
|
--s2f-accent-color-contrast: #a8f7e2;
|
||||||
--s2f-border-color: #333;
|
--s2f-border-color: #333;
|
||||||
|
--s2f-input-group-bg-color: #111;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue