From 0cf58004634efd56fdc81719877ad07be6a739a2 Mon Sep 17 00:00:00 2001 From: billisdead Date: Tue, 16 Jun 2026 08:31:34 +0200 Subject: [PATCH] fix(mobile): restore images on repost, improve media library 404 error, null-safe stripHtml - Pass post.image[] as JSON prefillImages param when prefilling compose from an existing post (repost/edit/retry), replacing the broken single-image prefillImagePath/prefillImageId approach - MediaLibraryModal now shows the attempted URL and a clear explanation on 404, making it easier to diagnose if the Postiz version does not expose GET /media - stripHtml accepts null/undefined input and returns "" instead of throwing Co-Authored-By: Claude Sonnet 4.6 --- .../postiz-mobile/app/(tabs)/compose.tsx | 26 +++++++++++++------ artifacts/postiz-mobile/app/(tabs)/posts.tsx | 15 ++++++----- .../components/MediaLibraryModal.tsx | 12 +++++++-- artifacts/postiz-mobile/lib/stripHtml.ts | 3 ++- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/artifacts/postiz-mobile/app/(tabs)/compose.tsx b/artifacts/postiz-mobile/app/(tabs)/compose.tsx index 30edb5e..f774cc2 100644 --- a/artifacts/postiz-mobile/app/(tabs)/compose.tsx +++ b/artifacts/postiz-mobile/app/(tabs)/compose.tsx @@ -68,12 +68,11 @@ export default function ComposeScreen() { const insets = useSafeAreaInsets(); const { workspaces, clients, isConfigured } = usePostiz(); const queryClient = useQueryClient(); - const { prefillContent, prefillIntegrationIds, prefillImagePath, prefillImageId } = + const { prefillContent, prefillIntegrationIds, prefillImages } = useLocalSearchParams<{ prefillContent?: string; prefillIntegrationIds?: string; - prefillImagePath?: string; - prefillImageId?: string; + prefillImages?: string; }>(); const [content, setContent] = useState(""); @@ -95,12 +94,23 @@ export default function ComposeScreen() { if (prefillIntegrationIds) { setSelectedChannels(String(prefillIntegrationIds).split(",").filter(Boolean)); } - if (prefillImagePath && prefillImageId) { - // Prefilled image has unknown workspace; associate with first workspace - const wsId = workspaces[0]?.id ?? ""; - setMediaItems([{ type: "uploaded", id: String(prefillImageId), path: String(prefillImagePath), workspaceId: wsId }]); + if (prefillImages && workspaces.length > 0) { + try { + const images: Array<{ id: string; path: string }> = JSON.parse(String(prefillImages)); + const wsId = workspaces[0]?.id ?? ""; + setMediaItems( + images + .filter((img) => img?.id && img?.path) + .map((img): MediaItem => ({ + type: "uploaded", + id: img.id, + path: img.path, + workspaceId: wsId, + })) + ); + } catch {} } - }, [prefillContent, prefillIntegrationIds, prefillImagePath, prefillImageId, workspaces]); + }, [prefillContent, prefillIntegrationIds, prefillImages, workspaces]); useEffect(() => { if (prefillContent) return; diff --git a/artifacts/postiz-mobile/app/(tabs)/posts.tsx b/artifacts/postiz-mobile/app/(tabs)/posts.tsx index 0e17c69..6e4e2f8 100644 --- a/artifacts/postiz-mobile/app/(tabs)/posts.tsx +++ b/artifacts/postiz-mobile/app/(tabs)/posts.tsx @@ -152,13 +152,14 @@ export default function PostsScreen() { const handlePrefillCompose = (post: PostizPost) => { const integrations = post.integrations ?? (post.integration ? [post.integration] : []); - router.push({ - pathname: "/(tabs)/compose", - params: { - prefillContent: stripHtml(post.content), - prefillIntegrationIds: integrations.map((i) => i.id).join(","), - }, - }); + const params: Record = { + prefillContent: stripHtml(post.content), + prefillIntegrationIds: integrations.map((i) => i.id).join(","), + }; + if (post.image?.length) { + params.prefillImages = JSON.stringify(post.image); + } + router.push({ pathname: "/(tabs)/compose", params }); }; const startReschedule = (post: PostizPost) => { diff --git a/artifacts/postiz-mobile/components/MediaLibraryModal.tsx b/artifacts/postiz-mobile/components/MediaLibraryModal.tsx index 6aa2292..aaa61df 100644 --- a/artifacts/postiz-mobile/components/MediaLibraryModal.tsx +++ b/artifacts/postiz-mobile/components/MediaLibraryModal.tsx @@ -66,12 +66,20 @@ export function MediaLibraryModal({ visible, workspaces, defaultWorkspaceId, max if (!activeWorkspace) return; setLoading(true); setError(null); + const url = `${activeWorkspace.baseUrl}/media`; try { // eslint-disable-next-line no-undef - const res = await globalThis.fetch(`${activeWorkspace.baseUrl}/media`, { + const res = await globalThis.fetch(url, { headers: { Authorization: activeWorkspace.apiKey }, }); - if (!res.ok) throw new Error(`HTTP ${res.status}`); + if (!res.ok) { + if (res.status === 404) { + throw new Error( + `Media listing endpoint not found (404).\nURL tried: ${url}\n\nThis feature requires Postiz to expose GET /media in its public API. Your version may not support it yet.` + ); + } + throw new Error(`HTTP ${res.status} — ${url}`); + } const data = await res.json(); const list: RawMediaItem[] = Array.isArray(data) ? data diff --git a/artifacts/postiz-mobile/lib/stripHtml.ts b/artifacts/postiz-mobile/lib/stripHtml.ts index af3c46c..5c39eaa 100644 --- a/artifacts/postiz-mobile/lib/stripHtml.ts +++ b/artifacts/postiz-mobile/lib/stripHtml.ts @@ -1,4 +1,5 @@ -export function stripHtml(html: string): string { +export function stripHtml(html: string | null | undefined): string { + if (!html) return ""; // Decode entities first so encoded tags like <p> are also stripped let s = html .replace(/&/g, "&")