[🐴] Post embeds polish (#4339)
* Handle message cleanup * Handle last message in chat list * Memoize lastMessagezio/stable
parent
da96fb1ef5
commit
de93e8de74
|
@ -3,6 +3,7 @@ import psl from 'psl'
|
||||||
import TLDs from 'tlds'
|
import TLDs from 'tlds'
|
||||||
|
|
||||||
import {BSKY_SERVICE} from 'lib/constants'
|
import {BSKY_SERVICE} from 'lib/constants'
|
||||||
|
import {isInvalidHandle} from 'lib/strings/handles'
|
||||||
|
|
||||||
export const BSKY_APP_HOST = 'https://bsky.app'
|
export const BSKY_APP_HOST = 'https://bsky.app'
|
||||||
const BSKY_TRUSTED_HOSTS = [
|
const BSKY_TRUSTED_HOSTS = [
|
||||||
|
@ -83,6 +84,10 @@ export function toShareUrl(url: string): string {
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toBskyAppUrl(url: string): string {
|
||||||
|
return new URL(url, BSKY_APP_HOST).toString()
|
||||||
|
}
|
||||||
|
|
||||||
export function isBskyAppUrl(url: string): boolean {
|
export function isBskyAppUrl(url: string): boolean {
|
||||||
return url.startsWith('https://bsky.app/')
|
return url.startsWith('https://bsky.app/')
|
||||||
}
|
}
|
||||||
|
@ -183,6 +188,22 @@ export function feedUriToHref(url: string): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function postUriToRelativePath(
|
||||||
|
uri: string,
|
||||||
|
options?: {handle?: string},
|
||||||
|
): string | undefined {
|
||||||
|
try {
|
||||||
|
const {hostname, rkey} = new AtUri(uri)
|
||||||
|
const handleOrDid =
|
||||||
|
options?.handle && !isInvalidHandle(options.handle)
|
||||||
|
? options.handle
|
||||||
|
: hostname
|
||||||
|
return `/profile/${handleOrDid}/post/${rkey}`
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the label in the post text matches the host of the link facet.
|
* Checks if the label in the post text matches the host of the link facet.
|
||||||
*
|
*
|
||||||
|
|
|
@ -312,25 +312,19 @@ export function MessagesList({
|
||||||
})
|
})
|
||||||
|
|
||||||
if (postLinkFacet) {
|
if (postLinkFacet) {
|
||||||
// remove the post link from the text
|
const isAtStart = postLinkFacet.index.byteStart === 0
|
||||||
rt.delete(
|
const isAtEnd =
|
||||||
postLinkFacet.index.byteStart,
|
postLinkFacet.index.byteEnd === rt.unicodeText.graphemeLength
|
||||||
postLinkFacet.index.byteEnd,
|
|
||||||
)
|
|
||||||
|
|
||||||
// re-trim the text, now that we've removed the post link
|
// remove the post link from the text
|
||||||
//
|
if (isAtStart || isAtEnd) {
|
||||||
// if the post link is at the start of the text, we don't want to leave a leading space
|
rt.delete(
|
||||||
// so trim on both sides
|
postLinkFacet.index.byteStart,
|
||||||
if (postLinkFacet.index.byteStart === 0) {
|
postLinkFacet.index.byteEnd,
|
||||||
rt = new RichText({text: rt.text.trim()}, {cleanNewlines: true})
|
|
||||||
} else {
|
|
||||||
// otherwise just trim the end
|
|
||||||
rt = new RichText(
|
|
||||||
{text: rt.text.trimEnd()},
|
|
||||||
{cleanNewlines: true},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rt = new RichText({text: rt.text.trim()}, {cleanNewlines: true})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, {useCallback, useState} from 'react'
|
||||||
import {GestureResponderEvent, View} from 'react-native'
|
import {GestureResponderEvent, View} from 'react-native'
|
||||||
import {
|
import {
|
||||||
AppBskyActorDefs,
|
AppBskyActorDefs,
|
||||||
|
AppBskyEmbedRecord,
|
||||||
ChatBskyConvoDefs,
|
ChatBskyConvoDefs,
|
||||||
moderateProfile,
|
moderateProfile,
|
||||||
ModerationOpts,
|
ModerationOpts,
|
||||||
|
@ -9,6 +10,11 @@ import {
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
postUriToRelativePath,
|
||||||
|
toBskyAppUrl,
|
||||||
|
toShortUrl,
|
||||||
|
} from '#/lib/strings/url-helpers'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
|
@ -95,21 +101,64 @@ function ChatListItemReady({
|
||||||
|
|
||||||
const isDimStyle = convo.muted || moderation.blocked || isDeletedAccount
|
const isDimStyle = convo.muted || moderation.blocked || isDeletedAccount
|
||||||
|
|
||||||
let lastMessage = _(msg`No messages yet`)
|
const {lastMessage, lastMessageSentAt} = React.useMemo(() => {
|
||||||
let lastMessageSentAt: string | null = null
|
let lastMessage = _(msg`No messages yet`)
|
||||||
if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) {
|
let lastMessageSentAt: string | null = null
|
||||||
if (convo.lastMessage.sender?.did === currentAccount?.did) {
|
|
||||||
lastMessage = _(msg`You: ${convo.lastMessage.text}`)
|
if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) {
|
||||||
} else {
|
const isFromMe = convo.lastMessage.sender?.did === currentAccount?.did
|
||||||
lastMessage = convo.lastMessage.text
|
|
||||||
|
if (convo.lastMessage.text) {
|
||||||
|
if (isFromMe) {
|
||||||
|
lastMessage = _(msg`You: ${convo.lastMessage.text}`)
|
||||||
|
} else {
|
||||||
|
lastMessage = convo.lastMessage.text
|
||||||
|
}
|
||||||
|
} else if (convo.lastMessage.embed) {
|
||||||
|
const defaultEmbeddedContentMessage = _(
|
||||||
|
msg`(contains embedded content)`,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (AppBskyEmbedRecord.isView(convo.lastMessage.embed)) {
|
||||||
|
const embed = convo.lastMessage.embed
|
||||||
|
|
||||||
|
if (AppBskyEmbedRecord.isViewRecord(embed.record)) {
|
||||||
|
const record = embed.record
|
||||||
|
const path = postUriToRelativePath(record.uri, {
|
||||||
|
handle: record.author.handle,
|
||||||
|
})
|
||||||
|
const href = path ? toBskyAppUrl(path) : undefined
|
||||||
|
const short = href
|
||||||
|
? toShortUrl(href)
|
||||||
|
: defaultEmbeddedContentMessage
|
||||||
|
if (isFromMe) {
|
||||||
|
lastMessage = _(msg`You: ${short}`)
|
||||||
|
} else {
|
||||||
|
lastMessage = short
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isFromMe) {
|
||||||
|
lastMessage = _(msg`You: ${defaultEmbeddedContentMessage}`)
|
||||||
|
} else {
|
||||||
|
lastMessage = defaultEmbeddedContentMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastMessageSentAt = convo.lastMessage.sentAt
|
||||||
}
|
}
|
||||||
lastMessageSentAt = convo.lastMessage.sentAt
|
if (ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage)) {
|
||||||
}
|
lastMessage = isDeletedAccount
|
||||||
if (ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage)) {
|
? _(msg`Conversation deleted`)
|
||||||
lastMessage = isDeletedAccount
|
: _(msg`Message deleted`)
|
||||||
? _(msg`Conversation deleted`)
|
}
|
||||||
: _(msg`Message deleted`)
|
|
||||||
}
|
return {
|
||||||
|
lastMessage,
|
||||||
|
lastMessageSentAt,
|
||||||
|
}
|
||||||
|
}, [_, convo.lastMessage, currentAccount?.did, isDeletedAccount])
|
||||||
|
|
||||||
const [showActions, setShowActions] = useState(false)
|
const [showActions, setShowActions] = useState(false)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue