+21
src/domlink.ts
+21
src/domlink.ts
···
65
65
this.wraps.classList.add("LRow");
66
66
}
67
67
}
68
+
export class Table extends Node {
69
+
constructor() {
70
+
let table = document.createElement("table");
71
+
table.classList.add("LTable");
72
+
super(table);
73
+
}
74
+
}
75
+
export class TableRow extends Node {
76
+
constructor() {
77
+
let tr = document.createElement("tr");
78
+
tr.classList.add("LTableRow");
79
+
super(tr);
80
+
}
81
+
}
82
+
export class TableCell extends Node {
83
+
constructor() {
84
+
let td = document.createElement("td");
85
+
td.classList.add("LTableCell");
86
+
super(td);
87
+
}
88
+
}
68
89
export class Text extends Node {
69
90
private _text: string = "";
70
91
get text() {
+148
src/issuesearch.ts
+148
src/issuesearch.ts
···
1
+
import { Body, Button, Column, Input, Label, Row, Table, TableCell, TableRow } from "./domlink.js";
2
+
3
+
type issueRecord = {
4
+
body: string;
5
+
createdAt: string;
6
+
issueId: number;
7
+
owner: string;
8
+
repo: string;
9
+
title: string;
10
+
};
11
+
type constellationRecordItem = {
12
+
did: string;
13
+
collection: string;
14
+
rkey: string;
15
+
};
16
+
type constellationResponse = {
17
+
total: number;
18
+
linking_records: [constellationRecordItem];
19
+
cursor: string;
20
+
};
21
+
type slingshotResponse<T> = {
22
+
cid: string;
23
+
uri: string;
24
+
value: T;
25
+
};
26
+
type miniDoc = {
27
+
did: string;
28
+
handle: string;
29
+
pds: string;
30
+
signing_key: string;
31
+
};
32
+
type repoRecord = {
33
+
knot: string;
34
+
name: string;
35
+
owner: string;
36
+
createdAt: string;
37
+
};
38
+
type recordListingItem<T> = {
39
+
uri: string;
40
+
cid: string;
41
+
value: T;
42
+
};
43
+
type recordListing<T> = {
44
+
cursor: string;
45
+
records: [recordListingItem<T>];
46
+
};
47
+
48
+
async function xcall(host: string, method: string, params: Record<string, string | number> | null = null): Promise<unknown> {
49
+
let url = `${host}/xrpc/${method}`;
50
+
if (params) {
51
+
let usp = new URLSearchParams();
52
+
for (let key in params) {
53
+
usp.append(key, params[key].toString());
54
+
}
55
+
url += "?" + usp.toString();
56
+
}
57
+
return await (await fetch(url)).json();
58
+
}
59
+
60
+
const SLINGSHOT = "https://slingshot.microcosm.blue";
61
+
async function listIssues(repo: string) {
62
+
let repoUrl = new URL(repo);
63
+
let repoUrlSplat = repoUrl.pathname.split("/");
64
+
let repoOwnerHandle = repoUrlSplat[1].substring(1);
65
+
let repoName = repoUrlSplat[2];
66
+
let repoOwnerDoc = await xcall(SLINGSHOT, "com.bad-example.identity.resolveMiniDoc", {identifier: repoOwnerHandle}) as miniDoc;
67
+
let ownedRepos = await xcall(repoOwnerDoc.pds, "com.atproto.repo.listRecords", { repo: repoOwnerDoc.did, collection: "sh.tangled.repo" }) as recordListing<repoRecord>;
68
+
let repoRecord = ownedRepos.records.find(x=>x.value.name == repoName)!;
69
+
let encodedRepoUri = encodeURIComponent(repoRecord.uri);
70
+
let view_issues_list = new Table().add(
71
+
new TableRow().with(
72
+
new TableCell().add("Handle"),
73
+
new TableCell().add("Issue#"),
74
+
new TableCell().add("Title")
75
+
)
76
+
);
77
+
Body.add(view_issues_list);
78
+
let irsp = await (await fetch(`https://constellation.microcosm.blue/links?target=${encodedRepoUri}&collection=sh.tangled.repo.issue&path=.repo`)).json() as constellationResponse;
79
+
let allIssueRefs: constellationRecordItem[] = irsp.linking_records;
80
+
let nextCursor = irsp.cursor;
81
+
while (allIssueRefs.length < irsp.total && nextCursor != null) {
82
+
let rsp = await (await fetch(`https://constellation.microcosm.blue/links?target=${encodedRepoUri}&collection=sh.tangled.repo.issue&path=.repo&cursor=${nextCursor}`)).json() as constellationResponse;
83
+
nextCursor = rsp.cursor;
84
+
allIssueRefs = allIssueRefs.concat(rsp.linking_records);
85
+
if (nextCursor == null) break;
86
+
}
87
+
let sortedIssues = (await Promise.all(allIssueRefs.map(async (issueRef) => {
88
+
let rsp = await xcall(SLINGSHOT, "com.atproto.repo.getRecord", {
89
+
repo: issueRef.did,
90
+
collection: issueRef.collection,
91
+
rkey: issueRef.rkey
92
+
}) as slingshotResponse<issueRecord>;
93
+
let issue = rsp.value;
94
+
let doc;
95
+
try {
96
+
doc = await xcall(SLINGSHOT, "com.bad-example.identity.resolveMiniDoc", { identifier: issue.owner }) as miniDoc;
97
+
} catch (error) {
98
+
return null;
99
+
}
100
+
let handle = doc.handle;
101
+
return {
102
+
handle: handle,
103
+
issue: issue
104
+
};
105
+
}))).sort((a,b)=>{
106
+
let x = 0;
107
+
if (a && b) {
108
+
x = b.issue.issueId-a.issue.issueId
109
+
}
110
+
return x;
111
+
});
112
+
sortedIssues.forEach(that =>{
113
+
if (that) {
114
+
let view_issue_row = new TableRow();
115
+
view_issue_row.with(
116
+
new TableCell().add(that.handle),
117
+
new TableCell().add(that.issue.issueId.toString()),
118
+
new TableCell().add(that.issue.title)
119
+
);
120
+
view_issues_list.add(view_issue_row);
121
+
}
122
+
});
123
+
return view_issues_list;
124
+
}
125
+
126
+
127
+
let repoUrl = new Input();
128
+
let last: any;
129
+
let statusText = new Label("waiting");
130
+
let runButton = new Button("GO!",async ()=>{
131
+
(runButton.wraps as HTMLButtonElement).disabled = true;
132
+
if (last) {
133
+
Body.remove(last);
134
+
}
135
+
try {
136
+
last = await listIssues(repoUrl.value);
137
+
} catch (e) {
138
+
last = new Label("Failed");
139
+
Body.add(last);
140
+
}
141
+
console.log(last);
142
+
(runButton.wraps as HTMLButtonElement).disabled = false;
143
+
});
144
+
Body.add(new Row().with(
145
+
"List issues for repo: ",
146
+
repoUrl,
147
+
runButton
148
+
));
+12
static/issuesearch.css
+12
static/issuesearch.css
+15
static/issuesearch.html
+15
static/issuesearch.html
···
1
+
<!DOCTYPE html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="UTF-8">
5
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+
<title>issuesearch</title>
7
+
<link rel="stylesheet" href="styles/domlink.css">
8
+
<link rel="stylesheet" href="pds.css">
9
+
<link rel="stylesheet" href="issuesearch.css">
10
+
</head>
11
+
<body>
12
+
<noscript>enable js or fukc off</noscript>
13
+
<script src="issuesearch.js" type="module"></script>
14
+
</body>
15
+
</html>