pleroma-like client for Bluesky pl.hexmani.ac
bluesky pleroma social-media

Create basic write-only text posts through post form

hexmani.ac 18468c0b 7eb5c43c

verified
Changed files
+66 -3
src
components
+7
bun.lock
···
"": {
"name": "vite-template-solid",
"dependencies": {
+
"@atcute/atproto": "^3.1.8",
"@atcute/bluesky": "^3.2.8",
+
"@atcute/bluesky-richtext-builder": "^2.0.4",
"@atcute/client": "^4.0.5",
"@atcute/lexicons": "^1.2.2",
"@atcute/oauth-browser-client": "^1.0.27",
+
"@atcute/tid": "^1.0.3",
"@solidjs/router": "^0.15.3",
"solid-js": "^1.9.5",
},
···
"@atcute/bluesky": ["@atcute/bluesky@3.2.8", "", { "dependencies": { "@atcute/atproto": "^3.1.8", "@atcute/lexicons": "^1.2.2" } }, "sha512-wxEnSOvX7nLH4sVzX9YFCkaNEWIDrTv3pTs6/x4NgJ3AJ3XJio0OYPM8tR7wAgsklY6BHvlAgt3yoCDK0cl1CA=="],
+
"@atcute/bluesky-richtext-builder": ["@atcute/bluesky-richtext-builder@2.0.4", "", { "dependencies": { "@atcute/bluesky": "^3.2.5", "@atcute/lexicons": "^1.2.2" } }, "sha512-ydA9VWBPsBE/gbu1vYbmh7AZ8FLfxp+LE4eH5GgOTCOxwhs7Mgy1oHrHY+Er6gu6PfdoUoGso0uI3Wl3ZF/Mxg=="],
+
"@atcute/client": ["@atcute/client@4.0.5", "", { "dependencies": { "@atcute/identity": "^1.1.1", "@atcute/lexicons": "^1.2.2" } }, "sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA=="],
"@atcute/identity": ["@atcute/identity@1.1.1", "", { "dependencies": { "@atcute/lexicons": "^1.2.2", "@badrap/valita": "^0.4.6" } }, "sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q=="],
···
"@atcute/multibase": ["@atcute/multibase@1.1.6", "", { "dependencies": { "@atcute/uint8array": "^1.0.5" } }, "sha512-HBxuCgYLKPPxETV0Rot4VP9e24vKl8JdzGCZOVsDaOXJgbRZoRIF67Lp0H/OgnJeH/Xpva8Z5ReoTNJE5dn3kg=="],
"@atcute/oauth-browser-client": ["@atcute/oauth-browser-client@1.0.27", "", { "dependencies": { "@atcute/client": "^4.0.4", "@atcute/identity": "^1.1.1", "@atcute/lexicons": "^1.2.2", "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5", "nanoid": "^5.1.5" } }, "sha512-Ng1tCOTMLgFHHoIHXTtCZR1/ND62an1qxPX2kBoUzkxxd7iCP7IBYYqOiKyJYT5n1R4zS+s29hFS4t9mxXa5kQ=="],
+
+
"@atcute/tid": ["@atcute/tid@1.0.3", "", {}, "sha512-wfMJx1IMdnu0CZgWl0uR4JO2s6PGT1YPhpytD4ZHzEYKKQVuqV6Eb/7vieaVo1eYNMp2FrY67FZObeR7utRl2w=="],
"@atcute/uint8array": ["@atcute/uint8array@1.0.5", "", {}, "sha512-XLWWxoR2HNl2qU+FCr0rp1APwJXci7HnzbOQLxK55OaMNBXZ19+xNC5ii4QCsThsDxa4JS/JTzuiQLziITWf2Q=="],
+3
package.json
···
"vite-plugin-solid": "^2.11.8"
},
"dependencies": {
+
"@atcute/atproto": "^3.1.8",
"@atcute/bluesky": "^3.2.8",
+
"@atcute/bluesky-richtext-builder": "^2.0.4",
"@atcute/client": "^4.0.5",
"@atcute/lexicons": "^1.2.2",
"@atcute/oauth-browser-client": "^1.0.27",
+
"@atcute/tid": "^1.0.3",
"@solidjs/router": "^0.15.3",
"solid-js": "^1.9.5"
}
+55 -2
src/components/postForm.tsx
···
-
import { Component } from "solid-js";
+
import { Component, createSignal } from "solid-js";
+
import { agent } from "./login";
+
import { Client } from "@atcute/client";
+
import * as TID from "@atcute/tid";
+
import RichtextBuilder from "@atcute/bluesky-richtext-builder";
const PostForm: Component = () => {
+
const [notice, setNotice] = createSignal("");
+
const [text, setText] = createSignal("");
+
+
async function handleSubmit() {
+
const rpc = new Client({ handler: agent });
+
const rawText = text();
+
+
document.querySelector(".submitInfo")?.removeAttribute("hidden");
+
+
try {
+
const res = await rpc.post("com.atproto.repo.createRecord", {
+
input: {
+
collection: "app.bsky.feed.post",
+
record: {
+
$type: "app.bsky.feed.post",
+
text: rawText,
+
langs: ["en"],
+
createdAt: new Date().toISOString(),
+
},
+
repo: agent.sub,
+
rkey: TID.now(),
+
},
+
});
+
+
if (!res.ok) {
+
throw new Error(`${res.data.error}/${res.data.message}`);
+
}
+
+
console.log(res);
+
setNotice("Post successful");
+
setTimeout(() => {
+
setNotice("");
+
document.querySelector(".submitInfo")?.setAttribute("hidden", "");
+
}, 3000);
+
} catch (e: unknown) {
+
if (e instanceof Error) {
+
setNotice(`Failed to post: ${e.message}`);
+
} else {
+
setNotice(`Failed to post`);
+
}
+
}
+
}
+
return (
<>
<form
···
rows="1"
cols="1"
placeholder="The car's on fire, and there's no driver at the wheel..."
+
onchange={(e) => setText(e.target.value)}
></textarea>
-
<button type="submit">Post</button>
+
<button type="submit" onclick={handleSubmit}>
+
Post
+
</button>
</form>
+
<p class="submitInfo" hidden>
+
{notice()}
+
</p>
</>
);
};
+1 -1
tsconfig.json
···
// Type Checking & Safety
"strict": true,
-
"types": ["vite/client", "@atcute/bluesky"]
+
"types": ["vite/client", "@atcute/bluesky", "@atcute/atproto"]
}
}