Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb64b671d0 | |||
| 0cf5800463 |
@@ -68,12 +68,11 @@ export default function ComposeScreen() {
|
|||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { workspaces, clients, isConfigured } = usePostiz();
|
const { workspaces, clients, isConfigured } = usePostiz();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { prefillContent, prefillIntegrationIds, prefillImagePath, prefillImageId } =
|
const { prefillContent, prefillIntegrationIds, prefillImages } =
|
||||||
useLocalSearchParams<{
|
useLocalSearchParams<{
|
||||||
prefillContent?: string;
|
prefillContent?: string;
|
||||||
prefillIntegrationIds?: string;
|
prefillIntegrationIds?: string;
|
||||||
prefillImagePath?: string;
|
prefillImages?: string;
|
||||||
prefillImageId?: string;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [content, setContent] = useState("");
|
const [content, setContent] = useState("");
|
||||||
@@ -95,12 +94,23 @@ export default function ComposeScreen() {
|
|||||||
if (prefillIntegrationIds) {
|
if (prefillIntegrationIds) {
|
||||||
setSelectedChannels(String(prefillIntegrationIds).split(",").filter(Boolean));
|
setSelectedChannels(String(prefillIntegrationIds).split(",").filter(Boolean));
|
||||||
}
|
}
|
||||||
if (prefillImagePath && prefillImageId) {
|
if (prefillImages && workspaces.length > 0) {
|
||||||
// Prefilled image has unknown workspace; associate with first workspace
|
try {
|
||||||
const wsId = workspaces[0]?.id ?? "";
|
const images: Array<{ id: string; path: string }> = JSON.parse(String(prefillImages));
|
||||||
setMediaItems([{ type: "uploaded", id: String(prefillImageId), path: String(prefillImagePath), workspaceId: wsId }]);
|
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(() => {
|
useEffect(() => {
|
||||||
if (prefillContent) return;
|
if (prefillContent) return;
|
||||||
@@ -757,6 +767,7 @@ export default function ComposeScreen() {
|
|||||||
workspaces={workspaces}
|
workspaces={workspaces}
|
||||||
maxSelect={MAX_IMAGES - mediaItems.length}
|
maxSelect={MAX_IMAGES - mediaItems.length}
|
||||||
onClose={() => setShowMediaLibrary(false)}
|
onClose={() => setShowMediaLibrary(false)}
|
||||||
|
onPickFromDevice={() => { setShowMediaLibrary(false); pickImage(); }}
|
||||||
onSelect={(items: LibraryMediaItem[]) => {
|
onSelect={(items: LibraryMediaItem[]) => {
|
||||||
setMediaItems((prev) =>
|
setMediaItems((prev) =>
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -152,13 +152,14 @@ export default function PostsScreen() {
|
|||||||
|
|
||||||
const handlePrefillCompose = (post: PostizPost) => {
|
const handlePrefillCompose = (post: PostizPost) => {
|
||||||
const integrations = post.integrations ?? (post.integration ? [post.integration] : []);
|
const integrations = post.integrations ?? (post.integration ? [post.integration] : []);
|
||||||
router.push({
|
const params: Record<string, string> = {
|
||||||
pathname: "/(tabs)/compose",
|
prefillContent: stripHtml(post.content),
|
||||||
params: {
|
prefillIntegrationIds: integrations.map((i) => i.id).join(","),
|
||||||
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) => {
|
const startReschedule = (post: PostizPost) => {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ interface Props {
|
|||||||
maxSelect: number;
|
maxSelect: number;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSelect: (items: LibraryMediaItem[]) => void;
|
onSelect: (items: LibraryMediaItem[]) => void;
|
||||||
|
onPickFromDevice?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveUrl(path: string, baseUrl: string): string {
|
function resolveUrl(path: string, baseUrl: string): string {
|
||||||
@@ -43,7 +44,7 @@ function resolveUrl(path: string, baseUrl: string): string {
|
|||||||
return `${origin}/${path.replace(/^\//, "")}`;
|
return `${origin}/${path.replace(/^\//, "")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MediaLibraryModal({ visible, workspaces, defaultWorkspaceId, maxSelect, onClose, onSelect }: Props) {
|
export function MediaLibraryModal({ visible, workspaces, defaultWorkspaceId, maxSelect, onClose, onSelect, onPickFromDevice }: Props) {
|
||||||
const colors = useColors();
|
const colors = useColors();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const [activeId, setActiveId] = useState<string>("");
|
const [activeId, setActiveId] = useState<string>("");
|
||||||
@@ -66,12 +67,22 @@ export function MediaLibraryModal({ visible, workspaces, defaultWorkspaceId, max
|
|||||||
if (!activeWorkspace) return;
|
if (!activeWorkspace) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
const apiBase = activeWorkspace.baseUrl.replace(/\/public\/v1$/, "");
|
||||||
|
const url = `${apiBase}/media?page=0&search=`;
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
const res = await globalThis.fetch(`${activeWorkspace.baseUrl}/media`, {
|
const res = await globalThis.fetch(url, {
|
||||||
headers: { Authorization: activeWorkspace.apiKey },
|
headers: { Authorization: activeWorkspace.apiKey },
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
if (!res.ok) {
|
||||||
|
if (res.status === 401 || res.status === 403) {
|
||||||
|
throw new Error("SESSION_REQUIRED");
|
||||||
|
}
|
||||||
|
if (res.status === 404) {
|
||||||
|
throw new Error("ENDPOINT_NOT_FOUND");
|
||||||
|
}
|
||||||
|
throw new Error(`HTTP ${res.status} — ${url}`);
|
||||||
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const list: RawMediaItem[] = Array.isArray(data)
|
const list: RawMediaItem[] = Array.isArray(data)
|
||||||
? data
|
? data
|
||||||
@@ -163,6 +174,29 @@ export function MediaLibraryModal({ visible, workspaces, defaultWorkspaceId, max
|
|||||||
<View style={styles.centered}>
|
<View style={styles.centered}>
|
||||||
<ActivityIndicator color={colors.primary} size="large" />
|
<ActivityIndicator color={colors.primary} size="large" />
|
||||||
</View>
|
</View>
|
||||||
|
) : error === "SESSION_REQUIRED" ? (
|
||||||
|
<View style={styles.centered}>
|
||||||
|
<Feather name="lock" size={28} color={colors.mutedForeground} />
|
||||||
|
<Text style={[styles.errorText, { color: colors.mutedForeground }]}>
|
||||||
|
{"Media library requires a web session.\nAPI key access is not supported by Postiz."}
|
||||||
|
</Text>
|
||||||
|
{onPickFromDevice && (
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => { onClose(); onPickFromDevice(); }}
|
||||||
|
style={[styles.retryBtn, { backgroundColor: colors.primary }]}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
>
|
||||||
|
<Text style={[styles.retryText, { color: colors.primaryForeground }]}>Use device gallery</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
) : error === "ENDPOINT_NOT_FOUND" ? (
|
||||||
|
<View style={styles.centered}>
|
||||||
|
<Feather name="slash" size={28} color={colors.mutedForeground} />
|
||||||
|
<Text style={[styles.errorText, { color: colors.mutedForeground }]}>
|
||||||
|
{"Media library endpoint not found on this server."}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
) : error ? (
|
) : error ? (
|
||||||
<View style={styles.centered}>
|
<View style={styles.centered}>
|
||||||
<Feather name="alert-circle" size={28} color={colors.error} />
|
<Feather name="alert-circle" size={28} color={colors.error} />
|
||||||
|
|||||||
@@ -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
|
// Decode entities first so encoded tags like <p> are also stripped
|
||||||
let s = html
|
let s = html
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
|
|||||||
Reference in New Issue
Block a user