i have a weird bug that all day i tried to solve it but nothing changed, now the onClientUploadComplete is not calling when the image is uploaded i don't know why although when i go to the dashboard i see the image and i have followed the documentation and i think i am right ```typescript // proxy.ts import arcjet, { createMiddleware, detectBot } from "@arcjet/next"; import { withAuth } from "@kinde-oss/kinde-auth-nextjs/server"; import { NextProxy } from "next/server"; export const config = { // matcher tells Next.js which routes to run the middleware on. // This runs the middleware on all routes except for static assets. matcher: [ "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", ], }; const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", // will block requests. Use "DRY_RUN" to log only // Block all bots except the following allow: [ "CATEGORY:SEARCH_ENGINE", // Google, Bing, etc "CATEGORY:MONITOR", // Uptime monitoring services "CATEGORY:PREVIEW", // Link previews e.g. Slack, Discord "CATEGORY:WEBHOOK", // Webhooks from trusted services // See the full list at https://arcjet.com/bot-list ], }), ], }); export default createMiddleware( aj, withAuth(undefined, { publicPaths: ["/", "/api/uploadthing"], }) as NextProxy ); ` ```typescript // app\api\uploadthing\core.ts import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server"; import { createUploadthing, type FileRouter } from "uploadthing/next"; import { UploadThingError } from "uploadthing/server"; const f = createUploadthing(); // FileRouter for your app, can contain multiple FileRoutes export const ourFileRouter = { // Define as many FileRoutes as you like, each with a unique routeSlug imageUploader: f({ image: { /** * For full list of options and defaults, see the File Route API reference * @see https://docs.uploadthing.com/file-routes#route-config */ maxFileSize: "1MB", maxFileCount: 1, }, }) // Set permissions and file types for this FileRoute .middleware(async () => { // This code runs on your server before upload const { getUser } = getKindeServerSession(); const user = await getUser(); // If you throw, the user will not be able to upload if (!user) throw new UploadThingError("Unauthorized"); // Whatever is returned here is accessible in onUploadComplete as `metadata` return { userId: user.id }; }) .onUploadComplete(async ({ metadata }) => { // This code RUNS ON YOUR SERVER after upload // console.log("Upload complete for userId:", metadata.userId); // console.log("file url", file.ufsUrl); // !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback return { uploadedBy: metadata.userId }; }), } satisfies FileRouter; export type OurFileRouter = typeof ourFileRouter; ` ```typescript app\api\uploadthing\route.ts import { createRouteHandler } from "uploadthing/next"; import { ourFileRouter } from "./core"; // Export routes for Next App Router export const { GET, POST } = createRouteHandler({ router: ourFileRouter, // Apply an (optional) custom config: // config: { ... }, }); ` ```typescript // lib\uploadthing.ts import type { OurFileRouter } from "@/app/api/uploadthing/core"; import { generateUploadButton, generateUploadDropzone } from "@uploadthing/react"; export const UploadButton = generateUploadButton(); export const UploadDropzone = generateUploadDropzone(); ` ```typescript // hooks\use-attachement-upload.ts "use client"; import { useState } from "react"; export function useAttachmentUpload() { const [isOpen, setIsOpen] = useState(false); const [stagedUrls, setStagedUrls] = useState([]); const [isUploading, setIsUploading] = useState(false); const handleModal = (isOpen: boolean) => { setIsOpen(isOpen); }; const handleUploadStart = () => { setIsUploading(true); }; const onUploadComplete = (urls: string[]) => { setStagedUrls(urls); setIsUploading(false); handleModal(false); // close modal after upload }; const clearStagedUrls = () => { setStagedUrls([]); }; return { isOpen, handleModal, onUploadComplete, stagedUrls, isUploading, handleUploadStart, clearStagedUrls }; } export type useAttachmentUploadType = ReturnType; ` ```typescript // app\(dashboard)\workspace\[workspaceId]\channel\[channelId]\_components\message\MessageInputForm.tsx "use client"; import { createMessageSchema, type CreateMessageSchemaType } from "@/app/schemas/message"; import { Field, FieldGroup } from "@/components/ui/field"; import { zodResolver } from "@hookform/resolvers/zod"; import { Controller, useForm } from "react-hook-form"; import { MessageComposer } from "./MessageComposer"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { orpc } from "@/lib/orpc"; import { toast } from "sonner"; import { useState } from "react"; import { useAttachmentUpload } from "@/hooks/use-attachement-upload"; interface IAppProps { channelId: string; } export function MessageInputForm({ channelId }: IAppProps) { const [editorKey, setEditorKey] = useState(0); const upload = useAttachmentUpload(); const form = useForm({ resolver: zodResolver(createMessageSchema), defaultValues: { channelId, content: "" }, }); // React Query const queryClient = useQueryClient(); const createMessageMutation = useMutation( orpc.message.create.mutationOptions({ onSuccess: () => { form.reset({ channelId, content: "" }); upload.clearStagedUrls(); setEditorKey((prev) => prev + 1); queryClient.invalidateQueries({ queryKey: orpc.message.list.key(), }); return toast.success("Message sent successfully!"); }, onError: () => { return toast.error("Failed to send message. Please try again."); }, }) ); const onSubmit = (data: CreateMessageSchemaType) => { // console.log("data submitted: ", data); createMessageMutation.mutate({ ...data, imageUrl: upload.stagedUrls[0] ?? undefined, }); }; return (
( {fieldState.error &&
{fieldState.error.message}
}
)} />
); } ` ```typescript // app\(dashboard)\workspace\[workspaceId]\channel\[channelId]\_components\message\MessageComposer.tsx "use client"; import { RichTextEditor } from "@/components/rich-text-editor/Editor"; import { ImageUploadModal } from "@/components/rich-text-editor/ImageUploadModal"; import { Button } from "@/components/ui/button"; import { useAttachmentUploadType } from "@/hooks/use-attachement-upload"; import { ImageIcon, SendIcon } from "lucide-react"; import AttachmentChip from "./AttachmentChip"; interface IAppProps { value: string; onChange: (value: string) => void; onSubmit: () => void; isSubmitting?: boolean; upload: useAttachmentUploadType; } export function MessageComposer({ value, onChange, onSubmit, isSubmitting, upload }: IAppProps) { return ( <> Send } footerLeft={ upload.stagedUrls.length > 0 ? ( ) : ( ) } /> ); } ` ```typescript // components\rich-text-editor\ImageUploadModal.tsx "use client"; import { DialogTitle } from "@radix-ui/react-dialog"; import { Dialog, DialogContent, DialogHeader } from "../ui/dialog"; import { UploadDropzone } from "@/lib/uploadthing"; import { toast } from "sonner"; interface TAppProps { isOpen: boolean; handleModal: (isOpen: boolean) => void; onUploadComplete: (urls: string[]) => void; handleStartUpload: () => void; isUploading: boolean; } export function ImageUploadModal({ isOpen, handleModal, onUploadComplete, handleStartUpload, isUploading }: TAppProps) { return ( { if (!isUploading) { handleModal(open); } }} > Upload Image { console.log("res: ", res); const urls = res.map((x) => x.ufsUrl); toast.success("Image uploaded successfully!"); onUploadComplete(urls); }} /> ); } `