POWR/components/social/archive/SocialPost.tsx
DocNR 6c7c54a54e feat(social): implement link and image rendering in social posts
- Create contentParser utility to detect URLs and images in text
- Enhance EnhancedSocialPost to render links as clickable and images as embedded
- Archive unused SocialPost component following project conventions
- Update CHANGELOG.md to document the enhancement

This fixes the issue where links and images weren't properly rendered in the social feeds, improving the user experience by making content more interactive and visual.
2025-04-09 21:50:47 -04:00

243 lines
7.5 KiB
TypeScript

// components/social/archive/SocialPost.tsx
// ARCHIVED: April 9, 2025
// This component was archived because it is no longer used in the application.
// The app exclusively uses EnhancedSocialPost for rendering social feed content.
// This file is kept for historical reference only.
import React from 'react';
import { View, TouchableOpacity } from 'react-native';
import { Text } from '@/components/ui/text';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
import { Badge } from '@/components/ui/badge';
import { Heart, MessageSquare, Repeat, Share2, Zap, Clock, Dumbbell, CheckCircle } from 'lucide-react-native';
import { cn } from '@/lib/utils';
// Type definition for a social post
interface PostAuthor {
name: string;
handle: string;
avatar: string;
pubkey: string;
verified?: boolean;
}
interface WorkoutInfo {
title: string;
exercises: string[];
duration?: number;
isProgramPreview?: boolean;
}
interface PostMetrics {
likes: number;
comments: number;
reposts: number;
zaps?: number;
}
interface SocialPostProps {
post: {
id: string;
author: PostAuthor;
content: string;
createdAt: Date;
metrics: PostMetrics;
workout?: WorkoutInfo;
featured?: boolean;
};
}
export default function SocialPost({ post }: SocialPostProps) {
const [liked, setLiked] = React.useState(false);
const [reposted, setReposted] = React.useState(false);
const formatDate = (date: Date) => {
const now = new Date();
const diffInHours = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60));
if (diffInHours < 1) {
return 'now';
} else if (diffInHours < 24) {
return `${diffInHours}h`;
} else {
const diffInDays = Math.floor(diffInHours / 24);
return `${diffInDays}d`;
}
};
const formatDuration = (minutes: number) => {
if (minutes < 60) {
return `${minutes}m`;
} else {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
}
};
return (
<View className={cn(
"p-4 border-b border-border",
post.featured && "bg-primary/5"
)}>
{/* Author info */}
<View className="flex-row mb-3">
<Avatar className="h-10 w-10 mr-3" alt={`${post.author.name}'s profile picture`}>
<AvatarImage source={{ uri: post.author.avatar }} />
<AvatarFallback>
<Text>{post.author.name.substring(0, 2)}</Text>
</AvatarFallback>
</Avatar>
<View className="flex-1">
<View className="flex-row items-center">
<Text className="font-semibold">{post.author.name}</Text>
{post.author.verified && (
<CheckCircle size={14} className="text-primary ml-1" fill="currentColor" />
)}
<Text className="text-muted-foreground ml-1 text-sm">
@{post.author.handle} · {formatDate(post.createdAt)}
</Text>
</View>
<Text className="text-xs text-muted-foreground">
{post.author.pubkey.substring(0, 10)}...
</Text>
</View>
</View>
{/* Post content */}
<Text className="mb-3">{post.content}</Text>
{/* Workout card - slimmer version */}
{post.workout && (
<TouchableOpacity
activeOpacity={0.7}
className="border border-border rounded-lg mb-3 bg-muted/20 overflow-hidden"
>
<View className="p-3">
<View className="flex-row items-center mb-1">
<Dumbbell size={14} className="text-primary mr-2" />
<Text className="font-medium">{post.workout.title}</Text>
</View>
<View className="flex-row items-center justify-between">
<View className="flex-row flex-wrap flex-1">
{post.workout.isProgramPreview ? (
<Badge variant="outline" className="mr-2 mb-1">
<Text className="text-xs">Program</Text>
</Badge>
) : (
post.workout.exercises.map((ex, index) => (
<Badge
key={index}
variant="outline"
className="mr-2 mb-1"
>
<Text className="text-xs">{ex}</Text>
</Badge>
))
)}
</View>
{post.workout.duration && (
<View className="flex-row items-center">
<Clock size={14} className="text-muted-foreground mr-1" />
<Text className="text-sm text-muted-foreground">
{formatDuration(post.workout.duration)}
</Text>
</View>
)}
</View>
</View>
</TouchableOpacity>
)}
{/* Action buttons */}
<View className="flex-row justify-between items-center mt-2">
{/* Comment button */}
<TouchableOpacity
activeOpacity={0.7}
className="flex-row items-center"
>
<MessageSquare size={18} className="text-muted-foreground" />
{post.metrics.comments > 0 && (
<Text className="text-xs text-muted-foreground ml-1">
{post.metrics.comments}
</Text>
)}
</TouchableOpacity>
{/* Repost button */}
<TouchableOpacity
activeOpacity={0.7}
className="flex-row items-center"
onPress={() => setReposted(!reposted)}
>
<Repeat
size={18}
className={reposted ? "text-green-500" : "text-muted-foreground"}
/>
{(reposted || post.metrics.reposts > 0) && (
<Text
className={cn(
"text-xs ml-1",
reposted ? "text-green-500" : "text-muted-foreground"
)}
>
{reposted ? post.metrics.reposts + 1 : post.metrics.reposts}
</Text>
)}
</TouchableOpacity>
{/* Like button */}
<TouchableOpacity
activeOpacity={0.7}
className="flex-row items-center"
onPress={() => setLiked(!liked)}
>
<Heart
size={18}
className={cn(
liked ? "text-red-500" : "text-muted-foreground"
)}
fill={liked ? "#ef4444" : "none"}
/>
{(liked || post.metrics.likes > 0) && (
<Text
className={cn(
"text-xs ml-1",
liked ? "text-red-500" : "text-muted-foreground"
)}
>
{liked ? post.metrics.likes + 1 : post.metrics.likes}
</Text>
)}
</TouchableOpacity>
{/* Zap button */}
<TouchableOpacity
activeOpacity={0.7}
className="flex-row items-center"
>
<Zap
size={18}
className="text-amber-500"
/>
{post.metrics.zaps && post.metrics.zaps > 0 && (
<Text className="text-xs text-muted-foreground ml-1">
{post.metrics.zaps}
</Text>
)}
</TouchableOpacity>
{/* Share button */}
<TouchableOpacity
activeOpacity={0.7}
>
<Share2 size={18} className="text-muted-foreground" />
</TouchableOpacity>
</View>
</View>
);
}