feat: UX improvements, security hardening, and code cleanup
- Extract shared extractError utility (lib/extractError.ts), remove 3 duplicate copies - Export DEFAULT_BASE_URL from PostizContext, remove duplicate in settings - Add 401 interceptor in axios client: fires UnauthorizedHandler in _layout → alert + redirect to Settings - Calendar day items now tappable: tap opens context menu (Copy / Edit / Repost) - Persist sort order (newest/oldest) across sessions via AsyncStorage - Filter chips show post count per status (Queue 3, Error 1, etc.) - Copy text action now shows a brief "Copied" toast + haptic feedback - PostCard: swipe right → Reschedule action (QUEUE posts only, amber color) - Compose: per-network char limit (Twitter 280, Instagram 2200…) with color warning at 90% - Compose: local draft save/restore via AsyncStorage with restore banner on open - Compose: prefillImagePath/prefillImageId params allow Edit/Repost to carry over existing media Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,29 +15,9 @@ import {
|
||||
} from "react-native";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { usePostiz } from "@/context/PostizContext";
|
||||
import { usePostiz, DEFAULT_BASE_URL } from "@/context/PostizContext";
|
||||
import { useColors } from "@/hooks/useColors";
|
||||
|
||||
const DEFAULT_BASE_URL = "https://postiz.gyozamancave.fr/api/public/v1";
|
||||
|
||||
function extractAxiosError(err: unknown): string {
|
||||
if (axios.isAxiosError(err)) {
|
||||
const status = err.response?.status;
|
||||
const data = err.response?.data;
|
||||
if (data) {
|
||||
const body =
|
||||
typeof data === "string"
|
||||
? data.slice(0, 200)
|
||||
: (data?.message ?? data?.error ?? JSON.stringify(data)).toString().slice(0, 200);
|
||||
return status ? `HTTP ${status}: ${body}` : body;
|
||||
}
|
||||
if (status) return `HTTP ${status} — ${err.message}`;
|
||||
if (err.code === "ECONNABORTED") return "Request timed out (10s). Check that the URL is reachable.";
|
||||
if (err.message) return err.message;
|
||||
}
|
||||
if (err instanceof Error) return err.message;
|
||||
return "Unknown error";
|
||||
}
|
||||
import { extractError } from "@/lib/extractError";
|
||||
|
||||
export default function SettingsScreen() {
|
||||
const colors = useColors();
|
||||
@@ -98,7 +78,7 @@ export default function SettingsScreen() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
lastError = extractAxiosError(err);
|
||||
lastError = extractError(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +99,7 @@ export default function SettingsScreen() {
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||
Alert.alert("Saved", "Settings saved successfully.");
|
||||
} catch (err: unknown) {
|
||||
Alert.alert("Error", `Failed to save settings.\n${extractAxiosError(err)}`);
|
||||
Alert.alert("Error", `Failed to save settings.\n${extractError(err)}`);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user