diff --git a/__e2e__/mock-server.ts b/__e2e__/mock-server.ts index 79c3185714..0a97dbcd2d 100644 --- a/__e2e__/mock-server.ts +++ b/__e2e__/mock-server.ts @@ -144,7 +144,7 @@ async function main() { await server.mocker.labelProfile('porn', 'porn-profile') await server.mocker.labelPost( 'porn', - await server.mocker.createPost('porn-posts', 'porn post'), + await server.mocker.createImagePost('porn-posts', 'porn post'), ) await server.mocker.labelPost( 'porn', @@ -167,7 +167,7 @@ async function main() { await server.mocker.labelProfile('nudity', 'nudity-profile') await server.mocker.labelPost( 'nudity', - await server.mocker.createPost('nudity-posts', 'nudity post'), + await server.mocker.createImagePost('nudity-posts', 'nudity post'), ) await server.mocker.labelPost( 'nudity', diff --git a/jest/test-pds.ts b/jest/test-pds.ts index 21d90b93b7..48b73e848d 100644 --- a/jest/test-pds.ts +++ b/jest/test-pds.ts @@ -29,13 +29,13 @@ export async function createServer( plc: {port: port2}, }) - const profilePic = fs.readFileSync( + const pic = fs.readFileSync( path.join(__dirname, '..', 'assets', 'default-avatar.jpg'), ) return { pdsUrl, - mocker: new Mocker(pds, pdsUrl, profilePic), + mocker: new Mocker(pds, pdsUrl, pic), async close() { await pds.server.destroy() await plc.server.destroy() @@ -50,7 +50,7 @@ class Mocker { constructor( public pds: DevEnvTestPDS, public service: string, - public profilePic: Uint8Array, + public pic: Uint8Array, ) { this.agent = new BskyAgent({service}) } @@ -90,7 +90,7 @@ class Mocker { password: 'hunter2', }) await agent.upsertProfile(async () => { - const blob = await agent.uploadBlob(this.profilePic, { + const blob = await agent.uploadBlob(this.pic, { encoding: 'image/jpeg', }) return { @@ -151,6 +151,25 @@ class Mocker { }) } + async createImagePost(user: string, text: string) { + const agent = this.users[user]?.agent + if (!agent) { + throw new Error(`Not a user: ${user}`) + } + const blob = await agent.uploadBlob(this.pic, { + encoding: 'image/jpeg', + }) + return await agent.post({ + text, + langs: ['en'], + embed: { + $type: 'app.bsky.embed.images', + images: [{image: blob.data.blob, alt: ''}], + }, + createdAt: new Date().toISOString(), + }) + } + async createQuotePost( user: string, text: string, diff --git a/src/lib/moderation.ts b/src/lib/moderation.ts index 758e3de3a5..1ed830fd86 100644 --- a/src/lib/moderation.ts +++ b/src/lib/moderation.ts @@ -75,3 +75,13 @@ export function getProfileModerationCauses( return true }) as ModerationCause[] } + +export function isCauseALabelOnUri( + cause: ModerationCause | undefined, + uri: string, +): boolean { + if (cause?.type !== 'label') { + return false + } + return cause.label.uri === uri +} diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index c7b5711c05..b71d6bdcfa 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -269,7 +269,10 @@ export const PostThreadItem = observer(function PostThreadItem({ ) : undefined} {item.post.embed && ( - + )} @@ -428,7 +431,10 @@ export const PostThreadItem = observer(function PostThreadItem({ ) : undefined} {item.post.embed && ( - + )} {needsTranslation && ( diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index f6873b0a05..bf9437076d 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -267,7 +267,10 @@ const PostLoaded = observer( ) : undefined} {item.post.embed ? ( - + ) : null} {needsTranslation && ( diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index eecb4c5335..bed3d3a61e 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -293,7 +293,10 @@ export const FeedItem = observer(function ({ - + ) : null} {needsTranslation && ( diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx index 9f11fe48ce..f82b5b7dfb 100644 --- a/src/view/com/util/post-embeds/QuoteEmbed.tsx +++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx @@ -5,6 +5,7 @@ import { AppBskyFeedPost, AppBskyEmbedImages, AppBskyEmbedRecordWithMedia, + ModerationUI, } from '@atproto/api' import {AtUri} from '@atproto/api' import {PostMeta} from '../PostMeta' @@ -13,14 +14,17 @@ import {Text} from '../text/Text' import {usePalette} from 'lib/hooks/usePalette' import {ComposerOptsQuote} from 'state/models/ui/shell' import {PostEmbeds} from '.' +import {PostAlerts} from '../moderation/PostAlerts' import {makeProfileLink} from 'lib/routes/links' import {InfoCircleIcon} from 'lib/icons' export function MaybeQuoteEmbed({ embed, + moderation, style, }: { embed: AppBskyEmbedRecord.View + moderation: ModerationUI style?: StyleProp }) { const pal = usePalette('default') @@ -39,6 +43,7 @@ export function MaybeQuoteEmbed({ text: embed.record.value.text, embeds: embed.record.embeds, }} + moderation={moderation} style={style} /> ) @@ -66,9 +71,11 @@ export function MaybeQuoteEmbed({ export function QuoteEmbed({ quote, + moderation, style, }: { quote: ComposerOptsQuote + moderation?: ModerationUI style?: StyleProp }) { const pal = usePalette('default') @@ -100,16 +107,19 @@ export function QuoteEmbed({ postHref={itemHref} timestamp={quote.indexedAt} /> + {moderation ? ( + + ) : null} {!isEmpty ? ( {quote.text} ) : null} {AppBskyEmbedImages.isView(imagesEmbed) && ( - + )} {AppBskyEmbedRecordWithMedia.isView(imagesEmbed) && ( - + )} ) @@ -140,4 +150,7 @@ const styles = StyleSheet.create({ paddingHorizontal: 14, borderWidth: 1, }, + alert: { + marginBottom: 6, + }, }) diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index 32e92119de..b1c1c6a2e3 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -14,6 +14,7 @@ import { AppBskyEmbedRecordWithMedia, AppBskyFeedDefs, AppBskyGraphDefs, + ModerationUI, } from '@atproto/api' import {Link} from '../Link' import {ImageLayoutGrid} from '../images/ImageLayoutGrid' @@ -28,6 +29,7 @@ import {AutoSizedImage} from '../images/AutoSizedImage' import {CustomFeedEmbed} from './CustomFeedEmbed' import {ListEmbed} from './ListEmbed' import {isDesktopWeb} from 'platform/detection' +import {isCauseALabelOnUri} from 'lib/moderation' type Embed = | AppBskyEmbedRecord.View @@ -38,9 +40,11 @@ type Embed = export function PostEmbeds({ embed, + moderation, style, }: { embed?: Embed + moderation: ModerationUI style?: StyleProp }) { const pal = usePalette('default') @@ -49,10 +53,15 @@ export function PostEmbeds({ // quote post with media // = if (AppBskyEmbedRecordWithMedia.isView(embed)) { + const isModOnQuote = + AppBskyEmbedRecord.isViewRecord(embed.record.record) && + isCauseALabelOnUri(moderation.cause, embed.record.record.uri) + const mediaModeration = isModOnQuote ? {} : moderation + const quoteModeration = isModOnQuote ? moderation : {} return ( - - + + ) } @@ -71,7 +80,9 @@ export function PostEmbeds({ // quote post // = - return + return ( + + ) } // image embed