A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

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

Use atmosphere post URL; relabel post links 'the ATmosphere'

+5 -28
+5 -5
src/components/PostActions.tsx
··· 15 15 import { 16 16 graphemeLength, 17 17 validateReplyText, 18 - bskyPostWebUrl, 19 18 MAX_REPLY_GRAPHEMES, 20 19 type StrongRef, 21 20 } from '../lib/social/records'; 21 + import { atmospherePostWebUrl } from '../lib/social/atmosphere-url'; 22 22 23 23 export interface PostActionsProps { 24 24 /** The companion `app.bsky.feed.post` AT-URI (the article's Bluesky post). */ ··· 169 169 setFlash( { 170 170 kind: 'success', 171 171 message: composer === 'reply' ? 'Reply posted to Bluesky.' : 'Quote posted to Bluesky.', 172 - href: bskyPostWebUrl( uri ), 172 + href: atmospherePostWebUrl( uri ), 173 173 } ); 174 174 refresh(); 175 175 } catch ( err ) { ··· 189 189 return null; 190 190 } 191 191 192 - const threadUrl = bskyPostWebUrl( postUri ); 192 + const threadUrl = atmospherePostWebUrl( postUri ); 193 193 194 194 // Signed-out: prompt sign-in (same OAuth flow as the editor) before any action. 195 195 if ( status !== 'signed-in' || ! agent || ! did ) { ··· 227 227 </p> 228 228 ) } 229 229 <a className="post-actions__thread" href={ threadUrl } target="_blank" rel="noopener noreferrer"> 230 - View this post on Bluesky 230 + View this post on the ATmosphere 231 231 </a> 232 232 </div> 233 233 ); ··· 323 323 { flash.message }{ ' ' } 324 324 { flash.href && ( 325 325 <a href={ flash.href } target="_blank" rel="noopener noreferrer"> 326 - View on Bluesky 326 + View on the ATmosphere 327 327 </a> 328 328 ) } 329 329 </p>
-13
src/lib/social/records.test.ts
··· 6 6 buildQuote, 7 7 graphemeLength, 8 8 validateReplyText, 9 - bskyPostWebUrl, 10 9 MAX_REPLY_GRAPHEMES, 11 10 type StrongRef, 12 11 } from './records'; ··· 95 94 expect( graphemeLength( '๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ' ) ).toBe( 1 ); 96 95 // Flag = two regional-indicator codepoints, one grapheme. 97 96 expect( graphemeLength( '๐Ÿ‡ซ๐Ÿ‡ท' ) ).toBe( 1 ); 98 - } ); 99 - } ); 100 - 101 - describe( 'bskyPostWebUrl', () => { 102 - it( 'maps an at:// post uri to its bsky.app profile/post URL', () => { 103 - expect( bskyPostWebUrl( SUBJECT.uri ) ).toBe( 104 - 'https://bsky.app/profile/did:plc:writer/post/3kpost' 105 - ); 106 - } ); 107 - 108 - it( 'falls back to bsky.app for an unparseable uri', () => { 109 - expect( bskyPostWebUrl( 'not-an-at-uri' ) ).toBe( 'https://bsky.app' ); 110 97 } ); 111 98 } ); 112 99
-10
src/lib/social/records.ts
··· 96 96 } 97 97 98 98 /** 99 - * The bsky.app web URL for a post AT-URI, for "view thread on Bluesky" links. 100 - * `at://<did>/app.bsky.feed.post/<rkey>` โ†’ `https://bsky.app/profile/<did>/post/<rkey>`. 101 - * Falls back to the bsky.app home for an unparseable URI. 102 - */ 103 - export function bskyPostWebUrl( postUri: string ): string { 104 - const match = postUri.match( /^at:\/\/([^/]+)\/app\.bsky\.feed\.post\/(.+)$/ ); 105 - return match ? `https://bsky.app/profile/${ match[ 1 ] }/post/${ match[ 2 ] }` : 'https://bsky.app'; 106 - } 107 - 108 - /** 109 99 * Count user-perceived characters (grapheme clusters), matching Bluesky's 300-grapheme 110 100 * rule. `Intl.Segmenter` is browser-native and counts ZWJ emoji / flags as one each, so 111 101 * a family emoji costs one toward the cap โ€” not the number of underlying codepoints.