From ced112e5475a20b9b4226f72cfe0ec69d0d930ea Mon Sep 17 00:00:00 2001 From: mslxl Date: Tue, 19 Mar 2024 22:44:59 +0800 Subject: [PATCH] feat: implement feature to rename items --- src/components/session/SessionContextMenu.tsx | 7 +- src/components/session/index.tsx | 61 ++++++-- src/hooks/useRenameDialog.tsx | 59 ++++--- src/lib/names.ts | 146 ++++++++++++++++++ src/pages/Main/event/menu-event.tsx | 3 +- src/store/source/index.ts | 9 +- 6 files changed, 248 insertions(+), 37 deletions(-) create mode 100644 src/lib/names.ts diff --git a/src/components/session/SessionContextMenu.tsx b/src/components/session/SessionContextMenu.tsx index 8c31d88..f7733fd 100644 --- a/src/components/session/SessionContextMenu.tsx +++ b/src/components/session/SessionContextMenu.tsx @@ -12,6 +12,7 @@ import { deleteSourceAtom, duplicateSourceAtom } from "@/store/source" interface SessionContextMenuProps { id: string children: ReactNode + onRename: (id: string) => void onChangeLanguage: (id: string) => void } export default function SessionContextMenu(props: SessionContextMenuProps) { @@ -27,9 +28,9 @@ export default function SessionContextMenu(props: SessionContextMenuProps) { duplicateSource(props.id, (name) => `${name} - Copy`)}> Duplicate - props.onChangeLanguage(props.id)}> - Change Language - + + props.onRename(props.id)}>Rename + props.onChangeLanguage(props.id)}>Change Language Reopen with... diff --git a/src/components/session/index.tsx b/src/components/session/index.tsx index de37c57..f74e86c 100644 --- a/src/components/session/index.tsx +++ b/src/components/session/index.tsx @@ -1,12 +1,16 @@ -import { activedSourceIdAtom, deleteSourceAtom, sourceAtom, sourceIdsAtom } from "@/store/source" +import { activedSourceIdAtom, createSourceAtom, deleteSourceAtom, sourceAtom, sourceIdsAtom } from "@/store/source" import clsx from "clsx" import { useAtom, useAtomValue, useSetAtom } from "jotai" import { isEmpty } from "lodash/fp" -import { VscClose, VscFile } from "react-icons/vsc" +import { VscClose, VscFile, VscNewFile } from "react-icons/vsc" import SessionContextMenu from "./SessionContextMenu" import useChangeLanguageDialog from "@/hooks/useChangeLanguageDialog" -import * as log from 'tauri-plugin-log-api' +import * as log from "tauri-plugin-log-api" +import { defaultLanguageAtom, defaultMemoryLimitsAtom, defaultTimeLimitsAtom } from "@/store/setting/setup" +import { useHoverDirty } from "react-use" +import { useRef } from "react" +import { useRenameDialog } from "@/hooks/useRenameDialog" interface FileItemProps { id: string @@ -28,7 +32,7 @@ function FileItem(props: FileItemProps) { onClick={() => props.onClick(props.id)} > - {nameDisplay} + {nameDisplay} @@ -44,33 +48,66 @@ export default function SessionPanel(props: SessionPanelProps) { const [activedSourceId, setActivedSourceId] = useAtom(activedSourceIdAtom) const removeSource = useSetAtom(deleteSourceAtom) const sourceStore = useAtomValue(sourceAtom) + const createSource = useSetAtom(createSourceAtom) + const defaultLanguage = useAtomValue(defaultLanguageAtom) + const defaultTimeLimit = useAtomValue(defaultTimeLimitsAtom) + const defaultMemoryLimit = useAtomValue(defaultMemoryLimitsAtom) - const [dialogChangeLanguage, showChangeLanguageDialog] = useChangeLanguageDialog() + const [dialogChangeLanguageElem, showChangeLanguageDialog] = useChangeLanguageDialog() + const [renameElem, showRenameDialog] = useRenameDialog() + const sessionPanelRef = useRef(null) + const isHover = useHoverDirty(sessionPanelRef) - async function changeLanguage(id: string){ + async function changeLanguage(id: string) { const src = sourceStore.get(id) - if(!src) return + if (!src) return const newLanguage = await showChangeLanguageDialog(src.language) - if(newLanguage){ + if (newLanguage) { log.info(`change lang of ${id} to ${newLanguage}`) src.language = newLanguage } } + async function renameSource(id: string) { + const src = sourceStore.get(id) + if (!src) return + const newName = await showRenameDialog(src.name.toString()) + if (newName) { + log.info(`rename ${id} to ${newName}`) + src.name.delete(0, src.name.length) + src.name.insert(0, newName) + } + } const filesList = sourceIds.map((v) => (
  • - +
  • )) + function newFile() { + createSource(defaultLanguage, defaultTimeLimit, defaultMemoryLimit) + } + return ( <> - {dialogChangeLanguage} -
    -
      {filesList}
    + {dialogChangeLanguageElem} + {renameElem} +
    +
      +
    • +
      + FILES + + +
      +
    • + {filesList} +
    ) diff --git a/src/hooks/useRenameDialog.tsx b/src/hooks/useRenameDialog.tsx index c1dfa6f..1b794cf 100644 --- a/src/hooks/useRenameDialog.tsx +++ b/src/hooks/useRenameDialog.tsx @@ -1,41 +1,62 @@ +import { FaRandom } from "react-icons/fa" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" -import { ReactNode, useState } from "react" -export function useRenameDialog( - callback: (newValue: string) => void, -): [ReactNode, (defaultValue: string) => void, boolean] { +import { ReactNode, useRef, useState } from "react" +import generateRandomName from "@/lib/names" +export function useRenameDialog(): [ReactNode, (defaultValue: string) => Promise] { const [renameDialogOpen, setRenameDialogOpen] = useState(false) - const [originTabName, setOriginTabName] = useState("") - const [targetTabName, setTargetName] = useState("") - function showDialog(defaultValue: string) { - setOriginTabName(defaultValue) + const [currentValue, setCurrentValue] = useState("") + const callback = useRef<(value: string | undefined) => void>() + + function showDialog(defaultValue: string): Promise { + setCurrentValue(defaultValue) setRenameDialogOpen(true) + return new Promise((resolve) => { + callback.current = resolve + }) + } + + function cancel() { + setRenameDialogOpen(false) + if (callback.current) { + callback.current(undefined) + callback.current = undefined + } + } + function confirm() { + setRenameDialogOpen(false) + if (callback.current) { + callback.current(currentValue) + callback.current = undefined + } + } + function randomName() { + setCurrentValue(generateRandomName("'s code")) } let element = ( - Rename {originTabName} + Rename File
    - setTargetName(e.target.value)} /> +
    + setCurrentValue(e.target.value)} /> + +
    - - @@ -43,5 +64,5 @@ export function useRenameDialog(
    ) - return [element, showDialog, renameDialogOpen] + return [element, showDialog] } diff --git a/src/lib/names.ts b/src/lib/names.ts new file mode 100644 index 0000000..5b1c987 --- /dev/null +++ b/src/lib/names.ts @@ -0,0 +1,146 @@ +import { capitalize, random } from "lodash/fp" + +export default function generateRandomName(suffix?: string){ + const adj = random(0, adjectives.length) + const n = random(0, nouns.length) + return capitalize(`${adjectives[adj]} ${nouns[n]}${suffix}`.trim()) +} + + +// Cop from https://github.com/zellij-org/zellij/blob/65a7fcf426e6131fe68b300fb746271419a08865/src/sessions.rs#L426-L560 +// Thanks for the organization's fantastic work +const adjectives = [ + "adamant", + "adept", + "adventurous", + "arcadian", + "auspicious", + "awesome", + "blossoming", + "brave", + "charming", + "chatty", + "circular", + "considerate", + "cubic", + "curious", + "delighted", + "didactic", + "diligent", + "effulgent", + "erudite", + "excellent", + "exquisite", + "fabulous", + "fascinating", + "friendly", + "glowing", + "gracious", + "gregarious", + "hopeful", + "implacable", + "inventive", + "joyous", + "judicious", + "jumping", + "kind", + "likable", + "loyal", + "lucky", + "marvellous", + "mellifluous", + "nautical", + "oblong", + "outstanding", + "polished", + "polite", + "profound", + "quadratic", + "quiet", + "rectangular", + "remarkable", + "rusty", + "sensible", + "sincere", + "sparkling", + "splendid", + "stellar", + "tenacious", + "tremendous", + "triangular", + "undulating", + "unflappable", + "unique", + "verdant", + "vitreous", + "wise", + "zippy", +] + +const nouns = [ + "aardvark", + "accordion", + "apple", + "apricot", + "bee", + "brachiosaur", + "cactus", + "capsicum", + "clarinet", + "cowbell", + "crab", + "cuckoo", + "cymbal", + "diplodocus", + "donkey", + "drum", + "duck", + "echidna", + "elephant", + "foxglove", + "galaxy", + "glockenspiel", + "goose", + "hill", + "horse", + "iguanadon", + "jellyfish", + "kangaroo", + "lake", + "lemon", + "lemur", + "magpie", + "megalodon", + "mountain", + "mouse", + "muskrat", + "newt", + "oboe", + "ocelot", + "orange", + "panda", + "peach", + "pepper", + "petunia", + "pheasant", + "piano", + "pigeon", + "platypus", + "quasar", + "rhinoceros", + "river", + "rustacean", + "salamander", + "sitar", + "stegosaurus", + "tambourine", + "tiger", + "tomato", + "triceratops", + "ukulele", + "viola", + "weasel", + "xylophone", + "yak", + "zebra", +] diff --git a/src/pages/Main/event/menu-event.tsx b/src/pages/Main/event/menu-event.tsx index 421ef0c..2f16e56 100644 --- a/src/pages/Main/event/menu-event.tsx +++ b/src/pages/Main/event/menu-event.tsx @@ -89,8 +89,7 @@ export default function MenuEventReceiver() { "fileMenu", async (event) => { if (event == "new") { - const src = createSource(defaultLanguage, defaultTimeLimit, defaultMemoryLimit) - src.name.insert(0, "Unamed") + createSource(defaultLanguage, defaultTimeLimit, defaultMemoryLimit) } else if (event == "open") { openFile(sourceStore, setSourceMeta) } else if (event == "save" || event == "saveAs") { diff --git a/src/store/source/index.ts b/src/store/source/index.ts index dcd99b7..7a6c1f1 100644 --- a/src/store/source/index.ts +++ b/src/store/source/index.ts @@ -7,6 +7,7 @@ import { LanguageMode } from "@/lib/ipc" import { createYjsHookAtom } from "@/hooks/useY" import cache from "@/lib/fs/cache" import { fromSource } from "@/lib/fs/model" +import generateRandomName from "@/lib/names" export const docAtom = atom(new Doc()) export const sourceAtom = atom((get) => new SourceStore(get(docAtom))) @@ -67,13 +68,19 @@ export const activedSourceAtom = atom((get) => { */ export const createSourceAtom = atom( null, - (get, _, targetLanguage: LanguageMode, defaultTimeLimits: number, defaultMemoryLimits: number) => { + (get, _, targetLanguage: LanguageMode, defaultTimeLimits: number, defaultMemoryLimits: number, name?: string) => { const store = get(sourceAtom) const [source, id] = store.create() store.doc.transact(() => { source.language = targetLanguage source.timelimit = defaultTimeLimits source.memorylimit = defaultMemoryLimits + if(name){ + source.name.insert(0, name) + }else{ + const name = generateRandomName("'s code") + source.name.insert(0, name) + } log.info(`create new source: ${id}`) log.info(JSON.stringify(get(sourceIdsAtom))) })