diff options
-rw-r--r-- | __tests__/lib/link-meta.test.ts | 10 | ||||
-rw-r--r-- | __tests__/state/models/link-metas-view.test.ts | 2 | ||||
-rw-r--r-- | src/lib/extractBskyMeta.ts | 99 | ||||
-rw-r--r-- | src/lib/link-meta.ts | 20 | ||||
-rw-r--r-- | src/state/models/link-metas-view.ts | 2 | ||||
-rw-r--r-- | src/view/com/composer/ComposePost.tsx | 2 | ||||
-rw-r--r-- | src/view/com/profile/ProfileHeader.tsx | 9 | ||||
-rw-r--r-- | src/view/com/util/PostEmbeds.tsx | 2 |
8 files changed, 123 insertions, 23 deletions
diff --git a/__tests__/lib/link-meta.test.ts b/__tests__/lib/link-meta.test.ts index 5df5153ee..5931a4ccb 100644 --- a/__tests__/lib/link-meta.test.ts +++ b/__tests__/lib/link-meta.test.ts @@ -1,4 +1,5 @@ import {LikelyType, getLinkMeta, getLikelyType} from '../../src/lib/link-meta' +import {mockedRootStore} from '../../__mocks__/state-mock' const exampleComHtml = `<!doctype html> <html> @@ -59,6 +60,7 @@ describe('getLinkMeta', () => { 'https://example.com/audio.ogg', 'https://example.com/text.txt', 'https://example.com/javascript.js', + 'https://bsky.app/', 'https://bsky.app/index.html', ] const outputs = [ @@ -106,6 +108,12 @@ describe('getLinkMeta', () => { }, { likelyType: LikelyType.AtpData, + url: '/', + title: 'Bluesky', + description: 'A new kind of social network', + }, + { + likelyType: LikelyType.AtpData, url: '/index.html', title: 'Not found', }, @@ -127,7 +135,7 @@ describe('getLinkMeta', () => { }) }) const input = inputs[i] - const output = await getLinkMeta(input) + const output = await getLinkMeta(mockedRootStore, input) expect(output).toEqual(outputs[i]) } }) diff --git a/__tests__/state/models/link-metas-view.test.ts b/__tests__/state/models/link-metas-view.test.ts index 037418932..0e5fb8da5 100644 --- a/__tests__/state/models/link-metas-view.test.ts +++ b/__tests__/state/models/link-metas-view.test.ts @@ -43,7 +43,7 @@ describe('LinkMetasViewModel', () => { const result = await viewModel.getLinkMeta(mockedMeta.url) - expect(getLinkMetaMockSpy).toHaveBeenCalledWith(mockedMeta.url) + expect(getLinkMetaMockSpy).toHaveBeenCalledWith(rootStore, mockedMeta.url) expect(result).toEqual(mockedMeta) }) diff --git a/src/lib/extractBskyMeta.ts b/src/lib/extractBskyMeta.ts new file mode 100644 index 000000000..e53036aec --- /dev/null +++ b/src/lib/extractBskyMeta.ts @@ -0,0 +1,99 @@ +import {LikelyType, LinkMeta} from './link-meta' +import {match as matchRoute} from '../view/routes' +import {convertBskyAppUrlIfNeeded, makeRecordUri} from './strings' +import {RootStoreModel} from '../state' +import {PostThreadViewModel} from '../state/models/post-thread-view' + +import {Home} from '../view/screens/Home' +import {Search} from '../view/screens/Search' +import {Notifications} from '../view/screens/Notifications' +import {PostThread} from '../view/screens/PostThread' +import {PostUpvotedBy} from '../view/screens/PostUpvotedBy' +import {PostRepostedBy} from '../view/screens/PostRepostedBy' +import {Profile} from '../view/screens/Profile' +import {ProfileFollowers} from '../view/screens/ProfileFollowers' +import {ProfileFollows} from '../view/screens/ProfileFollows' + +// NOTE +// this is a hack around the lack of hosted social metadata +// remove once that's implemented +// -prf +export async function extractBskyMeta( + store: RootStoreModel, + url: string, +): Promise<LinkMeta> { + url = convertBskyAppUrlIfNeeded(url) + const route = matchRoute(url) + let meta: LinkMeta = { + likelyType: LikelyType.AtpData, + url, + title: route.defaultTitle, + } + + if (route.Com === Home) { + meta = { + ...meta, + title: 'Bluesky', + description: 'A new kind of social network', + } + } else if (route.Com === Search) { + meta = { + ...meta, + title: 'Search - Bluesky', + description: 'A new kind of social network', + } + } else if (route.Com === Notifications) { + meta = { + ...meta, + title: 'Notifications - Bluesky', + description: 'A new kind of social network', + } + } else if ( + route.Com === PostThread || + route.Com === PostUpvotedBy || + route.Com === PostRepostedBy + ) { + // post and post-related screens + const threadUri = makeRecordUri( + route.params.name, + 'app.bsky.feed.post', + route.params.rkey, + ) + const threadView = new PostThreadViewModel(store, { + uri: threadUri, + depth: 0, + }) + await threadView.setup().catch(_err => undefined) + const title = [ + route.Com === PostUpvotedBy + ? 'Likes on a post by' + : route.Com === PostRepostedBy + ? 'Reposts of a post by' + : 'Post by', + threadView.thread?.post.author.displayName || + threadView.thread?.post.author.handle || + 'a bluesky user', + ].join(' ') + meta = { + ...meta, + title, + description: threadView.thread?.postRecord?.text, + } + } else if ( + route.Com === Profile || + route.Com === ProfileFollowers || + route.Com === ProfileFollows + ) { + // profile and profile-related screens + const profile = await store.profiles.getProfile(route.params.name) + if (profile?.data) { + meta = { + ...meta, + title: profile.data.displayName || profile.data.handle, + description: profile.data.description, + } + } + } + + return meta +} diff --git a/src/lib/link-meta.ts b/src/lib/link-meta.ts index 49e75cde2..9a0325c8f 100644 --- a/src/lib/link-meta.ts +++ b/src/lib/link-meta.ts @@ -1,10 +1,7 @@ import he from 'he' -import { - extractHtmlMeta, - isBskyAppUrl, - convertBskyAppUrlIfNeeded, -} from './strings' -import {match as matchRoute} from '../view/routes' +import {extractHtmlMeta, isBskyAppUrl} from './strings' +import {RootStoreModel} from '../state' +import {extractBskyMeta} from './extractBskyMeta' export enum LikelyType { HTML, @@ -26,19 +23,12 @@ export interface LinkMeta { } export async function getLinkMeta( + store: RootStoreModel, url: string, timeout = 5e3, ): Promise<LinkMeta> { if (isBskyAppUrl(url)) { - // TODO this could be better - url = convertBskyAppUrlIfNeeded(url) - const route = matchRoute(url) - return { - likelyType: LikelyType.AtpData, - url, - title: route.defaultTitle, - // description: '' - } + return extractBskyMeta(store, url) } let urlp diff --git a/src/state/models/link-metas-view.ts b/src/state/models/link-metas-view.ts index 0187f4260..6b787987d 100644 --- a/src/state/models/link-metas-view.ts +++ b/src/state/models/link-metas-view.ts @@ -31,7 +31,7 @@ export class LinkMetasViewModel { } } try { - const promise = getLinkMeta(url) + const promise = getLinkMeta(this.rootStore, url) this.cache.set(url, promise) const res = await promise this.cache.set(url, res) diff --git a/src/view/com/composer/ComposePost.tsx b/src/view/com/composer/ComposePost.tsx index 228b11035..a8def6405 100644 --- a/src/view/com/composer/ComposePost.tsx +++ b/src/view/com/composer/ComposePost.tsx @@ -94,7 +94,7 @@ export const ComposePost = observer(function ComposePost({ return cleanup } if (!extLink.meta) { - getLinkMeta(extLink.uri).then(meta => { + getLinkMeta(store, extLink.uri).then(meta => { if (aborted) { return } diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index 3982637fc..c14a5c827 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -1,6 +1,7 @@ import React from 'react' import {observer} from 'mobx-react-lite' import { + Share, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, @@ -16,7 +17,7 @@ import { ReportAccountModal, ProfileImageLightbox, } from '../../../state/models/shell-ui' -import {pluralize} from '../../../lib/strings' +import {pluralize, toShareUrl} from '../../../lib/strings' import {s, gradients} from '../../lib/styles' import {DropdownButton, DropdownItem} from '../util/forms/DropdownButton' import * as Toast from '../util/Toast' @@ -66,6 +67,9 @@ export const ProfileHeader = observer(function ProfileHeader({ const onPressFollows = () => { store.nav.navigate(`/profile/${view.handle}/follows`) } + const onPressShare = () => { + Share.share({url: toShareUrl(`/profile/${view.handle}`)}) + } const onPressMuteAccount = async () => { try { await view.muteAccount() @@ -133,9 +137,8 @@ export const ProfileHeader = observer(function ProfileHeader({ // loaded // = const isMe = store.me.did === view.did - let dropdownItems: DropdownItem[] | undefined + let dropdownItems: DropdownItem[] = [{label: 'Share', onPress: onPressShare}] if (!isMe) { - dropdownItems = dropdownItems || [] dropdownItems.push({ label: view.myState.muted ? 'Unmute Account' : 'Mute Account', onPress: view.myState.muted ? onPressUnmuteAccount : onPressMuteAccount, diff --git a/src/view/com/util/PostEmbeds.tsx b/src/view/com/util/PostEmbeds.tsx index 3fb93ed48..65518470a 100644 --- a/src/view/com/util/PostEmbeds.tsx +++ b/src/view/com/util/PostEmbeds.tsx @@ -92,7 +92,7 @@ export function PostEmbeds({ /> )} <View style={styles.extInner}> - <Text type="sm-bold" numberOfLines={2} style={[pal.text]}> + <Text type="md-bold" numberOfLines={2} style={[pal.text]}> {link.title || link.uri} </Text> <Text |