WaledRashed24/comics
0
1"use client"2 3import { useEffect, useState } from "react"4import { useSearchParams } from "next/navigation"5import { StaticImageData } from "next/image"6import { useLocalStorage } from "usehooks-ts"7 8import {9 Select,10 SelectContent,11 SelectItem,12 SelectTrigger,13 SelectValue,14} from "@/components/ui/select"15import { Label } from "@/components/ui/label"16import { cn } from "@/lib/utils"17import { FontName, defaultFont } from "@/lib/fonts"18import { Input } from "@/components/ui/input"19import { PresetName, defaultPreset, nonRandomPresets, presets } from "@/app/engine/presets"20import { useStore } from "@/app/store"21import { Button } from "@/components/ui/button"22import { LayoutName, defaultLayout, nonRandomLayouts } from "@/app/layouts"23import { Switch } from "@/components/ui/switch"24import { useOAuth } from "@/lib/useOAuth"25import { useIsBusy } from "@/lib/useIsBusy"26 27import { localStorageKeys } from "../settings-dialog/localStorageKeys"28import { defaultSettings } from "../settings-dialog/defaultSettings"29import { AuthWall } from "../auth-wall"30import { SelectLayout } from "../select-layout"31 32export function TopMenu() {33 const searchParams = useSearchParams()34 35 const requestedPreset = (searchParams?.get('preset') as PresetName) || defaultPreset36 const requestedFont = (searchParams?.get('font') as FontName) || defaultFont37 const requestedStylePrompt = (searchParams?.get('stylePrompt') as string) || ""38 const requestedStoryPrompt = (searchParams?.get('storyPrompt') as string) || ""39 const requestedLayout = (searchParams?.get('layout') as LayoutName) || defaultLayout40 41 // const font = useStore(s => s.font)42 // const setFont = useStore(s => s.setFont)43 const preset = useStore(s => s.preset)44 const prompt = useStore(s => s.prompt)45 const layout = useStore(s => s.layout)46 const setLayout = useStore(s => s.setLayout)47 48 const setShowSpeeches = useStore(s => s.setShowSpeeches)49 const showSpeeches = useStore(s => s.showSpeeches)50 51 const setShowCaptions = useStore(s => s.setShowCaptions)52 const showCaptions = useStore(s => s.showCaptions)53 54 const currentNbPages = useStore(s => s.currentNbPages)55 const setCurrentNbPages = useStore(s => s.setCurrentNbPages)56 57 const generate = useStore(s => s.generate)58 59 const isBusy = useIsBusy()60 61 const [lastDraftPromptA, setLastDraftPromptA] = useLocalStorage<string>(62 "AI_COMIC_FACTORY_LAST_DRAFT_PROMPT_A",63 requestedStylePrompt64 )65 66 const [lastDraftPromptB, setLastDraftPromptB] = useLocalStorage<string>(67 "AI_COMIC_FACTORY_LAST_DRAFT_PROMPT_B",68 requestedStoryPrompt69 )70 71 // TODO should be in the store72 const [draftPromptA, setDraftPromptA] = useState(lastDraftPromptA)73 const [draftPromptB, setDraftPromptB] = useState(lastDraftPromptB)74 const draftPrompt = `${draftPromptA}||${draftPromptB}`75 76 const [draftPreset, setDraftPreset] = useState<PresetName>(requestedPreset)77 const [draftLayout, setDraftLayout] = useState<LayoutName>(requestedLayout)78 79 const { isLoggedIn, enableOAuthWall } = useOAuth({ debug: false })80 81 const [hasGeneratedAtLeastOnce, setHasGeneratedAtLeastOnce] = useLocalStorage<boolean>(82 localStorageKeys.hasGeneratedAtLeastOnce,83 defaultSettings.hasGeneratedAtLeastOnce84 )85 86 const [showAuthWall, setShowAuthWall] = useState(false)87 88 // we synchronize the draft prompt with the local storage89 useEffect(() => { if (lastDraftPromptA !== draftPromptA) { setLastDraftPromptA(draftPromptA) } }, [draftPromptA])90 useEffect(() => { if (lastDraftPromptA !== draftPromptA) { setDraftPromptA(lastDraftPromptA) } }, [lastDraftPromptA])91 useEffect(() => { if (lastDraftPromptB !== draftPromptB) { setLastDraftPromptB(draftPromptB) } }, [draftPromptB])92 useEffect(() => { if (lastDraftPromptB !== draftPromptB) { setDraftPromptB(lastDraftPromptB) } }, [lastDraftPromptB])93 94 const handleSubmit = () => {95 if (enableOAuthWall && hasGeneratedAtLeastOnce && !isLoggedIn) {96 setShowAuthWall(true)97 return98 }99 100 const promptChanged = draftPrompt.trim() !== prompt.trim()101 const presetChanged = draftPreset !== preset.id102 const layoutChanged = draftLayout !== layout103 if (!isBusy && (promptChanged || presetChanged || layoutChanged)) {104 generate(draftPrompt, draftPreset, draftLayout)105 }106 }107 108 useEffect(() => {109 const layoutChanged = draftLayout !== layout110 if (layoutChanged && !isBusy) {111 setLayout(draftLayout)112 }113 }, [layout, draftLayout, isBusy])114 115 return (116 <div className={cn(117 `print:hidden`,118 `z-10 fixed top-0 left-0 right-0`,119 `flex flex-col md:flex-row w-full justify-between items-center`,120 `backdrop-blur-xl`,121 `transition-all duration-200 ease-in-out`,122 `px-2 py-2 border-b-1 border-gray-50 dark:border-gray-50`,123 //`bg-[#2d435c] dark:bg-[#2d435c] text-gray-50 dark:text-gray-50`,124 `bg-gradient-to-r from-[#102c4c] to-[#1a426f] dark:bg-gradient-to-r dark:from-[#102c4c] dark:to-[#1a426f]`,125 `space-y-2 md:space-y-0 md:space-x-3 lg:space-x-6`126 )}>127 <div className="flex flex-row space-x-2 md:space-x-3 w-full md:w-auto">128 <div className={cn(129 `transition-all duration-200 ease-in-out`,130 `flex flex-row items-center justify-start space-x-3`,131 `flex-grow`132 )}>133 134 {/* <Label className="flex text-2xs md:text-sm md:w-24">Style:</Label> */}135 136 <Select137 defaultValue={defaultPreset}138 onValueChange={(value) => { setDraftPreset(value as PresetName) }}139 disabled={isBusy}140 >141 <SelectTrigger className="flex-grow bg-gray-100 text-gray-700 dark:bg-gray-100 dark:text-gray-700">142 <SelectValue className="text-2xs md:text-sm" placeholder="Style" />143 </SelectTrigger>144 <SelectContent>145 {nonRandomPresets.map(key =>146 <SelectItem key={key} value={key}>{presets[key].label}</SelectItem>147 )}148 </SelectContent>149 </Select>150 </div>151 <div className={cn(152 `transition-all duration-200 ease-in-out`,153 `flex flex-row items-center justify-start space-x-3`,154 `w-40`155 )}>156 157 {/* <Label className="flex text-2xs md:text-sm md:w-24">Style:</Label> */}158 159 <SelectLayout160 defaultValue={defaultLayout}161 onLayoutChange={setDraftLayout}162 disabled={isBusy}163 layouts={nonRandomLayouts}164 />165 </div>166 <div className="flex flex-row items-center space-x-3">167 <Switch168 checked={showCaptions}169 onCheckedChange={setShowCaptions}170 />171 <Label className="text-gray-200 dark:text-gray-200">172 <span className="hidden md:inline">Caption</span>173 <span className="inline md:hidden">Cap.</span>174 </Label>175 </div>176 <div className="flex flex-row items-center space-x-3">177 <Switch178 checked={showSpeeches}179 onCheckedChange={setShowSpeeches}180 />181 <Label className="text-gray-200 dark:text-gray-200">182 <span className="hidden md:inline">Beta</span>183 <span className="inline md:hidden">Beta</span>184 </Label>185 </div>186 {/*187 <div className={cn(188 `transition-all duration-200 ease-in-out`,189 `flex flex-row items-center space-x-3 w-1/2 md:w-auto md:hidden`190 )}>191 <Label className="flex text-2xs md:text-sm md:w-24">Font:</Label>192 <Select193 defaultValue={fontList.includes(preset.font) ? preset.font : "cartoonist"}194 onValueChange={(value) => { setFont(value as FontName) }}195 disabled={atLeastOnePanelIsBusy}196 >197 <SelectTrigger className="flex-grow">198 <SelectValue className="text-2xs md:text-sm" placeholder="Type" />199 </SelectTrigger>200 <SelectContent>201 {Object.keys(fonts)202 .map((font) =>203 <SelectItem204 key={font}205 value={font}>{206 font207 }</SelectItem>208 )}209 </SelectContent>210 </Select>211 </div>212 */}213 </div>214 <div className={cn(215 `transition-all duration-200 ease-in-out`,216 `flex flex-grow flex-col space-y-2 md:space-y-0 md:flex-row items-center md:space-x-3 w-full md:w-auto`217 )}>218 <div className="flex flex-row flex-grow w-full">219 <div className="flex flex-row flex-grow w-full">220 <Input221 id="top-menu-input-story-prompt"222 placeholder="1. Story (eg. detective dog)"223 className={cn(224 `w-1/2 rounded-r-none`,225 `bg-gray-100 text-gray-700 dark:bg-gray-100 dark:text-gray-700`,226 `border-r-stone-100`227 )}228 // disabled={atLeastOnePanelIsBusy}229 onChange={(e) => {230 setDraftPromptB(e.target.value)231 }}232 onKeyDown={({ key }) => {233 if (key === 'Enter') {234 handleSubmit()235 }236 }}237 value={draftPromptB}238 />239 <Input240 id="top-menu-input-style-prompt"241 placeholder="2. Style (eg 'rain, shiba')"242 className={cn(243 `w-1/2`,244 `bg-gray-100 text-gray-700 dark:bg-gray-100 dark:text-gray-700`,245 `border-l-gray-300 rounded-l-none rounded-r-none`246 )}247 // disabled={atLeastOnePanelIsBusy}248 onChange={(e) => {249 setDraftPromptA(e.target.value)250 }}251 onKeyDown={({ key }) => {252 if (key === 'Enter') {253 handleSubmit()254 }255 }}256 value={draftPromptA}257 />258 </div>259 <Button260 className={cn(261 `rounded-l-none cursor-pointer`,262 `transition-all duration-200 ease-in-out`,263 `text-xl`,264 `bg-[rgb(59,134,247)] hover:bg-[rgb(69,144,255)] disabled:bg-[rgb(59,134,247)]`265 )}266 onClick={() => {267 handleSubmit()268 }}269 disabled={!draftPrompt?.trim().length || isBusy}270 >271 Go272 </Button>273 274 <AuthWall show={showAuthWall} />275 </div>276 </div>277 {/*278 Let's add this feature later, because right now people279 are confused about why they can't activate it280 <div className={cn(281 `transition-all duration-200 ease-in-out`,282 `hidden md:flex flex-row items-center space-x-3 w-full md:w-auto`283 )}>284 <Label className="flex text-2xs md:text-sm w-24">Font:</Label>285 <Select286 defaultValue={fontList.includes(preset.font) ? preset.font : "actionman"}287 onValueChange={(value) => { setFont(value as FontName) }}288 // disabled={isBusy}289 disabled={true}290 >291 <SelectTrigger className="flex-grow">292 <SelectValue className="text-2xs md:text-sm" placeholder="Type" />293 </SelectTrigger>294 <SelectContent>295 {Object.keys(fonts)296 .map((font) =>297 <SelectItem298 key={font}299 value={font}>{300 font301 }</SelectItem>302 )}303 </SelectContent>304 </Select>305 </div>306 */}307 </div>308 )309}