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:
@@ -6,15 +6,16 @@ import {
|
||||
useFonts,
|
||||
} from "@expo-google-fonts/inter";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { Stack } from "expo-router";
|
||||
import { router, Stack } from "expo-router";
|
||||
import * as SplashScreen from "expo-splash-screen";
|
||||
import React, { useEffect } from "react";
|
||||
import { Alert } from "react-native";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import { KeyboardProvider } from "react-native-keyboard-controller";
|
||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||
|
||||
import { ErrorBoundary } from "@/components/ErrorBoundary";
|
||||
import { PostizProvider } from "@/context/PostizContext";
|
||||
import { PostizProvider, usePostiz } from "@/context/PostizContext";
|
||||
import { useNotifications } from "@/hooks/useNotifications";
|
||||
|
||||
SplashScreen.preventAutoHideAsync();
|
||||
@@ -33,6 +34,28 @@ function NotificationBootstrap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
function UnauthorizedHandler() {
|
||||
const { unauthorized, clearUnauthorized } = usePostiz();
|
||||
useEffect(() => {
|
||||
if (!unauthorized) return;
|
||||
Alert.alert(
|
||||
"API key invalid",
|
||||
"Your API key was rejected (401). Update it in Settings.",
|
||||
[
|
||||
{
|
||||
text: "Open Settings",
|
||||
onPress: () => {
|
||||
clearUnauthorized();
|
||||
router.push("/(tabs)/settings");
|
||||
},
|
||||
},
|
||||
{ text: "Dismiss", style: "cancel", onPress: clearUnauthorized },
|
||||
]
|
||||
);
|
||||
}, [unauthorized, clearUnauthorized]);
|
||||
return null;
|
||||
}
|
||||
|
||||
function RootLayoutNav() {
|
||||
return (
|
||||
<Stack screenOptions={{ headerBackTitle: "Back" }}>
|
||||
@@ -63,6 +86,7 @@ export default function RootLayout() {
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<PostizProvider>
|
||||
<NotificationBootstrap />
|
||||
<UnauthorizedHandler />
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<KeyboardProvider>
|
||||
<RootLayoutNav />
|
||||
|
||||
Reference in New Issue
Block a user