From 81e8955c41111aa03070a67a65fb0de644c8319e Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Fri, 12 Jan 2024 22:14:33 +0900 Subject: [PATCH] refs #4653 Add jump modal --- locales/en/translation.json | 3 + renderer/components/Jump.tsx | 104 ++++++++++++++++++ renderer/components/layouts/timelines.tsx | 6 + .../components/timelines/Notifications.tsx | 4 +- renderer/components/timelines/Timeline.tsx | 4 +- renderer/pages/index.tsx | 2 +- 6 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 renderer/components/Jump.tsx diff --git a/locales/en/translation.json b/locales/en/translation.json index 2eefbb4d..d57e0db2 100644 --- a/locales/en/translation.json +++ b/locales/en/translation.json @@ -134,5 +134,8 @@ "title": "Report {user}", "detail": "Detail", "submit": "Submit" + }, + "search": { + "placeholder": "Search timeline" } } diff --git a/renderer/components/Jump.tsx b/renderer/components/Jump.tsx new file mode 100644 index 00000000..07a06d66 --- /dev/null +++ b/renderer/components/Jump.tsx @@ -0,0 +1,104 @@ +import { Dialog, DialogBody, Input, List, ListItem } from '@material-tailwind/react' +import { useRouter } from 'next/router' +import { useCallback, useEffect, useRef, useState } from 'react' +import { useIntl } from 'react-intl' + +type Props = { + opened: boolean + close: () => void +} + +type Timeline = { + title: string + path: string +} + +export default function Jump(props: Props) { + const router = useRouter() + const { formatMessage } = useIntl() + const [keyword, setKeyword] = useState('') + const [match, setMatch] = useState>([]) + const [selected, setSelected] = useState(0) + const [timelines, setTimelines] = useState>([]) + + const inputRef = useRef() + + const handleKeyPress = useCallback( + (event: KeyboardEvent) => { + if (event.key === 'Enter') { + if (match[selected]) { + jump(match[selected].path) + } + } + if (event.key === 'ArrowUp') { + setSelected(current => (current > 0 ? current - 1 : current)) + } else if (event.key === 'ArrowDown') { + setSelected(current => (match[current + 1] ? current + 1 : current)) + } + }, + [match, selected, setSelected] + ) + + useEffect(() => { + inputRef.current?.addEventListener('keydown', handleKeyPress) + + return () => { + inputRef.current?.removeEventListener('keydown', handleKeyPress) + } + }, [handleKeyPress, props.opened]) + + useEffect(() => { + if (keyword.length > 0) { + const m = timelines + .map(v => { + if (v.title.toLowerCase().includes(keyword.toLowerCase())) { + return v + } else { + return null + } + }) + .filter(v => v !== null) + setMatch(m) + } else { + setMatch(timelines) + } + }, [keyword, timelines]) + + useEffect(() => { + setTimelines([ + { title: formatMessage({ id: 'timeline.home' }), path: `/accounts/${router.query.id}/home` }, + { title: formatMessage({ id: 'timeline.notifications' }), path: `/accounts/${router.query.id}/notifications` }, + { title: formatMessage({ id: 'timeline.local' }), path: `/accounts/${router.query.id}/local` }, + { title: formatMessage({ id: 'timeline.public' }), path: `/accounts/${router.query.id}/public` } + ]) + }, [router.query.id]) + + const jump = (path: string) => { + props.close() + setKeyword('') + setSelected(0) + console.log(path) + router.push(path) + } + + return ( + + + setKeyword(e.target.value)} + size="lg" + label={formatMessage({ id: 'search.placeholder' })} + ref={inputRef} + /> + + {match.map((m, index) => ( + jump(m.path)}> + {m.title} + + ))} + + + + ) +} diff --git a/renderer/components/layouts/timelines.tsx b/renderer/components/layouts/timelines.tsx index e0f2a3dc..c5bb8e98 100644 --- a/renderer/components/layouts/timelines.tsx +++ b/renderer/components/layouts/timelines.tsx @@ -3,8 +3,10 @@ import { Card, List, ListItem, ListItemPrefix } from '@material-tailwind/react' import generator, { Entity } from 'megalodon' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' +import { useHotkeys } from 'react-hotkeys-hook' import { FaBell, FaGlobe, FaHouse, FaList, FaUsers } from 'react-icons/fa6' import { useIntl } from 'react-intl' +import Jump from '../Jump' type LayoutProps = { children: React.ReactNode @@ -16,6 +18,9 @@ export default function Layout({ children }: LayoutProps) { const [account, setAccount] = useState(null) const [lists, setLists] = useState>([]) + const [openJump, setOpenJump] = useState(false) + + useHotkeys('ctrl+k', () => setOpenJump(current => !current)) useEffect(() => { if (router.query.id) { @@ -67,6 +72,7 @@ export default function Layout({ children }: LayoutProps) { return (
+ setOpenJump(false)} />

{account?.username}

diff --git a/renderer/components/timelines/Notifications.tsx b/renderer/components/timelines/Notifications.tsx index fc986007..c70d2879 100644 --- a/renderer/components/timelines/Notifications.tsx +++ b/renderer/components/timelines/Notifications.tsx @@ -135,8 +135,8 @@ export default function Notifications(props: Props) { )} /> ) : ( -
- +
+
)}
diff --git a/renderer/components/timelines/Timeline.tsx b/renderer/components/timelines/Timeline.tsx index a7c02051..3c79afdf 100644 --- a/renderer/components/timelines/Timeline.tsx +++ b/renderer/components/timelines/Timeline.tsx @@ -224,8 +224,8 @@ export default function Timeline(props: Props) { )} /> ) : ( -
- +
+
)} diff --git a/renderer/pages/index.tsx b/renderer/pages/index.tsx index a7cb70cd..f4de810c 100644 --- a/renderer/pages/index.tsx +++ b/renderer/pages/index.tsx @@ -12,7 +12,7 @@ export default function Index() { if (accounts.length > 0) { if (typeof localStorage !== 'undefined') { const lastAccount = localStorage.getItem(`lastAccount`) - if (lastAccount) { + if (parseInt(lastAccount) >= 0) { router.push(`/accounts/${lastAccount}`) } else { router.push(`/accounts/${accounts[0].id}`)