this repo has no description

scuffed new /astral/user page

vielle.dev e5a46a95 db81e780

verified
+138 -137
+19 -12
src/components/astral/Points.astro
··· 1 1 --- 2 - 2 + interface Props { 3 + immutable?: true; 4 + } 3 5 --- 4 6 5 7 <script> ··· 48 50 console.warn(e), 49 51 ); 50 52 51 - this.dec.disabled = !( 52 - Powers.get().points - 1 >= 0 && 53 - Powers.get().points - 1 >= Powers.get().powers.length 54 - ); 53 + this.dec.disabled = 54 + !(Powers.get().points - 1 >= 0) || 55 + !(Powers.get().points - 1 >= Powers.get().powers.length) || 56 + this.getAttribute("disabled") !== null; 55 57 }); 56 58 57 59 this.inc.addEventListener("click", async () => { ··· 61 63 console.warn(e), 62 64 ); 63 65 64 - this.inc.disabled = false; 66 + this.inc.disabled = this.getAttribute("disabled") !== null; 65 67 }); 66 68 } 67 69 ··· 72 74 this.count.innerText = val.powers.length + ""; 73 75 this.max.innerText = val.points + ""; 74 76 75 - this.dec.disabled = !( 76 - val.points - 1 >= 0 && val.points - 1 >= val.powers.length 77 - ); 77 + this.dec.disabled = 78 + !(val.points - 1 >= 0) || 79 + !(val.points - 1 >= val.powers.length) || 80 + this.getAttribute("disabled") !== null; 78 81 } 79 82 } 80 83 81 84 customElements.define("points-meter", HTMLPointsMeterElement); 82 85 </script> 83 86 84 - <points-meter> 85 - <button aria-label="decrement" data-dec>-</button> 87 + <points-meter disabled={Astro.props.immutable}> 88 + <button aria-label="decrement" data-dec disabled={Astro.props.immutable} 89 + >-</button 90 + > 86 91 <meter></meter> 87 92 <span class="total" 88 93 ><span class="count"></span>/<span class="max"></span></span 89 94 > 90 - <button aria-label="increment" data-inc>+</button> 95 + <button aria-label="increment" data-inc disabled={Astro.props.immutable} 96 + >+</button 97 + > 91 98 </points-meter> 92 99 93 100 <style>
+6 -4
src/components/astral/Powers.astro
··· 11 11 children: (typeof powerTree)[]; 12 12 i: number; 13 13 siblings: number; 14 + immutable?: true; 14 15 } 15 16 16 - const { id: power, children } = Astro.props; 17 + const { id: power, children, immutable } = Astro.props; 17 18 18 19 const getDeepChildren = (root: typeof powerTree): number => 19 20 root.children.length === 0 ··· 69 70 // power has been selected by the user 70 71 else if (value.powers.includes(this.power)) { 71 72 this.#internals.states.add("selected"); 72 - button && (button.disabled = false); 73 + button && (button.disabled = this.getAttribute("disabled") !== null); 73 74 74 75 // if any selected powers rely on it OR if its force select 75 76 if ( ··· 92 93 // the user has not selected this, has selected its parent, and has enough points 93 94 else if (value.powers.includes(parentPowers[this.power].at(-2) ?? "")) { 94 95 this.#internals.states.add("avaliable"); 95 - button && (button.disabled = false); 96 + button && (button.disabled = this.getAttribute("disabled") !== null); 96 97 } 97 98 98 99 // the user has not selected the parent, but otherwise could unlock this ··· 162 163 "--sibling-count": Astro.props.siblings, 163 164 "--layer": getLayer(children), 164 165 }} 166 + disabled={immutable} 165 167 > 166 168 <template shadowrootmode="open"> 167 169 <button commandfor="info" command="show-modal"> ··· 418 420 419 421 { 420 422 children.map((child, i) => ( 421 - <Astro.self {...child} {i} siblings={children.length} /> 423 + <Astro.self {...child} {i} siblings={children.length} {immutable} /> 422 424 )) 423 425 } 424 426 </power->
+70
src/lib/powers.ts
··· 4 4 import { DevVielleDndAstral } from "../lexicons"; 5 5 import { map } from "nanostores"; 6 6 import { powerSchema } from "../lexicons/types/dev/vielle/dnd/astral"; 7 + import type { ActorIdentifier } from "@atcute/lexicons"; 8 + import { Client, simpleFetchHandler } from "@atcute/client"; 9 + import { 10 + CompositeDidDocumentResolver, 11 + CompositeHandleResolver, 12 + DohJsonHandleResolver, 13 + LocalActorResolver, 14 + PlcDidDocumentResolver, 15 + WebDidDocumentResolver, 16 + WellKnownHandleResolver, 17 + } from "@atcute/identity-resolver"; 7 18 8 19 const [client, _, did] = await getAuth(true); 9 20 ··· 157 168 ...powers.get(), 158 169 [key]: value, 159 170 }; 171 + }, 172 + 173 + async loadUser(user: ActorIdentifier) { 174 + const res = await new Client({ 175 + handler: simpleFetchHandler({ 176 + service: await new LocalActorResolver({ 177 + handleResolver: new CompositeHandleResolver({ 178 + methods: { 179 + http: new WellKnownHandleResolver(), 180 + dns: new DohJsonHandleResolver({ 181 + dohUrl: "https://mozilla.cloudflare-dns.com/dns-query", 182 + }), 183 + }, 184 + strategy: "race", 185 + }), 186 + didDocumentResolver: new CompositeDidDocumentResolver({ 187 + methods: { 188 + web: new WebDidDocumentResolver(), 189 + plc: new PlcDidDocumentResolver(), 190 + }, 191 + }), 192 + }) 193 + .resolve(user) 194 + .then((doc) => doc.pds), 195 + }), 196 + }) 197 + .get("com.atproto.repo.getRecord", { 198 + params: { 199 + repo: user, 200 + collection: "dev.vielle.dnd.astral", 201 + rkey: "self", 202 + }, 203 + }) 204 + .then((res) => 205 + res.ok 206 + ? res.data.value 207 + : (() => { 208 + throw res.data; 209 + })(), 210 + ) 211 + .then( 212 + async (res) => 213 + await DevVielleDndAstral.mainSchema["~standard"].validate(res), 214 + ) 215 + .then((res) => 216 + !res.issues 217 + ? res.value 218 + : (() => { 219 + throw res.issues; 220 + })(), 221 + ) 222 + .catch((err) => { 223 + console.error(err); 224 + return null; 225 + }); 226 + 227 + if (!res) throw "Failed to load user"; 228 + 229 + powers.set({ points: res.points, powers: res.powers }); 160 230 }, 161 231 };
+43 -121
src/pages/astral/[user].astro
··· 3 3 import { getPdsEndpoint, isAtprotoDid } from "@atcute/identity"; 4 4 import { isHandle } from "@atcute/lexicons/syntax"; 5 5 import Base from "../../Base.astro"; 6 - import Power from "../../components/astral/Power.astro"; 6 + import Powers from "../../components/astral/Powers.astro"; 7 7 import { powerData, powerTree } from "../../consts/astral"; 8 + import Points from "../../components/astral/Points.astro"; 8 9 9 10 // slug user to pds 10 11 const { user } = Astro.params; ··· 29 30 --- 30 31 31 32 <Base title={actor}> 32 - <script set:html={`window.actor = "${actor}";`} /> 33 - <script> 34 - import { Client, simpleFetchHandler } from "@atcute/client"; 35 - import { 36 - normalPowers, 37 - parentPowers, 38 - powerData, 39 - powers, 40 - type powerId, 41 - } from "../../consts/astral"; 42 - import { DevVielleDndAstral } from "../../lexicons"; 43 - import { 44 - CompositeDidDocumentResolver, 45 - CompositeHandleResolver, 46 - DohJsonHandleResolver, 47 - LocalActorResolver, 48 - PlcDidDocumentResolver, 49 - WebDidDocumentResolver, 50 - WellKnownHandleResolver, 51 - } from "@atcute/identity-resolver"; 52 - 53 - const actor = 54 - "actor" in window 55 - ? (window.actor as 56 - | undefined 57 - | `did:plc:${string}` 58 - | `did:web:${string}` 59 - | `${string}.${string}`) 60 - : undefined; 61 - if (!actor) throw "loaded without actor in window. missing `window.actor`"; 62 - 63 - const actorResolver = new LocalActorResolver({ 64 - handleResolver: new CompositeHandleResolver({ 65 - methods: { 66 - dns: new DohJsonHandleResolver({ 67 - dohUrl: "https://mozilla.cloudflare-dns.com/dns-query", 68 - }), 69 - http: new WellKnownHandleResolver(), 70 - }, 71 - }), 72 - didDocumentResolver: new CompositeDidDocumentResolver({ 73 - methods: { 74 - plc: new PlcDidDocumentResolver(), 75 - web: new WebDidDocumentResolver(), 76 - }, 77 - }), 78 - }); 79 - 80 - const doc = await actorResolver 81 - .resolve(actor) 82 - .catch((err) => console.error(err)); 83 - if (!doc) { 84 - document.body.innerText = 85 - "<p>Failed to resolve user. Check browser console for more info</p>"; 86 - throw ""; 33 + <style is:inline> 34 + :root { 35 + --text: #eceaeb; 36 + --background: #190d10; 37 + --green: #06aa79; 38 + --red: #da4460; 39 + --orange: #e46937; 40 + --gold: #d4aa3b; 41 + --accent: #d13181; 87 42 } 88 43 89 - const client = new Client({ 90 - handler: simpleFetchHandler({ service: doc.pds }), 91 - }); 92 - 93 - const res = await client 94 - .get("com.atproto.repo.getRecord", { 95 - params: { 96 - repo: doc.did, 97 - collection: "dev.vielle.dnd.astral", 98 - rkey: "self", 99 - }, 100 - }) 101 - .then((res) => 102 - res.ok 103 - ? res.data.value 104 - : (() => { 105 - throw res.data; 106 - })(), 107 - ) 108 - .then( 109 - async (res) => 110 - await DevVielleDndAstral.mainSchema["~standard"].validate(res), 111 - ) 112 - .then((res) => 113 - !res.issues 114 - ? res.value 115 - : (() => { 116 - throw res.issues; 117 - })(), 118 - ) 119 - .catch((err) => { 120 - console.error(err); 121 - return null; 122 - }); 44 + dt { 45 + font-weight: bold; 46 + } 123 47 124 - const isValidPowers = 125 - (res?.powers.reduce( 126 - (acc, curr) => 127 - acc && 128 - curr !== "dev.vielle.dnd.power#invalid" && 129 - curr !== "dev.vielle.dnd.power#locked", 130 - true, 131 - ) && 132 - res?.powers.reduce( 133 - (acc, curr) => 134 - acc && 135 - parentPowers[curr].reduce( 136 - (acc, curr) => acc && res.powers.includes(curr), 137 - true, 138 - ), 139 - true, 140 - ) && 141 - res?.points >= res?.powers.length) ?? 142 - false; 48 + .power { 49 + border-left: 1px solid white; 50 + padding-inline-start: 10px; 51 + } 143 52 144 - if (res) 145 - for (const power of res.powers) { 146 - const section = document.querySelector( 147 - `section[data-power-id="${power}"]`, 148 - ); 149 - if (!section) continue; 150 - section.classList.add("selected"); 151 - } 152 - </script> 153 - <style is:inline> 154 53 body, 155 54 html { 55 + min-width: 100%; 156 56 width: min-content; 157 57 box-sizing: border-box; 158 - margin: 0; 58 + background-color: var(--background); 59 + color: var(--text); 60 + scrollbar-color: var(--text) var(--background); 61 + } 62 + 63 + body { 64 + margin: 2em 0 2em 10em; 65 + display: flex; 66 + flex-direction: column; 67 + align-items: center; 68 + gap: 5em; 159 69 } 160 70 </style> 71 + <script set:html={`window.actor = "${actor}";`} /> 72 + <script> 73 + import type { ActorIdentifier } from "@atcute/lexicons"; 74 + import powers from "../../lib/powers"; 161 75 162 - <Power {...powerData[powerTree.id]} {...powerTree} first /> 76 + if (!("actor" in window)) throw ""; 77 + const actor = window.actor as ActorIdentifier; 78 + 79 + await powers.loadUser(actor); 80 + </script> 81 + 82 + <Points immutable /> 83 + 84 + <Powers {...powerTree} i={0} siblings={1} immutable /> 163 85 </Base>