diff --git a/src/utils/reverseParsers.ts b/src/utils/reverseParsers.ts index 6ed478e..8acbe6c 100644 --- a/src/utils/reverseParsers.ts +++ b/src/utils/reverseParsers.ts @@ -2,6 +2,7 @@ import type { ParsedURI } from './uriParser'; export type SourceApp = | 'bluesky' + | 'bluepy' | 'blacksky' | 'reddwarf' | 'witchsky' @@ -328,6 +329,40 @@ function matchAtpTools(host: string, pathname: string): ReverseMatch | null { }; } +function matchBluepy(host: string, pathname: string): ReverseMatch | null { + if (host !== 'bluepy.social') return null; + const atUri = parseAtUriPath(pathname); + if (!atUri) return null; + const { handle, collection, rkey } = atUri; + const did = handle.startsWith('did:') ? handle : undefined; + + if (collection === 'app.bsky.actor.profile') { + return { + source: 'bluepy', + parsed: { type: 'profile', uri: `at://${handle}`, handle, did }, + }; + } + + if (collection && rkey) { + return { + source: 'bluepy', + parsed: { + type: inferType(collection), + uri: `at://${handle}/${collection}/${rkey}`, + handle, + did, + collection, + rkey, + }, + }; + } + + return { + source: 'bluepy', + parsed: { type: 'profile', uri: `at://${handle}`, handle, did }, + }; +} + function matchSemble(host: string, parts: string[]): ReverseMatch | null { if (host !== 'semble.so') return null; if (parts[0] !== 'profile' || !parts[1]) return null; @@ -428,6 +463,7 @@ export function matchSupportedUrl(url: URL): ReverseMatch | null { matchMargin(host, parts) || matchPdsls(host, url.pathname) || matchAtpTools(host, url.pathname) || + matchBluepy(host, url.pathname) || matchSemble(host, parts) || matchStreamplace(host, parts) || matchGrain(host, parts) || @@ -485,6 +521,7 @@ export const SUPPORTED_HOSTS: string[] = [ 'margin.at', 'pdsls.dev', 'atp.tools', + 'bluepy.social', 'semble.so', 'stream.place', 'grain.social', diff --git a/src/utils/waypointIcons.tsx b/src/utils/waypointIcons.tsx index 8c3e248..5f0e21e 100644 --- a/src/utils/waypointIcons.tsx +++ b/src/utils/waypointIcons.tsx @@ -160,6 +160,15 @@ export const AturiSVG = () => ( ); +export const BluepySVG = () => ( + + + + + + +); + export const BlentoSVG = () => ( @@ -173,6 +182,7 @@ export const WAYPOINT_ICONS: Record = { aturi: , anisota: , bluesky: , + bluepy: , blacksky: , reddwarf: , leaflet: , diff --git a/src/utils/waypoints.data.ts b/src/utils/waypoints.data.ts index 930c745..f78a195 100644 --- a/src/utils/waypoints.data.ts +++ b/src/utils/waypoints.data.ts @@ -322,6 +322,27 @@ export const WAYPOINT_DESTINATIONS_DATA: Record = { redirectCompat: [], }, + bluepy: { + id: 'bluepy', + name: 'Bluepy', + description: (collection) => { + if (collection === 'app.bsky.feed.post') return 'View post on bluepy.social'; + if (collection === 'app.bsky.graph.list') return 'View list on bluepy.social'; + if (collection) return 'View record on bluepy.social'; + return 'View profile on bluepy.social'; + }, + getUrl: (handle, collection, rkey, did) => { + const identifier = did || handle; + if (collection && rkey) { + return `https://bluepy.social/at://${identifier}/${collection}/${rkey}`; + } + return `https://bluepy.social/at://${identifier}/app.bsky.actor.profile/self`; + }, + supportedTypes: ['post', 'profile', 'list', 'record'], + category: 'blueskyClients', + redirectCompat: ['bluesky-social'], + }, + witchsky: { id: 'witchsky', name: 'Witchsky', @@ -608,6 +629,7 @@ export const WAYPOINT_DESTINATIONS_DATA: Record = { export const WAYPOINT_ORDER = [ 'anisota', 'bluesky', + 'bluepy', 'blacksky', 'reddwarf', 'leaflet', diff --git a/src/utils/waypoints.tsx b/src/utils/waypoints.tsx index 24775ff..76e878c 100644 --- a/src/utils/waypoints.tsx +++ b/src/utils/waypoints.tsx @@ -16,6 +16,7 @@ import { WAYPOINT_ICONS } from './waypointIcons'; export { BlueskySVG, + BluepySVG, BlackskySVG, PdslsSVG, LeafletSVG,