A simple tool which lets you scrape twitter accounts and crosspost them to bluesky accounts! Comes with a CLI and a webapp for managing profiles! Works with images/videos/link embeds/threads.
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix Accounts crash and throttle bridge status loading

j4ckxyz be8e70e0 8c663a63

+36 -12
+1 -1
src/server.ts
··· 59 59 const APPVIEW_PROFILE_CHUNK_SIZE = 25; 60 60 const APPVIEW_MAX_ATTEMPTS = 2; 61 61 const APPVIEW_RETRY_DELAY_MS = 700; 62 - const FEDIVERSE_BRIDGE_STATUS_CHUNK_SIZE = 5; 62 + const FEDIVERSE_BRIDGE_STATUS_CHUNK_SIZE = 2; 63 63 64 64 function loadPersistedJwtSecret(): string | undefined { 65 65 if (!fs.existsSync(JWT_SECRET_FILE_PATH)) {
+24 -4
web/src/App.tsx
··· 11 11 Heart, 12 12 History, 13 13 LayoutDashboard, 14 + Link2, 14 15 Loader2, 15 16 LogOut, 16 17 MessageCircle, ··· 1240 1241 1241 1242 void fetchProfiles(mappingData.map((mapping) => mapping.bskyIdentifier)); 1242 1243 1243 - const shouldRefreshBridgeStatuses = 1244 - options?.refreshBridgeStatuses === true || 1245 - mappingData.some((mapping) => fediverseBridgeStatusByMappingIdRef.current[mapping.id] === undefined); 1244 + const shouldRefreshBridgeStatuses = options?.refreshBridgeStatuses === true; 1246 1245 if (shouldRefreshBridgeStatuses) { 1247 1246 void fetchFediverseBridgeStatuses(mappingData, { force: options?.refreshBridgeStatuses === true }); 1248 1247 } ··· 1321 1320 return; 1322 1321 } 1323 1322 1324 - void fetchData({ refreshBridgeStatuses: true }); 1323 + void fetchData(); 1325 1324 }, [token, fetchBootstrapStatus, fetchData]); 1325 + 1326 + useEffect(() => { 1327 + if (!token || activeTab !== 'accounts' || mappings.length === 0) { 1328 + return; 1329 + } 1330 + 1331 + const hasMissingStatuses = mappings.some( 1332 + (mapping) => fediverseBridgeStatusByMappingIdRef.current[mapping.id] === undefined, 1333 + ); 1334 + if (!hasMissingStatuses) { 1335 + return; 1336 + } 1337 + 1338 + const timer = window.setTimeout(() => { 1339 + void fetchFediverseBridgeStatuses(mappings, { force: true }); 1340 + }, 150); 1341 + 1342 + return () => { 1343 + window.clearTimeout(timer); 1344 + }; 1345 + }, [activeTab, fetchFediverseBridgeStatuses, mappings, token]); 1326 1346 1327 1347 useEffect(() => { 1328 1348 if (!bootstrapOpen && authView === 'register') {
+11 -7
web/src/lib/debug-logger.ts
··· 9 9 const id = target.id ? `#${target.id}` : ''; 10 10 const className = 11 11 typeof target.className === 'string' && target.className.trim().length > 0 12 - ? `.${target.className 13 - .trim() 14 - .split(/\s+/) 15 - .filter(Boolean) 16 - .slice(0, 3) 17 - .join('.')}` 12 + ? `.${target.className.trim().split(/\s+/).filter(Boolean).slice(0, 3).join('.')}` 18 13 : ''; 19 14 20 15 return `${tagName}${id}${className}`; ··· 29 24 path: window.location.pathname, 30 25 }; 31 26 32 - if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) { 27 + if ( 28 + target instanceof HTMLInputElement || 29 + target instanceof HTMLTextAreaElement || 30 + target instanceof HTMLSelectElement 31 + ) { 33 32 payload.name = target.name || undefined; 34 33 payload.value = target.value; 35 34 } ··· 44 43 } 45 44 46 45 export function setupBrowserDebugLogging(): void { 46 + const enabled = window.localStorage.getItem('debug-browser-events') === '1'; 47 + if (!enabled) { 48 + return; 49 + } 50 + 47 51 const events = ['click', 'change', 'input', 'submit']; 48 52 49 53 events.forEach((eventName) => {