4 Commits

Author SHA1 Message Date
billisdead 4a531df8bd fix: strip HTML-encoded tags (decode entities before stripping)
Release APK / build (push) Has been cancelled
The previous stripHtml decoded </> after the regex pass, so content
stored as <p>text</p> was never stripped. Now entities are
decoded first, then all tags are removed.

Also strip HTML when prefilling compose from an existing post (Edit/Repost)
so the text field shows clean content, not raw markup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 17:01:15 +02:00
billisdead 365f44dbe4 feat: official Postiz icon + strip HTML from post content display
Release APK / build (push) Has been cancelled
- Replace icon.png with official Postiz logo (1024x1024, generated from
  upstream postiz.svg at gitroomhq/postiz-app)
- Add lib/stripHtml.ts: converts <br>/<p> to newlines, strips all tags,
  decodes HTML entities
- PostCard: use stripHtml on content before truncation and display
- posts.tsx: use stripHtml for context menu preview and clipboard copy
  (API payloads keep original HTML for retry/reschedule)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 16:08:43 +02:00
billisdead 40c2ce20f3 feat: resize images to max 1920px before upload
Release APK / build (push) Has been cancelled
Add expo-image-manipulator. In pickImage(), detect if image dimensions
exceed 1920px and resize (keeping aspect ratio) + compress to JPEG 0.85.
Previously only JPEG quality was set but dimensions were untouched.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 15:58:12 +02:00
billisdead aa516667cd fix(ci): skip on Gitea + add contents:write for release creation
Release APK / build (push) Has been cancelled
- Add job condition `github.server_url == 'https://github.com'` so Gitea
  (no runner) ignores the workflow entirely
- Add `permissions: contents: write` required by softprops/action-gh-release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 13:36:13 +02:00
7 changed files with 51 additions and 8 deletions
+3
View File
@@ -7,8 +7,11 @@ on:
jobs:
build:
if: github.server_url == 'https://github.com'
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
steps:
- uses: actions/checkout@v4
+18 -2
View File
@@ -4,6 +4,7 @@ import DateTimePicker from "@react-native-community/datetimepicker";
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as Haptics from "expo-haptics";
import { Image } from "expo-image";
import * as ImageManipulator from "expo-image-manipulator";
import * as ImagePicker from "expo-image-picker";
import { fetch as expoFetch } from "expo/fetch";
import { useLocalSearchParams } from "expo-router";
@@ -146,10 +147,25 @@ export default function ComposeScreen() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ["images"],
allowsEditing: false,
quality: 0.85,
quality: 1,
});
if (!result.canceled && result.assets[0]) {
setImageUri(result.assets[0].uri);
const asset = result.assets[0];
const MAX_DIM = 1920;
const w = asset.width ?? 0;
const h = asset.height ?? 0;
const needsResize = w > MAX_DIM || h > MAX_DIM;
if (needsResize) {
const landscape = w >= h;
const resized = await ImageManipulator.manipulateAsync(
asset.uri,
[{ resize: landscape ? { width: MAX_DIM } : { height: MAX_DIM } }],
{ compress: 0.85, format: ImageManipulator.SaveFormat.JPEG }
);
setImageUri(resized.uri);
} else {
setImageUri(asset.uri);
}
setExistingMedia([]);
}
};
+5 -3
View File
@@ -22,6 +22,7 @@ import { PostCard } from "@/components/PostCard";
import { PostizPost, usePostiz } from "@/context/PostizContext";
import { useColors } from "@/hooks/useColors";
import { extractError } from "@/lib/extractError";
import { stripHtml } from "@/lib/stripHtml";
const SORT_STORAGE_KEY = "postiz_posts_sort";
@@ -154,7 +155,7 @@ export default function PostsScreen() {
router.push({
pathname: "/(tabs)/compose",
params: {
prefillContent: post.content,
prefillContent: stripHtml(post.content),
prefillIntegrationIds: integrations.map((i) => i.id).join(","),
},
});
@@ -193,14 +194,15 @@ export default function PostsScreen() {
const showContextMenu = (post: PostizPost) => {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
const preview = post.content.slice(0, 60) + (post.content.length > 60 ? "…" : "");
const plain = stripHtml(post.content);
const preview = plain.slice(0, 60) + (plain.length > 60 ? "…" : "");
const buttons: Array<{ text: string; style?: "cancel" | "destructive" | "default"; onPress?: () => void }> = [];
buttons.push({
text: "Copy text",
onPress: async () => {
await Clipboard.setStringAsync(post.content);
await Clipboard.setStringAsync(stripHtml(post.content));
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
setCopyToast(true);
setTimeout(() => setCopyToast(false), 2000);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 KiB

After

Width:  |  Height:  |  Size: 49 KiB

@@ -12,6 +12,7 @@ import {
import { Swipeable } from "react-native-gesture-handler";
import { useColors } from "@/hooks/useColors";
import { PostizPost } from "@/context/PostizContext";
import { stripHtml } from "@/lib/stripHtml";
import { StatusBadge } from "./StatusBadge";
interface PostCardProps {
@@ -118,10 +119,11 @@ export function PostCard({ post, onDelete, onLongPress, onReschedule }: PostCard
: undefined;
const integrations = post.integrations ?? (post.integration ? [post.integration] : []);
const plainContent = stripHtml(post.content);
const truncatedContent =
post.content.length > 140
? post.content.slice(0, 140) + "…"
: post.content;
plainContent.length > 140
? plainContent.slice(0, 140) + "…"
: plainContent;
return (
<Swipeable
+19
View File
@@ -0,0 +1,19 @@
export function stripHtml(html: string): string {
// Decode entities first so encoded tags like &lt;p&gt; are also stripped
let s = html
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/&nbsp;/g, " ");
// Block-level tags → newlines
s = s
.replace(/<br\s*\/?>/gi, "\n")
.replace(/<\/p>/gi, "\n")
.replace(/<\/div>/gi, "\n")
.replace(/<\/li>/gi, "\n");
// Strip all remaining tags
s = s.replace(/<[^>]+>/g, "");
return s.replace(/\n{3,}/g, "\n\n").trim();
}
+1
View File
@@ -33,6 +33,7 @@
"expo-glass-effect": "~0.1.4",
"expo-haptics": "~15.0.8",
"expo-image": "~3.0.11",
"expo-image-manipulator": "~13.0.6",
"expo-image-picker": "~17.0.9",
"expo-linear-gradient": "~15.0.8",
"expo-linking": "~8.0.10",