+38
-5
src/entrypoints/background.ts
+38
-5
src/entrypoints/background.ts
···
9
9
memberUriString,
10
10
putFronter,
11
11
frontersCache,
12
+
parseSocialAppPostUrl,
12
13
} from "@/lib/utils";
13
-
import { parseResourceUri, ResourceUri } from "@atcute/lexicons";
14
+
import {
15
+
parseCanonicalResourceUri,
16
+
parseResourceUri,
17
+
ResourceUri,
18
+
} from "@atcute/lexicons";
14
19
15
20
export default defineBackground({
16
21
persistent: true,
···
82
87
console.error(`fronter write: ${resp.error}`);
83
88
}
84
89
}
90
+
if (results.length === 0) return;
85
91
// hijack timeline fronter message because when a write is made it is either on the timeline
86
92
// or its a reply to a depth === 0 post on a threaded view, which is the same as a timeline post
87
93
browser.tabs.sendMessage(sender.tab?.id!, {
···
142
148
]),
143
149
),
144
150
);
151
+
if (results.size === 0) return;
145
152
browser.tabs.sendMessage(sender.tab?.id!, {
146
153
type: "TIMELINE_FRONTER",
147
154
results,
···
149
156
// console.log("sent timeline fronters", results);
150
157
};
151
158
const handleThread = async (
152
-
{ data: { body } }: any,
159
+
{
160
+
data: { body, requestUrl, documentUrl },
161
+
}: { data: { body: string; requestUrl: string; documentUrl: string } },
153
162
sender: globalThis.Browser.runtime.MessageSender,
154
163
) => {
164
+
// check if this request was made for fetching replies
165
+
// if anchor is not the same as current document url, that is the case
166
+
// which means the depth of the returned posts are invalid to us, in the case of THREAD_FRONTER
167
+
// if so we will use TIMELINE_FRONTER to send it back to content script
168
+
let isReplyThreadFetch = false;
169
+
const parsedDocumentUri = parseSocialAppPostUrl(documentUrl);
170
+
const anchorUri = new URL(requestUrl).searchParams.get("anchor");
171
+
// console.log(
172
+
// "parsedDocumentUri",
173
+
// parsedDocumentUri,
174
+
// "anchorUri",
175
+
// anchorUri,
176
+
// );
177
+
if (parsedDocumentUri && anchorUri) {
178
+
const parsedAnchorUri = expect(parseResourceUri(anchorUri));
179
+
isReplyThreadFetch = parsedDocumentUri.rkey !== parsedAnchorUri.rkey;
180
+
}
181
+
// console.log("isReplyThreadFetch", isReplyThreadFetch);
155
182
const data: any = JSON.parse(body);
156
183
const promises = (data.thread as any[]).flatMap((item) => {
157
184
return frontersCache.get(item.uri).then(async (cachedFronter) => {
···
165
192
}
166
193
return fronter.value;
167
194
});
168
-
return promise.then(async (fronter) => {
195
+
return promise.then(async (fronter): Promise<any> => {
169
196
if (!fronter) return;
170
197
const parsedUri = await cacheFronter(item.uri, fronter);
198
+
if (isReplyThreadFetch)
199
+
return {
200
+
rkey: parsedUri.rkey!,
201
+
...fronter,
202
+
};
171
203
if (item.depth === 0) await setTabFronter(item.uri, fronter);
172
204
return {
173
205
rkey: parsedUri.rkey!,
···
188
220
),
189
221
),
190
222
);
223
+
if (results.size === 0) return;
191
224
browser.tabs.sendMessage(sender.tab?.id!, {
192
-
type: "THREAD_FRONTER",
225
+
type: isReplyThreadFetch ? "TIMELINE_FRONTER" : "THREAD_FRONTER",
193
226
results,
194
227
});
195
228
// console.log("sent thread fronters", results);
···
197
230
198
231
browser.runtime.onMessage.addListener(async (message, sender) => {
199
232
if (message.type !== "RESPONSE_CAPTURED") return;
200
-
// console.log("handling response event", message);
233
+
console.log("handling response", message.data);
201
234
switch (message.data.type as string) {
202
235
case "write":
203
236
await handleWrite(
+35
-13
src/entrypoints/content.ts
+35
-13
src/entrypoints/content.ts
···
55
55
}
56
56
return authHeader?.split(" ")[1] || null;
57
57
};
58
+
const getRequestUrl = () => {
59
+
let url: string | null = null;
60
+
if (args[0] instanceof Request) {
61
+
url = args[0].url;
62
+
} else {
63
+
url = args[0].toString();
64
+
}
65
+
return decodeURI(url);
66
+
};
58
67
59
68
let detail: any = undefined;
60
69
if (response.url.includes("/xrpc/com.atproto.repo.applyWrites")) {
···
84
93
detail = {
85
94
type: "thread",
86
95
body,
96
+
requestUrl: getRequestUrl(),
97
+
documentUrl: document.location.href,
87
98
};
88
99
} else if (response.url.includes("/xrpc/app.bsky.feed.getPosts")) {
89
100
detail = {
···
118
129
el.textContent += ` [f: ${s}]`;
119
130
el.setAttribute("data-fronter", s);
120
131
};
121
-
const applyFrontersToPage = (fronters: Map<string, any>) => {
132
+
const applyFrontersToPage = (
133
+
fronters: Map<string, any>,
134
+
pageChange: boolean,
135
+
) => {
122
136
// console.log("applyFrontersToPage", fronters);
123
137
const match = parseSocialAppPostUrl(document.URL);
124
-
// console.log(match, fronters);
125
-
for (const el of document.querySelectorAll("[data-fronter]")) {
126
-
const previousFronter = el.getAttribute("data-fronter")!;
127
-
// remove fronter text
128
-
el.textContent = el.textContent.replace(` [f: ${previousFronter}]`, "");
129
-
el.removeAttribute("data-fronter");
138
+
console.log("applyFrontersToPage", match, fronters);
139
+
if (pageChange) {
140
+
console.log(
141
+
"page change so clearing all elements with data-fronter attribute",
142
+
);
143
+
for (const el of document.querySelectorAll("[data-fronter]")) {
144
+
const previousFronter = el.getAttribute("data-fronter")!;
145
+
// remove fronter text
146
+
el.textContent = el.textContent.replace(
147
+
` [f: ${previousFronter}]`,
148
+
"",
149
+
);
150
+
el.removeAttribute("data-fronter");
151
+
}
130
152
}
131
153
if (fronters.size === 0) return;
132
154
for (const el of document.getElementsByTagName("a")) {
···
134
156
const fronter = fronters.get(path);
135
157
if (!fronter || fronter.members.length === 0) continue;
136
158
const isFocusedPost = fronter.depth === 0;
137
-
if (isFocusedPost && match && match.rkey !== fronter.rkey) continue;
138
-
if (isFocusedPost && el.ariaLabel !== fronter.displayName) continue;
159
+
if (isFocusedPost) if (match && match.rkey !== fronter.rkey) continue;
160
+
if (isFocusedPost) if (el.ariaLabel !== fronter.displayName) continue;
139
161
const displayNameElement = isFocusedPost
140
162
? (el.firstElementChild?.firstElementChild?.firstElementChild
141
163
?.firstElementChild?.firstElementChild ?? null)
142
164
: (el.parentElement?.firstElementChild?.firstElementChild
143
165
?.firstElementChild?.firstElementChild ?? null);
144
166
if (!displayNameElement) continue;
145
-
// console.log(path, fronter, displayNameElement);
146
167
applyFronterName(displayNameElement, fronter.members);
147
168
}
148
169
};
···
162
183
]);
163
184
}),
164
185
);
165
-
// console.log("applying cached fronters");
166
-
applyFrontersToPage(updated);
186
+
console.log("applying cached fronters", updated);
187
+
applyFrontersToPage(updated, true);
167
188
};
168
189
// check if we are on profile so we can update fronters if the post tab is clicked on
169
190
const postTabElement = document.querySelector(
···
182
203
window.addEventListener("message", (event) => {
183
204
if (!["TIMELINE_FRONTER", "THREAD_FRONTER"].includes(event.data.type))
184
205
return;
185
-
applyFrontersToPage(event.data.results as Map<string, any>);
206
+
console.log(`received ${event.data.type} fronters`, event.data.results);
207
+
applyFrontersToPage(event.data.results, false);
186
208
});
187
209
},
188
210
});