From d3b5c40321667b81e7c05f0ec016c73276e37341 Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Sun, 31 May 2026 16:09:01 +0700 Subject: [PATCH 1/6] fix(comment-content): render reason text as comment content --- .../__tests__/comment-content.test.tsx | 7 ++--- .../comment-content/comment-content.tsx | 26 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/components/comment-content/__tests__/comment-content.test.tsx b/src/components/comment-content/__tests__/comment-content.test.tsx index a9c86b33..2e0832a6 100644 --- a/src/components/comment-content/__tests__/comment-content.test.tsx +++ b/src/components/comment-content/__tests__/comment-content.test.tsx @@ -408,11 +408,11 @@ describe('CommentContent', () => { cid: 'post-2', content: 'ignored', postCid: 'post-2', - reason: 'spam', + reason: 'duplicate of >>96', removed: true, }); expect(container.textContent).toContain('this_post_was_removed'); - expect(container.textContent).toContain('Reason: "spam"'); + expect(queryMarkdownText()).toEqual(['Reason: duplicate of >>96']); await renderContent({ cid: 'post-3', @@ -422,7 +422,7 @@ describe('CommentContent', () => { reason: 'self-delete', }); expect(container.textContent).toContain('user_deleted_this_post'); - expect(container.textContent).toContain('Reason: "self-delete"'); + expect(queryMarkdownText()).toEqual(['Reason: self-delete']); }); it('renders pending approval, ban details, and loading or failed states', async () => { @@ -442,6 +442,7 @@ describe('CommentContent', () => { expect(container.textContent).toContain('pending_mod_approval'); expect(container.textContent).toContain('pending-reason:rules violation'); + expect(queryMarkdownText()).toEqual(['queued body', 'pending-reason:rules violation']); const tooltip = container.querySelector('[data-testid="tooltip"]'); expect(tooltip?.getAttribute('title')).toContain('ban:short:music-posting.eth:2024-01-01 12:00:00'); diff --git a/src/components/comment-content/comment-content.tsx b/src/components/comment-content/comment-content.tsx index c522dc5b..c7853c76 100644 --- a/src/components/comment-content/comment-content.tsx +++ b/src/components/comment-content/comment-content.tsx @@ -156,7 +156,14 @@ const CommentContent = ({ const failedError = getFailedCommentError(resolvedPost); const failedErrorMessage = formatErrorMessageForDisplay(failedError); const shouldShowUnpublishedStateDetails = !cid && (!hasFailedState || Boolean(failedError)); + const reasonMessage = reason ? `${capitalize(t('reason'))}: ${reason}` : undefined; const pendingApprovalReason = pendingApproval ? reason?.trim() : undefined; + const pendingApprovalReasonMessage = pendingApprovalReason + ? t('pending_mod_approval_reason', { + reason: pendingApprovalReason, + interpolation: { escapeValue: false }, + }) + : undefined; const loadingString = (
@@ -193,21 +200,21 @@ const CommentContent = ({ {purged ? ( {capitalize(t('this_post_was_purged'))} ) : removed ? ( - reason ? ( + reasonMessage ? ( <> ({t('this_post_was_removed')})

- {`${capitalize(t('reason'))}: "${reason}"`} + {renderContent(reasonMessage)} ) : ( {capitalize(t('this_post_was_removed'))}. ) ) : deleted ? ( - reason ? ( + reasonMessage ? ( <> {t('user_deleted_this_post')}{' '} - {`${capitalize(t('reason'))}: "${reason}"`} + {renderContent(reasonMessage)} ) : ( {t('user_deleted_this_post')} @@ -220,18 +227,13 @@ const CommentContent = ({

({t('pending_mod_approval')}) - {pendingApprovalReason && ( + {pendingApprovalReasonMessage ? ( <>

- - {t('pending_mod_approval_reason', { - reason: pendingApprovalReason, - interpolation: { escapeValue: false }, - })} - + {renderContent(pendingApprovalReasonMessage)} - )} + ) : null} )} {((!isInPostView && content?.length > 1000 && !showFullComment) || (isInPostView && content?.length > 2000 && !showFullComment)) && !isPrivilegedAuthor && ( From 872116b13214ad2733097a3c42c6f72728e009fd Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Sun, 31 May 2026 16:27:02 +0700 Subject: [PATCH 2/6] fix(post-form): use native browser styling for flash tag select Exclude the flash tag dropdown from themed post-form select styling so it renders with the browser's default select appearance, matching the flag selector. --- src/components/post-form/post-form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx index a550bf40..c8c1a0ed 100644 --- a/src/components/post-form/post-form.tsx +++ b/src/components/post-form/post-form.tsx @@ -391,7 +391,7 @@ const PostFormFields = ({ {t('tag')} - {flashTagOptions.map((option) => (
)} diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index bd576630..ea6b6e03 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -5,11 +5,11 @@ import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { Comment, useEditedComment, useReplies, useAccount } from '@bitsocial/bitsocial-react-hooks'; import getShortAddress from '../../lib/get-short-address'; import styles from '../../views/post/post.module.css'; -import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils'; +import { CommentMediaInfo, getHasThumbnail, getMediaDimensions, getPostMediaTypeLabel, getYouTubeEmbedPostMediaFileLink } from '../../lib/utils/media-utils'; import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/post-utils'; import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { approvePendingCommentModeration, isPendingApprovalRejected, rejectPendingCommentModeration } from '../../lib/utils/pending-approval-moderation'; -import { isValidURL } from '../../lib/utils/url-utils'; +import { isValidURL, parseHttpUrl } from '../../lib/utils/url-utils'; import { isAllView, isModQueueView, isModView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; import { formatUserIDForDisplay, truncateWithEllipsisInMiddle } from '../../lib/utils/string-utils'; import useModQueueStore from '../../stores/use-mod-queue-store'; @@ -642,12 +642,16 @@ const PostMedia = ({ type = 'static gif'; } - const embedUrl = url && new URL(url); + const youtubeFileLink = getYouTubeEmbedPostMediaFileLink(commentMediaInfo); + const fileLinkUrl = youtubeFileLink ?? url; + const embedUrl = url ? parseHttpUrl(url) : null; const [showThumbnail, setShowThumbnail] = useState(true); const mediaDimensions = getMediaDimensions(commentMediaInfo); const directoryEntry = findDirectoryByAddress(directories, communityAddress); const requirePostLinkIsMedia = directoryEntry?.features?.requirePostLinkIsMedia === true; + const fileLabel = youtubeFileLink || requirePostLinkIsMedia ? t('file') : t('link'); + const mediaTypeLabel = type ? lowerCase(getPostMediaTypeLabel(commentMediaInfo, type, t)) : ''; const boardPath = communityAddress ? getBoardPath(communityAddress, directories) : undefined; const displayBoardPath = boardPath && communityAddress && boardPath !== communityAddress @@ -666,10 +670,11 @@ const PostMedia = ({ {t('board')}: {displayBoardPath}{' '} )} - {requirePostLinkIsMedia ? t('file') : t('link')}:{' '} - + {fileLabel}:{' '} + {(() => { if (spoiler) return capitalize(t('spoiler')); + if (youtubeFileLink) return truncateWithEllipsisInMiddle(youtubeFileLink); if (requirePostLinkIsMedia && url) { try { const pathParts = new URL(url).pathname.split('/'); @@ -677,10 +682,10 @@ const PostMedia = ({ if (filename && /\.\w+$/.test(filename)) return truncateWithEllipsisInMiddle(filename); } catch {} } - return truncateWithEllipsisInMiddle(url ?? ''); + return truncateWithEllipsisInMiddle(fileLinkUrl ?? ''); })()} {' '} - ({type && lowerCase(getDisplayMediaInfoType(type, t))} + ({mediaTypeLabel} {mediaDimensions && `, ${mediaDimensions}`}) {!showThumbnail && (type === 'iframe' || type === 'video' || type === 'audio') && ( diff --git a/src/lib/utils/__tests__/media-utils.test.ts b/src/lib/utils/__tests__/media-utils.test.ts index 9c1750ad..dd9a1fb4 100644 --- a/src/lib/utils/__tests__/media-utils.test.ts +++ b/src/lib/utils/__tests__/media-utils.test.ts @@ -20,9 +20,13 @@ vi.mock('@bitsocial/bitsocial-react-hooks/dist/lib/localforage-lru/index.js', () }, })); -vi.mock('../../../components/embed', () => ({ - canEmbed: (url: URL) => testState.canEmbedHosts.has(url.hostname), -})); +vi.mock('../../../components/embed/embed-utils', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + canEmbed: (url: URL) => testState.canEmbedHosts.has(url.hostname), + }; +}); vi.mock('@capacitor/core', () => ({ Capacitor: { @@ -33,7 +37,16 @@ vi.mock('@capacitor/core', () => ({ }, })); -import { fetchWebpageThumbnailIfNeeded, getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getLinkMediaInfo, getMediaDimensions } from '../media-utils'; +import { + fetchWebpageThumbnailIfNeeded, + getCommentMediaInfo, + getDisplayMediaInfoType, + getHasThumbnail, + getLinkMediaInfo, + getMediaDimensions, + getPostMediaTypeLabel, + getYouTubeEmbedPostMediaFileLink, +} from '../media-utils'; const clearMemoizedCache = (fn: unknown) => { const memoized = fn as { clear?: () => void }; @@ -102,6 +115,18 @@ describe('media-utils', () => { expect(getDisplayMediaInfoType('unknown', t)).toBe('translated:webpage'); }); + it('uses the youtube thumbnail url for post media file links and labels', () => { + const mediaInfo = { + patternThumbnailUrl: 'https://img.youtube.com/vi/abc123/0.jpg', + type: 'iframe', + url: 'https://www.youtube.com/watch?v=abc123', + }; + + expect(getYouTubeEmbedPostMediaFileLink(mediaInfo)).toBe('https://img.youtube.com/vi/abc123/0.jpg'); + expect(getPostMediaTypeLabel(mediaInfo, 'iframe', (key) => key)).toBe('youtube_video'); + expect(getYouTubeEmbedPostMediaFileLink({ type: 'iframe', url: 'https://streamable.com/clip123' })).toBeUndefined(); + }); + it('recognizes which media types expose thumbnails', () => { expect(getHasThumbnail(undefined, 'https://example.com/file.png')).toBe(false); expect(getHasThumbnail({ type: 'image', url: 'https://example.com/file.png' }, 'https://example.com/file.png')).toBe(true); @@ -156,6 +181,12 @@ describe('media-utils', () => { type: 'iframe', url: 'https://yt.example/watch?v=yt123', }); + testState.canEmbedHosts = new Set(['yewtu.be']); + expect(getLinkMediaInfo('https://yewtu.be/invidious123')).toEqual({ + patternThumbnailUrl: 'https://img.youtube.com/vi/invidious123/0.jpg', + type: 'iframe', + url: 'https://yewtu.be/invidious123', + }); }); it('builds comment media info and strips thumbnails for blacklisted domains', () => { diff --git a/src/lib/utils/media-utils.ts b/src/lib/utils/media-utils.ts index 79da5232..97f2553f 100644 --- a/src/lib/utils/media-utils.ts +++ b/src/lib/utils/media-utils.ts @@ -1,5 +1,5 @@ import localForageLru from '@bitsocial/bitsocial-react-hooks/dist/lib/localforage-lru/index.js'; -import { canEmbed } from '../../components/embed'; +import { canEmbed, getYouTubeVideoId } from '../../components/embed/embed-utils'; import memoize from 'memoizee'; import { isPrivateNetworkHostname, isValidURL, parseHttpUrl } from './url-utils'; import { Capacitor, CapacitorHttp } from '@capacitor/core'; @@ -40,6 +40,31 @@ export const getDisplayMediaInfoType = (type: string, t: Translate) => { } }; +export const getYouTubeEmbedPostMediaFileLink = (commentMediaInfo: CommentMediaInfo | undefined): string | undefined => { + if (!commentMediaInfo || commentMediaInfo.type !== 'iframe' || !commentMediaInfo.url) { + return undefined; + } + + const parsedUrl = parseHttpUrl(commentMediaInfo.url); + if (!parsedUrl || !getYouTubeVideoId(parsedUrl)) { + return undefined; + } + + return commentMediaInfo.patternThumbnailUrl || getPatternThumbnailUrl(parsedUrl); +}; + +export const getPostMediaTypeLabel = (commentMediaInfo: CommentMediaInfo | undefined, resolvedType: string | undefined, t: Translate): string => { + if (getYouTubeEmbedPostMediaFileLink(commentMediaInfo)) { + return t('youtube_video'); + } + + if (!resolvedType) { + return ''; + } + + return getDisplayMediaInfoType(resolvedType, t); +}; + export const getHasThumbnail = memoize( (commentMediaInfo: CommentMediaInfo | undefined, link: string | undefined): boolean => { if (!link || !commentMediaInfo) return false; @@ -55,17 +80,6 @@ export const getHasThumbnail = memoize( { max: 1000 }, ); -const getYouTubeVideoId = (url: URL): string | null => { - if (url.host.includes('youtu.be')) { - return url.pathname.slice(1); - } else if (url.pathname.includes('/shorts/')) { - return url.pathname.split('/shorts/')[1].split('/')[0]; - } else if (url.searchParams.has('v')) { - return url.searchParams.get('v'); - } - return null; -}; - const getPatternThumbnailUrl = (url: URL): string | undefined => { const videoId = getYouTubeVideoId(url); if (videoId) { From effe16f8a96d6eb44206bcb6ec414e4d9cf7b75e Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Mon, 1 Jun 2026 12:43:08 +0700 Subject: [PATCH 6/6] fix(embed): address youtube thumbnail review feedback Translate the youtube video label, handle mobile/music YouTube hosts as standard YouTube URLs, keep affected mocks current, and cap default Vitest workers to reduce local CPU spikes. --- package.json | 2 +- public/translations/ar/default.json | 2 +- public/translations/bn/default.json | 2 +- public/translations/cs/default.json | 2 +- public/translations/da/default.json | 2 +- public/translations/de/default.json | 2 +- public/translations/el/default.json | 2 +- public/translations/en/default.json | 2 +- public/translations/es/default.json | 2 +- public/translations/fa/default.json | 2 +- public/translations/fi/default.json | 2 +- public/translations/fil/default.json | 2 +- public/translations/fr/default.json | 2 +- public/translations/he/default.json | 2 +- public/translations/hi/default.json | 2 +- public/translations/hu/default.json | 2 +- public/translations/id/default.json | 2 +- public/translations/it/default.json | 2 +- public/translations/ja/default.json | 2 +- public/translations/ko/default.json | 2 +- public/translations/mr/default.json | 2 +- public/translations/nl/default.json | 2 +- public/translations/no/default.json | 2 +- public/translations/pl/default.json | 2 +- public/translations/pt/default.json | 2 +- public/translations/ro/default.json | 2 +- public/translations/ru/default.json | 2 +- public/translations/sq/default.json | 2 +- public/translations/sv/default.json | 2 +- public/translations/te/default.json | 2 +- public/translations/th/default.json | 2 +- public/translations/tr/default.json | 2 +- public/translations/uk/default.json | 2 +- public/translations/ur/default.json | 2 +- public/translations/vi/default.json | 2 +- public/translations/zh/default.json | 2 +- .../__tests__/post-community-address-compat.test.tsx | 3 +++ src/components/embed/__tests__/embed.test.tsx | 4 ++++ src/components/embed/embed-utils.ts | 2 +- src/components/markdown/__tests__/markdown.test.tsx | 2 +- 40 files changed, 45 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index cd4185b8..566621b7 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "build:fdroid": "cross-env PUBLIC_URL=./ GENERATE_SOURCEMAP=false VITE_APP_DISTRIBUTION=fdroid vite build", "build:preload": "vite build --config electron/vite.preload.config.js", "build-vercel": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" PUBLIC_URL=./ GENERATE_SOURCEMAP=true VITE_COMMIT_REF=$COMMIT_REF CI='' vite build", - "test": "vitest", + "test": "vitest --maxWorkers=4", "test:leaks": "vitest run --detectAsyncLeaks", "preview": "vite preview", "analyze-bundle": "cross-env PUBLIC_URL=./ GENERATE_SOURCEMAP=true vite build && npx source-map-explorer 'dist/assets/*.js'", diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index d737d0dd..b0d22087 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -400,5 +400,5 @@ "timestamp": "الطابع الزمني", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "فيديو YouTube" } diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index 5c0dc0a0..2085b7c4 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -400,5 +400,5 @@ "timestamp": "টাইমস্ট্যাম্প", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube ভিডিও" } diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index 1c141255..750ba134 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -400,5 +400,5 @@ "timestamp": "časové razítko", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "video YouTube" } diff --git a/public/translations/da/default.json b/public/translations/da/default.json index 77052199..27942ea8 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -400,5 +400,5 @@ "timestamp": "tidsstempel", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube-video" } diff --git a/public/translations/de/default.json b/public/translations/de/default.json index 4ad832d8..f4f36056 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -400,5 +400,5 @@ "timestamp": "Zeitstempel", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube-Video" } diff --git a/public/translations/el/default.json b/public/translations/el/default.json index 79dbc061..3c54ca98 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -400,5 +400,5 @@ "timestamp": "χρονοσήμανση", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "βίντεο YouTube" } diff --git a/public/translations/en/default.json b/public/translations/en/default.json index 7982fc65..4b8d99f3 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -419,5 +419,5 @@ "timestamp": "timestamp", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube video" } diff --git a/public/translations/es/default.json b/public/translations/es/default.json index d9ea586a..f5e43b78 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -400,5 +400,5 @@ "timestamp": "marca de tiempo", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "video de YouTube" } diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index 1b5e127c..075e2049 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -400,5 +400,5 @@ "timestamp": "مهر زمان", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "ویدیوی YouTube" } diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index a3d7f063..419488ed 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -400,5 +400,5 @@ "timestamp": "aikaleimaustiedosto", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube-video" } diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index f7443a60..66e459df 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -400,5 +400,5 @@ "timestamp": "timestamp", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "video sa YouTube" } diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index b1ae432b..39364684 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -400,5 +400,5 @@ "timestamp": "horodatage", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "vidéo YouTube" } diff --git a/public/translations/he/default.json b/public/translations/he/default.json index d9ac14d7..d823e75d 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -400,5 +400,5 @@ "timestamp": "חותמת זמן", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "סרטון YouTube" } diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index 9a56806b..e0546c6b 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -400,5 +400,5 @@ "timestamp": "टाइमस्टैंप", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube वीडियो" } diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index cf5531c4..30bf5d2c 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -400,5 +400,5 @@ "timestamp": "időbélyeg", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube-videó" } diff --git a/public/translations/id/default.json b/public/translations/id/default.json index 8025c053..dedc1dbb 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -400,5 +400,5 @@ "timestamp": "stempel waktu", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "video YouTube" } diff --git a/public/translations/it/default.json b/public/translations/it/default.json index 29f59e6e..2b9e9913 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -400,5 +400,5 @@ "timestamp": "marca temporale", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "video YouTube" } diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index 7e700879..c9108e4b 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -400,5 +400,5 @@ "timestamp": "タイムスタンプ", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube動画" } diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index 50b7835b..ba6556c5 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -400,5 +400,5 @@ "timestamp": "타임스탬프", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube 동영상" } diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index 5143ca9e..b5cd4a15 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -400,5 +400,5 @@ "timestamp": "टाइमस्टॅम्प", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube व्हिडिओ" } diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index 496c63f9..1177756e 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -400,5 +400,5 @@ "timestamp": "tijdstempel", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube-video" } diff --git a/public/translations/no/default.json b/public/translations/no/default.json index 28bac069..e5f06010 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -400,5 +400,5 @@ "timestamp": "tidsstempel", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube-video" } diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index 9030a235..09686d6a 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -400,5 +400,5 @@ "timestamp": "znacznik czasu", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "wideo YouTube" } diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index 240a677d..495362dd 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -400,5 +400,5 @@ "timestamp": "marca de tempo", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "vídeo do YouTube" } diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index 332a95e3..20a6c8b8 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -400,5 +400,5 @@ "timestamp": "marcă de timp", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "video YouTube" } diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index 190a428c..ee59405e 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -400,5 +400,5 @@ "timestamp": "временная метка", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "видео YouTube" } diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index 006b0806..d957627a 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -400,5 +400,5 @@ "timestamp": "vulë kohe", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "video YouTube" } diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index eabf0952..16461023 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -400,5 +400,5 @@ "timestamp": "tidsstämpel", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube-video" } diff --git a/public/translations/te/default.json b/public/translations/te/default.json index 2a708156..85819308 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -400,5 +400,5 @@ "timestamp": "టైమ్‌స్టాంప్", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube వీడియో" } diff --git a/public/translations/th/default.json b/public/translations/th/default.json index 1d7eb221..40d6aec4 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -400,5 +400,5 @@ "timestamp": "แสตมป์เวลา", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "วิดีโอ YouTube" } diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index cf77c07e..40ec38cb 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -400,5 +400,5 @@ "timestamp": "zaman damgası", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube videosu" } diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index f7106d30..bfc8b604 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -400,5 +400,5 @@ "timestamp": "часова мітка", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "відео YouTube" } diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index 1cbc5a5a..0effd6bc 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -400,5 +400,5 @@ "timestamp": "ٹائم سٹیمپ", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube ویڈیو" } diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index ee3050f5..3a8dc69d 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -400,5 +400,5 @@ "timestamp": "dấu thời gian", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "video YouTube" } diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 071c7935..a76b591e 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -400,5 +400,5 @@ "timestamp": "时间戳", "tag": "tag", "post_form_flash_upload_prompt": "Recommended SWF host: Catbox. Upload a .swf, then paste the direct https://files.catbox.moe/...swf link in Link. Other hosts must provide a direct HTTPS .swf URL with CORS enabled.", - "youtube_video": "youtube video" + "youtube_video": "YouTube 视频" } diff --git a/src/components/__tests__/post-community-address-compat.test.tsx b/src/components/__tests__/post-community-address-compat.test.tsx index c74b1898..377238e1 100644 --- a/src/components/__tests__/post-community-address-compat.test.tsx +++ b/src/components/__tests__/post-community-address-compat.test.tsx @@ -151,6 +151,8 @@ vi.mock('../../lib/utils/media-utils', () => ({ getDisplayMediaInfoType: (type?: string) => type ?? 'unknown', getHasThumbnail: () => true, getMediaDimensions: () => '100x100', + getPostMediaTypeLabel: (_commentMediaInfo: unknown, resolvedType?: string) => resolvedType ?? '', + getYouTubeEmbedPostMediaFileLink: () => undefined, })); vi.mock('../../lib/utils/post-utils', () => ({ @@ -171,6 +173,7 @@ vi.mock('../../lib/utils/pending-approval-moderation', () => ({ vi.mock('../../lib/utils/url-utils', () => ({ isValidURL: () => true, + parseHttpUrl: (value: string) => new URL(value), })); vi.mock('../../lib/utils/view-utils', () => ({ diff --git a/src/components/embed/__tests__/embed.test.tsx b/src/components/embed/__tests__/embed.test.tsx index eadcbb68..7444cecd 100644 --- a/src/components/embed/__tests__/embed.test.tsx +++ b/src/components/embed/__tests__/embed.test.tsx @@ -58,9 +58,13 @@ describe('Embed', () => { it('extracts youtube video ids for standard, short, and invidious urls', () => { expect(getYouTubeVideoId(new URL('https://www.youtube.com/watch?v=abc123'))).toBe('abc123'); + expect(getYouTubeVideoId(new URL('https://m.youtube.com/watch?v=mobile123'))).toBe('mobile123'); + expect(getYouTubeVideoId(new URL('https://music.youtube.com/watch?v=music123'))).toBe('music123'); expect(getYouTubeVideoId(new URL('https://youtu.be/short123'))).toBe('short123'); expect(getYouTubeVideoId(new URL('https://www.youtube.com/shorts/short456'))).toBe('short456'); expect(getYouTubeVideoId(new URL('https://yewtu.be/invidious789'))).toBe('invidious789'); + expect(getYouTubeVideoId(new URL('https://m.youtube.com/@channel'))).toBeNull(); + expect(getYouTubeVideoId(new URL('https://music.youtube.com/channel/UC123'))).toBeNull(); expect(getYouTubeVideoId(new URL('https://www.youtube.com/playlist?list=PL123'))).toBeNull(); }); diff --git a/src/components/embed/embed-utils.ts b/src/components/embed/embed-utils.ts index 9847e741..f9fdfc4e 100644 --- a/src/components/embed/embed-utils.ts +++ b/src/components/embed/embed-utils.ts @@ -1,4 +1,4 @@ -const STANDARD_YOUTUBE_HOSTS = new Set(['youtube.com', 'www.youtube.com', 'youtu.be', 'www.youtu.be']); +const STANDARD_YOUTUBE_HOSTS = new Set(['youtube.com', 'www.youtube.com', 'm.youtube.com', 'music.youtube.com', 'youtu.be', 'www.youtu.be']); export const youtubeHosts = new Set([ 'youtube.com', diff --git a/src/components/markdown/__tests__/markdown.test.tsx b/src/components/markdown/__tests__/markdown.test.tsx index ba568744..00ee866a 100644 --- a/src/components/markdown/__tests__/markdown.test.tsx +++ b/src/components/markdown/__tests__/markdown.test.tsx @@ -61,7 +61,7 @@ vi.mock('@floating-ui/react', () => ({ useFocus: () => ({}), useHover: () => ({ getReferenceProps: (props?: Record) => ({ - ...(props || {}), + ...props, onMouseEnter: () => testState.floatingOnOpenChange?.(true), onMouseLeave: () => testState.floatingOnOpenChange?.(false), }),