Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 146 additions & 15 deletions src/pages/Stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Heart, MessageCircle, Flag, Loader2 } from 'lucide-react';
import { Heart, MessageCircle, Flag, Loader2, X } from 'lucide-react';
import { toast } from 'react-hot-toast';
import { auth } from '../lib/firebase';
import {
Expand Down Expand Up @@ -130,6 +130,9 @@ export default function Stories() {
const [expandedStoryId, setExpandedStoryId] = useState<string | null>(null);
const [sortOption, setSortOption] = useState<'newest' | 'oldest' | 'likes'>('newest');
const [searchTerm, setSearchTerm] = useState('');

// Added state for handling the popup modal selection
const [selectedStoryForPopup, setSelectedStoryForPopup] = useState<Story | null>(null);

useEffect(() => {
fetchStories();
Expand Down Expand Up @@ -239,6 +242,11 @@ export default function Stories() {
setStories(prevStories => prevStories.map(s =>
s.id === storyId ? { ...s, reactionsCount: (s.reactionsCount || 0) + 1 } : s
));

// Keep the modal counter updated if it's currently showing this story
if (selectedStoryForPopup && selectedStoryForPopup.id === storyId) {
setSelectedStoryForPopup(prev => prev ? { ...prev, reactionsCount: (prev.reactionsCount || 0) + 1 } : null);
}
} catch (error) {
console.error('Error adding reaction:', error);
toast.error('Failed to add reaction.');
Expand Down Expand Up @@ -403,6 +411,11 @@ export default function Stories() {
return filtered;
}

// Helper variables for popup content translation rendering
const popupTargetLang = selectedStoryForPopup ? (targetLanguages[selectedStoryForPopup.id] || 'original') : 'original';
const popupTranslation = selectedStoryForPopup ? translatedStories[selectedStoryForPopup.id]?.[popupTargetLang] : null;
const popupDisplayTitle = selectedStoryForPopup && popupTargetLang !== 'original' && popupTranslation ? popupTranslation.title : selectedStoryForPopup?.title;
const popupDisplayContent = selectedStoryForPopup && popupTargetLang !== 'original' && popupTranslation ? popupTranslation.content : selectedStoryForPopup?.content;

return (
<div className='bg-white dark:bg-gray-900 min-h-screen'>
Expand Down Expand Up @@ -511,9 +524,13 @@ export default function Stories() {
const shouldTruncate = displayContent.length > 400 && !isExpanded;

return (
<div key={story.id} className="bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden flex flex-col transition-shadow hover:shadow-xl">
<div
key={story.id}
onClick={() => setSelectedStoryForPopup(story)}
className="bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden flex flex-col transition-shadow hover:shadow-xl cursor-pointer"
>
{/* Card Header with Language Selector */}
<div className="p-4 border-b border-gray-100 dark:border-gray-700 flex justify-between items-center">
<div className="p-4 border-b border-gray-100 dark:border-gray-700 flex justify-between items-center" onClick={(e) => e.stopPropagation()}>
<h2 className="text-xl font-semibold text-gray-800 dark:text-white flex-grow mr-2 truncate">{displayTitle}</h2>
<div className="flex items-center flex-shrink-0">
{isLoading && <Loader2 className="animate-spin mr-2 h-4 w-4 text-gray-500 dark:text-gray-400" />}
Expand All @@ -534,7 +551,7 @@ export default function Stories() {

<div className="px-4 pt-2 pb-1">
<RiskBadge level={story.risk_level} />
</div>
</div>
{/* Card Body */}
<div className="p-4 flex-grow">
<p className="text-gray-700 dark:text-gray-300 mb-4 text-sm leading-relaxed">
Expand All @@ -543,20 +560,15 @@ export default function Stories() {
: displayContent}
{shouldTruncate && (
<button
onClick={() => setExpandedStoryId(story.id)}
onClick={(e) => {
e.stopPropagation();
setSelectedStoryForPopup(story);
}}
className="ml-2 text-pink-600 hover:text-pink-700 dark:text-pink-400 dark:hover:text-pink-300 font-semibold transition-colors duration-200"
>
Read More
</button>
)}
{isExpanded && displayContent.length > 400 && (
<button
onClick={() => setExpandedStoryId(null)}
className="ml-2 text-purple-600 hover:text-purple-700 dark:text-purple-400 dark:hover:text-purple-300 font-semibold transition-colors duration-200"
>
Show Less
</button>
)}
</p>

{/* Media Display - only show when expanded */}
Expand Down Expand Up @@ -609,7 +621,7 @@ export default function Stories() {
</div>

{/* Card Footer - Actions */}
<div className="p-4 bg-gray-50 dark:bg-gray-700 border-t border-gray-100 dark:border-gray-600 flex items-center justify-between text-sm text-gray-500 dark:text-gray-400">
<div className="p-4 bg-gray-50 dark:bg-gray-700 border-t border-gray-100 dark:border-gray-600 flex items-center justify-between text-sm text-gray-500 dark:text-gray-400" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center space-x-4">
{/* Like Button */}
<button
Expand Down Expand Up @@ -654,6 +666,125 @@ export default function Stories() {
<p className="text-center text-gray-500 dark:text-gray-400 mt-12 text-lg">No stories found matching your search or selected filters.</p>
)}
</div>

{/* --- Detailed Story Popup Modal Section --- */}
{selectedStoryForPopup && (
<div
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm transition-opacity"
onClick={() => setSelectedStoryForPopup(null)}
>
<div
className="bg-white dark:bg-gray-800 w-full max-w-2xl rounded-2xl shadow-2xl flex flex-col overflow-hidden transform transition-all animate-in fade-in zoom-in-95 duration-200 max-h-[85vh]"
onClick={(e) => e.stopPropagation()}
>
{/* Modal Header */}
<div className="p-6 border-b border-gray-100 dark:border-gray-700 flex justify-between items-start">
<div>
<div className="mb-2">
<RiskBadge level={selectedStoryForPopup.risk_level} />
</div>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white leading-tight">
{popupDisplayTitle}
</h2>
</div>
<button
onClick={() => setSelectedStoryForPopup(null)}
className="p-1 rounded-full text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
>
<X size={24} />
</button>
</div>

{/* Modal Body (Scrollable Container) */}
<div className="p-6 overflow-y-auto space-y-4 flex-grow">
<p className="text-gray-700 dark:text-gray-300 text-base leading-relaxed whitespace-pre-wrap">
{popupDisplayContent}
</p>

{/* Media Display Inside Popup */}
{selectedStoryForPopup.media_urls && selectedStoryForPopup.media_urls.length > 0 && (
<div className="mt-4 space-y-3">
{selectedStoryForPopup.media_urls.map((url: string, index: number) => {
const isImage = url.match(/\.(jpeg|jpg|gif|png)$/i);
const isVideo = url.match(/\.(mp4|webm|ogg)$/i);
const isAudio = url.match(/\.(mp3|wav|ogg)$/i);

return (
<div key={index} className="relative rounded-lg overflow-hidden border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-900">
{isImage && (
<img
src={url}
alt={`Media ${index + 1}`}
className="w-full max-h-96 object-contain mx-auto"
/>
)}
{isVideo && (
<video controls className="w-full max-h-96 object-contain bg-black mx-auto">
<source src={url} type="video/mp4" />
Your browser does not support the video tag.
</video>
)}
{isAudio && (
<audio controls className="w-full p-4">
<source src={url} type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
)}
</div>
);
})}
</div>
)}

{/* Tags inside Popup */}
<div className="flex flex-wrap gap-2 pt-2">
{(selectedStoryForPopup.tags || []).map((tag: string) => (
<span
key={tag}
className="bg-pink-100 dark:bg-pink-900 text-pink-700 dark:text-pink-300 px-3 py-1 rounded-full text-xs font-semibold"
>
{tag}
</span>
))}
</div>
</div>

{/* Modal Footer Controls */}
<div className="p-4 bg-gray-50 dark:bg-gray-700 border-t border-gray-100 dark:border-gray-600 flex items-center justify-between text-sm text-gray-500 dark:text-gray-400">
<div className="flex items-center space-x-6">
<button
onClick={() => handleReaction(selectedStoryForPopup.id, 'heart')}
className="flex items-center space-x-2 text-pink-600 hover:text-pink-700 dark:text-pink-400 dark:hover:text-pink-300 transition-colors group text-base"
title="Like"
>
<Heart size={22} className="group-hover:fill-current" />
<span className="font-bold">{selectedStoryForPopup.reactionsCount || 0}</span>
</button>
<button
onClick={handleSupport}
className="flex items-center space-x-2 text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 transition-colors group text-base"
title="Send Support"
>
<MessageCircle size={22} className="group-hover:fill-current" />
<span className="font-medium text-sm">Send Support</span>
</button>
</div>
<div className="flex items-center space-x-4">
<span className="text-xs italic">
By Anon_{typeof selectedStoryForPopup.author_id === 'string' ? selectedStoryForPopup.author_id.slice(0, 6) : '...'}
</span>
<button
onClick={() => handleReport(selectedStoryForPopup.id)}
className="text-gray-400 hover:text-red-600 dark:hover:text-red-400 transition-colors"
title="Report story"
>
<Flag size={18} />
</button>
</div>
</div>
</div>
</div>
)}
</div>
);
}
}
Loading