aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXe Iaso <me@christine.website>2022-11-24 18:55:21 -0500
committerXe Iaso <me@christine.website>2022-11-24 18:55:21 -0500
commite174b7ba4dc1f6e755d6142f0d26a3e6357be771 (patch)
tree6d063747b3215100cc0e01038944f0b990d8a5fe
parentfacf97b1f861ea66961a72e43595c5a1b1db8a31 (diff)
downloadxesite-e174b7ba4dc1f6e755d6142f0d26a3e6357be771.tar.xz
xesite-e174b7ba4dc1f6e755d6142f0d26a3e6357be771.zip
make new mastodon share button
Signed-off-by: Xe Iaso <me@christine.website>
-rw-r--r--flake.nix21
-rwxr-xr-xsrc/frontend/build.sh6
-rw-r--r--src/frontend/deno.json8
-rw-r--r--src/frontend/import_map.json8
-rw-r--r--src/frontend/mastodon_share_button.tsx55
-rw-r--r--src/frontend/xeact/jsx-runtime.js16
-rw-r--r--src/frontend/xeact/xeact.js88
-rw-r--r--src/frontend/xeact/xeact.ts9
-rw-r--r--static/js/.gitignore1
-rw-r--r--templates/blogpost.rs.html61
10 files changed, 215 insertions, 58 deletions
diff --git a/flake.nix b/flake.nix
index 83c10df..4dcda34 100644
--- a/flake.nix
+++ b/flake.nix
@@ -54,6 +54,25 @@
'';
};
+ frontend = pkgs.stdenv.mkDerivation {
+ pname = "xesite-frontend";
+ inherit (bin) version;
+ src = ./src/frontend;
+ buildInputs = with pkgs; [ deno nodePackages.uglify-js ];
+
+ phases = "installPhase";
+
+ installPhase = ''
+ mkdir -p $out/static/js
+ mkdir -p .deno
+ export HOME=./.deno
+
+ deno bundle --config $src/deno.json $src/mastodon_share_button.tsx ./mastodon_share_button.js
+
+ uglifyjs ./mastodon_share_button.js -c -m > $out/static/js/mastodon_share_button.js
+ '';
+ };
+
static = pkgs.stdenv.mkDerivation {
pname = "xesite-static";
inherit (bin) version;
@@ -86,7 +105,7 @@
default = pkgs.symlinkJoin {
name = "xesite-${bin.version}";
- paths = [ config posts static bin ];
+ paths = [ config posts static bin frontend ];
};
docker = pkgs.dockerTools.buildLayeredImage {
diff --git a/src/frontend/build.sh b/src/frontend/build.sh
new file mode 100755
index 0000000..ad31763
--- /dev/null
+++ b/src/frontend/build.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -e
+
+export RUST_LOG=info
+deno bundle ./mastodon_share_button.tsx ../../static/js/mastodon_share_button.js
diff --git a/src/frontend/deno.json b/src/frontend/deno.json
new file mode 100644
index 0000000..b763d25
--- /dev/null
+++ b/src/frontend/deno.json
@@ -0,0 +1,8 @@
+{
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "jsxImportSource": "xeact",
+ "lib": ["esnext", "dom", "dom.iterable"]
+ },
+ "importMap": "./import_map.json",
+}
diff --git a/src/frontend/import_map.json b/src/frontend/import_map.json
new file mode 100644
index 0000000..20cfc64
--- /dev/null
+++ b/src/frontend/import_map.json
@@ -0,0 +1,8 @@
+{
+ "imports": {
+ "xeact": "./xeact/xeact.ts",
+ "xeact/jsx-runtime": "./xeact/jsx-runtime.js",
+ "/": "./",
+ "./": "./"
+ }
+}
diff --git a/src/frontend/mastodon_share_button.tsx b/src/frontend/mastodon_share_button.tsx
new file mode 100644
index 0000000..64164a7
--- /dev/null
+++ b/src/frontend/mastodon_share_button.tsx
@@ -0,0 +1,55 @@
+import { g, r, u, x } from "xeact";
+
+r(() => {
+ const root = g("mastodon_share_button");
+
+ let defaultURL = localStorage["mastodon_instance"];
+
+ const title = document.querySelectorAll('meta[property="og:title"]')[0]
+ .getAttribute("content");
+ let series = g("mastodon_share_series").innerText;
+ if (series != "") {
+ series = `#${series} `;
+ }
+ const tags = g("mastodon_share_tags");
+ const articleURL = u();
+
+ const tootTemplate = `${title}
+
+${articleURL}
+
+${series}${tags.innerText}@cadey@pony.social`;
+
+ const instanceBox = (
+ <input type="text" placeholder="https://pony.social" value={defaultURL} />
+ );
+ const tootBox = <textarea rows="6" cols="40">{tootTemplate}</textarea>;
+
+ const doShare = () => {
+ const instanceURL = instanceBox.value;
+ localStorage["mastodon_instance"] = instanceURL;
+ const text = tootBox.value;
+ const mastodon_url = u(instanceURL + "/share", { text });
+ console.log({ text, mastodon_url });
+ window.open(mastodon_url, "_blank");
+ };
+
+ const shareButton = <button onclick={doShare}>Share</button>;
+
+ x(root);
+
+ root.appendChild(
+ <div>
+ <details>
+ <summary>Share on Mastodon</summary>
+ <span>Instance URL (https://mastodon.example)</span>
+ <br />
+ {instanceBox}
+ <br />
+ {tootBox}
+ <br />
+ {shareButton}
+ </details>
+ </div>,
+ );
+});
diff --git a/src/frontend/xeact/jsx-runtime.js b/src/frontend/xeact/jsx-runtime.js
new file mode 100644
index 0000000..58ccfaa
--- /dev/null
+++ b/src/frontend/xeact/jsx-runtime.js
@@ -0,0 +1,16 @@
+import { h } from './xeact.ts';
+
+/**
+ * Create a DOM element, assign the properties of `data` to it, and append all `data.children`.
+ *
+ * @type{function(string, Object=): HTMLElement}
+ */
+export const jsx = (tag, data) => {
+ let children = data.children;
+ delete data.children;
+ const result = h(tag, data, children);
+ result.classList.value = result.class;
+ return result;
+};
+export const jsxs = jsx;
+export const jsxDEV = jsx;
diff --git a/src/frontend/xeact/xeact.js b/src/frontend/xeact/xeact.js
new file mode 100644
index 0000000..7be9a1c
--- /dev/null
+++ b/src/frontend/xeact/xeact.js
@@ -0,0 +1,88 @@
+/**
+ * Creates a DOM element, assigns the properties of `data` to it, and appends all `children`.
+ *
+ * @type{function(string|Function, Object=, Node|Array.<Node|string>=)}
+ */
+const h = (name, data = {}, children = []) => {
+ const result = typeof name == "function" ? name(data) : Object.assign(document.createElement(name), data);
+ if (!Array.isArray(children)) {
+ children = [children];
+ }
+ result.append(...children);
+ return result;
+};
+
+/**
+ * Create a text node.
+ *
+ * Equivalent to `document.createTextNode(text)`
+ *
+ * @type{function(string): Text}
+ */
+const t = (text) => document.createTextNode(text);
+
+/**
+ * Remove all child nodes from a DOM element.
+ *
+ * @type{function(Node)}
+ */
+const x = (elem) => {
+ while (elem.lastChild) {
+ elem.removeChild(elem.lastChild);
+ }
+};
+
+/**
+ * Get all elements with the given ID.
+ *
+ * Equivalent to `document.getElementById(name)`
+ *
+ * @type{function(string): HTMLElement}
+ */
+const g = (name) => document.getElementById(name);
+
+/**
+ * Get all elements with the given class name.
+ *
+ * Equivalent to `document.getElementsByClassName(name)`
+ *
+ * @type{function(string): HTMLCollectionOf.<Element>}
+ */
+const c = (name) => document.getElementsByClassName(name);
+
+/** @type{function(string): HTMLCollectionOf.<Element>} */
+const n = (name) => document.getElementsByName(name);
+
+/**
+ * Get all elements matching the given HTML selector.
+ *
+ * Matches selectors with `document.querySelectorAll(selector)`
+ *
+ * @type{function(string): Array.<HTMLElement>}
+ */
+const s = (selector) => Array.from(document.querySelectorAll(selector));
+
+/**
+ * Generate a relative URL from `url`, appending all key-value pairs from `params` as URL-encoded parameters.
+ *
+ * @type{function(string=, Object=): string}
+ */
+const u = (url = "", params = {}) => {
+ let result = new URL(url, window.location.href);
+ Object.entries(params).forEach((kv) => {
+ let [k, v] = kv;
+ result.searchParams.set(k, v);
+ });
+ return result.toString();
+};
+
+/**
+ * Takes a callback to run when all DOM content is loaded.
+ *
+ * Equivalent to `window.addEventListener('DOMContentLoaded', callback)`
+ *
+ * @type{function(function())}
+ */
+const r = (callback) => window.addEventListener('DOMContentLoaded', callback);
+
+export { h, t, x, g, c, n, u, s, r };
diff --git a/src/frontend/xeact/xeact.ts b/src/frontend/xeact/xeact.ts
new file mode 100644
index 0000000..8974ec1
--- /dev/null
+++ b/src/frontend/xeact/xeact.ts
@@ -0,0 +1,9 @@
+export * from "./xeact.js";
+
+declare global {
+ export namespace JSX {
+ interface IntrinsicElements {
+ [elemName: string]: any;
+ }
+ }
+}
diff --git a/static/js/.gitignore b/static/js/.gitignore
new file mode 100644
index 0000000..4bf7e61
--- /dev/null
+++ b/static/js/.gitignore
@@ -0,0 +1 @@
+mastodon_share_button.js
diff --git a/templates/blogpost.rs.html b/templates/blogpost.rs.html
index b0e1965..985fca0 100644
--- a/templates/blogpost.rs.html
+++ b/templates/blogpost.rs.html
@@ -75,7 +75,10 @@
}
<!-- The button that should be clicked. -->
-<button onclick="share_on_mastodon()">Share on Mastodon</button>
+<div id="mastodon_share_button"></div>
+<div id="mastodon_share_series" style="display:none">@post.front_matter.series.as_ref().unwrap_or(&"".to_string())</div>
+<div id="mastodon_share_tags" style="display:none">@for tag in post.front_matter.tags.as_ref().unwrap_or(&Vec::new()) {#@tag }</div>
+<script src="/static/js/mastodon_share_button.js"></script>
<p>This article was posted on @post.detri(). Facts and circumstances may have changed since publication. Please <a href="/contact">contact me</a> before jumping to conclusions if something seems wrong or unclear.</p>
@@ -103,60 +106,4 @@
<p>The art for Cadey was drawn by <a href="https://artzorastudios.weebly.com/">ArtZora Studios</a>.</p>
-<script>
- // The actual function. Set this as an onclick function for your "Share on Mastodon" button
- function share_on_mastodon() @{
- // Prefill the form with the user's previously-specified Mastodon instance, if applicable
- var default_url = localStorage['mastodon_instance'];
-
- // If there is no cached instance/domain, then insert a "https://" with no domain at the start of the prompt.
- if (!default_url)
- default_url = "https://";
-
- var instance = prompt("Enter your instance's address: (ex: https://linuxrocks.online)", default_url);
- if (instance) @{
- // Handle URL formats
- if ( !instance.startsWith("https://") && !instance.startsWith("http://") )
- instance = "https://" + instance;
-
- // get the current page's url
- var url = window.location.href;
-
- // get the page title from the og:title meta tag, if it exists.
- var title = document.querySelectorAll('meta[property="og:title"]')[0].getAttribute("content");
-
- // Otherwise, use the <title> tag as the title
- if (!title) var title = document.getElementsByTagName("title")[0].innerHTML;
-
- // Handle slash
- if ( !instance.endsWith("/") )
- instance = instance + "/";
-
- // Cache the instance/domain for future requests
- localStorage['mastodon_instance'] = instance;
-
- // Hashtags
- var hashtags = "#blogpost";
-
- @if post.front_matter.series.is_some() {
- hashtags += "#@post.front_matter.series.as_ref().unwrap()";
- }
-
- @if post.front_matter.tags.is_some() {
- hashtags += "@for tag in post.front_matter.tags.as_ref().unwrap() { #@tag }";
- }
-
- // Tagging users, such as offical accounts or the author of the post
- var author = "@@cadey@@pony.social";
-
- // Create the Share URL
- // https://someinstance.tld/share?text=URL%20encoded%20text
- mastodon_url = instance + "share?text=" + encodeURIComponent(title + "\n\n" + url + "\n\n" + hashtags + " " + author);
-
- // Open a new window at the share location
- window.open(mastodon_url, '_blank');
- @}
- @}
-</script>
-
@:footer_html()