Render original HTML text of posts bridged from the Fediverse or Wafrn #26

open
opened by maxine.puppykitty.racing targeting main
Changed files
+230 -67
src
screens
state
view
com
+42 -18
src/screens/PostThread/components/ThreadItemAnchor.tsx
···
import {PostAlerts} from '#/components/moderation/PostAlerts'
import {type AppModerationCause} from '#/components/Pills'
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
+
import {MastodonHtmlContent, useHasMastodonHtmlContent} from '#/components/Post/MastodonHtmlContent'
import {PostControls, PostControlsSkeleton} from '#/components/PostControls'
import {useFormatPostStatCount} from '#/components/PostControls/util'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
···
const moderation = item.moderation
const authorShadow = useProfileShadow(post.author)
const {isActive: live} = useActorStatus(post.author)
+
const hasMastodonHtml = useHasMastodonHtmlContent(record)
const richText = useMemo(
() =>
new RichTextAPI({
···
style={[a.pb_sm]}
additionalCauses={additionalPostAlerts}
/>
-
{richText?.text ? (
-
<RichText
-
enableTags
-
selectable
-
value={richText}
-
style={[a.flex_1, a.text_lg]}
-
authorHandle={post.author.handle}
-
shouldProxyLinks={true}
-
/>
-
) : undefined}
-
{post.embed && (
-
<View style={[a.py_xs]}>
-
<Embed
-
embed={post.embed}
-
moderation={moderation}
-
viewContext={PostEmbedViewContext.ThreadHighlighted}
-
onOpen={onOpenEmbed}
+
{hasMastodonHtml ? (
+
<>
+
<MastodonHtmlContent
+
record={record}
+
style={[a.flex_1]}
+
textStyle={[a.text_lg]}
/>
-
</View>
+
{post.embed && (
+
<View style={[a.py_xs]}>
+
<Embed
+
embed={post.embed}
+
moderation={moderation}
+
viewContext={PostEmbedViewContext.ThreadHighlighted}
+
onOpen={onOpenEmbed}
+
/>
+
</View>
+
)}
+
</>
+
) : (
+
<>
+
{richText?.text ? (
+
<RichText
+
enableTags
+
selectable
+
value={richText}
+
style={[a.flex_1, a.text_lg]}
+
authorHandle={post.author.handle}
+
shouldProxyLinks={true}
+
/>
+
) : undefined}
+
{post.embed && (
+
<View style={[a.py_xs]}>
+
<Embed
+
embed={post.embed}
+
moderation={moderation}
+
viewContext={PostEmbedViewContext.ThreadHighlighted}
+
onOpen={onOpenEmbed}
+
/>
+
</View>
+
)}
+
</>
)}
</ContentHider>
<ExpandedPostDetails
+46 -20
src/screens/PostThread/components/ThreadItemPost.tsx
···
import {PostHider} from '#/components/moderation/PostHider'
import {type AppModerationCause} from '#/components/Pills'
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
+
import {
+
MastodonHtmlContent,
+
useHasMastodonHtmlContent,
+
} from '#/components/Post/MastodonHtmlContent'
import {ShowMoreTextButton} from '#/components/Post/ShowMoreTextButton'
import {PostControls, PostControlsSkeleton} from '#/components/PostControls'
import {RichText} from '#/components/RichText'
···
const post = item.value.post
const record = item.value.post.record
const moderation = item.moderation
+
const hasMastodonHtml = useHasMastodonHtmlContent(post.record)
const richText = useMemo(
() =>
new RichTextAPI({
···
style={[a.pb_2xs]}
additionalCauses={additionalPostAlerts}
/>
-
{richText?.text ? (
+
{hasMastodonHtml ? (
<>
-
<RichText
-
enableTags
-
value={richText}
+
<MastodonHtmlContent
+
record={post.record}
style={[a.flex_1, a.text_md]}
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
-
authorHandle={post.author.handle}
-
shouldProxyLinks={true}
/>
-
{limitLines && (
-
<ShowMoreTextButton
-
style={[a.text_md]}
-
onPress={onPressShowMore}
-
/>
+
{post.embed && (
+
<View style={[a.pb_xs]}>
+
<Embed
+
embed={post.embed}
+
moderation={moderation}
+
viewContext={PostEmbedViewContext.Feed}
+
/>
+
</View>
+
)}
+
</>
+
) : (
+
<>
+
{richText?.text ? (
+
<>
+
<RichText
+
enableTags
+
value={richText}
+
style={[a.flex_1, a.text_md]}
+
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
+
authorHandle={post.author.handle}
+
shouldProxyLinks={true}
+
/>
+
{limitLines && (
+
<ShowMoreTextButton
+
style={[a.text_md]}
+
onPress={onPressShowMore}
+
/>
+
)}
+
</>
+
) : undefined}
+
{post.embed && (
+
<View style={[a.pb_xs]}>
+
<Embed
+
embed={post.embed}
+
moderation={moderation}
+
viewContext={PostEmbedViewContext.Feed}
+
/>
+
</View>
)}
</>
-
) : undefined}
-
{post.embed && (
-
<View style={[a.pb_xs]}>
-
<Embed
-
embed={post.embed}
-
moderation={moderation}
-
viewContext={PostEmbedViewContext.Feed}
-
/>
-
</View>
)}
<PostControls
post={postShadow}
+27
src/screens/Settings/DeerSettings.tsx
···
useNoDiscoverFallback,
useSetNoDiscoverFallback,
} from '#/state/preferences/no-discover-fallback'
+
import {
+
useRenderMastodonHtml,
+
useSetRenderMastodonHtml,
+
} from '#/state/preferences/render-mastodon-html'
import {
useRepostCarouselEnabled,
useSetRepostCarouselEnabled,
···
const hideSimilarAccountsRecomm = useHideSimilarAccountsRecomm()
const setHideSimilarAccountsRecomm = useSetHideSimilarAccountsRecomm()
+
const renderMastodonHtml = useRenderMastodonHtml()
+
const setRenderMastodonHtml = useSetRenderMastodonHtml()
+
const disableVerifyEmailReminder = useDisableVerifyEmailReminder()
const setDisableVerifyEmailReminder = useSetDisableVerifyEmailReminder()
···
<Toggle.Platform />
</Toggle.Item>
+
<Toggle.Item
+
+
<Toggle.Item
+
name="render_mastodon_html"
+
label={_(msg`Render Mastodon HTML from bridged posts`)}
+
value={renderMastodonHtml}
+
onChange={value => setRenderMastodonHtml(value)}
+
style={[a.w_full]}>
+
<Toggle.LabelText style={[a.flex_1]}>
+
<Trans>Render Mastodon HTML from bridged posts</Trans>
+
</Toggle.LabelText>
+
<Toggle.Platform />
+
</Toggle.Item>
+
<Admonition type="info" style={[a.flex_1]}>
+
<Trans>
+
When enabled, posts bridged from Mastodon will display their
+
original HTML formatting instead of the plain text version.
+
</Trans>
+
</Admonition>
+
<Toggle.Item
name="disable_verify_email_reminder"
label={_(msg`Disable verify email reminder`)}
+2
src/state/persisted/schema.ts
···
})
.optional(),
highQualityImages: z.boolean().optional(),
+
renderMastodonHtml: z.boolean().optional(),
showExternalShareButtons: z.boolean().optional(),
···
],
},
highQualityImages: false,
+
renderMastodonHtml: false,
showExternalShareButtons: false,
}
+10 -7
src/state/preferences/index.tsx
···
import {Provider as LargeAltBadgeProvider} from './large-alt-badge'
import {Provider as NoAppLabelersProvider} from './no-app-labelers'
import {Provider as NoDiscoverProvider} from './no-discover-fallback'
+
import {Provider as RenderMastodonHtmlProvider} from './render-mastodon-html'
import {Provider as RepostCarouselProvider} from './repost-carousel-enabled'
import {Provider as ShowLinkInHandleProvider} from './show-link-in-handle'
import {Provider as SubtitlesProvider} from './subtitles'
···
<DisableFollowedByMetricsProvider>
<DisablePostsMetricsProvider>
<HideSimilarAccountsRecommProvider>
-
<EnableSquareAvatarsProvider>
-
<EnableSquareButtonsProvider>
-
<DisableVerifyEmailReminderProvider>
-
{children}
-
</DisableVerifyEmailReminderProvider>
-
</EnableSquareButtonsProvider>
-
</EnableSquareAvatarsProvider>
+
<RenderMastodonHtmlProvider>
+
<EnableSquareAvatarsProvider>
+
<EnableSquareButtonsProvider>
+
<DisableVerifyEmailReminderProvider>
+
{children}
+
</DisableVerifyEmailReminderProvider>
+
</EnableSquareButtonsProvider>
+
</EnableSquareAvatarsProvider>
+
</RenderMastodonHtmlProvider>
</HideSimilarAccountsRecommProvider>
</DisablePostsMetricsProvider>
</DisableFollowedByMetricsProvider>
+49
src/state/preferences/render-mastodon-html.tsx
···
+
import React from 'react'
+
+
import * as persisted from '#/state/persisted'
+
+
type StateContext = persisted.Schema['renderMastodonHtml']
+
type SetContext = (v: persisted.Schema['renderMastodonHtml']) => void
+
+
const stateContext = React.createContext<StateContext>(
+
persisted.defaults.renderMastodonHtml,
+
)
+
const setContext = React.createContext<SetContext>(
+
(_: persisted.Schema['renderMastodonHtml']) => {},
+
)
+
+
export function Provider({children}: React.PropsWithChildren<{}>) {
+
const [state, setState] = React.useState(persisted.get('renderMastodonHtml'))
+
+
const setStateWrapped = React.useCallback(
+
(renderMastodonHtml: persisted.Schema['renderMastodonHtml']) => {
+
setState(renderMastodonHtml)
+
persisted.write('renderMastodonHtml', renderMastodonHtml)
+
},
+
[setState],
+
)
+
+
React.useEffect(() => {
+
return persisted.onUpdate('renderMastodonHtml', nextValue => {
+
setState(nextValue)
+
})
+
}, [setStateWrapped])
+
+
return (
+
<stateContext.Provider value={state}>
+
<setContext.Provider value={setStateWrapped}>
+
{children}
+
</setContext.Provider>
+
</stateContext.Provider>
+
)
+
}
+
+
export function useRenderMastodonHtml() {
+
return (
+
React.useContext(stateContext) ?? persisted.defaults.renderMastodonHtml
+
)
+
}
+
+
export function useSetRenderMastodonHtml() {
+
return React.useContext(setContext)
+
}
+54 -22
src/view/com/posts/PostFeedItem.tsx
···
import {type AppModerationCause} from '#/components/Pills'
import {Embed} from '#/components/Post/Embed'
import {PostEmbedViewContext} from '#/components/Post/Embed/types'
+
import {
+
MastodonHtmlContent,
+
useHasMastodonHtmlContent,
+
} from '#/components/Post/MastodonHtmlContent'
import {PostRepliedTo} from '#/components/Post/PostRepliedTo'
import {ShowMoreTextButton} from '#/components/Post/ShowMoreTextButton'
import {PostControls} from '#/components/PostControls'
···
threadgateRecord?: AppBskyFeedThreadgate.Record
}): React.ReactNode => {
const {currentAccount} = useSession()
+
const hasMastodonHtml = useHasMastodonHtmlContent(
+
post.record as AppBskyFeedPost.Record,
+
)
const [limitLines, setLimitLines] = useState(
() => countLines(richText.text) >= MAX_POST_LINES,
)
···
style={[a.pb_xs]}
additionalCauses={additionalPostAlerts}
/>
-
{richText.text ? (
+
{hasMastodonHtml ? (
<>
-
<RichText
-
enableTags
-
testID="postText"
-
value={richText}
-
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
+
<MastodonHtmlContent
+
record={post.record as AppBskyFeedPost.Record}
style={[a.flex_1, a.text_md]}
-
authorHandle={postAuthor.handle}
-
shouldProxyLinks={true}
+
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
/>
-
{limitLines && (
-
<ShowMoreTextButton style={[a.text_md]} onPress={onPressShowMore} />
-
)}
+
{postEmbed ? (
+
<View style={[a.pb_xs]}>
+
<Embed
+
embed={postEmbed}
+
moderation={moderation}
+
onOpen={onOpenEmbed}
+
viewContext={PostEmbedViewContext.Feed}
+
/>
+
</View>
+
) : null}
</>
-
) : undefined}
-
{postEmbed ? (
-
<View style={[a.pb_xs]}>
-
<Embed
-
embed={postEmbed}
-
moderation={moderation}
-
onOpen={onOpenEmbed}
-
viewContext={PostEmbedViewContext.Feed}
-
/>
-
</View>
-
) : null}
+
) : (
+
<>
+
{richText.text ? (
+
<>
+
<RichText
+
enableTags
+
testID="postText"
+
value={richText}
+
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
+
style={[a.flex_1, a.text_md]}
+
authorHandle={postAuthor.handle}
+
shouldProxyLinks={true}
+
/>
+
{limitLines && (
+
<ShowMoreTextButton
+
style={[a.text_md]}
+
onPress={onPressShowMore}
+
/>
+
)}
+
</>
+
) : undefined}
+
{postEmbed ? (
+
<View style={[a.pb_xs]}>
+
<Embed
+
embed={postEmbed}
+
moderation={moderation}
+
onOpen={onOpenEmbed}
+
viewContext={PostEmbedViewContext.Feed}
+
/>
+
</View>
+
) : null}
+
</>
+
)}
</ContentHider>
)
}