Skip to content
Snippets Groups Projects
Commit 5f913b2e authored by Yusuf Akgül's avatar Yusuf Akgül :hatching_chick:
Browse files

now validating create gweet input and minor ui fix

parent fd324f31
No related branches found
No related tags found
1 merge request!39now validating create gweet input and minor ui fix
Pipeline #39695 passed
...@@ -17,7 +17,7 @@ spielt, dass man in den jeweiligen Feeds nur Beiträge zu diesem Spiel zu sehen ...@@ -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 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. 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 bereits kennt. Also in Form von Beiträgen, die dann von Usern kommentiert werden können (es
ist keine Live Chat Funktion in Planung). ist keine Live Chat Funktion in Planung).
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
import { zodResolver } from "@hookform/resolvers/zod" import { zodResolver } from "@hookform/resolvers/zod"
import { useSession } from "next-auth/react" import { useSession } from "next-auth/react"
import Image from "next/image" import Image from "next/image"
import { useRef, useState } from "react" import { useEffect, useRef, useState } from "react"
import { useForm } from "react-hook-form" import { useForm } from "react-hook-form"
import * as z from "zod" import * as z from "zod"
...@@ -35,6 +35,10 @@ const FormSchema = z.object({ ...@@ -35,6 +35,10 @@ const FormSchema = z.object({
.max(240, { message: "Gweets cannot be more that 240 characters." }), .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 = ({ export const CreateGweet = ({
parent_gweet, parent_gweet,
quoted_gweet, quoted_gweet,
...@@ -52,12 +56,25 @@ export const CreateGweet = ({ ...@@ -52,12 +56,25 @@ export const CreateGweet = ({
const imageUploadRef = useRef<HTMLInputElement>(null) const imageUploadRef = useRef<HTMLInputElement>(null)
const { data: session } = useSession() const { data: session } = useSession()
const { isLoading, mutate } = useCreateGweet() const { isLoading, isSuccess, mutate } = useCreateGweet()
const form = useForm<z.infer<typeof FormSchema>>({ const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(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>) { async function onGweet(formData: z.infer<typeof FormSchema>) {
if (!session) return null if (!session) return null
...@@ -69,19 +86,37 @@ export const CreateGweet = ({ ...@@ -69,19 +86,37 @@ export const CreateGweet = ({
quoteGweetId: quoted_gweet?.id || null, quoteGweetId: quoted_gweet?.id || null,
}) })
toast({ disable = true
description: "Your gweet was send.",
})
form.setValue('gweet', '')
setChosenImages([])
} }
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 ( const chooseImages = async (
event: React.ChangeEvent<HTMLInputElement>, event: React.ChangeEvent<HTMLInputElement>,
setChosenImages: (images: IChosenImages[]) => void, setChosenImages: (images: IChosenImages[]) => void,
) => { ) => {
const files = event.target.files 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) { if (files && files.length > 0) {
const newImages: IChosenImages[] = [] const newImages: IChosenImages[] = []
...@@ -185,7 +220,12 @@ export const CreateGweet = ({ ...@@ -185,7 +220,12 @@ export const CreateGweet = ({
<FormItem> <FormItem>
<FormControl> <FormControl>
<Textarea <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]" className="resize-none min-h-[100px]"
disabled={isLoading || !session.user} disabled={isLoading || !session.user}
{...field} {...field}
...@@ -222,19 +262,20 @@ export const CreateGweet = ({ ...@@ -222,19 +262,20 @@ export const CreateGweet = ({
{chosenImages.map((image, i) => { {chosenImages.map((image, i) => {
const isFirstImage = chosenImages.length === 3 && i === 0 const isFirstImage = chosenImages.length === 3 && i === 0
return ( 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 <Button
type="button" type="button"
size="icon" size="icon"
variant="secondary" variant="secondary"
className="rounded-full absolute top-1 right-1 z-40" className="rounded-full absolute top-1 right-1 z-40"
disabled={isLoading}
onClick={() => { onClick={() => {
setChosenImages( setChosenImages(
chosenImages.filter((img, j) => j !== i), chosenImages.filter((img, j) => j !== i),
) )
}} }}
> >
<Icons.close className="w-6 h-6" /> <Icons.close />
</Button> </Button>
<Image <Image
src={image.url as string} src={image.url as string}
...@@ -252,7 +293,7 @@ export const CreateGweet = ({ ...@@ -252,7 +293,7 @@ export const CreateGweet = ({
</Card> </Card>
<div className="flex justify-end"> <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 ? ( {isLoading ? (
<Icons.spinner className="h-4 w-4 animate-spin" /> <Icons.spinner className="h-4 w-4 animate-spin" />
) : ( ) : (
......
...@@ -10,7 +10,6 @@ import { ...@@ -10,7 +10,6 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu" } from "@/components/ui/dropdown-menu"
import { toast } from "@/components/ui/use-toast" import { toast } from "@/components/ui/use-toast"
import { env } from "@/env.mjs"
import getURL from "@/lib/utils" import getURL from "@/lib/utils"
import { useSession } from "next-auth/react" import { useSession } from "next-auth/react"
import { useRef, useState } from "react" import { useRef, useState } from "react"
......
...@@ -36,7 +36,7 @@ const FormSchema = z.object({ ...@@ -36,7 +36,7 @@ const FormSchema = z.object({
}) })
const ImagesSchema = z.custom<File>().refine((file) => file instanceof File, { message: "Expected a file" }) 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() .optional()
export const EditProfileModal = ({ user }: { user: IUser }) => { export const EditProfileModal = ({ user }: { user: IUser }) => {
......
...@@ -54,46 +54,52 @@ export const UserGames = async ({ username }: { username: string }) => { ...@@ -54,46 +54,52 @@ export const UserGames = async ({ username }: { username: string }) => {
<div className="p-3 space-y-12"> <div className="p-3 space-y-12">
<div> <div>
<h1 className="text-2xl font-bold pb-3">Favorite Games</h1> <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 ? favoritegames.map((game: IGame) => ( <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">
<GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> {favoritegames.map((game: IGame) => (
)) <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} />
: ))}
<p>No favorites currently...</p>} </div>
</div> :
<span>No favorites currently...</span>
}
</div> </div>
<div> <div>
<h1 className="text-2xl font-bold pb-3">Currently playing</h1> <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 ? playingGames.map((game: IGame) => ( <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">
<GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> {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>
</div> :
<p>Currently not playing any games...</p>
}
</div> </div>
<div> <div>
<h1 className="text-2xl font-bold pb-3">Planning to play</h1> <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 ? planningGames.map((game: IGame) => ( <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">
<GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> {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>
</div> :
<p>Currently not planning to play any games...</p>}
</div> </div>
<div> <div>
<h1 className="text-2xl font-bold pb-3">Finished Games</h1> <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 ? finishedGames.map((game: IGame) => ( <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">
<GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> {finishedGames.map((game: IGame) => (
)) <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} />
: ))}
<p>No finished games...</p>} </div>
</div> :
<p>No finished games...</p>}
</div> </div>
</div > </div >
) )
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment