33import { useState , useEffect , useContext } from "react" ;
44import { Avatar , AvatarFallback , AvatarImage } from "@/components/ui/avatar" ;
55import { Button } from "@/components/ui/button" ;
6- import { Dialog , DialogContent , DialogTrigger } from "@/components/ui/dialog" ;
76import { Input } from "@/components/ui/input" ;
87import { Textarea } from "@/components/ui/textarea" ;
98import {
@@ -22,22 +21,41 @@ import { Paperclip, Video, Smile, Image } from "lucide-react";
2221import { EmojiPicker } from "./emoji-picker" ;
2322import { GifSelector } from "./gif-selector" ;
2423import { MediaPreview } from "./media-preview" ;
25- import { CommunityPost } from "@courselit/common-models" ;
24+ import { CommunityMediaTypes , CommunityPost } from "@courselit/common-models" ;
2625import { type MediaItem } from "./media-item" ;
2726import { ProfileContext } from "@components/contexts" ;
27+ import {
28+ AlertDialog ,
29+ AlertDialogContent ,
30+ AlertDialogFooter ,
31+ AlertDialogHeader ,
32+ AlertDialogTitle ,
33+ AlertDialogTrigger ,
34+ } from "@components/ui/alert-dialog" ;
35+ import {
36+ AlertDialogAction ,
37+ AlertDialogCancel ,
38+ } from "@radix-ui/react-alert-dialog" ;
39+ import { Progress } from "@/components/ui/progress" ;
2840
2941interface CreatePostDialogProps {
30- onPostCreated : (
42+ createPost : (
3143 post : Pick < CommunityPost , "title" | "content" | "category" > & {
3244 media : MediaItem [ ] ;
3345 } ,
3446 ) => void ;
3547 categories : string [ ] ;
48+ isFileUploading : boolean ;
49+ fileUploadProgress : number ;
50+ fileBeingUploadedNumber : number ;
3651}
3752
38- export function CreatePostDialog ( {
39- onPostCreated ,
53+ export default function CreatePostDialog ( {
54+ createPost ,
4055 categories,
56+ isFileUploading,
57+ fileUploadProgress,
58+ fileBeingUploadedNumber = 0 ,
4159} : CreatePostDialogProps ) {
4260 const [ isOpen , setIsOpen ] = useState ( false ) ;
4361 const [ title , setTitle ] = useState ( "" ) ;
@@ -53,10 +71,13 @@ export function CreatePostDialog({
5371 } > ( { } ) ;
5472 const [ isPostButtonDisabled , setIsPostButtonDisabled ] = useState ( true ) ;
5573 const { profile } = useContext ( ProfileContext ) ;
74+ const [ isPosting , setIsPosting ] = useState ( false ) ;
5675
5776 useEffect ( ( ) => {
58- setIsPostButtonDisabled ( title . trim ( ) === "" || content . trim ( ) === "" ) ;
59- } , [ title , content ] ) ;
77+ setIsPostButtonDisabled (
78+ title . trim ( ) === "" || content . trim ( ) === "" || isPosting ,
79+ ) ;
80+ } , [ title , content , isPosting ] ) ;
6081
6182 const handleEmojiSelect = ( emoji : string ) => {
6283 setContent ( ( prevContent ) => prevContent + emoji ) ;
@@ -98,9 +119,9 @@ export function CreatePostDialog({
98119 }
99120 } ;
100121
101- const handleLinkAdd = ( url : string ) => {
102- setContent ( ( prevContent ) => `${ prevContent } ${ url } ` ) ;
103- } ;
122+ // const handleLinkAdd = (url: string) => {
123+ // setContent((prevContent) => `${prevContent} ${url} `);
124+ // };
104125
105126 const handleVideoAdd = ( url : string ) => {
106127 if ( url . includes ( "youtube.com" ) || url . includes ( "youtu.be" ) ) {
@@ -127,7 +148,7 @@ export function CreatePostDialog({
127148 setMedia ( ( prevMedia ) => prevMedia . filter ( ( _ , i ) => i !== index ) ) ;
128149 } ;
129150
130- const handlePost = ( ) => {
151+ const handlePost = async ( ) => {
131152 if ( title . trim ( ) === "" || content . trim ( ) === "" ) {
132153 setErrors ( {
133154 title : title . trim ( ) === "" ? "Title is required" : undefined ,
@@ -145,61 +166,87 @@ export function CreatePostDialog({
145166 return ;
146167 }
147168
148- onPostCreated ( {
169+ setIsPosting ( true ) ;
170+ await createPost ( {
149171 category,
150172 title,
151173 content,
152174 media,
153175 } ) ;
176+ setIsPosting ( false ) ;
177+
178+ resetForm ( ) ;
179+ } ;
180+
181+ const resetForm = ( ) => {
154182 setIsOpen ( false ) ;
155- // Reset form
156183 setTitle ( "" ) ;
157184 setContent ( "" ) ;
158185 setCategory ( "" ) ;
159186 setMedia ( [ ] ) ;
160187 setErrors ( { } ) ;
161188 } ;
162189
190+ const getUploadableMediaCount = ( ) => {
191+ return media . filter ( ( x ) =>
192+ [
193+ CommunityMediaTypes . IMAGE ,
194+ CommunityMediaTypes . VIDEO ,
195+ CommunityMediaTypes . PDF ,
196+ ] . includes ( x . type as any ) ,
197+ ) . length ;
198+ } ;
199+
200+ if ( ! profile ) {
201+ return null ;
202+ }
203+
163204 return (
164- < Dialog open = { isOpen } onOpenChange = { setIsOpen } >
165- < DialogTrigger asChild >
205+ < AlertDialog open = { isOpen } >
206+ < AlertDialogTrigger asChild >
166207 < Button
167208 variant = "outline"
168209 className = "w-full !text-left cursor-text"
210+ onClick = { ( ) => setIsOpen ( true ) }
169211 >
170212 Write something...
171213 </ Button >
172- </ DialogTrigger >
173- < DialogContent className = "sm:max-w-[90vw] md:max-w-[600px] w-full overflow-y-auto max-h-[calc(100vh-4rem)] my-8" >
174- < div className = "flex items-center gap-2 mb-4" >
175- < Avatar className = "h-10 w-10" >
176- < AvatarImage
177- src = {
178- profile . avatar
179- ? profile . avatar . file
180- : "/courselit_backdrop_square.webp"
181- }
182- alt = { `${ profile . name } avatar` }
183- />
184- < AvatarFallback >
185- { ( profile . name
186- ? profile . name . charAt ( 0 )
187- : profile . email . charAt ( 0 )
188- ) . toUpperCase ( ) }
189- </ AvatarFallback >
190- </ Avatar >
191- < div >
192- < span className = "font-semibold" > { profile . name } </ span >
193- </ div >
194- </ div >
214+ </ AlertDialogTrigger >
215+ < AlertDialogContent className = "sm:max-w-[90vw] md:max-w-[600px] w-full overflow-y-auto max-h-[calc(100vh-4rem)] my-8" >
216+ < AlertDialogHeader >
217+ < AlertDialogTitle >
218+ < div className = "flex items-center gap-2 mb-4" >
219+ < Avatar className = "h-10 w-10" >
220+ < AvatarImage
221+ src = {
222+ profile . avatar
223+ ? profile . avatar . file
224+ : "/courselit_backdrop_square.webp"
225+ }
226+ alt = { `${ profile . name } avatar` }
227+ />
228+ < AvatarFallback >
229+ { ( profile . name
230+ ? profile . name . charAt ( 0 )
231+ : profile . email ! . charAt ( 0 )
232+ ) . toUpperCase ( ) }
233+ </ AvatarFallback >
234+ </ Avatar >
235+ < div >
236+ < span className = "font-semibold" >
237+ { profile . name }
238+ </ span >
239+ </ div >
240+ </ div >
241+ </ AlertDialogTitle >
242+ </ AlertDialogHeader >
195243
196244 < div className = "space-y-4" >
197245 < div >
198246 < Input
199247 placeholder = "Title"
200248 value = { title }
201249 onChange = { ( e ) => setTitle ( e . target . value ) }
202- className = "text-lg border-none px-0 font-semibold"
203250 />
204251 { errors . title && (
205252 < p className = "text-red-500 text-sm mt-1" >
@@ -409,7 +456,7 @@ export function CreatePostDialog({
409456 ) }
410457 </ div >
411458 </ div >
412- < div className = "flex items-center gap-2" >
459+ { /* <div className="flex items-center gap-2">
413460 <Button
414461 variant="ghost"
415462 onClick={() => setIsOpen(false)}
@@ -423,9 +470,38 @@ export function CreatePostDialog({
423470 >
424471 Post
425472 </Button>
426- </ div >
473+ </div> */ }
427474 </ div >
428- </ DialogContent >
429- </ Dialog >
475+ { isPosting && getUploadableMediaCount ( ) > 0 && (
476+ < >
477+ < p className = "text-xs text-muted-foreground" >
478+ Uploading { fileBeingUploadedNumber } of{ " " }
479+ { getUploadableMediaCount ( ) } files -{ " " }
480+ { Math . round ( fileUploadProgress ) } %
481+ </ p >
482+ < Progress value = { fileUploadProgress } className = "h-2" />
483+ </ >
484+ ) }
485+ < AlertDialogFooter >
486+ < AlertDialogCancel asChild >
487+ < Button
488+ onClick = { resetForm }
489+ variant = "outline"
490+ disabled = { isPosting }
491+ >
492+ Cancel
493+ </ Button >
494+ </ AlertDialogCancel >
495+ < AlertDialogAction asChild >
496+ < Button
497+ onClick = { handlePost }
498+ disabled = { isPostButtonDisabled }
499+ >
500+ { isPosting ? "Posting..." : "Post" }
501+ </ Button >
502+ </ AlertDialogAction >
503+ </ AlertDialogFooter >
504+ </ AlertDialogContent >
505+ </ AlertDialog >
430506 ) ;
431507}
0 commit comments