paytolaugh/assets/app.js

97 lines
3.6 KiB
JavaScript

// PayToLaugh front end.
//
// NOTE ON THE TOKEN BELOW: it is public by construction. A static page cannot
// keep a secret, so the endpoint behind it is built to be safe in public
// hands. It reads and writes small records only and cannot spend money.
// Script and video generation are deliberately NOT reachable from here.
//
// NOTE ON "LOGIN": an email address identifies you, it does not authenticate
// you. Anyone who knows an address can see that account. Temporary, by
// agreement, until real auth lands.
var API = "https://api.corben.world/functions/da0474ea-4e56-405c-9f0d-35c3e4037a69/invoke"
+ "?token=97cd624cd2bd6a74eb692a940e71851f";
async function api(action, payload) {
var body = Object.assign({ action: action }, payload || {});
var res;
try {
res = await fetch(API, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
} catch (e) {
return { ok: false, error: "Could not reach the server. Check your connection and try again." };
}
var outer;
try { outer = await res.json(); }
catch (e) { return { ok: false, error: "The server sent something unreadable." }; }
// The invoke route wraps the function result.
return outer && outer.result ? outer.result : outer;
}
var Session = {
key: "ptl_email",
nameKey: "ptl_name",
get: function () { try { return localStorage.getItem(this.key); } catch (e) { return null; } },
name: function () { try { return localStorage.getItem(this.nameKey) || ""; } catch (e) { return ""; } },
set: function (email, name) {
try {
localStorage.setItem(this.key, email);
if (name) localStorage.setItem(this.nameKey, name);
} catch (e) {}
},
clear: function () {
try { localStorage.removeItem(this.key); localStorage.removeItem(this.nameKey); } catch (e) {}
}
};
function esc(s) {
return String(s == null ? "" : s).replace(/[&<>\"']/g, function (c) {
return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '\"': "&quot;", "'": "&#39;" }[c];
});
}
function navBar(current) {
var email = Session.get();
var links = [["/", "Home", "home"], ["/create.html", "New video", "create"], ["/videos.html", "My videos", "videos"]];
var html = '<nav><div class="inner">'
+ '<a class="brand" href="/">Pay<span>To</span>Laugh</a>'
+ links.map(function (l) {
return '<a class="link' + (l[2] === current ? ' on' : '') + '" href="' + l[0] + '">' + l[1] + '</a>';
}).join("")
+ '<div class="who">'
+ (email
? esc(email) + ' <button type="button" id="ptl-signout">Sign out</button>'
: '<a class="link" href="/register.html">Sign in</a>')
+ '</div></div></nav>';
document.body.insertAdjacentHTML("afterbegin", html);
var b = document.getElementById("ptl-signout");
if (b) b.onclick = function () { Session.clear(); location.href = "/register.html"; };
}
// Pages that need an identity send you to sign in rather than failing oddly.
function requireSession() {
var e = Session.get();
if (!e) { location.href = "/register.html?next=" + encodeURIComponent(location.pathname); return null; }
return e;
}
function showMsg(id, text, kind) {
var el = document.getElementById(id);
if (!el) return;
el.className = "msg " + (kind || "err");
el.textContent = text;
}
function hideMsg(id) {
var el = document.getElementById(id);
if (el) el.className = "msg";
}
// Mirrors the server's arithmetic exactly: $0.40 per 8s clip plus $0.17 for
// script and plate, at a 5x markup. If one changes, change both.
function priceFor(segments) {
var cost = segments * 0.40 + 0.17;
return { cost: cost, price: Math.round(cost * 5 * 100) / 100, seconds: segments * 8 };
}