tangled
alpha
login
or
join now
vielle.dev
/
dnd-astral-powers
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
scuffed new /astral/user page
vielle.dev
1 month ago
e5a46a95
db81e780
verified
This commit was signed with the committer's
known signature
.
vielle.dev
SSH Key Fingerprint:
SHA256:/4bvxqoEh9iMdjAPgcgAgXKZZQTROL3ULiPt6nH9RSs=
+138
-137
4 changed files
expand all
collapse all
unified
split
src
components
astral
Points.astro
Powers.astro
lib
powers.ts
pages
astral
[user].astro
+19
-12
src/components/astral/Points.astro
···
1
1
---
2
2
-
2
2
+
interface Props {
3
3
+
immutable?: true;
4
4
+
}
3
5
---
4
6
5
7
<script>
···
48
50
console.warn(e),
49
51
);
50
52
51
51
-
this.dec.disabled = !(
52
52
-
Powers.get().points - 1 >= 0 &&
53
53
-
Powers.get().points - 1 >= Powers.get().powers.length
54
54
-
);
53
53
+
this.dec.disabled =
54
54
+
!(Powers.get().points - 1 >= 0) ||
55
55
+
!(Powers.get().points - 1 >= Powers.get().powers.length) ||
56
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
64
-
this.inc.disabled = false;
66
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
75
-
this.dec.disabled = !(
76
76
-
val.points - 1 >= 0 && val.points - 1 >= val.powers.length
77
77
-
);
77
77
+
this.dec.disabled =
78
78
+
!(val.points - 1 >= 0) ||
79
79
+
!(val.points - 1 >= val.powers.length) ||
80
80
+
this.getAttribute("disabled") !== null;
78
81
}
79
82
}
80
83
81
84
customElements.define("points-meter", HTMLPointsMeterElement);
82
85
</script>
83
86
84
84
-
<points-meter>
85
85
-
<button aria-label="decrement" data-dec>-</button>
87
87
+
<points-meter disabled={Astro.props.immutable}>
88
88
+
<button aria-label="decrement" data-dec disabled={Astro.props.immutable}
89
89
+
>-</button
90
90
+
>
86
91
<meter></meter>
87
92
<span class="total"
88
93
><span class="count"></span>/<span class="max"></span></span
89
94
>
90
90
-
<button aria-label="increment" data-inc>+</button>
95
95
+
<button aria-label="increment" data-inc disabled={Astro.props.immutable}
96
96
+
>+</button
97
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
14
+
immutable?: true;
14
15
}
15
16
16
16
-
const { id: power, children } = Astro.props;
17
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
72
-
button && (button.disabled = false);
73
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
95
-
button && (button.disabled = false);
96
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
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
421
-
<Astro.self {...child} {i} siblings={children.length} />
423
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
7
+
import type { ActorIdentifier } from "@atcute/lexicons";
8
8
+
import { Client, simpleFetchHandler } from "@atcute/client";
9
9
+
import {
10
10
+
CompositeDidDocumentResolver,
11
11
+
CompositeHandleResolver,
12
12
+
DohJsonHandleResolver,
13
13
+
LocalActorResolver,
14
14
+
PlcDidDocumentResolver,
15
15
+
WebDidDocumentResolver,
16
16
+
WellKnownHandleResolver,
17
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
171
+
},
172
172
+
173
173
+
async loadUser(user: ActorIdentifier) {
174
174
+
const res = await new Client({
175
175
+
handler: simpleFetchHandler({
176
176
+
service: await new LocalActorResolver({
177
177
+
handleResolver: new CompositeHandleResolver({
178
178
+
methods: {
179
179
+
http: new WellKnownHandleResolver(),
180
180
+
dns: new DohJsonHandleResolver({
181
181
+
dohUrl: "https://mozilla.cloudflare-dns.com/dns-query",
182
182
+
}),
183
183
+
},
184
184
+
strategy: "race",
185
185
+
}),
186
186
+
didDocumentResolver: new CompositeDidDocumentResolver({
187
187
+
methods: {
188
188
+
web: new WebDidDocumentResolver(),
189
189
+
plc: new PlcDidDocumentResolver(),
190
190
+
},
191
191
+
}),
192
192
+
})
193
193
+
.resolve(user)
194
194
+
.then((doc) => doc.pds),
195
195
+
}),
196
196
+
})
197
197
+
.get("com.atproto.repo.getRecord", {
198
198
+
params: {
199
199
+
repo: user,
200
200
+
collection: "dev.vielle.dnd.astral",
201
201
+
rkey: "self",
202
202
+
},
203
203
+
})
204
204
+
.then((res) =>
205
205
+
res.ok
206
206
+
? res.data.value
207
207
+
: (() => {
208
208
+
throw res.data;
209
209
+
})(),
210
210
+
)
211
211
+
.then(
212
212
+
async (res) =>
213
213
+
await DevVielleDndAstral.mainSchema["~standard"].validate(res),
214
214
+
)
215
215
+
.then((res) =>
216
216
+
!res.issues
217
217
+
? res.value
218
218
+
: (() => {
219
219
+
throw res.issues;
220
220
+
})(),
221
221
+
)
222
222
+
.catch((err) => {
223
223
+
console.error(err);
224
224
+
return null;
225
225
+
});
226
226
+
227
227
+
if (!res) throw "Failed to load user";
228
228
+
229
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
6
-
import Power from "../../components/astral/Power.astro";
6
6
+
import Powers from "../../components/astral/Powers.astro";
7
7
import { powerData, powerTree } from "../../consts/astral";
8
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
32
-
<script set:html={`window.actor = "${actor}";`} />
33
33
-
<script>
34
34
-
import { Client, simpleFetchHandler } from "@atcute/client";
35
35
-
import {
36
36
-
normalPowers,
37
37
-
parentPowers,
38
38
-
powerData,
39
39
-
powers,
40
40
-
type powerId,
41
41
-
} from "../../consts/astral";
42
42
-
import { DevVielleDndAstral } from "../../lexicons";
43
43
-
import {
44
44
-
CompositeDidDocumentResolver,
45
45
-
CompositeHandleResolver,
46
46
-
DohJsonHandleResolver,
47
47
-
LocalActorResolver,
48
48
-
PlcDidDocumentResolver,
49
49
-
WebDidDocumentResolver,
50
50
-
WellKnownHandleResolver,
51
51
-
} from "@atcute/identity-resolver";
52
52
-
53
53
-
const actor =
54
54
-
"actor" in window
55
55
-
? (window.actor as
56
56
-
| undefined
57
57
-
| `did:plc:${string}`
58
58
-
| `did:web:${string}`
59
59
-
| `${string}.${string}`)
60
60
-
: undefined;
61
61
-
if (!actor) throw "loaded without actor in window. missing `window.actor`";
62
62
-
63
63
-
const actorResolver = new LocalActorResolver({
64
64
-
handleResolver: new CompositeHandleResolver({
65
65
-
methods: {
66
66
-
dns: new DohJsonHandleResolver({
67
67
-
dohUrl: "https://mozilla.cloudflare-dns.com/dns-query",
68
68
-
}),
69
69
-
http: new WellKnownHandleResolver(),
70
70
-
},
71
71
-
}),
72
72
-
didDocumentResolver: new CompositeDidDocumentResolver({
73
73
-
methods: {
74
74
-
plc: new PlcDidDocumentResolver(),
75
75
-
web: new WebDidDocumentResolver(),
76
76
-
},
77
77
-
}),
78
78
-
});
79
79
-
80
80
-
const doc = await actorResolver
81
81
-
.resolve(actor)
82
82
-
.catch((err) => console.error(err));
83
83
-
if (!doc) {
84
84
-
document.body.innerText =
85
85
-
"<p>Failed to resolve user. Check browser console for more info</p>";
86
86
-
throw "";
33
33
+
<style is:inline>
34
34
+
:root {
35
35
+
--text: #eceaeb;
36
36
+
--background: #190d10;
37
37
+
--green: #06aa79;
38
38
+
--red: #da4460;
39
39
+
--orange: #e46937;
40
40
+
--gold: #d4aa3b;
41
41
+
--accent: #d13181;
87
42
}
88
43
89
89
-
const client = new Client({
90
90
-
handler: simpleFetchHandler({ service: doc.pds }),
91
91
-
});
92
92
-
93
93
-
const res = await client
94
94
-
.get("com.atproto.repo.getRecord", {
95
95
-
params: {
96
96
-
repo: doc.did,
97
97
-
collection: "dev.vielle.dnd.astral",
98
98
-
rkey: "self",
99
99
-
},
100
100
-
})
101
101
-
.then((res) =>
102
102
-
res.ok
103
103
-
? res.data.value
104
104
-
: (() => {
105
105
-
throw res.data;
106
106
-
})(),
107
107
-
)
108
108
-
.then(
109
109
-
async (res) =>
110
110
-
await DevVielleDndAstral.mainSchema["~standard"].validate(res),
111
111
-
)
112
112
-
.then((res) =>
113
113
-
!res.issues
114
114
-
? res.value
115
115
-
: (() => {
116
116
-
throw res.issues;
117
117
-
})(),
118
118
-
)
119
119
-
.catch((err) => {
120
120
-
console.error(err);
121
121
-
return null;
122
122
-
});
44
44
+
dt {
45
45
+
font-weight: bold;
46
46
+
}
123
47
124
124
-
const isValidPowers =
125
125
-
(res?.powers.reduce(
126
126
-
(acc, curr) =>
127
127
-
acc &&
128
128
-
curr !== "dev.vielle.dnd.power#invalid" &&
129
129
-
curr !== "dev.vielle.dnd.power#locked",
130
130
-
true,
131
131
-
) &&
132
132
-
res?.powers.reduce(
133
133
-
(acc, curr) =>
134
134
-
acc &&
135
135
-
parentPowers[curr].reduce(
136
136
-
(acc, curr) => acc && res.powers.includes(curr),
137
137
-
true,
138
138
-
),
139
139
-
true,
140
140
-
) &&
141
141
-
res?.points >= res?.powers.length) ??
142
142
-
false;
48
48
+
.power {
49
49
+
border-left: 1px solid white;
50
50
+
padding-inline-start: 10px;
51
51
+
}
143
52
144
144
-
if (res)
145
145
-
for (const power of res.powers) {
146
146
-
const section = document.querySelector(
147
147
-
`section[data-power-id="${power}"]`,
148
148
-
);
149
149
-
if (!section) continue;
150
150
-
section.classList.add("selected");
151
151
-
}
152
152
-
</script>
153
153
-
<style is:inline>
154
53
body,
155
54
html {
55
55
+
min-width: 100%;
156
56
width: min-content;
157
57
box-sizing: border-box;
158
158
-
margin: 0;
58
58
+
background-color: var(--background);
59
59
+
color: var(--text);
60
60
+
scrollbar-color: var(--text) var(--background);
61
61
+
}
62
62
+
63
63
+
body {
64
64
+
margin: 2em 0 2em 10em;
65
65
+
display: flex;
66
66
+
flex-direction: column;
67
67
+
align-items: center;
68
68
+
gap: 5em;
159
69
}
160
70
</style>
71
71
+
<script set:html={`window.actor = "${actor}";`} />
72
72
+
<script>
73
73
+
import type { ActorIdentifier } from "@atcute/lexicons";
74
74
+
import powers from "../../lib/powers";
161
75
162
162
-
<Power {...powerData[powerTree.id]} {...powerTree} first />
76
76
+
if (!("actor" in window)) throw "";
77
77
+
const actor = window.actor as ActorIdentifier;
78
78
+
79
79
+
await powers.loadUser(actor);
80
80
+
</script>
81
81
+
82
82
+
<Points immutable />
83
83
+
84
84
+
<Powers {...powerTree} i={0} siblings={1} immutable />
163
85
</Base>