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:
@@ -18,6 +18,7 @@ interface PostCardProps {
|
||||
post: PostizPost;
|
||||
onDelete: (id: string) => Promise<void>;
|
||||
onLongPress: (post: PostizPost) => void;
|
||||
onReschedule?: (post: PostizPost) => void;
|
||||
}
|
||||
|
||||
function formatDate(dateStr: string): string {
|
||||
@@ -44,7 +45,7 @@ function getNetworkIcon(type?: string): React.ComponentProps<typeof Feather>["na
|
||||
return "globe";
|
||||
}
|
||||
|
||||
export function PostCard({ post, onDelete, onLongPress }: PostCardProps) {
|
||||
export function PostCard({ post, onDelete, onLongPress, onReschedule }: PostCardProps) {
|
||||
const colors = useColors();
|
||||
const swipeRef = useRef<Swipeable>(null);
|
||||
|
||||
@@ -88,6 +89,34 @@ export function PostCard({ post, onDelete, onLongPress }: PostCardProps) {
|
||||
);
|
||||
};
|
||||
|
||||
const renderLeftActions =
|
||||
post.state === "QUEUE" && onReschedule
|
||||
? (
|
||||
_progress: Animated.AnimatedInterpolation<number>,
|
||||
dragX: Animated.AnimatedInterpolation<number>
|
||||
) => {
|
||||
const scale = dragX.interpolate({
|
||||
inputRange: [0, 80],
|
||||
outputRange: [0.8, 1],
|
||||
extrapolate: "clamp",
|
||||
});
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.rescheduleAction, { backgroundColor: colors.warning }]}
|
||||
onPress={() => {
|
||||
swipeRef.current?.close();
|
||||
onReschedule(post);
|
||||
}}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Animated.View style={{ transform: [{ scale }] }}>
|
||||
<Feather name="clock" size={20} color="#fff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const integrations = post.integrations ?? (post.integration ? [post.integration] : []);
|
||||
const truncatedContent =
|
||||
post.content.length > 140
|
||||
@@ -98,7 +127,9 @@ export function PostCard({ post, onDelete, onLongPress }: PostCardProps) {
|
||||
<Swipeable
|
||||
ref={swipeRef}
|
||||
renderRightActions={renderRightActions}
|
||||
renderLeftActions={renderLeftActions}
|
||||
rightThreshold={40}
|
||||
leftThreshold={40}
|
||||
friction={2}
|
||||
>
|
||||
<TouchableOpacity
|
||||
@@ -218,4 +249,9 @@ const styles = StyleSheet.create({
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
rescheduleAction: {
|
||||
width: 72,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user