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