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:
|
||||
|
||||
- **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
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* SPDX-FileCopyrightText: © 2023 Nikita Karamov <me@kytta.dev>
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
const { prefilledInstance } = Astro.props;
|
||||
const { instance, errors } = Astro.props;
|
||||
---
|
||||
|
||||
<datalist id="popular-instances"></datalist>
|
||||
|
@ -20,10 +20,23 @@ const { prefilledInstance } = Astro.props;
|
|||
id="instance"
|
||||
list="popular-instances"
|
||||
required
|
||||
aria-invalid={Boolean(errors)}
|
||||
aria-errormessage={errors ? "instance-error" : undefined}
|
||||
aria-describedby="https-label"
|
||||
value={prefilledInstance}
|
||||
value={instance}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
errors && (
|
||||
<p
|
||||
class="error"
|
||||
id="instance-error"
|
||||
aria-live="assertive"
|
||||
>
|
||||
{errors}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</label>
|
||||
|
||||
<div
|
||||
|
|
|
@ -21,11 +21,11 @@ export const get: APIRoute = async ({ params }) => {
|
|||
|
||||
const softwareName = await getSoftwareName(domain);
|
||||
if (softwareName === undefined) {
|
||||
return error("Could not detect software; NodeInfo not present.");
|
||||
return error("Could not detect Fediverse project.");
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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 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 prefilledText = searchParameters.get("text");
|
||||
const prefilledInstance = searchParameters.get("instance");
|
||||
let text: string | null = searchParameters.get("text");
|
||||
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">
|
||||
<form
|
||||
id="form"
|
||||
action="/api/share"
|
||||
method="POST"
|
||||
>
|
||||
<label data-translate="postText">
|
||||
|
@ -28,13 +69,29 @@ const prefilledInstance = searchParameters.get("instance");
|
|||
rows="7"
|
||||
placeholder="What’s on your mind?"
|
||||
required
|
||||
aria-invalid={Boolean(errors.text)}
|
||||
aria-errormessage={errors.text ? "text-error" : undefined}
|
||||
data-translate="postTextPlaceholder"
|
||||
data-translate-attribute="placeholder"
|
||||
>{prefilledText}</textarea
|
||||
>{text}</textarea
|
||||
>
|
||||
{
|
||||
errors.text && (
|
||||
<p
|
||||
class="error"
|
||||
id="text-error"
|
||||
aria-live="assertive"
|
||||
>
|
||||
{errors.text}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</label>
|
||||
|
||||
<InstanceSelect {prefilledInstance} />
|
||||
<InstanceSelect
|
||||
{instance}
|
||||
errors={errors.instance}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
--s2f-accent-color: #40665c;
|
||||
--s2f-accent-color-light: #5d8379;
|
||||
--s2f-accent-color-contrast: #005e4e;
|
||||
--s2f-error-color: #a12f2d;
|
||||
--s2f-border-color: #ccc;
|
||||
--s2f-border-width: 1px;
|
||||
--s2f-input-group-bg-color: #eee;
|
||||
--s2f-input-bg-color: #fff;
|
||||
--s2f-input-text-color: #000;
|
||||
|
@ -116,7 +118,7 @@ textarea {
|
|||
width: 100%;
|
||||
color: var(--s2f-input-text-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"],
|
||||
|
@ -163,3 +165,16 @@ input[type="submit"] {
|
|||
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