view who was fronting when a record was made

fix: make replies have fronter after being created

ptr.pet a88e7061 2170a51c

verified
Changed files
+73 -18
src
+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
··· 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 });