diff --git a/README.md b/README.md index bad35e834dd37df40cd5d98fb254b7bb8cf306c2..939946404c29cd3d5ac7443ca5929e28b709e854 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ spielt, dass man in den jeweiligen Feeds nur Beiträge zu diesem Spiel zu sehen es also vllt. Spieler die bestimmte „Guide“ Beiträge verfassen, ihre Erfolge mit den anderen teilen wollen oder sich zum Beispiel über ihren Lieblingscharakter in einem Spiel unterhalten wollen. -Der direkte Kontakt zu anderen Personen soll in etwa so stattfinden, wie man es über Twitter +Der direkte Kontakt zu anderen Personen soll in etwa so stattfinden, wie man es über Twitter bereits kennt. Also in Form von Beiträgen, die dann von Usern kommentiert werden können (es ist keine Live Chat Funktion in Planung). diff --git a/components/create-gweet/components/create-gweet.tsx b/components/create-gweet/components/create-gweet.tsx index 1b00d96b7bdea89719c80e5a0293f12f4302f4bf..491c57b1e6f8ff072408abb933d2cbed510cff12 100644 --- a/components/create-gweet/components/create-gweet.tsx +++ b/components/create-gweet/components/create-gweet.tsx @@ -3,7 +3,7 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useSession } from "next-auth/react" import Image from "next/image" -import { useRef, useState } from "react" +import { useEffect, useRef, useState } from "react" import { useForm } from "react-hook-form" import * as z from "zod" @@ -35,6 +35,10 @@ const FormSchema = z.object({ .max(240, { message: "Gweets cannot be more that 240 characters." }), }) +const ImagesArraySchema = z.array(z.custom<File>().refine((file) => file instanceof File, { message: "Expected a file" }) + .refine((file) => file?.size <= 4000000, { message: "Images must be less than 4MB" })) + .optional() + export const CreateGweet = ({ parent_gweet, quoted_gweet, @@ -52,12 +56,25 @@ export const CreateGweet = ({ const imageUploadRef = useRef<HTMLInputElement>(null) const { data: session } = useSession() - const { isLoading, mutate } = useCreateGweet() + const { isLoading, isSuccess, mutate } = useCreateGweet() const form = useForm<z.infer<typeof FormSchema>>({ resolver: zodResolver(FormSchema), }) + let disable = true + if (!isLoading && session?.user.username !== undefined) { + // console.log("form is valid", form.formState.isValid) + if (form.formState.isDirty && form.formState.isValid) { + if (chosenImages.length !== 0) { + disable = false + } else { + disable = true + } + disable = false + } + } + async function onGweet(formData: z.infer<typeof FormSchema>) { if (!session) return null @@ -69,19 +86,37 @@ export const CreateGweet = ({ quoteGweetId: quoted_gweet?.id || null, }) - toast({ - description: "Your gweet was send.", - }) - - form.setValue('gweet', '') - setChosenImages([]) + disable = true } + useEffect(() => { + form.formState.isValid // needs to be checked to trigger validation on first load (weird behavior) + if (isSuccess) { + toast({ + description: "Your gweet was send.", + }) + + form.reset() + form.setValue('gweet', '') + setChosenImages([]) + } + }, [form, isSuccess]) + const chooseImages = async ( event: React.ChangeEvent<HTMLInputElement>, setChosenImages: (images: IChosenImages[]) => void, ) => { const files = event.target.files + try { + const fileArray = Array.from(files?.length ? files : []) + ImagesArraySchema.parse(fileArray) + } catch (error: any) { + const err = error as z.ZodError + return toast({ + variant: "destructive", + description: err.issues[0].message, + }) + } if (files && files.length > 0) { const newImages: IChosenImages[] = [] @@ -185,7 +220,12 @@ export const CreateGweet = ({ <FormItem> <FormControl> <Textarea - placeholder={placeholder || "What's on your mind?"} + placeholder={placeholder || + chosenImages.length !== 0 ? + "Tell us something about your media." + : + "What's on your mind?" + } className="resize-none min-h-[100px]" disabled={isLoading || !session.user} {...field} @@ -222,19 +262,20 @@ export const CreateGweet = ({ {chosenImages.map((image, i) => { const isFirstImage = chosenImages.length === 3 && i === 0 return ( - <Card key={i} className={`relative max-h-[600px] overflow-hidden ${isFirstImage ? "row-span-2" : ""}`}> + <Card key={i} className={`relative max-h-[600px] overflow-hidden ${isFirstImage ? "row-span-2" : ""} ${isLoading ? "opacity-50" : ""}`}> <Button type="button" size="icon" variant="secondary" className="rounded-full absolute top-1 right-1 z-40" + disabled={isLoading} onClick={() => { setChosenImages( chosenImages.filter((img, j) => j !== i), ) }} > - <Icons.close className="w-6 h-6" /> + <Icons.close /> </Button> <Image src={image.url as string} @@ -252,7 +293,7 @@ export const CreateGweet = ({ </Card> <div className="flex justify-end"> - <Button type="submit" size="lg" className="w-20" disabled={isLoading || !session.user}> + <Button type="submit" size="lg" className="w-20" disabled={disable}> {isLoading ? ( <Icons.spinner className="h-4 w-4 animate-spin" /> ) : ( diff --git a/components/gweets/components/gweet-options.tsx b/components/gweets/components/gweet-options.tsx index da121bd6596790b803a8d7b329c0ae2107b242ce..9ce0ff115aa9bea191b552129e0121ece0e80836 100644 --- a/components/gweets/components/gweet-options.tsx +++ b/components/gweets/components/gweet-options.tsx @@ -10,7 +10,6 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { toast } from "@/components/ui/use-toast" -import { env } from "@/env.mjs" import getURL from "@/lib/utils" import { useSession } from "next-auth/react" import { useRef, useState } from "react" diff --git a/components/profile/components/edit-profile-modal.tsx b/components/profile/components/edit-profile-modal.tsx index bf4dbc6b54919ed0a4591bcea1404d6151da6591..d77e18860bd1da6799bd836a9a516efd079ae50d 100644 --- a/components/profile/components/edit-profile-modal.tsx +++ b/components/profile/components/edit-profile-modal.tsx @@ -36,7 +36,7 @@ const FormSchema = z.object({ }) const ImagesSchema = z.custom<File>().refine((file) => file instanceof File, { message: "Expected a file" }) - .refine((file) => file?.size < 4000000, { message: "Images must be less than 4MB" }) + .refine((file) => file?.size <= 4000000, { message: "Images must be less than 4MB" }) .optional() export const EditProfileModal = ({ user }: { user: IUser }) => { diff --git a/components/profile/components/user-games.tsx b/components/profile/components/user-games.tsx index 4f9d414fa9519d54cd1be6c2e6fca86d458f36cd..df8e8fe1467ec1c43ad8b3ed244e9e540927f4a8 100644 --- a/components/profile/components/user-games.tsx +++ b/components/profile/components/user-games.tsx @@ -54,46 +54,52 @@ export const UserGames = async ({ username }: { username: string }) => { <div className="p-3 space-y-12"> <div> <h1 className="text-2xl font-bold pb-3">Favorite Games</h1> - <div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"> - {favoritegames ? favoritegames.map((game: IGame) => ( - <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> - )) - : - <p>No favorites currently...</p>} - </div> + {favoritegames ? + <div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"> + {favoritegames.map((game: IGame) => ( + <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> + ))} + </div> + : + <span>No favorites currently...</span> + } </div> <div> <h1 className="text-2xl font-bold pb-3">Currently playing</h1> - <div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"> - {playingGames ? playingGames.map((game: IGame) => ( - <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> - )) - : - <p>Currently not playing any games...</p>} - </div> + {playingGames ? + <div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"> + {playingGames.map((game: IGame) => ( + <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> + ))} + </div> + : + <p>Currently not playing any games...</p> + } </div> <div> <h1 className="text-2xl font-bold pb-3">Planning to play</h1> - <div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"> - {planningGames ? planningGames.map((game: IGame) => ( - <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> - )) - : - <p>Currently not planning to play any games...</p>} - </div> + {planningGames ? + <div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"> + {planningGames.map((game: IGame) => ( + <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> + ))} + </div> + : + <p>Currently not planning to play any games...</p>} </div> <div> <h1 className="text-2xl font-bold pb-3">Finished Games</h1> - <div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"> - {finishedGames ? finishedGames.map((game: IGame) => ( - <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> - )) - : - <p>No finished games...</p>} - </div> + {finishedGames ? + <div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"> + {finishedGames.map((game: IGame) => ( + <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> + ))} + </div> + : + <p>No finished games...</p>} </div> </div > )