diff --git a/backend/src/db/api/conversations.js b/backend/src/db/api/conversations.js index 3ee8694..5df2b2b 100644 --- a/backend/src/db/api/conversations.js +++ b/backend/src/db/api/conversations.js @@ -1,4 +1,3 @@ - const db = require('../models'); const FileDBApi = require('./file'); const crypto = require('crypto'); @@ -332,6 +331,16 @@ module.exports = class ConversationsDBApi { model: db.file, as: 'group_image', }, + { + model: db.conversation_members, + as: 'conversation_members_conversation', + include: [ + { + model: db.users, + as: 'user', + } + ] + } ]; @@ -496,5 +505,4 @@ module.exports = class ConversationsDBApi { } -}; - +}; \ No newline at end of file diff --git a/backend/src/db/api/messages.js b/backend/src/db/api/messages.js index 20efb2a..e572398 100644 --- a/backend/src/db/api/messages.js +++ b/backend/src/db/api/messages.js @@ -1,4 +1,3 @@ - const db = require('../models'); const FileDBApi = require('./file'); const crypto = require('crypto'); @@ -322,6 +321,16 @@ module.exports = class MessagesDBApi { + + + + + + + + + + @@ -343,6 +352,11 @@ module.exports = class MessagesDBApi { }); + output.reply_to = await messages.getReply_to({ + transaction + }); + + output.images = await messages.getImages({ transaction }); @@ -353,11 +367,6 @@ module.exports = class MessagesDBApi { }); - output.reply_to = await messages.getReply_to({ - transaction - }); - - return output; } @@ -524,10 +533,6 @@ module.exports = class MessagesDBApi { - - - - if (filter.createdAtRange) { @@ -620,5 +625,4 @@ module.exports = class MessagesDBApi { } -}; - +}; \ No newline at end of file diff --git a/backend/src/db/api/posts.js b/backend/src/db/api/posts.js index 2bf8718..9c53a73 100644 --- a/backend/src/db/api/posts.js +++ b/backend/src/db/api/posts.js @@ -1,4 +1,3 @@ - const db = require('../models'); const FileDBApi = require('./file'); const crypto = require('crypto'); @@ -421,6 +420,20 @@ module.exports = class PostsDBApi { model: db.file, as: 'video', }, + { + model: db.post_reactions, + as: 'post_reactions_post', + }, + { + model: db.post_comments, + as: 'post_comments_post', + include: [ + { + model: db.users, + as: 'author', + } + ] + } ]; @@ -610,5 +623,4 @@ module.exports = class PostsDBApi { } -}; - +}; \ No newline at end of file diff --git a/frontend/src/components/Posts/CreatePost.tsx b/frontend/src/components/Posts/CreatePost.tsx new file mode 100644 index 0000000..53d70f0 --- /dev/null +++ b/frontend/src/components/Posts/CreatePost.tsx @@ -0,0 +1,167 @@ +import React, { useState, useRef } from 'react'; +import { mdiImageOutline, mdiVideoOutline, mdiMapMarkerOutline, mdiClose, mdiLoading } from '@mdi/js'; +import BaseIcon from '../BaseIcon'; +import UserAvatar from '../UserAvatar'; +import { useAppDispatch, useAppSelector } from '../../stores/hooks'; +import { create as createPost, setRefetch } from '../../stores/posts/postsSlice'; +import FileUploader from '../Uploaders/UploadService'; + +export default function CreatePost() { + const dispatch = useAppDispatch(); + const { currentUser } = useAppSelector((state) => state.auth); + const [caption, setCaption] = useState(''); + const [files, setFiles] = useState([]); + const [isUploading, setIsUploading] = useState(false); + const [isPosting, setIsPosting] = useState(false); + const fileInputRef = useRef(null); + + const authorName = currentUser ? `${currentUser.firstName} ${currentUser.lastName}` : 'Anonymous'; + const authorAvatar = currentUser ? currentUser.avatar : null; + + const handleFileSelect = async (e: React.ChangeEvent) => { + const selectedFiles = e.target.files; + if (!selectedFiles) return; + + setIsUploading(true); + const newFiles = [...files]; + + for (let i = 0; i < selectedFiles.length; i++) { + const file = selectedFiles[i]; + try { + const remoteFile = await FileUploader.upload('posts', file, { image: file.type.startsWith('image'), size: 10 * 1024 * 1024 }); + newFiles.push({ + ...remoteFile, + type: file.type.startsWith('image') ? 'image' : 'video' + }); + } catch (err) { + console.error('Upload failed', err); + } + } + + setFiles(newFiles); + setIsUploading(false); + if (fileInputRef.current) fileInputRef.current.value = ''; + }; + + const removeFile = (index: number) => { + setFiles(files.filter((_, i) => i !== index)); + }; + + const handleSubmit = async () => { + if (!caption && files.length === 0) return; + + setIsPosting(true); + const postData = { + caption, + post_type: 'post', + published_at: new Date().toISOString(), + author: currentUser.id, + images: files.filter(f => f.type === 'image'), + video: files.find(f => f.type === 'video') || null, + comments_enabled: true, + is_archived: false, + }; + + try { + await dispatch(createPost(postData)).unwrap(); + setCaption(''); + setFiles([]); + dispatch(setRefetch(true)); + } catch (err) { + console.error('Failed to create post', err); + } finally { + setIsPosting(false); + } + }; + + return ( +
+
+ +
+