Replace /api/share with / + errors
This commit is contained in:
parent
9828b123d9
commit
725d7316c4
6 changed files with 97 additions and 42 deletions
|
@ -32,7 +32,8 @@ now takes seconds! This comes with changes, though:
|
||||||
|
|
||||||
Some changes came with the name change:
|
Some changes came with the name change:
|
||||||
|
|
||||||
- **changed API endpoint path**: ~~`/api/toot`~~ → `/api/share`
|
- **changed API endpoint path**: ~~`/api/toot`~~ → `/`
|
||||||
|
- just send a POST request instead of a GET request with the same body
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* SPDX-FileCopyrightText: © 2023 Nikita Karamov <me@kytta.dev>
|
* SPDX-FileCopyrightText: © 2023 Nikita Karamov <me@kytta.dev>
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
const { prefilledInstance } = Astro.props;
|
const { instance, errors } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<datalist id="popular-instances"></datalist>
|
<datalist id="popular-instances"></datalist>
|
||||||
|
@ -20,10 +20,23 @@ const { prefilledInstance } = Astro.props;
|
||||||
id="instance"
|
id="instance"
|
||||||
list="popular-instances"
|
list="popular-instances"
|
||||||
required
|
required
|
||||||
|
aria-invalid={Boolean(errors)}
|
||||||
|
aria-errormessage={errors ? "instance-error" : undefined}
|
||||||
aria-describedby="https-label"
|
aria-describedby="https-label"
|
||||||
value={prefilledInstance}
|
value={instance}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{
|
||||||
|
errors && (
|
||||||
|
<p
|
||||||
|
class="error"
|
||||||
|
id="instance-error"
|
||||||
|
aria-live="assertive"
|
||||||
|
>
|
||||||
|
{errors}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -21,11 +21,11 @@ export const get: APIRoute = async ({ params }) => {
|
||||||
|
|
||||||
const softwareName = await getSoftwareName(domain);
|
const softwareName = await getSoftwareName(domain);
|
||||||
if (softwareName === undefined) {
|
if (softwareName === undefined) {
|
||||||
return error("Could not detect software; NodeInfo not present.");
|
return error("Could not detect Fediverse project.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(softwareName in supportedProjects)) {
|
if (!(softwareName in supportedProjects)) {
|
||||||
return error(`"${softwareName}" is not supported yet.`);
|
return error(`Fediverse project "${softwareName}" is not supported yet.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishConfig = supportedProjects[softwareName] as ProjectPublishConfig;
|
const publishConfig = supportedProjects[softwareName] as ProjectPublishConfig;
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
/*!
|
|
||||||
* This file is part of Share₂Fedi
|
|
||||||
* https://github.com/kytta/share2fedi
|
|
||||||
*
|
|
||||||
* SPDX-FileCopyrightText: © 2023 Nikita Karamov <me@kytta.dev>
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { getUrlDomain } from "@lib/url";
|
|
||||||
import type { APIRoute } from "astro";
|
|
||||||
import type { Detection } from "./detect/[domain]";
|
|
||||||
import { error } from "@lib/response";
|
|
||||||
|
|
||||||
export const post: APIRoute = async ({ redirect, request, url }) => {
|
|
||||||
const formData = await request.formData();
|
|
||||||
|
|
||||||
const text = (formData.get("text") as string) || "";
|
|
||||||
const instanceHost =
|
|
||||||
getUrlDomain(formData.get("instance") as string) || "mastodon.social";
|
|
||||||
|
|
||||||
const response = await fetch(new URL(`/api/detect/${instanceHost}`, url));
|
|
||||||
const json = await response.json();
|
|
||||||
if (json.error) {
|
|
||||||
return error(json.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { domain, endpoint, params } = json as Detection;
|
|
||||||
const publishUrl = new URL(endpoint, `https://${domain}/`);
|
|
||||||
publishUrl.search = new URLSearchParams([[params.text, text]]).toString();
|
|
||||||
return redirect(publishUrl.toString(), 303);
|
|
||||||
};
|
|
|
@ -9,15 +9,56 @@
|
||||||
import Layout from "@layouts/layout.astro";
|
import Layout from "@layouts/layout.astro";
|
||||||
import InstanceSelect from "@components/instance-select.astro";
|
import InstanceSelect from "@components/instance-select.astro";
|
||||||
|
|
||||||
|
import { getUrlDomain } from "@lib/url";
|
||||||
|
import type { Detection } from "./api/detect/[domain]";
|
||||||
|
|
||||||
const searchParameters = new URL(Astro.request.url).searchParams;
|
const searchParameters = new URL(Astro.request.url).searchParams;
|
||||||
const prefilledText = searchParameters.get("text");
|
let text: string | null = searchParameters.get("text");
|
||||||
const prefilledInstance = searchParameters.get("instance");
|
let instance: string | null = searchParameters.get("instance");
|
||||||
|
|
||||||
|
const errors = { text: "", instance: "" };
|
||||||
|
if (Astro.request.method === "POST") {
|
||||||
|
const formData = await Astro.request.formData();
|
||||||
|
text = formData.get("text") as string | null;
|
||||||
|
instance = formData.get("instance") as string | null;
|
||||||
|
|
||||||
|
if (typeof text !== "string" || text.length === 0) {
|
||||||
|
errors.text += "Please enter post text. ";
|
||||||
|
}
|
||||||
|
|
||||||
|
let detection: Detection | undefined;
|
||||||
|
if (typeof instance !== "string" || instance.length === 0) {
|
||||||
|
errors.instance += "Please enter instance domain. ";
|
||||||
|
} else {
|
||||||
|
instance = getUrlDomain(instance);
|
||||||
|
|
||||||
|
const detectResponse = await fetch(
|
||||||
|
new URL(`/api/detect/${instance}`, Astro.url),
|
||||||
|
);
|
||||||
|
const detectJson = await detectResponse.json();
|
||||||
|
if (detectJson.error) {
|
||||||
|
errors.instance += detectJson.error + " ";
|
||||||
|
} else {
|
||||||
|
detection = detectJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasErrors = Object.values(errors).some(Boolean);
|
||||||
|
if (!hasErrors && detection !== undefined) {
|
||||||
|
const { domain, endpoint, params } = detection;
|
||||||
|
const publishUrl = new URL(endpoint, `https://${domain}/`);
|
||||||
|
publishUrl.search = new URLSearchParams([
|
||||||
|
[params.text, text as string],
|
||||||
|
]).toString();
|
||||||
|
// eslint-disable-next-line unicorn/prefer-module
|
||||||
|
return Astro.redirect(publishUrl.toString(), 303);
|
||||||
|
}
|
||||||
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Share₂Fedi — an instance-agnostic share page for the Fediverse">
|
<Layout title="Share₂Fedi — an instance-agnostic share page for the Fediverse">
|
||||||
<form
|
<form
|
||||||
id="form"
|
id="form"
|
||||||
action="/api/share"
|
|
||||||
method="POST"
|
method="POST"
|
||||||
>
|
>
|
||||||
<label data-translate="postText">
|
<label data-translate="postText">
|
||||||
|
@ -28,13 +69,29 @@ const prefilledInstance = searchParameters.get("instance");
|
||||||
rows="7"
|
rows="7"
|
||||||
placeholder="What’s on your mind?"
|
placeholder="What’s on your mind?"
|
||||||
required
|
required
|
||||||
|
aria-invalid={Boolean(errors.text)}
|
||||||
|
aria-errormessage={errors.text ? "text-error" : undefined}
|
||||||
data-translate="postTextPlaceholder"
|
data-translate="postTextPlaceholder"
|
||||||
data-translate-attribute="placeholder"
|
data-translate-attribute="placeholder"
|
||||||
>{prefilledText}</textarea
|
>{text}</textarea
|
||||||
>
|
>
|
||||||
|
{
|
||||||
|
errors.text && (
|
||||||
|
<p
|
||||||
|
class="error"
|
||||||
|
id="text-error"
|
||||||
|
aria-live="assertive"
|
||||||
|
>
|
||||||
|
{errors.text}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<InstanceSelect {prefilledInstance} />
|
<InstanceSelect
|
||||||
|
{instance}
|
||||||
|
errors={errors.instance}
|
||||||
|
/>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
--s2f-accent-color: #40665c;
|
--s2f-accent-color: #40665c;
|
||||||
--s2f-accent-color-light: #5d8379;
|
--s2f-accent-color-light: #5d8379;
|
||||||
--s2f-accent-color-contrast: #005e4e;
|
--s2f-accent-color-contrast: #005e4e;
|
||||||
|
--s2f-error-color: #a12f2d;
|
||||||
--s2f-border-color: #ccc;
|
--s2f-border-color: #ccc;
|
||||||
|
--s2f-border-width: 1px;
|
||||||
--s2f-input-group-bg-color: #eee;
|
--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;
|
||||||
|
@ -116,7 +118,7 @@ textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--s2f-input-text-color);
|
color: var(--s2f-input-text-color);
|
||||||
background-color: var(--s2f-input-bg-color);
|
background-color: var(--s2f-input-bg-color);
|
||||||
border: 1px solid var(--s2f-border-color);
|
border: var(--s2f-border-width) solid var(--s2f-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"],
|
input[type="checkbox"],
|
||||||
|
@ -163,3 +165,16 @@ input[type="submit"] {
|
||||||
width: math.div(100%, 3);
|
width: math.div(100%, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[aria-invalid="true"],
|
||||||
|
[aria-errormessage] {
|
||||||
|
--s2f-input-text-color: var(--s2f-error-color);
|
||||||
|
--s2f-border-color: var(--s2f-error-color);
|
||||||
|
|
||||||
|
accent-color: var(--s2f-error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.error {
|
||||||
|
color: var(--s2f-error-color);
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
Reference in a new issue