From 223ba68a0b6b7cc8ba7a343391b7623db4e4e34f Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Sun, 16 Mar 2025 13:44:49 -0500 Subject: [PATCH] Handle images --- bskyogcard/package.json | 1 + bskyogcard/src/components/Box.tsx | 2 +- bskyogcard/src/components/Grid.tsx | 2 +- bskyogcard/src/components/Image.tsx | 16 +- .../src/components/MediaInsetBorder.tsx | 46 ++++++ bskyogcard/src/components/Post/index.tsx | 3 +- bskyogcard/src/components/PostEmbed.tsx | 143 +++++++++++++++--- .../src/components/PostEmbed/QuotePost.tsx | 3 +- bskyogcard/src/theme/index.ts | 11 +- bskyogcard/src/util/sanitizeHandle.ts | 7 + bskyogcard/yarn.lock | 5 + 11 files changed, 211 insertions(+), 28 deletions(-) create mode 100644 bskyogcard/src/components/MediaInsetBorder.tsx create mode 100644 bskyogcard/src/util/sanitizeHandle.ts diff --git a/bskyogcard/package.json b/bskyogcard/package.json index a46cf17d60..31abd366a6 100644 --- a/bskyogcard/package.json +++ b/bskyogcard/package.json @@ -25,6 +25,7 @@ "devDependencies": { "@types/node": "^20.14.3", "pino-pretty": "^11.2.2", + "prettier": "^3.5.3", "ts-node": "^10.9.2", "typescript": "^5.4.5" } diff --git a/bskyogcard/src/components/Box.tsx b/bskyogcard/src/components/Box.tsx index 96b0691ddd..b6439703d1 100644 --- a/bskyogcard/src/components/Box.tsx +++ b/bskyogcard/src/components/Box.tsx @@ -7,7 +7,7 @@ export function Box({ cx = [], }: { children?: React.ReactNode - cx?: Record[] + cx?: (Record | undefined)[] }) { return
{children}
} diff --git a/bskyogcard/src/components/Grid.tsx b/bskyogcard/src/components/Grid.tsx index 38e3175ce3..236e647c76 100644 --- a/bskyogcard/src/components/Grid.tsx +++ b/bskyogcard/src/components/Grid.tsx @@ -29,7 +29,7 @@ export function Row({ export function Column({ children, - gutter = 0, + gutter, cx, width = 1, }: { diff --git a/bskyogcard/src/components/Image.tsx b/bskyogcard/src/components/Image.tsx index 1d6ce670d8..aefdffae2d 100644 --- a/bskyogcard/src/components/Image.tsx +++ b/bskyogcard/src/components/Image.tsx @@ -1,8 +1,9 @@ import React from 'react' import {Image as ImageSource} from '../data/getPostData.js' -import {atoms as a, style as s, theme as t} from '../theme/index.js' +import {atoms as a, style as s, StyleProp,theme as t} from '../theme/index.js' import {Box} from './Box.js' +import {MediaInsetBorder} from './MediaInsetBorder.js' export type ImageProps = Omit< React.ImgHTMLAttributes, @@ -23,16 +24,24 @@ export function Image({image, cx, ...rest}: ImageProps) { ) } -export function SquareImage({image}: {image: ImageSource}) { +export function SquareImage({ + image, + style, + insetBorderStyle, +}: { + image: ImageSource + insetBorderStyle?: StyleProp['style'] +} & StyleProp) { return ( + ) } diff --git a/bskyogcard/src/components/MediaInsetBorder.tsx b/bskyogcard/src/components/MediaInsetBorder.tsx new file mode 100644 index 0000000000..7734638044 --- /dev/null +++ b/bskyogcard/src/components/MediaInsetBorder.tsx @@ -0,0 +1,46 @@ +import React from 'react' + +import {atoms as a, StyleProp,theme as t} from '../theme/index.js' +import {Box} from './Box.js' + +/** + * Applies and thin border within a bounding box. Used to contrast media from + * bg of the container. + */ +export function MediaInsetBorder({ + children, + style, + opaque, +}: { + children?: React.ReactNode + /** + * Used where this border needs to match adjacent borders, such as in + * external link previews + */ + opaque?: boolean +} & StyleProp) { + const isLight = t.name === 'light' + return ( + + {children} + + ) +} diff --git a/bskyogcard/src/components/Post/index.tsx b/bskyogcard/src/components/Post/index.tsx index 61c5e7f18a..e4c14e621a 100644 --- a/bskyogcard/src/components/Post/index.tsx +++ b/bskyogcard/src/components/Post/index.tsx @@ -10,6 +10,7 @@ import {atoms as a, theme as t} from '../../theme/index.js' import {formatCount} from '../../util/formatCount.js' import {formatDate} from '../../util/formatDate.js' import {moderatePost} from '../../util/moderatePost.js' +import {sanitizeHandle} from '../../util/sanitizeHandle.js' import {Avatar} from '../Avatar.js' import {Box} from '../Box.js' import {Heart} from '../icons/Heart.js' @@ -85,7 +86,7 @@ export function Post({ {post.author.displayName || post.author.handle} - @{post.author.handle} + {sanitizeHandle(post.author.handle, '@')} diff --git a/bskyogcard/src/components/PostEmbed.tsx b/bskyogcard/src/components/PostEmbed.tsx index 1556b99985..fde6aa0496 100644 --- a/bskyogcard/src/components/PostEmbed.tsx +++ b/bskyogcard/src/components/PostEmbed.tsx @@ -8,14 +8,14 @@ import {ModeratorData} from '../data/getModeratorData.js' import {Image as ImageSource, PostData} from '../data/getPostData.js' import {atoms as a} from '../theme/index.js' import {getStarterPackImageUri} from '../util/getStarterPackImageUri.js' -import {Embed, EmbedType,parseEmbed} from '../util/parseEmbed.js' +import {Embed, EmbedType, parseEmbed} from '../util/parseEmbed.js' import {Box} from './Box.js' import {FeedCard} from './FeedCard.js' import * as Grid from './Grid.js' import {Image, SquareImage} from './Image.js' import {LinkCard} from './LinkCard.js' import {ListCard} from './ListCard.js' -import {NotQuotePost,QuotePost} from './PostEmbed/QuotePost.js' +import {NotQuotePost, QuotePost} from './PostEmbed/QuotePost.js' type CommonProps = { data: PostData @@ -188,6 +188,7 @@ export function ImagesEmbed({ embed: EmbedType<'images'> }) { const {images} = embed.view + const gutter = a.p_2xs.padding if (images.length > 0) { const imgs = images @@ -201,37 +202,139 @@ export function ImagesEmbed({ ) } else if (imgs.length === 2) { return ( - - - + + + - - + + ) } else if (imgs.length === 3) { return ( - - - + + + - - - + + + ) } else { return ( - - - - + + + + - - - + + + ) diff --git a/bskyogcard/src/components/PostEmbed/QuotePost.tsx b/bskyogcard/src/components/PostEmbed/QuotePost.tsx index a96af2cd30..de40c15e0f 100644 --- a/bskyogcard/src/components/PostEmbed/QuotePost.tsx +++ b/bskyogcard/src/components/PostEmbed/QuotePost.tsx @@ -10,6 +10,7 @@ import {PostData} from '../../data/getPostData.js' import {atoms as a, theme as t} from '../../theme/index.js' import {getModerationCauseInfo} from '../../util/getModerationCauseInfo.js' import {moderatePost} from '../../util/moderatePost.js' +import {sanitizeHandle} from '../../util/sanitizeHandle.js' import {viewRecordToPostView} from '../../util/viewRecordToPostView.js' import {Avatar} from '../Avatar.js' import {Box} from '../Box.js' @@ -71,7 +72,7 @@ export function QuotePost({ {author.displayName || author.handle} - @{author.handle} + {sanitizeHandle(author.handle, '@')} diff --git a/bskyogcard/src/theme/index.ts b/bskyogcard/src/theme/index.ts index e83439ccd4..3454ca55b0 100644 --- a/bskyogcard/src/theme/index.ts +++ b/bskyogcard/src/theme/index.ts @@ -7,6 +7,7 @@ export * as tokens from './tokens.js' const fontFamily = 'Inter' export const theme = { + name: 'light', palette: colors, atoms: { text: { @@ -86,7 +87,15 @@ export const theme = { }, } -export function style(styleObjects: Record[]) { +export type StyleObject = Record + +export type StyleProp = { + style?: StyleObject +} + +export function style( + styleObjects: (StyleObject | boolean | null | undefined)[], +) { return Object.assign({}, ...styleObjects.filter(Boolean).flat()) } diff --git a/bskyogcard/src/util/sanitizeHandle.ts b/bskyogcard/src/util/sanitizeHandle.ts new file mode 100644 index 0000000000..5c3fe7ed11 --- /dev/null +++ b/bskyogcard/src/util/sanitizeHandle.ts @@ -0,0 +1,7 @@ +export function isInvalidHandle(handle: string): boolean { + return handle === 'handle.invalid' +} + +export function sanitizeHandle(handle: string, prefix = ''): string { + return isInvalidHandle(handle) ? '⚠Invalid Handle' : `${prefix}${handle}` +} diff --git a/bskyogcard/yarn.lock b/bskyogcard/yarn.lock index 25b3d431cb..ef1e8c0bbc 100644 --- a/bskyogcard/yarn.lock +++ b/bskyogcard/yarn.lock @@ -1309,6 +1309,11 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0: resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== +prettier@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== + process-warning@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz"