diff options
| author | Xe Iaso <me@christine.website> | 2022-11-24 18:55:21 -0500 |
|---|---|---|
| committer | Xe Iaso <me@christine.website> | 2022-11-24 18:55:21 -0500 |
| commit | e174b7ba4dc1f6e755d6142f0d26a3e6357be771 (patch) | |
| tree | 6d063747b3215100cc0e01038944f0b990d8a5fe | |
| parent | facf97b1f861ea66961a72e43595c5a1b1db8a31 (diff) | |
| download | xesite-e174b7ba4dc1f6e755d6142f0d26a3e6357be771.tar.xz xesite-e174b7ba4dc1f6e755d6142f0d26a3e6357be771.zip | |
make new mastodon share button
Signed-off-by: Xe Iaso <me@christine.website>
| -rw-r--r-- | flake.nix | 21 | ||||
| -rwxr-xr-x | src/frontend/build.sh | 6 | ||||
| -rw-r--r-- | src/frontend/deno.json | 8 | ||||
| -rw-r--r-- | src/frontend/import_map.json | 8 | ||||
| -rw-r--r-- | src/frontend/mastodon_share_button.tsx | 55 | ||||
| -rw-r--r-- | src/frontend/xeact/jsx-runtime.js | 16 | ||||
| -rw-r--r-- | src/frontend/xeact/xeact.js | 88 | ||||
| -rw-r--r-- | src/frontend/xeact/xeact.ts | 9 | ||||
| -rw-r--r-- | static/js/.gitignore | 1 | ||||
| -rw-r--r-- | templates/blogpost.rs.html | 61 |
10 files changed, 215 insertions, 58 deletions
@@ -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() |
