Skip to content

Commit b0a21b5

Browse files
committed
more graphql fixes and some frontend changes
1 parent dbb582c commit b0a21b5

16 files changed

Lines changed: 308 additions & 67 deletions

File tree

‎config/sites.dev.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ repository_id = "R_kgDOUc9png"
1919
installation_id = 162105624
2020
default_category = "general"
2121
intents = ["upvotes", "reactions", "discussion", "comments", "answers", "polls", "authors", "moderation", "comment_reactions", "comment_upvotes", "labels", "github_link"]
22-
reaction_counters = ["LAUGH", "HOORAY", "CONFUSED", "HEART", "ROCKET", "EYES"]
22+
reaction_counters = ["THUMBS_UP", "THUMBS_DOWN", "LAUGH", "HOORAY", "CONFUSED", "HEART", "ROCKET", "EYES"]
2323
known_discussions = [
2424
{ key = "feedback/example", id = "D_kwDOUc9pns4ApUHg", number = 1, category = "general", title = "feedback/example" },
2525
{ key = "feedback/documentation", id = "D_kwDOUc9pns4ApVT9", number = 2, category = "general", title = "feedback/documentation" },

‎config/sites.example.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ discussion_body = "Discussion for [{title}]({url})."
2424
# refresh_sweep_seconds = 86400
2525
# max_batch_size = 100
2626
intents = ["upvotes", "reactions", "discussion", "comments", "answers", "polls", "authors", "moderation", "comment_reactions", "comment_upvotes", "labels", "github_link"]
27-
reaction_counters = ["LAUGH", "HOORAY", "CONFUSED", "HEART", "ROCKET", "EYES"]
27+
reaction_counters = ["THUMBS_UP", "THUMBS_DOWN", "LAUGH", "HOORAY", "CONFUSED", "HEART", "ROCKET", "EYES"]
2828
# Optional: pre-register discussions whose stable numeric and node IDs are known
2929
# so anonymous reads work immediately after starting with an empty database.
3030
# known_discussions = [

‎config/sites.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ installation_id = 162105624
1818
default_category = "general"
1919
discussion_body = "Feedback for [{title}]({url})"
2020
intents = ["upvotes", "reactions", "discussion", "comments", "answers", "polls", "authors", "moderation", "comment_reactions", "comment_upvotes", "labels", "github_link"]
21-
reaction_counters = ["LAUGH", "HOORAY", "CONFUSED", "HEART", "ROCKET", "EYES"]
21+
reaction_counters = ["THUMBS_UP", "THUMBS_DOWN", "LAUGH", "HOORAY", "CONFUSED", "HEART", "ROCKET", "EYES"]
2222
known_discussions = [
2323
{ key = "feedback/example", id = "D_kwDOUc9pns4ApUHg", number = 1, category = "general", title = "feedback/example" },
2424
{ key = "feedback/documentation", id = "D_kwDOUc9pns4ApVT9", number = 2, category = "general", title = "feedback/documentation" },

‎runtime/pages/example/index.html‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<meta charset="utf-8">
55
<meta name="viewport" content="width=device-width">
66
<meta name="referrer" content="no-referrer">
7-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; connect-src https://feedback-api.cpp.social https://api.github.com http://localhost:* http://127.0.0.1:*; base-uri 'none'; form-action 'none'; frame-ancestors 'none'">
7+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; img-src https://avatars.githubusercontent.com https://user-images.githubusercontent.com https://github.com data:; connect-src https://feedback-api.cpp.social https://api.github.com http://localhost:* http://127.0.0.1:*; base-uri 'none'; form-action 'none'; frame-ancestors 'none'">
88
<link rel="stylesheet" href="/example/style.css">
99
<title>Feedback integration example</title>
1010
</head>
@@ -21,7 +21,11 @@ <h2>Full discussions</h2>
2121
<section id="thread" class="thread"></section>
2222
<form id="comment-form" class="comment-form">
2323
<label>Thread <select id="comment-key"></select></label>
24-
<textarea id="comment-body" rows="4" maxlength="16000" placeholder="Add a comment"></textarea>
24+
<div class="comment-box">
25+
<div class="comment-box-header">Write</div>
26+
<textarea id="comment-body" rows="4" maxlength="16000" placeholder="Leave a comment"></textarea>
27+
<small>Markdown is supported. Content is rendered using GitHub's sanitized Markdown HTML.</small>
28+
</div>
2529
<button id="comment-submit" type="submit">Sign in and comment</button>
2630
<button id="clear-reply" type="button" hidden>Cancel reply</button>
2731
</form>

‎runtime/pages/example/style.css‎

Lines changed: 112 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
body {
2-
background: #fafafa;
3-
color: #202124;
4-
font: 16px system-ui, sans-serif;
2+
background: #f6f8fa;
3+
color: #1f2328;
4+
font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
55
margin: 0 auto;
6-
max-width: 42rem;
6+
max-width: 72rem;
77
padding: 3rem 1.5rem;
88
}
99

1010
main {
1111
background: white;
12-
border: 1px solid #ddd;
13-
border-radius: 0.75rem;
12+
border: 1px solid #d0d7de;
13+
border-radius: 0.375rem;
1414
padding: 2rem;
1515
}
1616

1717
.authentication-status {
18+
align-items: center;
1819
display: flex;
1920
justify-content: flex-end;
2021
margin: 1rem 0;
@@ -24,9 +25,34 @@ main {
2425
background: #c9362b;
2526
border: 2px solid #8f241d;
2627
border-radius: 50%;
27-
height: 1.25rem;
28-
padding: 0;
29-
width: 1.25rem;
28+
display: inline-block;
29+
height: 0.65rem;
30+
width: 0.65rem;
31+
}
32+
33+
.feedback-authentication-status > span:nth-child(2) {
34+
align-items: center;
35+
display: flex;
36+
font-weight: 600;
37+
gap: 0.4rem;
38+
margin-left: 0.5rem;
39+
}
40+
41+
.feedback-authentication-status > span:nth-child(2) a {
42+
align-items: center;
43+
color: inherit;
44+
display: flex;
45+
gap: 0.4rem;
46+
text-decoration: none;
47+
}
48+
49+
.feedback-authentication-status img,
50+
.comment-author img {
51+
border-radius: 50%;
52+
}
53+
54+
.feedback-authentication-action {
55+
margin-left: 0.75rem;
3056
}
3157

3258
.feedback-authentication-dot.authenticated {
@@ -47,14 +73,16 @@ main {
4773
}
4874

4975
.ranking-card {
50-
border: 1px solid #d7dbe0;
51-
border-radius: 0.5rem;
76+
background: #fff;
77+
border: 1px solid #d0d7de;
78+
border-radius: 0.375rem;
79+
box-shadow: 0 1px 0 rgba(31, 35, 40, 0.04);
5280
padding: 1rem;
5381
}
5482

5583
.ranking-card h3 { margin-top: 0; }
5684

57-
.discussion { border: 1px solid #ddd; border-radius: 0.5rem; padding: 1rem; }
85+
.discussion { border: 1px solid #d0d7de; border-radius: 0.375rem; padding: 1rem; }
5886
.own-post { border-color: #356fc0; box-shadow: inset 4px 0 #356fc0; }
5987
.controls { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem; }
6088

@@ -66,14 +94,23 @@ main {
6694
.reactions {
6795
display: flex;
6896
flex-wrap: wrap;
69-
gap: 0.75rem;
97+
gap: 0.35rem;
7098
margin-top: 1rem;
7199
}
72100

73101
.reaction-control {
74-
border: 1px solid #c7ced8;
75-
border-radius: 0.25rem;
76-
padding: 0.35rem 0.55rem;
102+
background: #f6f8fa;
103+
border: 1px solid #d0d7de;
104+
border-radius: 2rem;
105+
color: #0969da;
106+
min-width: 3.25rem;
107+
padding: 0.25rem 0.55rem;
108+
}
109+
110+
.reaction-control:hover,
111+
.reaction-control[aria-pressed="true"] {
112+
background: #ddf4ff;
113+
border-color: #54aeff;
77114
}
78115

79116
.poll-option {
@@ -94,27 +131,79 @@ main {
94131
}
95132

96133
.comment {
97-
border-left: 3px solid #d7dbe0;
134+
border: 1px solid #d0d7de;
135+
border-radius: 0.375rem;
98136
margin-top: 1.5rem;
99-
padding-left: 1rem;
137+
overflow: hidden;
138+
padding: 0 1rem 1rem;
139+
}
140+
141+
.comment-author {
142+
align-items: center;
143+
background: #f6f8fa;
144+
border-bottom: 1px solid #d0d7de;
145+
display: flex;
146+
gap: 0.5rem;
147+
margin: 0 -1rem 1rem;
148+
padding: 0.55rem 1rem;
100149
}
101150

151+
.comment-author time { color: #656d76; margin-left: auto; }
152+
102153
.comment-form {
103154
display: grid;
104155
gap: 0.75rem;
105156
margin-top: 1.5rem;
106157
}
107158

108159
textarea {
160+
border: 1px solid #d0d7de;
161+
border-radius: 0.375rem;
109162
font: inherit;
163+
min-height: 7rem;
110164
padding: 0.75rem;
111165
resize: vertical;
112166
}
113167

168+
.comment-box {
169+
border: 1px solid #d0d7de;
170+
border-radius: 0.375rem;
171+
overflow: hidden;
172+
}
173+
174+
.comment-box-header {
175+
background: #f6f8fa;
176+
border-bottom: 1px solid #d0d7de;
177+
font-weight: 600;
178+
padding: 0.6rem 0.9rem;
179+
}
180+
181+
.comment-box textarea {
182+
border: 0;
183+
box-sizing: border-box;
184+
display: block;
185+
margin: 0;
186+
width: 100%;
187+
}
188+
189+
.comment-box small {
190+
color: #656d76;
191+
display: block;
192+
padding: 0 0.75rem 0.65rem;
193+
}
194+
114195
.reply {
115196
margin-left: 1.5rem;
116197
}
117198

199+
.markdown-body { line-height: 1.5; overflow-wrap: anywhere; }
200+
.markdown-body pre { background: #f6f8fa; border-radius: 0.375rem; overflow: auto; padding: 1rem; }
201+
.markdown-body code { background: rgba(175, 184, 193, 0.2); border-radius: 0.375rem; padding: 0.15em 0.3em; }
202+
.markdown-body pre code { background: transparent; padding: 0; }
203+
.markdown-body img { max-width: 100%; }
204+
.markdown-body table { border-collapse: collapse; display: block; overflow: auto; }
205+
.markdown-body td, .markdown-body th { border: 1px solid #d0d7de; padding: 0.4rem 0.8rem; }
206+
118207
.tag {
119208
background: #eef2f6;
120209
border-radius: 0.25rem;
@@ -123,13 +212,17 @@ textarea {
123212
}
124213

125214
button {
215+
background: #f6f8fa;
216+
border: 1px solid rgba(31, 35, 40, 0.15);
217+
border-radius: 0.375rem;
126218
cursor: pointer;
127219
font: inherit;
128220
padding: 0.6rem 1rem;
129221
}
130222

131223
button[aria-pressed="true"] {
132-
background: #fff4bd;
224+
background: #ddf4ff;
225+
border-color: #54aeff;
133226
}
134227

135228
#status {

‎runtime/src/api/client.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export interface AccessToken {
5454
expiresAt: number;
5555
creationGrant: string;
5656
viewerId?: string;
57+
viewerLogin?: string;
58+
viewerAvatarUrl?: string;
5759
}
5860

5961
export interface EnsuredDiscussion {

‎runtime/src/auth/controller.ts‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,13 @@ export class Authentication {
9696
signal,
9797
);
9898
const token = await this.#service.exchange(code, authorization.state, verifier, signal);
99-
const viewerId = await validateViewer(this.#fetch, token.value, signal);
100-
const authenticated = { ...token, viewerId };
99+
const viewer = await validateViewer(this.#fetch, token.value, signal);
100+
const authenticated = {
101+
...token,
102+
viewerId: viewer.id,
103+
viewerLogin: viewer.login,
104+
viewerAvatarUrl: viewer.avatarUrl,
105+
};
101106
this.#tokens.set(authenticated);
102107
return authenticated;
103108
} catch (error) {
@@ -172,7 +177,7 @@ async function validateViewer(
172177
fetch: typeof globalThis.fetch,
173178
token: string,
174179
signal?: AbortSignal,
175-
): Promise<string> {
180+
): Promise<{ id: string; login: string; avatarUrl: string }> {
176181
const init: RequestInit = {
177182
method: "POST",
178183
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
@@ -182,20 +187,21 @@ async function validateViewer(
182187
const response = await fetch("https://api.github.com/graphql", init);
183188
if (!response.ok) throw new AuthenticationError("token_invalid");
184189
const body: unknown = await response.json();
185-
const viewerId = viewer(body);
186-
if (viewerId === null) throw new AuthenticationError("token_invalid");
187-
return viewerId;
190+
const result = viewer(body);
191+
if (result === null) throw new AuthenticationError("token_invalid");
192+
return result;
188193
}
189194

190-
function viewer(value: unknown): string | null {
195+
function viewer(value: unknown): { id: string; login: string; avatarUrl: string } | null {
191196
if (!value || typeof value !== "object") return null;
192197
const data = (value as { data?: unknown }).data;
193198
if (!data || typeof data !== "object") return null;
194199
const viewer = (data as { viewer?: unknown }).viewer;
195200
if (!viewer || typeof viewer !== "object") return null;
196-
const candidate = viewer as { id?: unknown; login?: unknown };
197-
return typeof candidate.id === "string" && typeof candidate.login === "string"
198-
? candidate.id
201+
const candidate = viewer as { id?: unknown; login?: unknown; avatarUrl?: unknown };
202+
return typeof candidate.id === "string" && typeof candidate.login === "string" &&
203+
typeof candidate.avatarUrl === "string"
204+
? { id: candidate.id, login: candidate.login, avatarUrl: candidate.avatarUrl }
199205
: null;
200206
}
201207

‎runtime/src/auth/session.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ interface StoredToken {
77
expiresAt: number;
88
creationGrant: string;
99
viewerId: string;
10+
viewerLogin: string;
11+
viewerAvatarUrl: string;
1012
}
1113

1214
export class SessionTokenStore {
@@ -32,6 +34,10 @@ export class SessionTokenStore {
3234
typeof parsed.creationGrant !== "string" ||
3335
typeof parsed.viewerId !== "string" ||
3436
parsed.viewerId.length === 0 ||
37+
typeof parsed.viewerLogin !== "string" ||
38+
parsed.viewerLogin.length === 0 ||
39+
typeof parsed.viewerAvatarUrl !== "string" ||
40+
!parsed.viewerAvatarUrl.startsWith("https://") ||
3541
(parsed.expiresAt ?? 0) * 1000 <= this.#clock()
3642
) {
3743
this.clear();
@@ -42,6 +48,8 @@ export class SessionTokenStore {
4248
expiresAt: parsed.expiresAt,
4349
creationGrant: parsed.creationGrant,
4450
viewerId: parsed.viewerId,
51+
viewerLogin: parsed.viewerLogin,
52+
viewerAvatarUrl: parsed.viewerAvatarUrl,
4553
} as AccessToken;
4654
} catch {
4755
this.clear();

0 commit comments

Comments
 (0)