diff --git a/src/components/Comment copy.jsx b/src/components/Comment copy.jsx new file mode 100644 index 0000000..3357618 --- /dev/null +++ b/src/components/Comment copy.jsx @@ -0,0 +1,371 @@ +import React, { useEffect, useState } from 'react'; +import MDEditor, { commands } from '@uiw/react-md-editor'; +import { Card } from "./ui/card"; +import { Label } from "./ui/label"; +import { Button } from "./ui/button"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./ui/dialog"; +import { Input } from "./ui/input"; +import { useIsLoggedIn } from '../lib/isLoggedIn'; + +const COMMENTS_API_URL = 'https://host-api.cs1.hz.siliconpin.com/v1/comments/'; +const MINIO_UPLOAD_URL = 'https://hostapi2.cs1.hz.siliconpin.com/api/storage/upload'; + +export default function Comment(props) { + const [comments, setComments] = useState([]); + const [newComment, setNewComment] = useState({ comment: '' }); + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitSuccess, setSubmitSuccess] = useState(false); + const [submitError, setSubmitError] = useState(''); + const [isLoadingComments, setIsLoadingComments] = useState(true); + const [editorMode, setEditorMode] = useState('edit'); + const [imageDialogOpen, setImageDialogOpen] = useState(false); + const [imageUrlInput, setImageUrlInput] = useState(''); + const [imageUploadFile, setImageUploadFile] = useState(null); + const [imageUploadPreview, setImageUploadPreview] = useState(''); + const [uploadProgress, setUploadProgress] = useState(0); + const { isLoggedIn, loading, error, sessionData } = useIsLoggedIn(); + + // Custom image command for MDEditor + const customImageCommand = { + name: 'image', + keyCommand: 'image', + buttonProps: { 'aria-label': 'Insert image' }, + icon: ( + + + + ), + execute: () => { + setImageDialogOpen(true); + }, + }; + + // Get all default commands and replace the image command + const allCommands = commands.getCommands().map(cmd => { + if (cmd.name === 'image') { + return customImageCommand; + } + return cmd; + }); + + // Load comments when component mounts + useEffect(() => { + if (props.topicId) { + fetchComments(props.topicId); + } + }, [props.topicId]); + + const fetchComments = async (topicId) => { + setIsLoadingComments(true); + try { + const response = await fetch(`${COMMENTS_API_URL}?topicId=${topicId}`, { + method: 'GET', + credentials: 'include', + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || 'Failed to fetch comments'); + } + + if (data.success && data.comments) { + setComments(data.comments); + } else { + throw new Error('Invalid response format'); + } + } catch (error) { + console.error('Error fetching comments:', error); + setSubmitError('Failed to load comments'); + } finally { + setIsLoadingComments(false); + } + }; + + // Upload file to MinIO + const uploadToMinIO = async (file) => { + const formData = new FormData(); + formData.append('file', file); + formData.append('api_key', 'wweifwehfwfhwhtuyegbvijvbfvegfreyf'); + + try { + const response = await fetch(MINIO_UPLOAD_URL, { + method: 'POST', + body: formData, + credentials: 'include' + }); + + if (!response.ok) { + throw new Error('Upload failed'); + } + + const data = await response.json(); + return data.publicUrl; + } catch (error) { + console.error('Upload error:', error); + throw error; + } + }; + + // Handle image URL insertion + const handleInsertImageUrl = () => { + if (imageUrlInput) { + const imgMarkdown = `![Image](${imageUrlInput})`; + setNewComment(prev => ({ + ...prev, + comment: prev.comment ? `${prev.comment}\n${imgMarkdown}` : imgMarkdown + })); + setImageDialogOpen(false); + setImageUrlInput(''); + } + }; + + // Handle image file selection + const handleImageFileSelect = (e) => { + const file = e.target.files[0]; + if (file) { + setImageUploadFile(file); + + // Create preview + const reader = new FileReader(); + reader.onload = () => { + setImageUploadPreview(reader.result); + }; + reader.readAsDataURL(file); + } + }; + + // Upload image file to MinIO and insert into editor + const handleImageUpload = async () => { + if (!imageUploadFile) return; + + try { + setIsSubmitting(true); + setUploadProgress(0); + + const uploadedUrl = await uploadToMinIO(imageUploadFile); + + // Insert markdown for the uploaded image + const imgMarkdown = `![Image](${uploadedUrl})`; + setNewComment(prev => ({ + ...prev, + comment: prev.comment ? `${prev.comment}\n${imgMarkdown}` : imgMarkdown + })); + + setImageDialogOpen(false); + setImageUploadFile(null); + setImageUploadPreview(''); + } catch (error) { + setSubmitError('Failed to upload image: ' + error.message); + } finally { + setIsSubmitting(false); + } + }; + + const handleSubmitComment = async (e) => { + e.preventDefault(); + setIsSubmitting(true); + setSubmitError(''); + + try { + const response = await fetch(`${COMMENTS_API_URL}?query=new-comment`, { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...newComment, + topicId: props.topicId + }), + }); + + if (response.ok) { + setNewComment({ comment: '' }); + setSubmitSuccess(true); + setTimeout(() => setSubmitSuccess(false), 3000); + + // Refresh comments after successful submission + await fetchComments(props.topicId); + } else { + const errorData = await response.json(); + setSubmitError(errorData.message || 'Failed to submit comment'); + } + } catch (error) { + setSubmitError('Network error. Please try again.'); + } finally { + setIsSubmitting(false); + } + }; + + const getUserInitials = (name) => { + if (!name) return 'U'; + const words = name.trim().split(' '); + return words + .slice(0, 2) + .map(word => word[0].toUpperCase()) + .join(''); + }; + + return ( + <> + {/* Comments List */} +
+

Comments ({comments.length})

+ + {isLoadingComments ? ( +
+
+
+ ) : comments.length === 0 ? ( +

No comments yet. Be the first to comment!

+ ) : ( + comments.map(comment => ( +
+
+
+
+ {getUserInitials(comment.userName)} +
+
+
+
+

+ {comment.userName || 'User'} +

+ + {new Date(comment.created_at).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} + +
+
+ +
+
+
+
+ )) + )} +
+ + {/* Comments Section with MDEditor */} + +
+
+

Leave a Comment

+ {submitSuccess && ( +
+ Thank you for your comment! +
+ )} + {submitError && ( +
+ {submitError} +
+ )} +
+
+ +
+ setNewComment(prev => ({ ...prev, comment: value || '' }))} + height={300} + preview={editorMode} + commands={allCommands} + /> +
+
+ +
+
+ +
+
+ + {/* Image Upload Dialog */} + + + + Insert Image + +
+
+ + setImageUrlInput(e.target.value)} + /> + +
+
+ + + {imageUploadPreview && ( +
+ Preview +
+ )} + +
+
+
+
+ + {loading ? ( + <> +
+
+ Loading... +
+ + ) : !isLoggedIn ? ( + <> +
+

+ Join the conversation! Log in or sign up to post a comment +

+ + ) : null} +
+ + + ); +} \ No newline at end of file diff --git a/src/components/Comment.jsx b/src/components/Comment.jsx new file mode 100644 index 0000000..e6b0512 --- /dev/null +++ b/src/components/Comment.jsx @@ -0,0 +1,200 @@ +import React, { useEffect, useState } from 'react'; +import { Card } from "./ui/card"; +import { Label } from "./ui/label"; +import { Textarea } from "./ui/textarea"; +import { Button } from "./ui/button"; +import { useIsLoggedIn } from '../lib/isLoggedIn'; + +const COMMENTS_API_URL = 'https://host-api.cs1.hz.siliconpin.com/v1/comments/'; + +export default function Comment(props) { + const [comments, setComments] = useState([]); + const [newComment, setNewComment] = useState({ comment: '' }); + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitSuccess, setSubmitSuccess] = useState(false); + const [submitError, setSubmitError] = useState(''); + const [isLoadingComments, setIsLoadingComments] = useState(true); + const { isLoggedIn, loading, error, sessionData } = useIsLoggedIn(); + + // Load comments when component mounts or when topicId changes + useEffect(() => { + if (props.topicId) { + fetchComments(props.topicId); + } + }, [props.topicId]); + + const fetchComments = async (topicId) => { + setIsLoadingComments(true); + try { + const response = await fetch(`${COMMENTS_API_URL}?topicId=${topicId}`, { + method: 'GET', + credentials: 'include', + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || 'Failed to fetch comments'); + } + + if (data.success && data.comments) { + setComments(data.comments); + } else { + throw new Error('Invalid response format'); + } + } catch (error) { + console.error('Error fetching comments:', error); + setSubmitError('Failed to load comments'); + } finally { + setIsLoadingComments(false); + } + }; + + const handleInputChange = (e) => { + const { name, value } = e.target; + setNewComment(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleSubmitComment = async (e) => { + e.preventDefault(); + setIsSubmitting(true); + setSubmitError(''); + + try { + const response = await fetch(`${COMMENTS_API_URL}?query=new-comment`, { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...newComment, + topicId: props.topicId + }), + }); + + if (response.ok) { + setNewComment({ comment: '' }); + setSubmitSuccess(true); + setTimeout(() => setSubmitSuccess(false), 3000); + + // Refresh comments after successful submission + await fetchComments(props.topicId); + } else { + const errorData = await response.json(); + setSubmitError(errorData.message || 'Failed to submit comment'); + } + } catch (error) { + setSubmitError('Network error. Please try again.'); + } finally { + setIsSubmitting(false); + } + }; + + const getUserInitials = (name) => { + if (!name) return 'U'; + const words = name.trim().split(' '); + return words + .slice(0, 2) + .map(word => word[0].toUpperCase()) + .join(''); + }; + + return ( + <> + {/* Comments List */} +
+

Comments ({comments.length})

+ + {isLoadingComments ? ( +
+ {/*
*/} +
+ ) : comments.length === 0 ? ( +

No comments yet. Be the first to comment!

+ ) : ( + comments.map(comment => ( +
+
+
+
+ {getUserInitials(comment.userName)} +
+
+
+
+

+ {comment.userName || 'User'} +

+ + {new Date(comment.created_at).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} + +
+

{comment.comment}

+
+
+
+ )) + )} +
+ + {/* Comments Section */} + +
+
+

Leave a Comment

+ {submitSuccess && ( +
+ Thank you for your comment! +
+ )} + {submitError && ( +
+ {submitError} +
+ )} +
+
+ +