From 8bd659ec6cfbe44459c67a97a17fae3e0651716e Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 5 Sep 2024 12:34:46 +0300 Subject: [PATCH 001/124] Tauri macOS dev buiild/bundle (#138) * try do build * fixes * do build * change config --- app/tauri.conf.json | 12 ++++---- bun.lockb | Bin 404992 -> 404992 bytes package.json | 65 ++++++++++++++++++++++---------------------- 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/app/tauri.conf.json b/app/tauri.conf.json index bef4ac9d..0b255c5d 100644 --- a/app/tauri.conf.json +++ b/app/tauri.conf.json @@ -1,12 +1,11 @@ { - "productName": "learn-anything", + "productName": "Learn Anything", "version": "0.1.0", - "identifier": "xyz.learn-anything", + "identifier": "xyz.learnanything.desktop", "build": { - "frontendDist": "../out", + "frontendDist": "https://dev.learn-anything.xyz", "devUrl": "http://localhost:3000", - "beforeDevCommand": "bun dev", - "beforeBuildCommand": "bun build" + "beforeDevCommand": "bun dev" }, "app": { "windows": [ @@ -15,8 +14,7 @@ "width": 800, "height": 600, "resizable": true, - "fullscreen": false, - "url": "http://localhost:3000" + "fullscreen": false } ], "security": { diff --git a/bun.lockb b/bun.lockb index 5e5de966a4ea3618e92c4940e271d91729ad9d08..0549f2a50e85f26ecb3e63a3586aa41324d54674 100755 GIT binary patch delta 630 zcmV-+0*U>A+!%n|7?3U?BpP!B1x4Y8`HZ$BzCYjwF>fdCOWFV95@WOo0hOLWb&}icU#PA-KFeqzc3tf+oGu_c2Ul8l@(E};D#KWRr^0J=MKHUYD!mog z)@8&Q%j4B%G3KQR#p z9}nT{0STnuk3<|@q3Q|75jQH2FN0GTJ3o$&z}Kr}vGe#JDO z(RhIqv%u3sCTKd#$^E2{7+V`)ZSr|h!)NS#elrmlwRaHL&uvS)A@Z4mIk+?0q@u|Z zlV=q8cDMEM0jO#~?4-+0+}&lCEOAFV<$0qPAbZ~MVFmWj&d`{3LvNLuIaLcT0+Cde8L%T!%zfGz(sbJ*xt@J*xx7 zQ2{fT1N{TF0X3Jo{R4mjF}FJY16Be7H?@%jivt5SE;6?t1OzE0mn8TD5SMZ&1SA(~ zS^xkR004l);f$7Tk23}o0V)6h005V5W&{_vswf0u9sxI(;2{JQhag7;w;)FaYX$=~ zE;6@{O$Eg%0XLUacLiXVBBTW&m*98>54YrZ1r22ZHJ6c>1tgd5%>@j%BHabSNtfa= Q1t6E;lm!R33MU5k1{OpP{Qv*} delta 613 zcmV-r0-F7R+!%n|7?3U?In8OblC(wkg!478%)|$oPM595YDCC_qTZ?HfNhHcu}*e? zlSDlOo0hOLWfO3>(zdSiJ1-ZEhK1PUV99P%F;X|SuV-RRvMO_565GkGQczuDl6BShz zEW0uvD$Tj{$xG4cU;-vz^Jn57w@tyK3%B+00jO#~0|%S->CO_AH+ws{Lrnud`^?#t)QZwwG^daM>yU!INCb+m=e`JO zniNEbdjzXiF%dA#roB20&&}-C81;oix7}O@j%BHabSNtgAa1tFDi1qQbYCkFNg!J7>x diff --git a/package.json b/package.json index f045e0df..567ef05d 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,35 @@ { - "name": "learn-anything", - "scripts": { - "dev": "bun web", - "web": "cd web && bun dev", - "web:build": "bun run --filter '*' build", - "cli": "bun run --watch cli/run.ts", - "seed": "bun --watch cli/seed.ts", - "tauri": "tauri" - }, - "workspaces": [ - "web" - ], - "dependencies": { - "@tauri-apps/cli": "^2.0.0-rc.6", - "@tauri-apps/plugin-fs": "^2.0.0-rc.2", - "jazz-nodejs": "^0.7.34", - "react-icons": "^5.3.0" - }, - "devDependencies": { - "bun-types": "^1.1.26" - }, - "prettier": { - "plugins": [ - "prettier-plugin-tailwindcss" - ], - "useTabs": true, - "semi": false, - "trailingComma": "none", - "printWidth": 120, - "arrowParens": "avoid" - }, - "license": "MIT" + "name": "learn-anything", + "scripts": { + "dev": "bun web", + "web": "cd web && bun dev", + "web:build": "bun run --filter '*' build", + "cli": "bun run --watch cli/run.ts", + "seed": "bun --watch cli/seed.ts", + "tauri": "tauri", + "app:build": "bun tauri build -b dmg -v" + }, + "workspaces": [ + "web" + ], + "dependencies": { + "@tauri-apps/cli": "^2.0.0-rc.6", + "@tauri-apps/plugin-fs": "^2.0.0-rc.2", + "jazz-nodejs": "^0.7.34", + "react-icons": "^5.3.0" + }, + "devDependencies": { + "bun-types": "^1.1.26" + }, + "prettier": { + "plugins": [ + "prettier-plugin-tailwindcss" + ], + "useTabs": true, + "semi": false, + "trailingComma": "none", + "printWidth": 120, + "arrowParens": "avoid" + }, + "license": "MIT" } From 1c01a18c89efeba8ebe973b83a18bf81368bd960 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Thu, 5 Sep 2024 19:13:50 +0700 Subject: [PATCH 002/124] chore: update package json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 567ef05d..f3eee142 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "web" ], "dependencies": { - "@tauri-apps/cli": "^2.0.0-rc.6", + "@tauri-apps/cli": "^2.0.0-rc.11", "@tauri-apps/plugin-fs": "^2.0.0-rc.2", - "jazz-nodejs": "^0.7.34", + "jazz-nodejs": "0.7.35-unique.2", "react-icons": "^5.3.0" }, "devDependencies": { From 2bebcbc20a886cd42aec8fc10793f802727e330e Mon Sep 17 00:00:00 2001 From: Aslam Date: Fri, 6 Sep 2024 07:31:22 +0700 Subject: [PATCH 003/124] fix: topic selector virtual (#142) --- bun.lockb | Bin 404992 -> 404512 bytes .../routes/page/detail/PageDetailRoute.tsx | 106 +++++++----------- web/components/routes/page/detail/header.tsx | 63 ++++++----- 3 files changed, 72 insertions(+), 97 deletions(-) diff --git a/bun.lockb b/bun.lockb index 0549f2a50e85f26ecb3e63a3586aa41324d54674..4c75131e3921a04ba524398d948dcd825d5f9727 100755 GIT binary patch delta 75041 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 Rz1TRFJ*5Qs1!TMM{{VRq!Po!* delta 75366 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 v4n6PBQdz|1A&yvVE0Lvf4o^R|d+ZAOsU) const router = useRouter() - const confirm = useConfirm() const handleDelete = async () => { @@ -38,17 +36,11 @@ export function PageDetailRoute({ pageId }: { pageId: string }) { description: "Are you sure you want to delete this page?", confirmText: "Delete", cancelText: "Cancel", - cancelButton: { - variant: "outline" - }, - confirmButton: { - variant: "destructive" - } + cancelButton: { variant: "outline" }, + confirmButton: { variant: "destructive" } }) - if (result) { - if (!me?.root.personalPages) return - + if (result && me?.root.personalPages) { try { const index = me.root.personalPages.findIndex(item => item?.id === pageId) if (index === -1) { @@ -56,22 +48,11 @@ export function PageDetailRoute({ pageId }: { pageId: string }) { return } - toast.success("Page deleted.", { - position: "bottom-right", - description: ( - - {page?.title} has been deleted. - - ) - }) - me.root.personalPages.splice(index, 1) - - // push without history + toast.success("Page deleted.", { position: "bottom-right" }) router.replace("/") } catch (error) { console.error("Delete operation fail", { error }) - return } } } @@ -82,48 +63,49 @@ export function PageDetailRoute({ pageId }: { pageId: string }) {

- +
- - {!isMobile && ( -
-
-
- Page actions -
- -
- { - page.topic = topic - page.updatedAt = new Date() - }} - variant="ghost" - className="-ml-1.5" - renderSelectedText={() => ( - {page.topic?.prettyName || "Select a topic"} - )} - /> - -
-
-
- )} + {!isMobile && }
) } -export const DetailPageForm = ({ page }: { page: PersonalPage }) => { +const SidebarActions = ({ page, handleDelete }: { page: PersonalPage; handleDelete: () => void }) => ( +
+
+
+ Page actions +
+
+
+ { + page.topic = topic + page.updatedAt = new Date() + }} + variant="ghost" + className="-ml-1.5" + renderSelectedText={() => {page.topic?.prettyName || "Select a topic"}} + /> +
+
+ +
+
+
+
+) + +const DetailPageForm = ({ page }: { page: PersonalPage }) => { const { me } = useAccount() const titleEditorRef = useRef(null) const contentEditorRef = useRef(null) - const isTitleInitialMount = useRef(true) const isContentInitialMount = useRef(true) @@ -132,7 +114,6 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => { isContentInitialMount.current = false return } - model.content = content model.updatedAt = new Date() } @@ -143,13 +124,10 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => { return } - const personalPages = me?.root?.personalPages?.toJSON() || [] const newTitle = editor.getText() - - // Only update if the title has actually changed if (newTitle !== page.title) { + const personalPages = me?.root?.personalPages?.toJSON() || [] const slug = generateUniqueSlug(personalPages, page.slug || "") - page.title = newTitle page.slug = slug page.updatedAt = new Date() @@ -181,7 +159,6 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => { } break } - return false }, []) @@ -198,7 +175,6 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => { titleEditorRef.current?.commands.focus("end") return true } - return false }, []) @@ -217,9 +193,7 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => { strike: false, focus: false, gapcursor: false, - placeholder: { - placeholder: TITLE_PLACEHOLDER - } + placeholder: { placeholder: TITLE_PLACEHOLDER } }) ], editorProps: { diff --git a/web/components/routes/page/detail/header.tsx b/web/components/routes/page/detail/header.tsx index e66989fb..bb9b6afc 100644 --- a/web/components/routes/page/detail/header.tsx +++ b/web/components/routes/page/detail/header.tsx @@ -1,42 +1,43 @@ -"use client" - -import * as React from "react" +import React from "react" import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header" import { PersonalPage } from "@/lib/schema/personal-page" -import { useMedia } from "react-use" import { TopicSelector } from "@/components/custom/topic-selector" import { Button } from "@/components/ui/button" import { LaIcon } from "@/components/custom/la-icon" -export const DetailPageHeader = ({ page, handleDelete }: { page: PersonalPage; handleDelete: () => void }) => { - const isMobile = useMedia("(max-width: 770px)") +interface DetailPageHeaderProps { + page: PersonalPage + handleDelete: () => void + isMobile: boolean +} + +export const DetailPageHeader: React.FC = ({ page, handleDelete, isMobile }) => { + if (!isMobile) return null return ( - isMobile && ( - <> - -
- -
-
- -
- { - page.topic = topic - page.updatedAt = new Date() - }} - align="start" - variant="outline" - renderSelectedText={() => {page.topic?.prettyName || "Select a topic"}} - /> - + <> + +
+
- - ) +
+ +
+ { + page.topic = topic + page.updatedAt = new Date() + }} + align="start" + variant="outline" + renderSelectedText={() => {page.topic?.prettyName || "Select a topic"}} + /> + +
+ ) } From c3e99d1366e5cc089062867c094b000f77ef6d08 Mon Sep 17 00:00:00 2001 From: Aslam Date: Fri, 6 Sep 2024 16:22:48 +0700 Subject: [PATCH 004/124] feat: command palette (#140) * wip * feat: new command palette * chore: add universal search * chore: cleanup * feat: use title class for heading * feat: add topic * chore: advance search --- web/app/(pages)/layout.tsx | 2 +- web/app/command-palette.css | 127 ++++++++++ web/app/globals.css | 2 + .../custom/command-palette/command-data.ts | 67 +++++ .../custom/command-palette/command-items.tsx | 42 ++++ .../command-palette/command-palette.tsx | 230 ++++++++++++++++++ .../hooks/use-command-actions.ts | 60 +++++ .../routes/public/PublicHomeRoute.tsx | 2 +- web/components/ui/CommandPalette.tsx | 177 -------------- web/lib/providers/deep-link-provider.tsx | 4 +- web/lib/utils/index.ts | 16 ++ 11 files changed, 548 insertions(+), 181 deletions(-) create mode 100644 web/app/command-palette.css create mode 100644 web/components/custom/command-palette/command-data.ts create mode 100644 web/components/custom/command-palette/command-items.tsx create mode 100644 web/components/custom/command-palette/command-palette.tsx create mode 100644 web/components/custom/command-palette/hooks/use-command-actions.ts delete mode 100644 web/components/ui/CommandPalette.tsx diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index 8cadcceb..47c7fa0d 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -1,9 +1,9 @@ import { SignedInClient } from "@/components/custom/clerk/signed-in-client" import { Sidebar } from "@/components/custom/sidebar/sidebar" import { PublicHomeRoute } from "@/components/routes/public/PublicHomeRoute" -import { CommandPalette } from "@/components/ui/CommandPalette" import { JazzClerkAuth, JazzProvider } from "@/lib/providers/jazz-provider" import { currentUser } from "@clerk/nextjs/server" +import { CommandPalette } from "@/components/custom/command-palette/command-palette" export default async function PageLayout({ children }: { children: React.ReactNode }) { const user = await currentUser() diff --git a/web/app/command-palette.css b/web/app/command-palette.css new file mode 100644 index 00000000..808aa88b --- /dev/null +++ b/web/app/command-palette.css @@ -0,0 +1,127 @@ +@keyframes scaleIn { + 0% { + transform: scale(0.97) translateX(-50%); + opacity: 0; + } + to { + transform: scale(1) translateX(-50%); + opacity: 1; + } +} + +@keyframes scaleOut { + 0% { + transform: scale(1) translateX(-50%); + opacity: 1; + } + to { + transform: scale(0.97) translateX(-50%); + opacity: 0; + } +} + +@keyframes fadeIn { + 0% { + opacity: 0; + } + to { + opacity: 0.8; + } +} +@keyframes fadeOut { + 0% { + opacity: 0.8; + } + to { + opacity: 0; + } +} + +:root { + --cmdk-shadow: rgba(0, 0, 0, 0.12) 0px 4px 30px, rgba(0, 0, 0, 0.04) 0px 3px 17px, rgba(0, 0, 0, 0.04) 0px 2px 8px, + rgba(0, 0, 0, 0.04) 0px 1px 1px; + --cmdk-bg: rgb(255, 255, 255); + --cmdk-border-color: rgb(216, 216, 216); + + --cmdk-input-color: rgb(48, 48, 49); + --cmdk-input-placeholder: hsl(0, 0%, 56.1%); + + --cmdk-accent: rgb(243, 243, 243); +} + +.dark { + --cmdk-shadow: rgba(0, 0, 0, 0.15) 0px 4px 40px, rgba(0, 0, 0, 0.184) 0px 3px 20px, rgba(0, 0, 0, 0.184) 0px 3px 12px, + rgba(0, 0, 0, 0.184) 0px 2px 8px, rgba(0, 0, 0, 0.184) 0px 1px 1px; + --cmdk-bg: rgb(27, 28, 31); + --cmdk-border-color: rgb(56, 59, 65); + + --cmdk-input-color: rgb(228, 229, 233); + --cmdk-input-placeholder: hsl(0, 0%, 43.9%); + + --cmdk-accent: rgb(44, 48, 57); +} + +[la-overlay][cmdk-overlay] { + animation: fadeIn 0.2s ease; + @apply fixed inset-0 z-50 opacity-80; +} + +[la-dialog][cmdk-dialog] { + top: 15%; + transform: translateX(-50%); + width: 640px; + background: var(--cmdk-bg); + box-shadow: var(--cmdk-shadow); + transform-origin: left; + animation: scaleIn 0.2s ease; + transition: transform 0.1s ease; + border: 0.5px solid var(--cmdk-border-color); + @apply fixed left-1/2 z-50 overflow-hidden rounded-lg outline-none; +} + +[la-dialog][cmdk-dialog][data-state="closed"] { + animation: scaleOut 0.2s ease; +} + +.la [cmdk-input-wrapper] { + border-bottom: 1px solid var(--cmdk-border-color); + height: 62px; + font-size: 1.125rem; + @apply relative; +} + +.la [cmdk-input] { + font-size: inherit; + height: 62px; + color: var(--cmdk-input-color); + caret-color: rgb(110, 94, 210); + @apply m-0 w-full appearance-none border-none bg-transparent p-5 outline-none; +} + +.la [cmdk-input]::placeholder { + color: var(--cmdk-input-placeholder); +} + +.la [cmdk-list] { + max-height: 400px; + overflow: auto; + overscroll-behavior: contain; + transition: 100ms ease; + transition-property: height; + @apply p-2; +} + +.la [cmdk-group-heading] { + font-size: 13px; + height: 30px; + @apply text-muted-foreground flex items-center px-2; +} + +.la [cmdk-empty] { + @apply text-muted-foreground flex h-16 items-center justify-center whitespace-pre-wrap text-sm; +} + +.la [cmdk-item] { + scroll-margin: 8px 0; + @apply flex min-h-10 cursor-pointer items-center gap-3 rounded-md px-2 text-sm aria-selected:bg-[var(--cmdk-accent)]; +} diff --git a/web/app/globals.css b/web/app/globals.css index 623eca9c..8bdc5be5 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -71,3 +71,5 @@ @apply bg-background text-foreground; } } + +@import "./command-palette.css"; diff --git a/web/components/custom/command-palette/command-data.ts b/web/components/custom/command-palette/command-data.ts new file mode 100644 index 00000000..e6d7a3d1 --- /dev/null +++ b/web/components/custom/command-palette/command-data.ts @@ -0,0 +1,67 @@ +import { icons } from "lucide-react" +import { useCommandActions } from "./hooks/use-command-actions" +import { LaAccount } from "@/lib/schema" + +export type CommandAction = string | (() => void) + +export type CommandItemType = { + icon?: keyof typeof icons + label: string + action: CommandAction + payload?: any + shortcut?: string +} + +export type CommandGroupType = { + heading?: string + items: CommandItemType[] +}[] + +export const createCommandGroups = ( + actions: ReturnType, + me: LaAccount +): Record => ({ + home: [ + { + heading: "General", + items: [ + { icon: "SunMoon", label: "Change Theme...", action: "CHANGE_PAGE", payload: "changeTheme" }, + { + icon: "Copy", + label: "Copy Current URL", + action: actions.copyCurrentURL + } + ] + }, + { + heading: "Personal Links", + items: [ + { icon: "TextSearch", label: "Search Links...", action: "CHANGE_PAGE", payload: "searchLinks" }, + { icon: "Plus", label: "Create New Link...", action: () => actions.navigateTo("/") } + ] + }, + { + heading: "Personal Pages", + items: [ + { icon: "FileSearch", label: "Search Pages...", action: "CHANGE_PAGE", payload: "searchPages" }, + { + icon: "Plus", + label: "Create New Page...", + action: () => actions.createNewPage(me) + } + ] + } + ], + searchLinks: [], + searchPages: [], + topics: [], + changeTheme: [ + { + items: [ + { icon: "Moon", label: "Change Theme to Dark", action: () => actions.changeTheme("dark") }, + { icon: "Sun", label: "Change Theme to Light", action: () => actions.changeTheme("light") }, + { icon: "Monitor", label: "Change Theme to System", action: () => actions.changeTheme("system") } + ] + } + ] +}) diff --git a/web/components/custom/command-palette/command-items.tsx b/web/components/custom/command-palette/command-items.tsx new file mode 100644 index 00000000..0404a01e --- /dev/null +++ b/web/components/custom/command-palette/command-items.tsx @@ -0,0 +1,42 @@ +import { Command } from "cmdk" +import { CommandSeparator, CommandShortcut } from "@/components/ui/command" +import { LaIcon } from "../la-icon" +import { CommandItemType, CommandAction } from "./command-data" + +export interface CommandItemProps extends Omit { + action: CommandAction + handleAction: (action: CommandAction, payload?: any) => void +} + +export const CommandItem: React.FC = ({ icon, label, action, payload, shortcut, handleAction }) => ( + handleAction(action, payload)}> + {icon && } + {label} + {shortcut && {shortcut}} + +) +export interface CommandGroupProps { + heading?: string + items: CommandItemType[] + handleAction: (action: CommandAction, payload?: any) => void + isLastGroup: boolean +} + +export const CommandGroup: React.FC = ({ heading, items, handleAction, isLastGroup }) => { + return ( + <> + {heading ? ( + + {items.map((item, index) => ( + + ))} + + ) : ( + items.map((item, index) => ( + + )) + )} + {!isLastGroup && } + + ) +} diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx new file mode 100644 index 00000000..a6b5386a --- /dev/null +++ b/web/components/custom/command-palette/command-palette.tsx @@ -0,0 +1,230 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { Command } from "cmdk" +import { Dialog, DialogPortal, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog" +import { CommandGroup } from "./command-items" +import { CommandAction, CommandItemType, createCommandGroups } from "./command-data" +import { useAccount } from "@/lib/providers/jazz-provider" +import { searchSafeRegExp, toTitleCase } from "@/lib/utils" +import { GraphNode } from "@/components/routes/public/PublicHomeRoute" +import { useCommandActions } from "./hooks/use-command-actions" + +let graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) + +const filterItems = (items: CommandItemType[], searchRegex: RegExp) => + items.filter(item => searchRegex.test(item.label)).slice(0, 6) + +export function CommandPalette() { + const { me } = useAccount({ root: { personalLinks: [], personalPages: [] } }) + const dialogRef = React.useRef(null) + const [inputValue, setInputValue] = React.useState("") + const [activePage, setActivePage] = React.useState("home") + const [open, setOpen] = React.useState(false) + + const actions = useCommandActions() + const commandGroups = React.useMemo(() => me && createCommandGroups(actions, me), [actions, me]) + + const raw_graph_data = React.use(graph_data_promise) as GraphNode[] + + React.useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + e.preventDefault() + setOpen(prev => !prev) + } + } + + document.addEventListener("keydown", down) + return () => document.removeEventListener("keydown", down) + }, []) + + const bounce = React.useCallback(() => { + if (dialogRef.current) { + dialogRef.current.style.transform = "scale(0.99) translateX(-50%)" + setTimeout(() => { + if (dialogRef.current) { + dialogRef.current.style.transform = "" + } + }, 100) + } + }, []) + + const handleKeyDown = React.useCallback( + (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + bounce() + } + + if (activePage !== "home" && !inputValue && e.key === "Backspace") { + e.preventDefault() + setActivePage("home") + setInputValue("") + bounce() + } + }, + [activePage, inputValue, bounce] + ) + + const allCommands = React.useMemo(() => { + if (!commandGroups) return [] + + return Object.entries(commandGroups).map(([key, value]) => ({ + heading: toTitleCase(key), + items: value.flatMap(subgroup => subgroup.items) + })) + }, [commandGroups]) + + const topics = React.useMemo( + () => ({ + heading: "Topics", + items: raw_graph_data.map(topic => ({ + icon: "Circle" as const, + label: topic?.prettyName || "", + action: () => actions.navigateTo(`/${topic?.name}`) + })) + }), + [raw_graph_data, actions] + ) + + const personalLinks = React.useMemo( + () => ({ + heading: "Personal Links", + items: + me?.root.personalLinks?.map(link => ({ + icon: "Link" as const, + label: link?.title || "Untitled", + action: () => actions.openLinkInNewTab(link?.url || "#") + })) || [] + }), + [me?.root.personalLinks, actions] + ) + + const personalPages = React.useMemo( + () => ({ + heading: "Personal Pages", + items: + me?.root.personalPages?.map(page => ({ + icon: "FileText" as const, + label: page?.title || "Untitled", + action: () => actions.navigateTo(`/pages/${page?.id}`) + })) || [] + }), + [me?.root.personalPages, actions] + ) + + const getFilteredCommands = React.useCallback(() => { + if (!commandGroups) return [] + + const searchRegex = searchSafeRegExp(inputValue) + + if (activePage === "home") { + if (!inputValue) return commandGroups.home + + return [...allCommands, personalLinks, personalPages, topics] + .map(group => ({ + heading: group.heading, + items: filterItems(group.items, searchRegex) + })) + .filter(group => group.items.length > 0) + } + + switch (activePage) { + case "searchLinks": + return [...commandGroups.searchLinks, { items: filterItems(personalLinks.items, searchRegex) }] + case "searchPages": + return [...commandGroups.searchPages, { items: filterItems(personalPages.items, searchRegex) }] + default: + const pageCommands = commandGroups[activePage] + if (!inputValue) return pageCommands + return pageCommands + .map(group => ({ + heading: group.heading, + items: filterItems(group.items, searchRegex) + })) + .filter(group => group.items.length > 0) + } + }, [inputValue, activePage, allCommands, personalLinks, personalPages, commandGroups, topics]) + + const handleAction = React.useCallback( + (action: CommandAction, payload?: any) => { + const closeDialog = () => { + setOpen(false) + } + + if (typeof action === "function") { + action() + closeDialog() + return + } + + switch (action) { + case "CHANGE_PAGE": + if (payload) { + setActivePage(payload) + setInputValue("") + bounce() + } else { + console.error(`Invalid page: ${payload}`) + } + break + default: + console.log(`Unhandled action: ${action}`) + closeDialog() + } + }, + [bounce] + ) + + const filteredCommands = React.useMemo(() => getFilteredCommands(), [getFilteredCommands]) + + const commandKey = React.useMemo(() => { + return filteredCommands + .map(group => { + const itemsKey = group.items.map(item => `${item.label}-${item.action}`).join("|") + return `${group.heading}:${itemsKey}` + }) + .join("__") + }, [filteredCommands]) + + if (!me) return null + + return ( + + + + + + Command Palette + Search for commands and actions + + + +
+ +
+ + + No results found. + {filteredCommands.map((group, index, array) => ( + + ))} + +
+
+
+
+ ) +} diff --git a/web/components/custom/command-palette/hooks/use-command-actions.ts b/web/components/custom/command-palette/hooks/use-command-actions.ts new file mode 100644 index 00000000..453e365c --- /dev/null +++ b/web/components/custom/command-palette/hooks/use-command-actions.ts @@ -0,0 +1,60 @@ +import * as React from "react" +import { ensureUrlProtocol } from "@/lib/utils" +import { useTheme } from "next-themes" +import { toast } from "sonner" +import { useRouter } from "next/navigation" +import { LaAccount, PersonalPage } from "@/lib/schema" + +export const useCommandActions = () => { + const { setTheme } = useTheme() + const router = useRouter() + + const changeTheme = React.useCallback( + (theme: string) => { + setTheme(theme) + toast.success(`Theme changed to ${theme}.`, { position: "bottom-right" }) + }, + [setTheme] + ) + + const navigateTo = React.useCallback( + (path: string) => { + router.push(path) + }, + [router] + ) + + const openLinkInNewTab = React.useCallback((url: string) => { + window.open(ensureUrlProtocol(url), "_blank") + }, []) + + const copyCurrentURL = React.useCallback(() => { + navigator.clipboard.writeText(window.location.href) + toast.success("URL copied to clipboard.", { position: "bottom-right" }) + }, []) + + const createNewPage = React.useCallback( + (me: LaAccount) => { + try { + const newPersonalPage = PersonalPage.create( + { public: false, createdAt: new Date(), updatedAt: new Date() }, + { owner: me._owner } + ) + + me.root?.personalPages?.push(newPersonalPage) + router.push(`/pages/${newPersonalPage.id}`) + } catch (error) { + toast.error("Failed to create page") + } + }, + [router] + ) + + return { + changeTheme, + navigateTo, + openLinkInNewTab, + copyCurrentURL, + createNewPage + } +} diff --git a/web/components/routes/public/PublicHomeRoute.tsx b/web/components/routes/public/PublicHomeRoute.tsx index f6668b7a..ea1f7ab1 100644 --- a/web/components/routes/public/PublicHomeRoute.tsx +++ b/web/components/routes/public/PublicHomeRoute.tsx @@ -10,7 +10,7 @@ let graph_data_promise = import("./graph-data.json").then(a => a.default) const ForceGraphClient = dynamic(() => import("./force-graph-client-lazy"), { ssr: false }) -interface GraphNode { +export interface GraphNode { name: string prettyName: string connectedTopics: string[] diff --git a/web/components/ui/CommandPalette.tsx b/web/components/ui/CommandPalette.tsx deleted file mode 100644 index d860e500..00000000 --- a/web/components/ui/CommandPalette.tsx +++ /dev/null @@ -1,177 +0,0 @@ -"use client" - -import { AnimatePresence, motion } from "framer-motion" -import { useEffect, useState, KeyboardEvent as ReactKeyboardEvent } from "react" -import { Icon } from "../la-editor/components/ui/icon" -import { linkShowCreateAtom } from "@/store/link" -import { generateUniqueSlug } from "@/lib/utils" -import { useAtom } from "jotai" -import { PersonalPage } from "@/lib/schema/personal-page" -import { useRouter } from "next/navigation" -import { useAccount } from "@/lib/providers/jazz-provider" -import { toast } from "sonner" - -export function CommandPalette() { - const [showPalette, setShowPalette] = useState(false) - const [showCreate, setShowCreate] = useAtom(linkShowCreateAtom) - const router = useRouter() - const { me } = useAccount() - - const [commands, setCommands] = useState< - { name: string; icon?: React.ReactNode; keybind?: string[]; action: () => void }[] - >([ - { - name: "Create new link", - icon: , - // keybind: ["Ctrl", "K"], - action: () => { - if (window.location.pathname !== "/") { - router.push("/") - } - setShowCreate(true) - } - }, - { - name: "Create page", - icon: , - // keybind: ["Ctrl", "P"], - action: () => { - const personalPages = me?.root?.personalPages?.toJSON() || [] - const slug = generateUniqueSlug(personalPages, "Untitled Page") - - const newPersonalPage = PersonalPage.create( - { - title: "Untitled Page", - slug: slug, - content: "" - }, - { owner: me._owner } - ) - - me.root?.personalPages?.push(newPersonalPage) - - router.push(`/pages/${newPersonalPage.id}`) - } - } - // { - // name: "Assign status..", - // // icon: , - // // keybind: ["Ctrl", "P"], - // action: () => {} - // } - ]) - const [searchTerm, setSearchTerm] = useState("") - const [commandResults, setCommandResults] = useState(commands) - const [selectedIndex, setSelectedIndex] = useState(0) - - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if ((event.metaKey || event.ctrlKey) && event.key === "k") { - event.preventDefault() - setShowPalette(prev => !prev) - } else if (showPalette) { - if (["Escape", "Enter", "ArrowDown", "ArrowUp"].includes(event.key)) { - event.preventDefault() - event.stopPropagation() - - // Handle the key events here - if (event.key === "Escape") { - setShowPalette(false) - } else if (event.key === "Enter" && commandResults.length > 0) { - commandResults[selectedIndex].action() - setShowPalette(false) - } else if (event.key === "ArrowDown") { - setSelectedIndex(prevIndex => (prevIndex < commandResults.length - 1 ? prevIndex + 1 : prevIndex)) - } else if (event.key === "ArrowUp") { - setSelectedIndex(prevIndex => (prevIndex > 0 ? prevIndex - 1 : prevIndex)) - } - } - } - } - - document.addEventListener("keydown", handleKeyDown, true) - - return () => { - document.removeEventListener("keydown", handleKeyDown, true) - } - }, [showPalette, commandResults, selectedIndex]) - - // Remove the separate handleKeyDown function for the input - // as we're now handling all key events in the global listener - - if (!showPalette) return null - - return ( - - setShowPalette(false)} - > -
e.stopPropagation()} - className="relative h-fit w-[600px] rounded-lg border border-slate-400/20 bg-white drop-shadow-xl dark:bg-neutral-900" - > -
-
-
    - {commandResults.map((command, index) => ( -
  • { - command.action() - setShowPalette(false) - }} - > -
    - - - {command.name} -
    - {command.keybind && ( -
    - {command.keybind.map(key => ( - - {key} - - ))} -
    - )} -
  • - ))} - {commandResults.length === 0 && ( -
  • No results found
  • - )} -
-
-
-
- ) -} diff --git a/web/lib/providers/deep-link-provider.tsx b/web/lib/providers/deep-link-provider.tsx index 11236507..7d22c8d9 100644 --- a/web/lib/providers/deep-link-provider.tsx +++ b/web/lib/providers/deep-link-provider.tsx @@ -11,11 +11,11 @@ export function DeepLinkProvider({ children }: DeepLinkProviderProps) { const eventHandlers: { [key: string]: (event: Event) => void } = { click: (event: Event) => { const e = event as MouseEvent - console.log("Click event:", { x: e.clientX, y: e.clientY }) + // console.log("Click event:", { x: e.clientX, y: e.clientY }) }, keydown: (event: Event) => { const e = event as KeyboardEvent - console.log("Keydown event:", { key: e.key, code: e.code }) + // console.log("Keydown event:", { key: e.key, code: e.code }) } } diff --git a/web/lib/utils/index.ts b/web/lib/utils/index.ts index 742f4e90..5762b1d0 100644 --- a/web/lib/utils/index.ts +++ b/web/lib/utils/index.ts @@ -9,6 +9,22 @@ export const randomId = () => { return Math.random().toString(36).substring(7) } +export const toTitleCase = (str: string): string => { + return str + .replace(/([A-Z])/g, " $1") + .replace(/^./, str => str.toUpperCase()) + .trim() +} + +function escapeRegExp(string: string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") +} + +export const searchSafeRegExp = (inputValue: string) => { + const escapedChars = inputValue.split("").map(escapeRegExp) + return new RegExp(escapedChars.join(".*"), "i") +} + export * from "./urls" export * from "./slug" export * from "./keyboard" From bc072890fcc88cdd722ffa4ce45ac5c0ca31ca99 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Fri, 6 Sep 2024 12:27:35 +0300 Subject: [PATCH 005/124] profile in process --- bun.lockb | Bin 404512 -> 404520 bytes .../(pages)/profile/_components/wrapper.tsx | 175 +++++++++++------- .../sidebar/partial/profile-section.tsx | 72 +++---- web/lib/schema/index.ts | 9 +- 4 files changed, 152 insertions(+), 104 deletions(-) diff --git a/bun.lockb b/bun.lockb index 4c75131e3921a04ba524398d948dcd825d5f9727..b2cb3516c2f5bd60dedd32edef65fa5b4e6e73d2 100755 GIT binary patch delta 42515 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�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 Aq5uE@ diff --git a/web/app/(pages)/profile/_components/wrapper.tsx b/web/app/(pages)/profile/_components/wrapper.tsx index a8433c32..a236a42e 100644 --- a/web/app/(pages)/profile/_components/wrapper.tsx +++ b/web/app/(pages)/profile/_components/wrapper.tsx @@ -1,10 +1,11 @@ "use client" import { useAccount } from "@/lib/providers/jazz-provider" -import { useParams, useRouter } from "next/navigation" +import { useParams } from "next/navigation" +import { useState, useRef } from "react" +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" +import { Input } from "@/components/ui/input" import Link from "next/link" -import { LaIcon } from "@/components/custom/la-icon" -import { Icon } from "@/components/la-editor/components/ui/icon" import { Button } from "@/components/ui/button" interface ProfileStatsProps { @@ -12,16 +13,6 @@ interface ProfileStatsProps { label: string } -interface ProfileLinksProps { - linklabel?: string - link?: string - topic?: string -} - -interface ProfilePagesProps { - topic?: string -} - const ProfileStats: React.FC = ({ number, label }) => { return (
@@ -31,37 +22,47 @@ const ProfileStats: React.FC = ({ number, label }) => { ) } -const ProfileLinks: React.FC = ({ linklabel, link, topic }) => { - return ( -
-
-

{linklabel || "Untitled"}

-
- -

{link || "#"}

-
-
-
{topic || "Uncategorized"}
-
- ) -} - -const ProfilePages: React.FC = ({ topic }) => { - return ( -
-
{topic || "Uncategorized"}
-
- ) -} - export const ProfileWrapper = () => { const account = useAccount() const params = useParams() const username = params.username as string + const avatarInputRef = useRef(null) - const router = useRouter() + const [isEditing, setIsEditing] = useState(false) + const [newName, setNewName] = useState(account.me?.profile?.name || "") + const [originalName, setOriginalName] = useState(account.me?.profile?.name || "") - const clickEdit = () => router.push("/edit-profile") + const editProfileClicked = () => { + setIsEditing(!isEditing) + } + + const changeName = (e: React.ChangeEvent) => { + setNewName(e.target.value) + } + + const saveProfile = () => { + if (newName.trim() !== "") { + if (account.me && account.me.profile) { + account.me.profile.name = newName.trim() + } + setOriginalName(newName.trim()) + setIsEditing(false) + } + } + + const clickOutside = () => { + if (isEditing) { + setNewName(originalName) + setIsEditing(false) + } + } + + const editAvatar = (event: React.ChangeEvent) => { + const file = event.target.files?.[0] + if (file) { + console.log("File selected:", file) + } + } if (!account.me || !account.me.profile) { return ( @@ -87,31 +88,36 @@ export const ProfileWrapper = () => {

Profile

-

{username}

-
+ + + avatarInputRef.current?.click()} className="cursor-pointer"> + {account.me?.profile?.name?.charAt(0)} + + + +
-

{account.me.profile.name}

-
- @

{account.me.root?.username}

-
- - -

{account.me.root?.website}

-
+ {isEditing ? ( + + ) : ( +

{account.me?.profile?.name}

+ )}
- +
@@ -121,17 +127,52 @@ export const ProfileWrapper = () => {
- - {/*
-

Public Pages

- {account.me.root?.personalPages?.map((page, index) => )} +
+

Public profiles are coming soon

-
-

Public Links

- {account.me.root?.personalLinks?.map((link, index) => ( - - ))} -
*/}
) } + +{ + // interface ProfileLinksProps { + // linklabel?: string + // link?: string + // topic?: string + // } + // interface ProfilePagesProps { + // topic?: string + // } + // const ProfileLinks: React.FC = ({ linklabel, link, topic }) => { + // return ( + //
+ //
+ //

{linklabel || "Untitled"}

+ //
+ // + //

{link || "#"}

+ //
+ //
+ //
{topic || "Uncategorized"}
+ //
+ // ) + // } + // const ProfilePages: React.FC = ({ topic }) => { + // return ( + //
+ //
{topic || "Uncategorized"}
+ //
+ // ) + // } + /* + +

{account.me.root?.website}

+
*/ + /* */ +} diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index df626468..68f6bc2c 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -1,7 +1,7 @@ +import { useAccount } from "@/lib/providers/jazz-provider" import { LaIcon } from "../../la-icon" import { useState } from "react" -import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" -import { Button } from "@/components/ui/button" +import { useAuth } from "@clerk/nextjs" import { DropdownMenu, DropdownMenuContent, @@ -9,9 +9,8 @@ import { DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" -import { useAccount } from "@/lib/providers/jazz-provider" +import { Avatar, AvatarImage } from "@/components/ui/avatar" import Link from "next/link" -import { useAuth } from "@clerk/nextjs" const MenuItem = ({ icon, @@ -52,7 +51,7 @@ export const ProfileSection: React.FC = () => { const { me } = useAccount({ profile: true }) - const { signOut } = useAuth() + const { signOut, isSignedIn } = useAuth() const [menuOpen, setMenuOpen] = useState(false) const closeMenu = () => setMenuOpen(false) @@ -67,11 +66,16 @@ export const ProfileSection: React.FC = () => { aria-label="Profile" className="hover:bg-accent focus-visible:ring-ring hover:text-accent-foreground flex items-center gap-1.5 truncate rounded pl-1 pr-1.5 focus-visible:outline-none focus-visible:ring-1" > - - - {/* CN */} - - {me?.profile?.name} + {isSignedIn ? ( + + + + ) : ( + + )} + + {isSignedIn ? me?.profile?.name : "guest"} + { - - - - - - - - - - - - - - + {isSignedIn ? ( + <> + + + + + + + + + + + + + ) : ( + + + + )}
- {/*
-
- - - - -
*/}
) } + +/* + + */ diff --git a/web/lib/schema/index.ts b/web/lib/schema/index.ts index c0a29f54..3ba49c10 100644 --- a/web/lib/schema/index.ts +++ b/web/lib/schema/index.ts @@ -13,6 +13,11 @@ import { PersonalPageLists } from "./personal-page" import { PersonalLinkLists } from "./personal-link" import { ListOfTopics } from "./master/topic" +declare module "jazz-tools" { + interface Profile { + avatarUrl?: string + } +} export class UserRoot extends CoMap { name = co.string username = co.string @@ -33,7 +38,7 @@ export class LaAccount extends Account { profile = co.ref(Profile) root = co.ref(UserRoot) - migrate(this: LaAccount, creationProps?: { name: string }) { + migrate(this: LaAccount, creationProps?: { name: string; avatarUrl?: string }) { // since we dont have a custom AuthProvider yet. // and still using the DemoAuth. the creationProps will only accept name. // so just do default profile create provided by jazz-tools @@ -46,7 +51,7 @@ export class LaAccount extends Account { { name: creationProps.name, username: creationProps.name, - avatar: "", + avatar: creationProps.avatarUrl || "", website: "", bio: "", is_public: false, From 2551a49f953c3f604c0e0abdaad0b6f029415c8c Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Fri, 6 Sep 2024 12:49:58 +0300 Subject: [PATCH 006/124] links sidebar --- .../custom/sidebar/partial/link-section.tsx | 97 +++++++++++++++++++ web/components/custom/sidebar/sidebar.tsx | 4 +- web/components/routes/search/wrapper.tsx | 11 ++- 3 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 web/components/custom/sidebar/partial/link-section.tsx diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx new file mode 100644 index 00000000..a3da175a --- /dev/null +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -0,0 +1,97 @@ +import React from "react" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { useAccount } from "@/lib/providers/jazz-provider" +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { LaIcon } from "@/components/custom/la-icon" +import { PersonalLinkLists } from "@/lib/schema/personal-link" + +export const LinkSection: React.FC = () => { + const { me } = useAccount({ + root: { + personalLinks: [] + } + }) + + const linkCount = me?.root.personalLinks?.length || 0 + + if (!me) return null + + return ( +
+ + +
+ ) +} + +interface LinkSectionHeaderProps { + linkCount: number +} + +const LinkSectionHeader: React.FC = ({ linkCount }) => ( +
+ +

+ Links + {linkCount > 0 && {linkCount}} +

+ +
+) + +interface ListProps { + personalLinks: PersonalLinkLists +} + +const List: React.FC = ({ personalLinks }) => { + const pathname = usePathname() + + return ( +
+ +
+ ) +} + +interface ListItemProps { + label: string + href: string + count: number + isActive: boolean +} + +const ListItem: React.FC = ({ label, href, count, isActive }) => { + return ( +
+
+ +
+ +

{label}

+
+ + + {count > 0 && ( + {count} + )} +
+
+ ) +} diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index bfccd464..893dcebb 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -10,6 +10,7 @@ import { Logo } from "@/components/custom/logo" import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" import { isCollapseAtom } from "@/store/sidebar" +import { LinkSection } from "./partial/link-section" import { PageSection } from "./partial/page-section" import { TopicSection } from "./partial/topic-section" import { ProfileSection } from "./partial/profile-section" @@ -77,7 +78,7 @@ const LogoAndSearch: React.FC = React.memo(() => { return (
- +
@@ -116,6 +117,7 @@ const SidebarContent: React.FC = React.memo(() => {
+
diff --git a/web/components/routes/search/wrapper.tsx b/web/components/routes/search/wrapper.tsx index 11d59430..36cc66fb 100644 --- a/web/components/routes/search/wrapper.tsx +++ b/web/components/routes/search/wrapper.tsx @@ -130,7 +130,7 @@ export const SearchWrapper = () => { type="text" value={searchText} onChange={handleSearch} - placeholder="Search something..." + placeholder="Search topics, links, pages" className="dark:bg-input w-full rounded-lg border border-neutral-300 p-2 pl-8 focus:outline-none dark:border-neutral-600" /> {searchText && ( @@ -178,12 +178,13 @@ export const SearchWrapper = () => {
) : (
- {searchText && !showAiSearch && ( + {/* {searchText && !showAiSearch && ( */} + {searchText && (
setShowAiSearch(true)} + className="cursor-default rounded-lg bg-blue-700 p-4 font-semibold text-white" + // onClick={() => setShowAiSearch(true)} > - ✨ Didn't find what you were looking for? Ask AI + ✨ Didn't find what you were looking for? Will soon have AI assistant builtin
)} {showAiSearch && } From 3014116b5644af3e9724ad2f0f1a4cf1d68e6b46 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Fri, 6 Sep 2024 13:07:41 +0300 Subject: [PATCH 007/124] active path sidebar --- .../custom/sidebar/partial/link-section.tsx | 17 +++++++------ .../custom/sidebar/partial/page-section.tsx | 24 +++++++++++++++---- .../custom/sidebar/partial/topic-section.tsx | 16 +++++++++---- web/components/custom/sidebar/sidebar.tsx | 7 +++--- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index a3da175a..72eeb615 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -3,11 +3,10 @@ import Link from "next/link" import { usePathname } from "next/navigation" import { useAccount } from "@/lib/providers/jazz-provider" import { cn } from "@/lib/utils" -import { Button } from "@/components/ui/button" import { LaIcon } from "@/components/custom/la-icon" import { PersonalLinkLists } from "@/lib/schema/personal-link" -export const LinkSection: React.FC = () => { +export const LinkSection: React.FC<{ pathname: string }> = ({ pathname }) => { const { me } = useAccount({ root: { personalLinks: [] @@ -15,12 +14,13 @@ export const LinkSection: React.FC = () => { }) const linkCount = me?.root.personalLinks?.length || 0 + const isActive = pathname === "/" if (!me) return null return (
- +
) @@ -28,18 +28,22 @@ export const LinkSection: React.FC = () => { interface LinkSectionHeaderProps { linkCount: number + isActive: boolean } -const LinkSectionHeader: React.FC = ({ linkCount }) => ( +const LinkSectionHeader: React.FC = ({ linkCount, isActive }) => (

@@ -83,7 +87,6 @@ const ListItem: React.FC = ({ label, href, count, isActive }) => )} >

-

{label}

diff --git a/web/components/custom/sidebar/partial/page-section.tsx b/web/components/custom/sidebar/partial/page-section.tsx index b5064c0f..d8213c05 100644 --- a/web/components/custom/sidebar/partial/page-section.tsx +++ b/web/components/custom/sidebar/partial/page-section.tsx @@ -46,20 +46,32 @@ const SHOWS: Option[] = [ const pageSortAtom = atomWithStorage("pageSort", "title") const pageShowAtom = atomWithStorage("pageShow", 5) -export const PageSection: React.FC = () => { - const { me } = useAccount({ root: { personalPages: [] } }) +export const PageSection: React.FC<{ pathname?: string }> = ({ pathname }) => { + const { me } = useAccount({ + root: { + personalPages: [] + } + }) + + const [sort, setSort] = useAtom(pageSortAtom) + const [show, setShow] = useAtom(pageShowAtom) + const pageCount = me?.root.personalPages?.length || 0 + const isActive = pathname ? pathname.startsWith("/pages") : false + + if (!me) return null return (
- - {me?.root.personalPages && } + +
) } interface PageSectionHeaderProps { pageCount: number + isActive: boolean } const PageSectionHeader: React.FC = ({ pageCount }) => ( @@ -121,6 +133,8 @@ const NewPageButton: React.FC = () => { interface PageListProps { personalPages: PersonalPageLists + sort: SortOption + show: ShowOption } const PageList: React.FC = ({ personalPages }) => { @@ -250,4 +264,4 @@ const ShowAllForm: React.FC = () => { ) -} \ No newline at end of file +} diff --git a/web/components/custom/sidebar/partial/topic-section.tsx b/web/components/custom/sidebar/partial/topic-section.tsx index e1aaeb9b..0a2300b8 100644 --- a/web/components/custom/sidebar/partial/topic-section.tsx +++ b/web/components/custom/sidebar/partial/topic-section.tsx @@ -8,7 +8,7 @@ import { LaIcon } from "@/components/custom/la-icon" import { ListOfTopics } from "@/lib/schema" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" -export const TopicSection: React.FC = () => { +export const TopicSection: React.FC<{ pathname: string }> = ({ pathname }) => { const { me } = useAccount({ root: { topicsWantToLearn: [], @@ -22,11 +22,13 @@ export const TopicSection: React.FC = () => { (me?.root.topicsLearning?.length || 0) + (me?.root.topicsLearned?.length || 0) + const isActive = pathname.startsWith("/topics") + if (!me) return null return (
- + { interface TopicSectionHeaderProps { topicCount: number + isActive: boolean } -const TopicSectionHeader: React.FC = ({ topicCount }) => ( +const TopicSectionHeader: React.FC = ({ topicCount, isActive }) => (
) -} \ No newline at end of file +} diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index 893dcebb..f05afe08 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -109,6 +109,7 @@ const LogoAndSearch: React.FC = React.memo(() => { LogoAndSearch.displayName = "LogoAndSearch" const SidebarContent: React.FC = React.memo(() => { + const pathname = usePathname() return ( <>
- - - + + +
From fbc9fd156c4c8018f7e1ffe521be8bebf32a20f5 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Fri, 6 Sep 2024 13:34:03 +0300 Subject: [PATCH 008/124] / route for force graph, /links for links --- web/app/(pages)/layout.tsx | 3 +- web/app/(pages)/{ => links}/page.tsx | 2 +- web/app/(public)/layout.tsx | 7 +++ web/app/page.tsx | 5 ++ .../custom/sidebar/partial/link-section.tsx | 8 +-- web/components/custom/sidebar/sidebar.tsx | 2 +- .../routes/public/PublicHomeRoute.tsx | 60 ++++++++++--------- web/middleware.ts | 3 +- 8 files changed, 53 insertions(+), 37 deletions(-) rename web/app/(pages)/{ => links}/page.tsx (69%) create mode 100644 web/app/(public)/layout.tsx create mode 100644 web/app/page.tsx diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index 47c7fa0d..c4d3ec97 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -1,6 +1,5 @@ import { SignedInClient } from "@/components/custom/clerk/signed-in-client" import { Sidebar } from "@/components/custom/sidebar/sidebar" -import { PublicHomeRoute } from "@/components/routes/public/PublicHomeRoute" import { JazzClerkAuth, JazzProvider } from "@/lib/providers/jazz-provider" import { currentUser } from "@clerk/nextjs/server" import { CommandPalette } from "@/components/custom/command-palette/command-palette" @@ -9,7 +8,7 @@ export default async function PageLayout({ children }: { children: React.ReactNo const user = await currentUser() if (!user) { - return + return children } return ( diff --git a/web/app/(pages)/page.tsx b/web/app/(pages)/links/page.tsx similarity index 69% rename from web/app/(pages)/page.tsx rename to web/app/(pages)/links/page.tsx index daa5ee79..682f9c0a 100644 --- a/web/app/(pages)/page.tsx +++ b/web/app/(pages)/links/page.tsx @@ -1,5 +1,5 @@ import { LinkRoute } from "@/components/routes/link/LinkRoute" -export default function HomePage() { +export default function LinksPage() { return } diff --git a/web/app/(public)/layout.tsx b/web/app/(public)/layout.tsx new file mode 100644 index 00000000..95c7ee6d --- /dev/null +++ b/web/app/(public)/layout.tsx @@ -0,0 +1,7 @@ +export default function PublicLayout({ + children +}: Readonly<{ + children: React.ReactNode +}>) { + return
{children}
+} diff --git a/web/app/page.tsx b/web/app/page.tsx new file mode 100644 index 00000000..5cb37316 --- /dev/null +++ b/web/app/page.tsx @@ -0,0 +1,5 @@ +import { PublicHomeRoute } from "@/components/routes/public/PublicHomeRoute" + +export default function HomePage() { + return +} diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index 72eeb615..1d143b82 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -14,7 +14,7 @@ export const LinkSection: React.FC<{ pathname: string }> = ({ pathname }) => { }) const linkCount = me?.root.personalLinks?.length || 0 - const isActive = pathname === "/" + const isActive = pathname === "/links" if (!me) return null @@ -39,7 +39,7 @@ const LinkSectionHeader: React.FC = ({ linkCount, isActi )} > = ({ label, href, count, isActive }) => { return (
-
+ {/*
= ({ label, href, count, isActive }) => {count > 0 && ( {count} )} -
+
*/}
) } diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index f05afe08..ec947915 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -78,7 +78,7 @@ const LogoAndSearch: React.FC = React.memo(() => { return (
- +
diff --git a/web/components/routes/public/PublicHomeRoute.tsx b/web/components/routes/public/PublicHomeRoute.tsx index ea1f7ab1..ab4a14a5 100644 --- a/web/components/routes/public/PublicHomeRoute.tsx +++ b/web/components/routes/public/PublicHomeRoute.tsx @@ -5,6 +5,7 @@ import dynamic from "next/dynamic" import { motion } from "framer-motion" import { Autocomplete } from "./Autocomplete" import { useRouter } from "next/navigation" +import { useAccount } from "@/lib/providers/jazz-provider" let graph_data_promise = import("./graph-data.json").then(a => a.default) @@ -20,6 +21,7 @@ export function PublicHomeRoute() { const router = useRouter() const raw_graph_data = React.use(graph_data_promise) as GraphNode[] const [filterQuery, setFilterQuery] = React.useState("") + const { me } = useAccount() const handleTopicSelect = (topicName: string) => { router.push(`/${topicName}`) @@ -30,35 +32,37 @@ export function PublicHomeRoute() { } return ( -
- handleTopicSelect(val)} - filter_query={filterQuery} - /> - - - - I want to learn - - handleTopicSelect(topic.name)} - onInputChange={handleInputChange} + <> +
+ handleTopicSelect(val)} + filter_query={filterQuery} /> - -
+ + + + I want to learn + + handleTopicSelect(topic.name)} + onInputChange={handleInputChange} + /> + +
+ ) } diff --git a/web/middleware.ts b/web/middleware.ts index 9a3894dd..3059c4a8 100644 --- a/web/middleware.ts +++ b/web/middleware.ts @@ -1,6 +1,7 @@ import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server" -const isPublicRoute = createRouteMatcher(["/sign-in(.*)", "/sign-up(.*)", "/"]) +const publicRoutes = ["/", "/sign-in(.*)", "/sign-up(.*)"] +const isPublicRoute = createRouteMatcher(publicRoutes) export default clerkMiddleware((auth, request) => { if (!isPublicRoute(request)) { From be71156f3d957d12decb6b789b53ef4dbef2db54 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Fri, 6 Sep 2024 15:23:33 +0300 Subject: [PATCH 009/124] sidebar links learning status --- .../custom/sidebar/partial/link-section.tsx | 37 ++++++++++--- web/components/routes/link/header.tsx | 55 +++++++++++++++++-- 2 files changed, 79 insertions(+), 13 deletions(-) diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index 1d143b82..e4639ccc 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -3,8 +3,10 @@ import Link from "next/link" import { usePathname } from "next/navigation" import { useAccount } from "@/lib/providers/jazz-provider" import { cn } from "@/lib/utils" -import { LaIcon } from "@/components/custom/la-icon" import { PersonalLinkLists } from "@/lib/schema/personal-link" +import { useQueryState, parseAsStringLiteral } from "nuqs" +import { LEARNING_STATES } from "@/lib/constants" +import { useRouter } from "next/navigation" export const LinkSection: React.FC<{ pathname: string }> = ({ pathname }) => { const { me } = useAccount({ @@ -54,16 +56,38 @@ const LinkSectionHeader: React.FC = ({ linkCount, isActi
) +const ALL_STATES = ["all", ...LEARNING_STATES.map(ls => ls.value)] interface ListProps { personalLinks: PersonalLinkLists } const List: React.FC = ({ personalLinks }) => { const pathname = usePathname() + const [activeState] = useQueryState("state", parseAsStringLiteral(ALL_STATES)) + const toLearnCount = personalLinks.filter(link => link?.learningState === "wantToLearn").length + const learningCount = personalLinks.filter(link => link?.learningState === "learning").length + const learnedCount = personalLinks.filter(link => link?.learningState === "learned").length return (
- + + +
) } @@ -78,13 +102,10 @@ interface ListItemProps { const ListItem: React.FC = ({ label, href, count, isActive }) => { return (
- {/*
+

{label}

@@ -94,7 +115,7 @@ const ListItem: React.FC = ({ label, href, count, isActive }) => {count > 0 && ( {count} )} -
*/} +
) } diff --git a/web/components/routes/link/header.tsx b/web/components/routes/link/header.tsx index 2c7c07f0..527894db 100644 --- a/web/components/routes/link/header.tsx +++ b/web/components/routes/link/header.tsx @@ -24,6 +24,11 @@ export const learningStateAtom = atom("all") export const LinkHeader = React.memo(() => { const isTablet = useMedia("(max-width: 1024px)") + const [activeState, setActiveState] = useQueryState( + "state", + parseAsStringLiteral(["all", "ToLearn", "learning", "learned"]).withDefault("all") + ) + return ( <> @@ -52,6 +57,42 @@ export const LinkHeader = React.memo(() => { LinkHeader.displayName = "LinkHeader" +// const LearningTab = React.memo(() => { +// const [activeTab, setActiveTab] = useAtom(learningStateAtom) +// const [activeState, setActiveState] = useQueryState( +// "state", +// parseAsStringLiteral(Object.values(ALL_STATES_STRING)).withDefault(ALL_STATES_STRING[0]) +// ) + +// const handleTabChange = React.useCallback( +// (value: string) => { +// setActiveTab(value) +// setActiveState(value) +// }, +// [setActiveTab, setActiveState] +// ) + +// React.useEffect(() => { +// setActiveTab(activeState) +// }, [activeState, setActiveTab]) + +// return ( +// { +// handleTabChange(value as string) +// }} +// options={ALL_STATES} +// className="bg-secondary flex rounded-lg" +// highlighterClassName="bg-secondary-foreground/10 rounded-lg" +// radioClassName={cn( +// "relative mx-2 flex h-8 cursor-pointer items-center justify-center rounded-full px-1 text-sm text-secondary-foreground/60 data-[checked]:text-secondary-foreground font-medium transition-colors focus:outline-none" +// )} +// highlighterIncludeMargin={true} +// /> +// ) +// }) + const LearningTab = React.memo(() => { const [activeTab, setActiveTab] = useAtom(learningStateAtom) const [activeState, setActiveState] = useQueryState( @@ -61,15 +102,19 @@ const LearningTab = React.memo(() => { const handleTabChange = React.useCallback( (value: string) => { - setActiveTab(value) - setActiveState(value) + if (value !== activeTab) { + setActiveTab(value) + setActiveState(value) + } }, - [setActiveTab, setActiveState] + [activeTab, setActiveTab, setActiveState] ) React.useEffect(() => { - setActiveTab(activeState) - }, [activeState, setActiveTab]) + if (activeState !== activeTab) { + setActiveTab(activeState) + } + }, [activeState, activeTab, setActiveTab]) return ( Date: Fri, 6 Sep 2024 15:24:16 +0300 Subject: [PATCH 010/124] Encore API setup (#139) * first encore service * encore run * api * . * save link * link * . * try deploy * try * . --- api/.gitignore | 6 +++++ api/api/api.ts | 62 +++++++++++++++++++++++++++++++++++++++++++ api/api/links.ts | 39 +++++++++++++++++++++++++++ api/bun.lockb | Bin 0 -> 51428 bytes api/encore.app | 4 +++ api/package.json | 22 ++++++++++++++++ api/readme.md | 1 + api/tsconfig.json | 31 ++++++++++++++++++++++ bun.lockb | Bin 404520 -> 439856 bytes package.json | 66 +++++++++++++++++++++++----------------------- 10 files changed, 198 insertions(+), 33 deletions(-) create mode 100644 api/.gitignore create mode 100644 api/api/api.ts create mode 100644 api/api/links.ts create mode 100755 api/bun.lockb create mode 100644 api/encore.app create mode 100644 api/package.json create mode 100644 api/readme.md create mode 100644 api/tsconfig.json diff --git a/api/.gitignore b/api/.gitignore new file mode 100644 index 00000000..30a613ef --- /dev/null +++ b/api/.gitignore @@ -0,0 +1,6 @@ +.encore +encore.gen.go +encore.gen.cue +/.encore +node_modules +/encore.gen diff --git a/api/api/api.ts b/api/api/api.ts new file mode 100644 index 00000000..e4b52d6c --- /dev/null +++ b/api/api/api.ts @@ -0,0 +1,62 @@ +import { api, APIError } from "encore.dev/api" +import { startWorker } from "jazz-nodejs" +import { ID } from "jazz-tools" + +const JAZZ_WORKER_ACCOUNT_ID = process.env.JAZZ_WORKER_ACCOUNT_ID +const JAZZ_WORKER_SECRET = process.env.JAZZ_WORKER_SECRET +const JAZZ_PUBLIC_GLOBAL_GROUP = process.env.JAZZ_PUBLIC_GLOBAL_GROUP + +// return all content for GlobalTopic +export const getTopic = api( + { expose: true, method: "GET", path: "/topic/:topic" }, + async ({ + topic + }: { + topic: string + // TODO: can return type be inferred like Elysia? + }): Promise<{ + links: { + label: string + url: string + }[] + }> => { + const { worker } = await startWorker({ + accountID: "co_zhvp7ryXJzDvQagX61F6RCZFJB9", + accountSecret: JAZZ_WORKER_SECRET + }) + + // TODO: how to get the import from outside this package? + // const globalGroupId = process.env.JAZZ_PUBLIC_GLOBAL_GROUP as ID + // const globalGroup = await PublicGlobalGroup.load(globalGroupId, worker, { + // root: { + // topics: [ + // { + // latestGlobalGuide: { + // sections: [ + // { + // links: [{}] + // } + // ] + // } + // } + // ], + // forceGraphs: [ + // { + // connections: [{}] + // } + // ] + // } + // }) + // if (!globalGroup) throw APIError.notFound("GlobalGroup not found") + + // const globalGroupId = process.env.JAZZ_PUBLIC_GLOBAL_GROUP as ID + // console.log(globalGroupId) + // console.log(worker) + // console.log("runs..") + + const topicContent = { + links: [] + } + return topicContent + } +) diff --git a/api/api/links.ts b/api/api/links.ts new file mode 100644 index 00000000..ccb4f5c3 --- /dev/null +++ b/api/api/links.ts @@ -0,0 +1,39 @@ +// TODO: not sure if `links` should be separate service +// it is responsible for adding and getting links into LA from API + +import { api, APIError } from "encore.dev/api" +import { startWorker } from "jazz-nodejs" + +const JAZZ_WORKER_SECRET = process.env.JAZZ_WORKER_SECRET + +export const addPersonalLink = api( + { expose: true, method: "POST", path: "/save-link" }, + async ({ url }: { url: string }): Promise => { + // const { worker } = await startWorker({ + // accountID: "co_zhvp7ryXJzDvQagX61F6RCZFJB9", + // accountSecret: JAZZ_WORKER_SECRET + // }) + } +) + +export const getLinkDetails = api( + { expose: true, method: "GET", path: "/global-link-details/:url" }, + async ({ + url + }: { + url: string + }): Promise<{ + title: string + summary?: string + }> => { + // const { worker } = await startWorker({ + // accountID: "co_zhvp7ryXJzDvQagX61F6RCZFJB9", + // accountSecret: JAZZ_WORKER_SECRET + // }) + + return { + title: "Jazz", + summary: "Jazz is local first framework for building web apps" + } + } +) diff --git a/api/bun.lockb b/api/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..470f8e988c1e4f660507fd6eeea401090fb6f908 GIT binary patch literal 51428 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�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 literal 0 HcmV?d00001 diff --git a/api/encore.app b/api/encore.app new file mode 100644 index 00000000..a95572f9 --- /dev/null +++ b/api/encore.app @@ -0,0 +1,4 @@ +{ + "id": "encore-test-76k2", + "lang": "typescript" +} diff --git a/api/package.json b/api/package.json new file mode 100644 index 00000000..b0d34260 --- /dev/null +++ b/api/package.json @@ -0,0 +1,22 @@ +{ + "name": "api", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "test": "vitest", + "dev": "encore run" + }, + "devDependencies": { + "@types/node": "^20.5.7", + "typescript": "^5.2.2", + "vitest": "^1.5.0" + }, + "dependencies": { + "encore.dev": "^1.35.3", + "jazz-nodejs": "^0.7.34" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.13.0" + } +} diff --git a/api/readme.md b/api/readme.md new file mode 100644 index 00000000..437b2b1c --- /dev/null +++ b/api/readme.md @@ -0,0 +1 @@ +Using [Encore](https://encore.dev). diff --git a/api/tsconfig.json b/api/tsconfig.json new file mode 100644 index 00000000..ffe650c7 --- /dev/null +++ b/api/tsconfig.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + /* Basic Options */ + "lib": ["ES2022"], + "target": "ES2022", + "module": "ES2022", + "types": ["node"], + "paths": { + "~encore/*": ["./encore.gen/*"] + }, + + /* Workspace Settings */ + "composite": true, + + /* Strict Type-Checking Options */ + "strict": true, + + /* Module Resolution Options */ + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "isolatedModules": true, + "sourceMap": true, + + "declaration": true, + + /* Advanced Options */ + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + } +} diff --git a/bun.lockb b/bun.lockb index b2cb3516c2f5bd60dedd32edef65fa5b4e6e73d2..84e6f77a76e70ac0a45e98abf096561c7c74a715 100755 GIT binary patch delta 102538 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 P0!qcCCeqY;E{pyTbn_}{ delta 79932 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

U^XBt-~iA@ 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 Ns!j>%^~T)h{{bXx_)-7> diff --git a/package.json b/package.json index f3eee142..372187e9 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,35 @@ { - "name": "learn-anything", - "scripts": { - "dev": "bun web", - "web": "cd web && bun dev", - "web:build": "bun run --filter '*' build", - "cli": "bun run --watch cli/run.ts", - "seed": "bun --watch cli/seed.ts", - "tauri": "tauri", - "app:build": "bun tauri build -b dmg -v" - }, - "workspaces": [ - "web" - ], - "dependencies": { - "@tauri-apps/cli": "^2.0.0-rc.11", - "@tauri-apps/plugin-fs": "^2.0.0-rc.2", - "jazz-nodejs": "0.7.35-unique.2", - "react-icons": "^5.3.0" - }, - "devDependencies": { - "bun-types": "^1.1.26" - }, - "prettier": { - "plugins": [ - "prettier-plugin-tailwindcss" - ], - "useTabs": true, - "semi": false, - "trailingComma": "none", - "printWidth": 120, - "arrowParens": "avoid" - }, - "license": "MIT" + "name": "learn-anything", + "scripts": { + "dev": "bun web", + "web": "cd web && bun dev", + "web:build": "bun run --filter '*' build", + "cli": "bun run --watch cli/run.ts", + "seed": "bun --watch cli/seed.ts", + "tauri": "tauri", + "app:build": "bun tauri build -b dmg -v" + }, + "workspaces": [ + "web" + ], + "dependencies": { + "@tauri-apps/cli": "^2.0.0-rc.11", + "@tauri-apps/plugin-fs": "^2.0.0-rc.2", + "jazz-nodejs": "0.7.35-unique.2", + "react-icons": "^5.3.0" + }, + "devDependencies": { + "bun-types": "^1.1.26" + }, + "prettier": { + "plugins": [ + "prettier-plugin-tailwindcss" + ], + "useTabs": true, + "semi": false, + "trailingComma": "none", + "printWidth": 120, + "arrowParens": "avoid" + }, + "license": "MIT" } From aae2e283536a3024bb5a410ada79b8c0900100e5 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 6 Sep 2024 15:49:58 +0300 Subject: [PATCH 011/124] Encore deploy (#143) * new deploy * remove jazz from api for now * . * secrets * . * . --- api/api/api.ts | 28 +++++++++++++------- api/api/links.ts | 5 ++-- api/package.json | 4 +-- bun.lockb | Bin 439856 -> 440680 bytes package.json | 66 +++++++++++++++++++++++------------------------ 5 files changed, 56 insertions(+), 47 deletions(-) diff --git a/api/api/api.ts b/api/api/api.ts index e4b52d6c..f3aacecb 100644 --- a/api/api/api.ts +++ b/api/api/api.ts @@ -1,10 +1,20 @@ import { api, APIError } from "encore.dev/api" -import { startWorker } from "jazz-nodejs" -import { ID } from "jazz-tools" +// import { startWorker } from "jazz-nodejs" +// import { ID } from "jazz-tools" +import { secret } from "encore.dev/config" +import log from "encore.dev/log" -const JAZZ_WORKER_ACCOUNT_ID = process.env.JAZZ_WORKER_ACCOUNT_ID -const JAZZ_WORKER_SECRET = process.env.JAZZ_WORKER_SECRET -const JAZZ_PUBLIC_GLOBAL_GROUP = process.env.JAZZ_PUBLIC_GLOBAL_GROUP +const jazzWorkerAccountId = secret("jazzWorkerAccountId") +const jazzWorkerSecret = secret("jazzWorkerSecret") +const jazzPublicGlobalGroup = secret("jazzPublicGlobalGroup") + +export const testRoute = api( + { expose: true, method: "GET", path: "/test" }, + async ({}: {}): Promise => { + console.log(jazzPublicGlobalGroup(), "group") + log.info("better logs from encore") + } +) // return all content for GlobalTopic export const getTopic = api( @@ -20,10 +30,10 @@ export const getTopic = api( url: string }[] }> => { - const { worker } = await startWorker({ - accountID: "co_zhvp7ryXJzDvQagX61F6RCZFJB9", - accountSecret: JAZZ_WORKER_SECRET - }) + // const { worker } = await startWorker({ + // accountID: "co_zhvp7ryXJzDvQagX61F6RCZFJB9", + // accountSecret: JAZZ_WORKER_SECRET + // }) // TODO: how to get the import from outside this package? // const globalGroupId = process.env.JAZZ_PUBLIC_GLOBAL_GROUP as ID diff --git a/api/api/links.ts b/api/api/links.ts index ccb4f5c3..9c17de3d 100644 --- a/api/api/links.ts +++ b/api/api/links.ts @@ -2,9 +2,10 @@ // it is responsible for adding and getting links into LA from API import { api, APIError } from "encore.dev/api" -import { startWorker } from "jazz-nodejs" +// import { startWorker } from "jazz-nodejs" +import { secret } from "encore.dev/config" -const JAZZ_WORKER_SECRET = process.env.JAZZ_WORKER_SECRET +const jazzWorkerSecret = secret("jazzWorkerSecret") export const addPersonalLink = api( { expose: true, method: "POST", path: "/save-link" }, diff --git a/api/package.json b/api/package.json index b0d34260..3a130cad 100644 --- a/api/package.json +++ b/api/package.json @@ -4,7 +4,6 @@ "version": "0.0.1", "type": "module", "scripts": { - "test": "vitest", "dev": "encore run" }, "devDependencies": { @@ -13,8 +12,7 @@ "vitest": "^1.5.0" }, "dependencies": { - "encore.dev": "^1.35.3", - "jazz-nodejs": "^0.7.34" + "encore.dev": "^1.35.3" }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.13.0" diff --git a/bun.lockb b/bun.lockb index 84e6f77a76e70ac0a45e98abf096561c7c74a715..b6cbd8eb66f5749367737e4e1e1417b61d5b4a68 100755 GIT binary patch delta 82436 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 AMF0Q* delta 82676 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 Date: Fri, 6 Sep 2024 15:57:41 +0200 Subject: [PATCH 012/124] Improve initial force-graph node positions Also increase performance of generating color map --- bun.lockb | Bin 440680 -> 404552 bytes .../routes/public/force-graph-client-lazy.tsx | 85 ++++++------------ web/package.json | 3 +- 3 files changed, 29 insertions(+), 59 deletions(-) diff --git a/bun.lockb b/bun.lockb index b6cbd8eb66f5749367737e4e1e1417b61d5b4a68..033042b27ffa78e3a4fca159ab68d26fae342e74 100755 GIT binary patch delta 23606 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 r+`kpcsG delta 45926 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 KcDU}Z`QhrhPH(=#OkG*;dAPSi;eGvozcDR zqkZk%@b{laGKX%A8?~=h`E0kv{obS;%3Ie!=6UnbUEQ4XHmM!@UELDb;qJ5og2>a zDEyw@qnF;dKJd?Q?N>czTg)rXkY2$nH|;SM|1ykSue#m4{S0P2oVDgm;?XohW@ei zX7G${T55(%)D6F?r!yhl*y+4$;il2yBbRM>v8`3q<%!x~*}LVlU=rKVf1FN9p18A-X_I!HN&OqhJSW6IyIzZpz|fIL&u7*FKpj) zRKeLkNf#nYDt~^mVKyWm@!Rw8Y@BwxnG2qHI0W5T_G5Z~W{+KwOY(IdlP22vv2y>tcAxay9{V) z`O(8vFY&6$iKUy0=IrzAx%ccoHN%(G4Zr(%>DWW}7rS;ezIf@}%t7n-D#k2~m+sf! z_03e*_vZKa%@>ZiHM?*!p@wo|@e&>UI+*^}MdA?s>CY!ICWx8_exGVr1L>BP-^#96z;Qr*zpK zSG~vMq-76pg>BhC&vZrU;1yx7uU5G>33qJ!QFlhkvcT|nYKF_y4bL=cU9XSL`O=~_ zx3uPL(|xAdUhnRtuQw|@4qRq6>q3*W1246Idb`IX&9w053emWR#WsGX5zdXwuU=2G z@7mgAe#8E1hOep{UQ}6kG?IClzHmB_Dr!9ZF`rN&(Iqx=GK975JtG)k})0y|{y9X(bJslv|({c;> zarWMkAC*p#!^fn)&CedNeZvsl;Kd;=)mnT*-S8Y;3-`Nwzm83?Y#e|8N&bLs?;eG0 zwqCO3*d0f|`<EsP78Wy@4?s(@^@U#BrNf%O7hQV*D+nu)7PH)QmrT2!Nx|}*HDrdros6DP#L%u2I zeeG?wXO{oX(Z>qUzI|hx<2|?e60bJePadCgY5Vk(N1Ku1h8dpGPmf$uGkiVjYwpb) zloGeMCi?#Kn+4Yu&K7EhZ>t;DU$xg_Ywy&6dqF!Y#~FvUd!)a0$!y8pK`$-(b~FFb!{VPUN_ z?!oQb3ir8^0}b!HZLf3Bvxx?DtcHQyCR`JN_Y531CZ@#{?`H`k(_z!i%X|Ja?t5f{NIi>9@y>9qx zo|!I+x3_*aE7!|0Nq#7LpIxV+X~PNbnhTtR?AFS1HPSU+PrLf&*n*>#4>kqfzJC+9 zpMP!H?y1|gR%}+V`3<+-?mTP2!f)TVbsTrLU7yOyjvlp-T;rY&FmaDLcR;&VRH=B_ zer-WXM8CRa6DPjh>(Z$Iw34{asfEv~)ePTPH>{zx{8QB>QkFK`QOUC5T0-B);Hk6<1RJH zVW%(Gb8epZwfDkKt;_ms%HD=s7BJ1-JgngC^6^I`X}smqk~cGKcNC8H9h2H;wswi@ z4ULcb>)WP?uNS2ibf~ji*`$8P**g=H+dO68bQ+n{QoD3;uV?d5XIx;)E03NZ%{7=e z{7}8Yx5oCXb9RE+xw;m6%U7(pd`o!4is)?rgl-kYk3a_S)~{xE4pS@ue| zhjx)nP4A^s+n#^4PWw!ctD13>>!=xiq;B}Y*pHe~n(x-OzIf-oc#B7;?4oN42fjO> zsM^^jq1nDc-f!!)pR}g6dEn4Cw#zT3b$XR--Lam2j|1khhLu<5J_^D8e0V}4QqW_c z$LfZkutsN1I)p5^`F2rNtrnx}P-S?#ILl;0-2Lgh=jbmpy}mE|{=G%}mTnildD4M7 zsk^<{JobmDN1rAUSI(Sk)cBELnEd=i-EK-(_G5^NRR3N#r}f)jHaIu*+3~aSL5k`v zx-o4UPnW-J7j%8G`0BQX21NzsHU@X1yLw(9qTfivB6)rI>Mw=G{crZM^#5r5 zqQ*#j?XIDg!Hib?zZzq@6bn|qSSlH0HLKCPK^_no-M#fRnRZba5Uvuel1=J&iU zy(T9F{8)b8Bcs*kz3X|yV!8$XOx^IM7j5=hJ(tX@$;eDR=9+e1W;AY|-ejY8i!~PC zS@(2VQImP6s-|?R2!9n66x^kv*N}G&q7{aI$G3Qzw=aLuF){r*XE=*n9#!gw%Z+aQ zSfy|D-f6?$Bl)X}kA?Z`r@z!q+_7)bxg4`&Pqe@6e9RO%Hk_pYR#sT_(`0*(Ckb_! z!+S5ri@$cgohW%Mc$gaeT;1;UJ&K*no4K|)9v*Qv(`K*M57$Ov?k7yg4t%t}?cUah zcf2x-Xm+e+zlO`lHUF62phu*kS;>LiI!kl&z6C$=3HQ%?D7cs=@e6gs&F@FMnSH#} zu{6BwMx%s*ng`}x&@kPa8`4Ho^f_nywI3Vr$rTHHJ=W_fHf`FxEx@Wnvy2TPPRsn| z_cCvkM||o}_tf!EoBn&LZrEV|+`vG+28M}^tg1VH&3wA`aY$<5z&$uusG3AT)51A`nGNRdrZk$~{3cVt`Li`a)j@Z1pXjkQv z)TI2}(lG;%FFsiv9$p?-*+*h%cVg6g z&ch{hNWnRt7vCX12 zf>R%F?K;r5i{0!yXV2bkHtC@Mk%5Z>qL1$^?LRxSyU<{Y6V>W=cfUD5D?)Ga&_$Nh z^_sj~>>Tmw_Tp8F?Txm^+8uPhbF<+7{A=f1?;SbGGi%?c5&4_Cc9?PZ;XAj$iGwsv zOsq~XjN&iLMcP>~{7&6)ftKRN)ANU2i=XexJ>IpX;9SwEvO&R1n=c*T?rmR<%t)`} zwe1&%PI#9&W2Z}(KJOc+ws~B7U~q36(~56~3eo4cHbRU2VEDbd;TO+rmJEm}@eAs` zuxit7$Ela|PiAb+|J>@!^|}73`bA?mjGl3?IP+Scj-R(CxAs3{x^0WA()Q`v)q|rG zCO^{L) z?{L7YKCR{7`rR3@+2E6P`>?9@%Ph2NrUtzql7tr>urS2j(0x=l9QWyqXiR+kPpD?aUXRpZUpPZR)}HZ7b8an>|u|%&GfjY1?Nj z57>R{csh|YoW(t@`lN37hIK`wO}*N+88X=+*=FbD(Vh0bx2m$5Ur*dqA-WiE_3ou& zp>kcqsN!3v9vNqu#!DxpUFCo*%o`-n7)X5s2Ps~UB?;}xlQHfWnE{)kFFe8JD& zu=|*6_V+EG`!wAsnQlA4)@m4gD>rIQmkrj_hWdZqJ~OC6aqr1ruWKH7njuG+ZKGDfdfAH7I-LvI^C6AiY3Amx$?@*2{nM_naN02@=ds1d?-iQ`yIE-P zSJ+j>ACaUg(0Oc+1udqpAM2f!yn3oQF7bN*j+z;1MR7Upnk_GE*i>56&&oY+VA00* zom@Yj4745Q$wZI;5@GPfVuQieA1egIwCujAH#lbBfL`A{ragG~;K;bNIjIKc_iHBJ z)oe4|>8pc&>*tEbuChnP&hE2otaKjF@OwA1Q?H^Pd*W|6=f(v*4>?-)P-4AucNo?|g>5s2Y{p0lz!ws7af`;v%@!HHa zJ7i*|t1QIhaQ3;HrR`68J@dWUclMnQEgM(c1nR#ZR-!+7nZwuqwYrb^Td(|H@Ta=n zfMerqPkr^2xGhRHTvA+rXP?D)OBJ8%&ghmQi|BP%u)b1mj$e670SKX-W@Ycq1vp@H>6gCfRl*xo`uVd3NQ9j0fN z_zpiZZGG|>v3=jy_l~XJ;M45L#EJ{IHpm|DzkbW~!i!gX6`!Bgea~MY@avg-iia_z zLo%`V3bVx9Bb{dqU2x-ya!^^%pa(m;y*_y3#?xc&o|C?t7~cG0zqqee{kYhXvUpLs z!`r@%YN~XrOg6mlIP|35s5e}LdAkfv4B?Pmy58CLT+X|U>o*kHgVG8oNKbr?is;F~x9*oLCq|(= zyvyKHt3EEwf;;XHpDm2i3mtyEi|*d|cubqyCmPf%9d~E_QF+DrlxBza z4J+22RZzqfeGCun)1}T*p|MrGj^15nNN<@Tr(KF|xUf z&t2)dEhX4_m&^GJz3%So-?#E=efw^o@{jKGTQ#)xhUpKR-LjdRvEsn8fEkrekAB2e zm~EPW^W=cA@mYdhGO2~@4`E7RvejH^+(B{MwVwA}z0X~CTJCgz9;UeKGWJ_cSb}4Z zklw@f$1T3nxzE$RTeleR8P>(Rb?@Zn9oOs+te>&&bDG_~24$RK{>hEDdV>?zH9d5B z#XXCA`aP!S>{-ZYy}u+rC3oDFG;YDGT1EBvo|5k0G~{dN1dP5~|MB*!VvWk_Et2aM zIgA+?vG|X7zPFRq49nCFU#qx&Y~!rZyspdYA6k6O*5F*0(~h8vgXJUgiVKGQD7t^( zMw`puPYirnwci>_Z$Frg~xlPQ~8mzBw_W;X$@3nMaU9qCsf+Nn_kE-?r zO?nkS(|GP_H**|YPrj$?pqK8)!fY>G{nIx8%8D-E+{b=sWtucG zs)vGkxoUrvzOv(`p?gM|jJVZQ&9I@m;eK|*GYbBg7CNR*z>2S{vcEa#&bHsTpunrG zIQa35T(MzTnaji*;ROX*pT04#cdyF*G_lLqFPX}&bxZc1nh@S%lbT&4HMOP42HLmYv|jVrgI9)&R6yC*l(fEff2YQr?#czm-rFSKFMOO`|4gH-?W@d= zC^B{WhF8CfIv{?1Zs(O6@0K4!Crg`izn|cX7a1jmA-lzy5+;ScSRh`_$l3V);wWY| zTee?3R@&GaKL%4+T_HBmVAt#sOV}ZY#7Cr49k3^3llF`I$vWY+O1yxO)vGZ3uz0gr zYTHIA@Ir%ZvsT4oZRuXTVXRkJ{YE@ct8hr6_`8_J|F)DbQ~j7#aN+J!@myVIS)uBhE*n}(*q(beym8{HS9f+pAekl!-Uj6^{PqcY^hO`Xg*yraj9zQR8>Kj~aQ6!! zy*eo8K)eDeV06EtgMb|nFnYfvK)??G*L&7d z%W=>{d&2616d3|Y58UyOetbxjCSdgFnH1}RVy0QjGmtoM(;@a3m82~ zq_Sq9jsivx0STkGYv)og$^Y~Ok3f2rbRG|i=;0f=%MvtS!07Q7VOF58G=Mnh2@;iU z0Qx3i^zer;dMEe0fYE~+D%%M36BxntXaql5BobLes1+3Hxds((4C3A;1*1n2gf#&% zLQm*v0%10w-5@$D1k4ucJsctu(M^2PYYN&cV08bTux6kl8h<+I-Z_EIL4$-I(|vEk z>_CGBjBZ&IW)JEqV05>cuoj?R0!DX=32O;bo%ZM^FJY}fCW4-VZqO2ls3sarKEgpa zR|%s@L^tATCewXUn!N~lBDxVv7~KOQJtq*01WjbReL-gE)xzZhMt2BE&jmym_;k?4 zJ=&n)UO}eEjdalEC?)ZCJp8Rf0-;41{%(LXO0%1;0tutFZcA$q2VKAs<_VgN2~4w_ zuDz%+cqd$RlmY<`y1XGhiU~zh!T)q=LSS1^S7G+!vVz0#M?9ka!no5F0qIfjC=xK* z%oElQbdkHkiL-)$`2yQ7V6=xOJvr#KFyHDZ1kexQ83CjaNv-w=of9zH9a7=;pgl;_ zYN3rCVI4pPNYg=^GB}Y7Q2Pap#+vj3L4^WFI}5@T9r51*0i<04C4)c*16R<`C))iQhfLVi6vK#2Q>cY(y2vrgcIwdGJ6R_^URv}HX zzz&=$=>b|O=-CTc2(U#0)LgvzJP2%t^mmk z13>ptm=1SAaUjxZNYmjVV1tm}PJ@lZQ^4>CB%&Px<|Sa^!01d$M;if)K$^~^RKKXL z01iQ#-k+m&?=4`FNPiYE9|0Q*jGm*BUONGcLYm%oB+OU9qLF6MBf{jssBuP*+}OJ?vE0%Nx&+AQE-VC zu*pbQ3VJaDmIG|8fDIF{Twoa-h9(Rbz$pNa1Ee4{0vHYBRL}_lOAz#?0b`M-Ae1Oz z(~))(ypSYdGk{6qqW<8?0yY!rhk{-TVKn};Kw}}$kt!(8Mmm)Ii-XoZCFg+ZpfPda zV}Q{Z&jqbROKCD@2zv97zJ|#~$2dW6KGLs{relJjw*YCyTLGL1PGhVDeH5@PL2)6l zS!gv)vTOlcg!F8oCzAwhF|hf9-edt|fkgwO$(SQxOOW1yj$n={M7aXE6yP?1G}coD zY#Gv-!04DNV9Sx72rL(Tnt-i9I!n--4h;W9D?z7#O#z=R=&eF}3DPu#b2tqCUk%|g zT1aCvS5RDoG`W+Gc>=Z;X+s#KVVEyq>yS1Qumu9P9+;^xbV>o+0L%;+4Go>RD0?Hw zg4Q2_iv(~J(vx72dbC);HY1%QU^MZl@D|WAq*Xm4EDtmu7#&Lmy{$-_pzK<3(kH!b zAo2p$<-J@0w?m+bLB|RK+krF{q5>-g%+HcnI^ZB-w;j`H*BG7?(VvPf0xbrypd}zz z;Ixl(XTKcNXjy1iq|u3C9NDzv8qJ3}0Jj0@fOJ86ASV$0Nf}+GtOKnFZ2)ZqZ31lu zZ2{$hwt}{Swu5$n@bSS4NASI0H(vZW_9mM~ok zBa1b38Kxl{e^R3>qs<-!W3^9dSh05aPhpDo&|j2Y23-M_fv$qCfv$t-uf*kS7!8=YxHMvr$A>w^p|KSK=h|{he1S=I9`s+ZZ(0VP{;_@Q~3ap&Mp#Fni*nMh{0i1yIN* zm{uFD!gi?I8${S&iEVgEW0<}k^y5I`pmk{CC5?s(3YZiODfpcf>`~C$DWtuDt8AGI z7{x!r=mK>W(iG?;POMLD?XRIuvDcL6=zj%NiYYpV3wr$E?2GT5qa>yAy@%WsQ1F;jSwh zhK9_E!V?u5&Fd-X(Q-#zy1u7N?HaV%5OvT?`Sh-g z0n&8oM3+=_eeMn2P9VB*KsPAph6G*9)6E5X-=6Md;7$m7>-<8)OhGq0=q3o6BlXre zp;1>!z6_$`^b$GU!=OAj5WTEU_b=#`YHAi$MS65wf^_M20^Meyw_xcO1nJT%yu?YS zS2wlMTy&=a*B`2k+DsS6ben)~A0$JjCR!^xBSpDEpiju5_Z~ig=!Fe>8-!jpq1Ra6 zfL?>>?H+n_h%UeBvYam0pMmK5p04}pegNGkp!)}OV}WisRDh^kR4ZM?(#0)Z^-5^F z&0P{hpi5{vPtqm#PtXq#Jr}3za=KopA*E}1dO$;0uJqi4($qa46eUi$9>^3_A5;&N zgS^S0f$(iz$i^TekO|04fDoLeozgW?GtF zjyRpnVSD`0Fn3q|IQrH_S!*B93$1CwggZGpJ7TG;em;Hafvk`c*P;)|0q%%1OF6p| zMJ-jow=S7;xUkgpZf_{KIJ!_#9kvV#j5Yg$^!yFEOTnym^&ihNt7m7(>2XzrvyFbD z4H;0Vhdsx9$%=Gh^RS8&to?AEXuEVrbh&Wr6}Gyy}qb$Au<}+7+>s@4vpy00hw!ov1ZHhL7t)Ki>PGzS8M&wsjbU?XC&PiBet_xGMuqz zcZ<H>fHKC{aj9A$Cn##J09Y;7lR7uf64h47BcgN1udH?vU zWzhsEpxLyJ;n(8|z3j+3Jo6}`;PMp5P#yIB)`QhlH)qq^YMUHOrTV#WzR zX@e-Wnl;dZ-hS3zOA^RTWfQa{H!UAy7Gq11;#3SP32+7ff|M@u$S zTVf`zwPY(2B=scj&}Vld$Gxr~Gk zwqt*wDC5e0(+Bfson>G>*sfrfs;`<^xf**ly<)o(rQ8wT(8G9EL3&y2QW<8!EA}F> zmn@@$8N@ZFenWUj+>oDiEpMz(E*rIH5Xw9A<+Iq8Iw+yRmg&HZDf>bPvnYx!szR+v z)_gA}?~1wHqD_y3Tqz1RqVa6JE=m=!YjvT%pM9f?!d|Sb4!nHQmUmm|ke9Vz=S6-( zDHm?)iQ2MBb-1?91hZ6q=d4BRPTxNdGM4mIl{ zG#clN2JHJf5?8_RW~`r{#DZDO4%CwbyPp;c&5T~S`^cU155x@Is&6Um2|4+T)PAtysLsI9*>)(6rdlrAXu$>>f;DApNpfM$4J9pQ<6Gf`jDB;OuFN?$n{>+9!!z`Vu&J58J>9ZE3^y zG(ubIu&a$EEp4tM1D6@UzRULZ)P%$pH%qUP0ZdQ~%sSRT57WNJnSDJ)V#Z8n8yllx z^Vqd_G3ABNu4Vm*V|FDr3Alq|?g#{!*m8^*jpOI9<3|*SE!-$(?111ZEUO1=V1g!( zhDaeIy8p@IRR(LMVx~V3ItypAM^T04Qi#}KWI5|pzi83XL?UK#fnZOXRl!zM8?0E} zx?nxn>;+)WSvfByZ9!qxXWL#z4+y>Ib36bILu{aVc7UA?J@>Vk9b|oIl5IObT>~#D z(44`tQGFq|GSK{MSx%MTM@ZK1w%$S#h^x&FVzhrsWRX zyaKa>P5J|dRNt8Ef5xNN^DbXrLXY>kXdF8qKEmRPoB%!5#~8mD#fn07 z5W*gi-hTFr35IqytB8hlgbk{PK#;`ltcQ+fW7mz1dsgeAi^sika{0~`ZbOSh^ z=r4`u2RLG@9)#kv+3mH8GQxMp!avSlx#DC5pPt&e#+ zn~gVV$ zk4>`zuMFs2A=lfa^#*XF>2r=ttZyS8KXEYPfA zwz&o3NWYH!UUwHe5F*o$T}zpNVba66T(M%ArjmNdctiQttjrRvSjM)qlz6)fU$@K3 z-|~KHuVuT>iy3sng{F+^J9y$vZ!ZiRaiFD`q2a{D7D?ELlO^@NoSj7mxG17XR?>Iz z#jDpRMeq(pVUeAX}mMhCSOr(wINr znp^(s$(B3CN3jhWN?JB*5W-K6zwgov87_w#4p}mkTB4CR*xZJwyn;PQtdxD#P%_+d zLN8oJ&z5F}OoEW3!RlH|0&P?#RNn)OONW3jopQr5D+~C}cpe|p<3~eN7TJq*J zIdh?G%UDUvcK^wm38TtUeF^g1=mAT6Tx>p$TkBl>3S`S{F+B#cKWruDzt$t?py7-G zJGCiJyZ=RBX_}IogW+bIW|HNMCtK7E@%HbXGeNB5Pis1?UUN*%32ccyqNQJR?2Y)d zfe@g~7zsPKxg?^|p9A=_@ZU`tGF%Q@)=g5EwYHP!{3@8sMs^0a#zCUVhTBP6>8MN~ z^i?=Y0%R)LpM7XY8)Pmbnlatj#vLVfwK$)M!r92azl0>V@eoNpDfymj6~*!iXt$;6 z6LG8b+FQ^w1IsPiutmSRXF`R3+kxO zjXxsUOHPsisqRp^su0eD4$dg{>pTgmF{8<@bcPWFw$S;P5vcs2Jgy!`wl+p0UF<9| z7E1oALN#YN=LAZ*9#j|j>oh#~$V$$QJ4pwD|1b%AA?)(6gjp+NAJ(ey8uD=srHwYV1uM;dLGMA)1RDx3hQR zwuS2BreE}$6x8!u^H$6(XPbM#Y2IvS547+v6|uWKB;C7;Z%K@Bn!5Rr2A|GFG&BRwJU*-qR9H0&>b!zgoK32 zvAF{RLv9F1F6D3qL0FK*1DGU}WMDEgWG0XxG6{#Ef)H@2T|^Mj#g${hl?;MQAo6{( zh#X`0xq_7Z|r^yI46=k&Y z!0Kq8J12blRz8Euph5j)V(c%4#bwepfjUspC+veKu3U4btU&CCax<`|7_gYE%9p}| zvS?-3kV}vprF`4ew&vNn6)D3Pabg+=en{f`K!YzyKU|`ePnN#bV_JUZ@3#dqI!xp- z4iOs&vXzgbjZk)t z-uNR~xsy?0JtiQ)OunUxxJcW+P(=A?>!oi?`(8bC!E0*0k>rCQJKoOcS1+%6XY}po z4vdB%x`2Tql<&Wme|KT`tjsatOhp#mOePDcY+uVELapE<3<-eVPs){NxrKbPE32w3{u~Zm9vgt)Z)VMXh*ZLCe-W1MJOUt zOdoAYew&vWLsZ3^H7tlBPmtTRlG-93B13YSVy0Yl%a!DBx$xD$kh3(UwTj?lm#`q} zcD$Vxdj)0EidxCRJjmNZzQM)6L0IDpF?9V;xC=z$p4GLJjH#Y533)yrgMcXfBH5k~ zvC8*#Bg6hZaG$<&46`$bAwk0prNkS@o&i*Zm-Yi`0)NS=y;+L z{0Z{00hbx^qGUFi@^mU~?-PFTIypf5qR{a@@)Yg0Y!2n=%537K*L_MRjfAClqBF)K4%+aT z^M$L~A0AWPL->;D@~WkScy}KEiyfeuKIGjPd@gYx-NCQ7dv*L@RL0YaZ2q9n*1DsFk-T}KEMZmjhIVSM49zuri@MoE#8i}@3Er={W&^-D>ho(S42QzwoTQ_ahyi9zk{#OKtAje5Vg5zWHAM3iqeSPrJ9KZQsU2X|`xC zk=w=S03jrz1W&oo$q;%Gn>hX&GpbDozCPaJr@u~bcjmW=+%*x?YeOp*_2k|o6cMpj z@*nt$tMbuw7N%pGoffc^xJ}Y|{@^{p*IV_0SZbYLhNBAo$d_f(z*uD^^zKlHA-?Oi z3V&zva=Fy;|GZwS*YTy=W(8rXmP|I|tOQ%$^_1?uX_mI>bc5VmCP`ykAd1FTVi-xZ zN!yj3WwD`1cD#%&C$_DjcTccO7zadRq(j<^Pt&kXf6}W4s~6&aMGc~Ktl8I~8RN+% zdRax{r(sR>YBuRV4W?t;!r}DH0NdJ&aBrI?&C_A){^9AE4R$5<)1^AGgHIlOPKxnn zaAPkpSc3e?b<%l7S@P$3D3dEj-kK?uqu`KPQk@51KTD~_t9&~#+`VMB)HV=X%PXIk zGNV_+d+5&8*v8B9^5d7fvY;xRZuj#Y-&zpb=1N<1?~vqqQkh3C${nT{r2Pw0jt++t z2fv6)2qSHLQF>jcW4q6K-8*jce5qfcAIlQ(M!HiLN+WebJ+g3-)LZO_xlb;ll9{c- zN$O&0gl@PSyS+BFh5OrAq_!+{G8st&72rhETN>46q}2VYOFuU4s^UL*ZWDWAYWB8g?KG!whhSJq;)W;#|J zTEniRKfRbV&DLQo>?#gA3@3Dln$v}oRc}d?VB+Ct5q2FkaGuz}uf~nIe3|YWqchSu z`)W5f;adg@*N~7C=q_F-VTFb-h>=4Zq~*Fc#I_NVcas-3qVtLrTa!Dl>+hbr2_>QO zr#Hhdce^9rk-nu~KuabGTi_*JF=F2Wj}pfQFw2i2Z*GB8@-1Of&;Y{ENL2$`I^O+3 zgVaLDJt^FM%q`hyf(mq(zDI5UwtLPt2|KN)-4FgyP80JHX+vw}w8I_}dlc(ke(a3}HEM7F2QDbY4_xTHk3k&Z`}uWB-BBgD zzNigV-hJ}8WNoE8=!R+Fy7za`Mo%8NfQCqRcex;4(urM_Fmm#uB$1vMQ78VOAe$~q zk>2Pw|AmuO3<<6Nw3KR&{Sqe`{GUmZ;EaqFX7No-kzo^cwDy;vOJudPpg zazVGdj|}_`A@p^yE3`9NB23VA|UiE68uB?fqL%2thwm}J5B_W4i?PH zE}$IBGym)DO{RbYqZ25SnH__LCnFwNw4lbr?L&7BtRDvmM~r%AW(APIA6oQa`-tRv zoYavVijxw^$Yk8ny+zVKUW!UDH)0_gzeRG**`52unF4eZtjp5l5Xz6GXmh*KS#AQ-bJq5xu?Z*f)^4cF zp3@FziNR!@5>OKN^k)0>XDXpZ5QD zA-dU)h`M(flHGG8>~dW|KQ0Kb4*SPcjEh*Z!9F@p?Fyu+`sDVHhTg8C&v!P#4e)#7 zj;#dqXOAlgU&x~tjm?=g>@(|Ztj#N|AwcMU{L+RYYg+Z*jnx9^m`S%jV}Q`z_kvcd zyKne0Z@9|SO&}XTSbcm={>6<W2=CzeZ#LNI!bI6kh-q!Y(xFw zvg|NjH>Bv_6iB0{ozS6Y)rgP!9-;^>AAc@D)a8PPe7JJ`kt@%Jc?$A%?J(U7pbZ7> za$M;XFeE89bky=&=Tx3`K&aZ2>p!SW=}}grs^Bn}nXRR^0N2?379W$VivpjZdNTvH z#%Bkqc0qYBm=?=jgX!@Hwo-l%2Wz&8$gY`EREFR4Kdg&2=d)zfOH!xd^x*-2YrdH+ zwkkVy`H<=hYa%`fr|Y!z)E*xG>+x0}>KaY1VW(^-wvPjnN#36+#gS`uQl#tO0?r#3 z`2Ihyw4$3(Sb(C1RpAXSl=0DbzU(JoZ&=1evi>!BAMf+2Yx8!VnNx(*9|})#vmLq_ zucocte647wbl7VGe)mMxg88QoG%ywDQMQ0)>Wg)0YRD-@OTg)|L*x2?FffuS$2xzB z$z(T}CR_IWKrSYwy}JcS)^9K_ppSRR=Y1}9;v=K$=Cwa8YLrPj5GibwvR2%TMd0F5 z;HRF~V!Zyle+IA56@@l$#SD*RW@~&Kk-6Hrw_XfBd^m{43-tK~{$gV>?G6E#-w*n^ z>OycHkX{&Hagg_yhsdbroC<}pm^V{-S;*s-qmKl?Sy&J0I3Gj{R$edCAu;#isV%>A z1A*_uSmanssk199GoaLAnqsm#9a|U8jF@wI-f>AcgSs57X)!q*ROe;=51rfFuEkZa z-e%;;$g>Z<7*bze?S(jNELGsjUt!E_pEqZxia1S`4pSw5fid&RJ8Khlg*Yma?ZTls z7{^gnW~_w7_dYKEXQRAlqsr9$An|*=FNF@GGqzSa_T+q)K~G-LilqgV0HF>av9w3c zu?@E`sM4%PtIb@3=hX1nq=cr!F9nkEZKT#BhI7#vuGs^+7a+7FgdRWM_}aRRzxRw5R3cWYD>aCY9+8fporkGkxUVUM3aUDv+jt%Y#$S2NtNv zK7kyaRXQkr>5*M3a#A4OI(E&}z2rK*N`)_b18!FQ3VXoYgeisubE?72C zMWO|==SFSp^fyK?QIRwtolv0_QEA&gJzxB!iVOro&$o@t`^42fIwoYT3OyyDb`b;4 zuh=-{Llv1UkadI1&s64jS*{{;1!9@7`p)6{_X<_yH6Rfv=-{djF}u5-AFd+G$lXC5 zy6MQB9YEf0FGag7c>fRm9mn>t@Q%+c{L@THhn~$2^v(rBqsA?FE?#RBxi?uw)&QZa z#UqwozFhG7!Jk#62?%_=HpqFX#IpZ8_>dy)q~Q5mvh+;axjCT%abZ-vBcPnC&kk?j zVeUbd={G}k^c4)XFD<;i+?^P5K}8CHBm((y+@EG-E#G@bMT&t?70whUxBC9!0lQVC z4oDo3-d#52Wc>S+K@_3A0xGNmlmuvTUG&%)Q@-!2GX0MbH|lufs=3$eB30xx5PH5a zzvYP1%6b1NQIT6f@Uy7em}$dxUr+xGCl3`X_(tOB6$+%sOQ9*#yvkR*9Gmysb}Cba z#Zp7Z$?(RMBOTis5b0qk0vld$F_MQKC1LB^HVhr`GeZ#T;dx-QmKNEn10tKofAvb& z^yzF|X0v>)v}7R;kBVjkV*K#3u%)DTWi}VkG^YuBoK_)fqJM#3e|2N5`&q7IHr3H) zH95@{rhqYh*NtBgq9f3eo|?vheG!oZ4XF+f8?^SDg{SE0_RO9nb-vWOm5AN>Iz#7o zdh!rs$4K)Bfj@t`2!w3ImekIdQV|ZZO|rHh^E0P!E?sd^j+;;jA`vKc2118C`{)C| zKRc$<#mZqC&_|+SlBwp9qw(j@M)q$_1(ToWOZtxDfJDxC{LT`8IYybj2rHE2U6Im= z{fhLsYc$3J8kzs0A!bwf+)c?7kfya6RXXbFHnZPtG9yh}+VyJR&(AeI_UGpXea(9f zT}SM@`b9m`G)fru(3t&|O??)=F7TYzeO9ktH?ZeAfp0oCHMDR*)IYW%O(S$m!lLjQ zyCyxfdJi%>B7@A``R1O@>HCi$O-G@(4_#idVSKlpSCPhlwfoMWXnMA6$zG??Y?*4d zmJELCP|D@*o4YI{byuZWY<8@|C4M);a9W+Cqn~|a%(^)J0i@}Zu`XhC`#WJ-$B&Xz zSEYq*%8lG0kGkE{zLmBFy9;%)V-C6gyc`{t19x?nO)Ie4oaJU~nW3o4nMyWi%5h}v z^YU+S@>^FeC%7}`%Kg@)6!2gpm0t`Nv$d+)P~9ubP-d+%S&MCzrk*9HDdbc)IhuU( zs4S7e-(j_2%OD6l@Te@4X9mfgS+bi-ooYx`69#l1`imea#HX@7E6C=QpcofVftKHZ zj=CerEVg2!#bBRQMgz?vNGhik2CJ>aR3MlTepVRE%*6(Wvl2B_va6hCi-Ts_Q4vHa zMvH=&s_mv?r;@6yvRWsaDwU+eYP37bZO*CXH3qYT6=a|!kX&xG+iMJ%DJis7W^p7m zZu<1Nyj&WU^-NR1qDtFTL`4Ork}?yOK>J(j6x8>I6xH94bX49?u7L(@%n}Z#v3ODu z{wMK;a!e)Woa$<;>Fj0t4orrO&oD!{=OH`1;;?|H(J_(W&W4+A?*gI)J zvk_xkIgw0AkfpXJz$|nHt4=Q`br!;@rA(Co`Hc|VU9kyk6QJy>HnIuLWHK418ZDDd zq<)DU-P1dViqoVG)FRA@3Y;}|lOvU^3JdDeQ4p!r`2A2MYQ-iZ$Yob%&)(Ft%1ll} zX_d8@R^x4HfN(p)|ajYnhaB`}RoZLy7f&o)*4OwKu4T-mW zJ51aFi76aOy$RX2kK~{T(Z_&k`WzY22#QwUN#Xd}2JIiB>$8p}dVjK~OM}~UuewK( z*@|ou|E?U@lfnvBHikCl1{QN!xs&$LVu!KjZIMW&JxQwbSSifYFlmBTx*QIJX! z(d;tR}N8RZ&rsovJ9Q&E*tTE(*H5}@@;`Jq*;%4xk)_n<|&kgL!lrP3y8 z?Mr(vNY$?Fhf?kOpi~onD7iI-Of}($QZzoVl$uZ}6SR7ALarw6{m^;FIwTurQME7Zg;rEKEIzPT!3LAD$gG8l z@sI?T)^wspO}ja;F^L$fAi=SEGb#(`)k)Z)K#D`WNJc-r5#b z$xttU+{jj$`)HZ9HBh;=`6_$5mR+@s%Btq6Z2h!sTxE~BRbC%J6)@PJ08gXxHuV+b z1FORF{R#8(AYVaVkSavHC0pscoBPY5QCzOU$6-~Tf&L2C`o3@3T2vL~Q#|cjtv$T7 zr*)D%4wI`b*0+~>*bKgiTAYsv-cEd!@aMEDqKfd*IOMkEF_^bFjko#~%MB}pWAIWn z_eIoVeR_*m71@Y4%sD7}ep zq8#5A3vI~5zo6RngOu1wtN|iZ+@Y9svU!CZ&s3z_*ubcU^cG+anLI*{4`w^&K-krg zgJ1q*#!cr}un9F66hQ{VDAxl9ZRaxiHcgZXJgqud#%bQtrs zMmd~>{)pTjJ28K)Z;(?+3GNcvVj6w$v9&b(Gj(|75Xa-jhv&KRx-SDl-L5VuOjQZ2fg+m5kT`1 diff --git a/web/components/routes/public/force-graph-client-lazy.tsx b/web/components/routes/public/force-graph-client-lazy.tsx index 924b003b..21fc1f6c 100644 --- a/web/components/routes/public/force-graph-client-lazy.tsx +++ b/web/components/routes/public/force-graph-client-lazy.tsx @@ -2,7 +2,7 @@ import * as react from "react" import * as fg from "@nothing-but/force-graph" -import { ease, trig, raf } from "@nothing-but/utils" +import { ease, trig, raf, color } from "@nothing-but/utils" import * as schedule from "@/lib/utils/schedule" import * as canvas from "@/lib/utils/canvas" @@ -13,70 +13,37 @@ export type RawGraphNode = { connectedTopics: string[] } -type HSL = [hue: number, saturation: number, lightness: number] - -const COLORS: readonly HSL[] = [ +const COLORS: readonly color.HSL[] = [ [3, 86, 64], + [15, 87, 66], [31, 90, 69], - [15, 87, 66] + [15, 87, 66], + [31, 90, 69], + [344, 87, 70], ] -/* use a plain object instead of Map for faster lookups */ -type ColorMap = {[key: string]: string} -type HSLMap = Map +type ColorMap = Record -const MAX_COLOR_ITERATIONS = 10 +function generateColorMap(g: fg.graph.Graph): ColorMap { + const hsl_map: ColorMap = {} -/** - * Add a color to a node and all its connected nodes. - */ -function visitColorNode( - g: fg.graph.Graph, - prev: fg.graph.Node, - node: fg.graph.Node, - hsl_map: HSLMap, - add: HSL, - iteration: number = 1 -): void { - if (iteration > MAX_COLOR_ITERATIONS) return - - const color = hsl_map.get(node) - - if (!color) { - hsl_map.set(node, [...add]) - } else { - const add_strength = MAX_COLOR_ITERATIONS / iteration - color[0] = (color[0] + add[0] * add_strength) / (1 + add_strength) - color[1] = (color[1] + add[1] * add_strength) / (1 + add_strength) - color[2] = (color[2] + add[2] * add_strength) / (1 + add_strength) + for (let i = 0; i < g.nodes.length; i++) { + hsl_map[g.nodes[i].key as string] = COLORS[i % COLORS.length] } - for (let edge of g.edges) { - let b: fg.graph.Node - if (edge.a === node) b = edge.b - else if (edge.b === node) b = edge.a - else continue - if (b !== prev) { - visitColorNode(g, node, b, hsl_map, add, iteration + 1) - } - } -} + for (let {a, b} of g.edges) { -function generateColorMap(g: fg.graph.Graph, nodes: readonly fg.graph.Node[]): ColorMap { - const hls_map: HSLMap = new Map() + let a_hsl = hsl_map[a.key as string] + let b_hsl = hsl_map[b.key as string] - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]! - const color = COLORS[i % COLORS.length]! - visitColorNode(g, node, node, hls_map, color) + let am = a.mass-1 + let bm = b.mass-1 + + hsl_map[a.key as string] = color.mix(a_hsl, b_hsl, am*am*am, bm) + hsl_map[b.key as string] = color.mix(a_hsl, b_hsl, am, bm*bm*bm) } - const color_map: ColorMap = {} - for (const [node, [hue, saturation, lightness]] of hls_map.entries()) { - color_map[node.key as string] = `${hue} ${saturation}% ${lightness}%` - } - - return color_map + return hsl_map } function generateNodesFromRawData(g: fg.graph.Graph, raw_data: RawGraphNode[]): void { @@ -107,8 +74,6 @@ function generateNodesFromRawData(g: fg.graph.Graph, raw_data: RawGraphNode[]): let edges = fg.graph.get_node_edges(g, node) node.mass = fg.graph.node_mass_from_edges(edges.length) } - - fg.graph.randomize_positions(g) } function filterNodes( @@ -135,7 +100,7 @@ const GRAPH_OPTIONS: fg.graph.Options = { origin_strength: 0.01, repel_distance: 40, repel_strength: 2, - link_strength: 0.015, + link_strength: 0.03, grid_size: 500 } @@ -209,7 +174,7 @@ const drawGraph = (c: fg.canvas.CanvasState, color_map: ColorMap): void => { c.ctx.fillStyle = node.anchor || c.hovered_node === node ? `rgba(129, 140, 248, ${opacity})` - : `hsl(${color_map[node.key as string]} / ${opacity})` + : color.hsl_to_hsla_string(color_map[node.key as string], opacity) c.ctx.fillText(node.label, x, y) } @@ -250,10 +215,12 @@ function init( if (s.ctx == null) return generateNodesFromRawData(s.graph, raw_nodes) + fg.graph.set_positions_smart(s.graph) + s.nodes = s.graph.nodes.slice() s.edges = s.graph.edges.slice() - let color_map = generateColorMap(s.graph, s.nodes) + let color_map = generateColorMap(s.graph) let canvas_state = fg.canvas.canvasState({ ctx: s.ctx, @@ -270,6 +237,8 @@ function init( }) s.ro.observe(canvas_el) + simulateGraph(6, s.graph, canvas_state, window.innerWidth, window.innerHeight) + function loop(time: number) { let is_active = gestures.mode.type === fg.canvas.Mode.DraggingNode let iterations = Math.min(2, raf.calcIterations(s.frame_iter_limit, time)) diff --git a/web/package.json b/web/package.json index 9ddd9b66..70983899 100644 --- a/web/package.json +++ b/web/package.json @@ -13,7 +13,8 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@hookform/resolvers": "^3.9.0", - "@nothing-but/force-graph": "^0.8.3", + "@nothing-but/force-graph": "^0.9.3", + "@nothing-but/utils": "^0.16.0", "@omit/react-confirm-dialog": "^1.1.5", "@omit/react-fancy-switch": "^0.1.1", "@radix-ui/react-avatar": "^1.1.0", From a4c23ecbbf3377914d45caca663a1e30b5570443 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Fri, 6 Sep 2024 15:58:16 +0200 Subject: [PATCH 013/124] Fix placement of the title and search on landing page --- .../routes/public/PublicHomeRoute.tsx | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/web/components/routes/public/PublicHomeRoute.tsx b/web/components/routes/public/PublicHomeRoute.tsx index ab4a14a5..ec0b3c91 100644 --- a/web/components/routes/public/PublicHomeRoute.tsx +++ b/web/components/routes/public/PublicHomeRoute.tsx @@ -40,27 +40,27 @@ export function PublicHomeRoute() { filter_query={filterQuery} /> - - + - I want to learn - - handleTopicSelect(topic.name)} - onInputChange={handleInputChange} - /> - + + I want to learn + + handleTopicSelect(topic.name)} + onInputChange={handleInputChange} + /> + +

) From 22e0d4e191df00989bfe94751d5a0d0cd2b235a5 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Fri, 6 Sep 2024 22:59:54 +0700 Subject: [PATCH 014/124] refactor(public): remove unused me --- web/components/routes/public/PublicHomeRoute.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/web/components/routes/public/PublicHomeRoute.tsx b/web/components/routes/public/PublicHomeRoute.tsx index ec0b3c91..5090e143 100644 --- a/web/components/routes/public/PublicHomeRoute.tsx +++ b/web/components/routes/public/PublicHomeRoute.tsx @@ -21,7 +21,6 @@ export function PublicHomeRoute() { const router = useRouter() const raw_graph_data = React.use(graph_data_promise) as GraphNode[] const [filterQuery, setFilterQuery] = React.useState("") - const { me } = useAccount() const handleTopicSelect = (topicName: string) => { router.push(`/${topicName}`) @@ -41,11 +40,7 @@ export function PublicHomeRoute() { />
- + Date: Fri, 6 Sep 2024 23:00:18 +0700 Subject: [PATCH 015/124] refactor(public): remove unused import --- web/components/routes/public/PublicHomeRoute.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/components/routes/public/PublicHomeRoute.tsx b/web/components/routes/public/PublicHomeRoute.tsx index 5090e143..e575a180 100644 --- a/web/components/routes/public/PublicHomeRoute.tsx +++ b/web/components/routes/public/PublicHomeRoute.tsx @@ -5,7 +5,6 @@ import dynamic from "next/dynamic" import { motion } from "framer-motion" import { Autocomplete } from "./Autocomplete" import { useRouter } from "next/navigation" -import { useAccount } from "@/lib/providers/jazz-provider" let graph_data_promise = import("./graph-data.json").then(a => a.default) From 26386be50f1014f0192e3ea4bcf4ca70aa984ec3 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Fri, 6 Sep 2024 23:04:54 +0700 Subject: [PATCH 016/124] fix: escape regex special character --- web/components/routes/public/Autocomplete.tsx | 5 +- .../routes/public/force-graph-client-lazy.tsx | 86 +++++++++---------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/web/components/routes/public/Autocomplete.tsx b/web/components/routes/public/Autocomplete.tsx index 8b00b037..e288e99b 100644 --- a/web/components/routes/public/Autocomplete.tsx +++ b/web/components/routes/public/Autocomplete.tsx @@ -4,7 +4,7 @@ import React, { useState, useRef, useCallback, useMemo } from "react" import { Command, CommandGroup, CommandItem, CommandList } from "@/components/ui/command" import { Command as CommandPrimitive } from "cmdk" import { motion, AnimatePresence } from "framer-motion" -import { cn } from "@/lib/utils" +import { cn, searchSafeRegExp } from "@/lib/utils" interface GraphNode { name: string @@ -27,7 +27,8 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl if (!inputValue) { return topics.slice(0, 5) } - const regex = new RegExp(inputValue.split("").join(".*"), "i") + + const regex = searchSafeRegExp(inputValue) return topics.filter( topic => regex.test(topic.name) || diff --git a/web/components/routes/public/force-graph-client-lazy.tsx b/web/components/routes/public/force-graph-client-lazy.tsx index 21fc1f6c..52da5315 100644 --- a/web/components/routes/public/force-graph-client-lazy.tsx +++ b/web/components/routes/public/force-graph-client-lazy.tsx @@ -2,10 +2,10 @@ import * as react from "react" import * as fg from "@nothing-but/force-graph" -import { ease, trig, raf, color } from "@nothing-but/utils" - import * as schedule from "@/lib/utils/schedule" import * as canvas from "@/lib/utils/canvas" +import { searchSafeRegExp } from "@/lib/utils" +import { ease, trig, raf, color } from "@nothing-but/utils" export type RawGraphNode = { name: string @@ -18,8 +18,8 @@ const COLORS: readonly color.HSL[] = [ [15, 87, 66], [31, 90, 69], [15, 87, 66], - [31, 90, 69], - [344, 87, 70], + [31, 90, 69], + [344, 87, 70] ] type ColorMap = Record @@ -31,16 +31,15 @@ function generateColorMap(g: fg.graph.Graph): ColorMap { hsl_map[g.nodes[i].key as string] = COLORS[i % COLORS.length] } - for (let {a, b} of g.edges) { - + for (let { a, b } of g.edges) { let a_hsl = hsl_map[a.key as string] let b_hsl = hsl_map[b.key as string] - let am = a.mass-1 - let bm = b.mass-1 + let am = a.mass - 1 + let bm = b.mass - 1 - hsl_map[a.key as string] = color.mix(a_hsl, b_hsl, am*am*am, bm) - hsl_map[b.key as string] = color.mix(a_hsl, b_hsl, am, bm*bm*bm) + hsl_map[a.key as string] = color.mix(a_hsl, b_hsl, am * am * am, bm) + hsl_map[b.key as string] = color.mix(a_hsl, b_hsl, am, bm * bm * bm) } return hsl_map @@ -76,10 +75,7 @@ function generateNodesFromRawData(g: fg.graph.Graph, raw_data: RawGraphNode[]): } } -function filterNodes( - s: State, - filter: string -): void { +function filterNodes(s: State, filter: string): void { fg.graph.clear_nodes(s.graph) if (filter === "") { @@ -87,10 +83,16 @@ function filterNodes( fg.graph.add_edges(s.graph, s.edges) } else { // regex matching all letters of the filter (out of order) - const regex = new RegExp(filter.split("").join(".*"), "i") - - fg.graph.add_nodes(s.graph, s.nodes.filter(node => regex.test(node.label))) - fg.graph.add_edges(s.graph, s.edges.filter(edge => regex.test(edge.a.label) && regex.test(edge.b.label))) + const regex = searchSafeRegExp(filter) + + fg.graph.add_nodes( + s.graph, + s.nodes.filter(node => regex.test(node.label)) + ) + fg.graph.add_edges( + s.graph, + s.edges.filter(edge => regex.test(edge.a.label) && regex.test(edge.b.label)) + ) } } @@ -129,7 +131,6 @@ const simulateGraph = ( 80 /* additional margin for when scrolled in */ for (let node of graph.nodes) { - let dist_x = node.pos.x - origin_x let dist_y = (node.pos.y - origin_y) * 2 let dist = Math.sqrt(dist_x * dist_x + dist_y * dist_y) @@ -150,32 +151,31 @@ const drawGraph = (c: fg.canvas.CanvasState, color_map: ColorMap): void => { Draw text nodes */ let grid_size = c.graph.options.grid_size - let max_size = Math.max(c.ctx.canvas.width, c.ctx.canvas.height) + let max_size = Math.max(c.ctx.canvas.width, c.ctx.canvas.height) - let clip_rect = fg.canvas.get_ctx_clip_rect(c.ctx, {x: 100, y: 20}) + let clip_rect = fg.canvas.get_ctx_clip_rect(c.ctx, { x: 100, y: 20 }) c.ctx.textAlign = "center" c.ctx.textBaseline = "middle" for (let node of c.graph.nodes) { - - let x = node.pos.x / grid_size * max_size - let y = node.pos.y / grid_size * max_size + let x = (node.pos.x / grid_size) * max_size + let y = (node.pos.y / grid_size) * max_size if (fg.canvas.in_rect_xy(clip_rect, x, y)) { - - let base_size = max_size / 220 + let base_size = max_size / 220 let mass_boost_size = max_size / 140 - let mass_boost = (node.mass - 1) / 8 / c.scale + let mass_boost = (node.mass - 1) / 8 / c.scale c.ctx.font = `${base_size + mass_boost * mass_boost_size}px sans-serif` - + let opacity = 0.6 + ((node.mass - 1) / 50) * 4 - c.ctx.fillStyle = node.anchor || c.hovered_node === node - ? `rgba(129, 140, 248, ${opacity})` - : color.hsl_to_hsla_string(color_map[node.key as string], opacity) - + c.ctx.fillStyle = + node.anchor || c.hovered_node === node + ? `rgba(129, 140, 248, ${opacity})` + : color.hsl_to_hsla_string(color_map[node.key as string], opacity) + c.ctx.fillText(node.label, x, y) } } @@ -207,7 +207,7 @@ function init( canvas_el: HTMLCanvasElement | null } ) { - let {canvas_el, raw_nodes} = props + let { canvas_el, raw_nodes } = props if (canvas_el == null) return @@ -247,7 +247,7 @@ function init( s.alpha = raf.updateAlpha(s.alpha, is_active || time < s.bump_end) simulateGraph(s.alpha, s.graph, canvas_state, window.innerWidth, window.innerHeight) } - + if (iterations > 0) { drawGraph(canvas_state, color_map) } @@ -260,15 +260,15 @@ function init( canvas: canvas_state, onGesture: e => { switch (e.type) { - case fg.canvas.GestureEventType.Translate: - s.bump_end = raf.bump(s.bump_end) - break - case fg.canvas.GestureEventType.NodeClick: - props.onNodeClick(e.node.key as string) - break - case fg.canvas.GestureEventType.NodeDrag: - fg.graph.set_position(canvas_state.graph, e.node, e.pos) - break + case fg.canvas.GestureEventType.Translate: + s.bump_end = raf.bump(s.bump_end) + break + case fg.canvas.GestureEventType.NodeClick: + props.onNodeClick(e.node.key as string) + break + case fg.canvas.GestureEventType.NodeDrag: + fg.graph.set_position(canvas_state.graph, e.node, e.pos) + break } } })) From 45d727136496a3f35ad8907dab0db67ede507aea Mon Sep 17 00:00:00 2001 From: Aslam H Date: Fri, 6 Sep 2024 23:05:06 +0700 Subject: [PATCH 017/124] chore: update package json --- web/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/package.json b/web/package.json index 70983899..61fff4b0 100644 --- a/web/package.json +++ b/web/package.json @@ -68,7 +68,7 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", - "framer-motion": "^11.5.2", + "framer-motion": "^11.5.4", "jazz-react": "0.7.35-unique.2", "jazz-react-auth-clerk": "0.7.33-new-auth.1", "jazz-tools": "0.7.35-unique.2", @@ -77,7 +77,7 @@ "lucide-react": "^0.429.0", "next": "14.2.5", "next-themes": "^0.3.0", - "nuqs": "^1.19.0", + "nuqs": "^1.19.1", "react": "^18.3.1", "react-day-picker": "^8.10.1", "react-dom": "^18.3.1", @@ -97,7 +97,7 @@ "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.1", "@types/jest": "^29.5.12", - "@types/node": "^22.5.3", + "@types/node": "^22.5.4", "@types/react": "^18.3.5", "@types/react-dom": "^18.3.0", "eslint": "^8.57.0", From e7bd9a9d42c4c9d1796eb8b1190b11fbc88573bd Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sat, 7 Sep 2024 01:06:19 +0700 Subject: [PATCH 018/124] chore: enhance Autocomplete --- web/components/routes/public/Autocomplete.tsx | 103 ++++++++++++------ 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/web/components/routes/public/Autocomplete.tsx b/web/components/routes/public/Autocomplete.tsx index e288e99b..fae825d5 100644 --- a/web/components/routes/public/Autocomplete.tsx +++ b/web/components/routes/public/Autocomplete.tsx @@ -1,10 +1,9 @@ -"use client" - -import React, { useState, useRef, useCallback, useMemo } from "react" +import React, { useState, useRef, useCallback, useMemo, useEffect } from "react" import { Command, CommandGroup, CommandItem, CommandList } from "@/components/ui/command" import { Command as CommandPrimitive } from "cmdk" import { motion, AnimatePresence } from "framer-motion" -import { cn, searchSafeRegExp } from "@/lib/utils" +import { cn, searchSafeRegExp, shuffleArray } from "@/lib/utils" +import { useMountedState } from "react-use" interface GraphNode { name: string @@ -20,22 +19,30 @@ interface AutocompleteProps { export function Autocomplete({ topics = [], onSelect, onInputChange }: AutocompleteProps): JSX.Element { const inputRef = useRef(null) + const isMounted = useMountedState() const [open, setOpen] = useState(false) const [inputValue, setInputValue] = useState("") + const [isInitialOpen, setIsInitialOpen] = useState(true) + const [hasInteracted, setHasInteracted] = useState(false) + + const initialShuffledTopics = useMemo(() => shuffleArray(topics).slice(0, 5), [topics]) const filteredTopics = useMemo(() => { if (!inputValue) { - return topics.slice(0, 5) + return initialShuffledTopics } const regex = searchSafeRegExp(inputValue) - return topics.filter( - topic => - regex.test(topic.name) || - regex.test(topic.prettyName) || - topic.connectedTopics.some(connectedTopic => regex.test(connectedTopic)) - ) - }, [inputValue, topics]) + return topics + .filter( + topic => + regex.test(topic.name) || + regex.test(topic.prettyName) || + topic.connectedTopics.some(connectedTopic => regex.test(connectedTopic)) + ) + .sort((a, b) => a.prettyName.localeCompare(b.prettyName)) + .slice(0, 10) + }, [inputValue, topics, initialShuffledTopics]) const handleSelect = useCallback( (topic: GraphNode) => { @@ -52,62 +59,96 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl handleSelect(filteredTopics[0]) } else if ((e.key === "Backspace" || e.key === "Delete") && inputRef.current?.value === "") { setOpen(true) + setIsInitialOpen(true) + } + if (!hasInteracted) { + setHasInteracted(true) } }, - [filteredTopics, handleSelect] + [filteredTopics, handleSelect, hasInteracted] ) const handleInputChange = useCallback( (value: string) => { setInputValue(value) setOpen(true) + setIsInitialOpen(false) onInputChange(value) + if (!hasInteracted) { + setHasInteracted(true) + } }, - [onInputChange] + [onInputChange, hasInteracted] ) + const commandKey = useMemo(() => { + return filteredTopics + .map(topic => `${topic.name}:${topic.prettyName}:${topic.connectedTopics.join(",")}`) + .join("__") + }, [filteredTopics]) + + useEffect(() => { + if (inputRef.current && isMounted() && hasInteracted) { + inputRef.current.focus() + } + }, [commandKey, isMounted, hasInteracted]) + + const animationProps = { + initial: { opacity: 0, y: -10 }, + animate: { opacity: 1, y: 0 }, + exit: { opacity: 0, y: -10 }, + transition: { duration: 0.1 } + } + return ( -
+
setTimeout(() => setOpen(false), 100)} - onFocus={() => setOpen(true)} + onBlur={() => { + setTimeout(() => { + setOpen(false) + setIsInitialOpen(true) + }, 100) + }} + onFocus={() => { + setOpen(true) + setIsInitialOpen(true) + if (!hasInteracted) { + setHasInteracted(true) + } + }} placeholder="Search for a topic..." - className={cn("placeholder:text-muted-foreground flex-1 bg-transparent px-2 py-1 outline-none", { - "mb-1 border-b pb-2.5": open - })} + className={cn("placeholder:text-muted-foreground flex-1 bg-transparent px-2 outline-none")} />
{open && ( - - - {filteredTopics.map(topic => ( + + + {filteredTopics.map((topic, index) => ( handleSelect(topic)} className="min-h-10 rounded-none px-3 py-1.5" > {topic.prettyName} - {topic.connectedTopics.length > 0 ? topic.connectedTopics.join(", ") : "-"} + {topic.connectedTopics.length > 0 && topic.connectedTopics.join(", ")} ))} From 3fe1f8012b13e3c24876683de117e29c118d28c2 Mon Sep 17 00:00:00 2001 From: Aslam Date: Sat, 7 Sep 2024 01:48:51 +0700 Subject: [PATCH 019/124] feat: homepage font (#145) * feat: new fonts file * chore: apply geist * chore: use relaway font for 'I want to learn' * chore: config font geist * feat(util): suffle array * feat: add geist --- web/app/fonts.ts | 7 +++++++ web/app/layout.tsx | 10 ++-------- web/components/routes/public/Autocomplete.tsx | 2 +- web/components/routes/public/PublicHomeRoute.tsx | 9 +++++++-- web/lib/utils/index.ts | 9 +++++++++ web/package.json | 1 + web/tailwind.config.ts | 4 +++- 7 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 web/app/fonts.ts diff --git a/web/app/fonts.ts b/web/app/fonts.ts new file mode 100644 index 00000000..bcdcb16f --- /dev/null +++ b/web/app/fonts.ts @@ -0,0 +1,7 @@ +import { Raleway } from "next/font/google" +export { GeistSans } from "geist/font/sans" +export { GeistMono } from "geist/font/mono" +// import { Inter } from "next/font/google" + +// export const inter = Inter({ subsets: ["latin"] }) +export const raleway = Raleway({ subsets: ["latin"] }) diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 7a526ab8..d2e3d43a 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -1,19 +1,13 @@ import type { Metadata } from "next" -import { Inter as FontSans } from "next/font/google" import { cn } from "@/lib/utils" import { ThemeProvider } from "@/lib/providers/theme-provider" import "./globals.css" - import { ClerkProviderClient } from "@/components/custom/clerk/clerk-provider-client" import { JotaiProvider } from "@/lib/providers/jotai-provider" import { Toaster } from "@/components/ui/sonner" import { ConfirmProvider } from "@/lib/providers/confirm-provider" import { DeepLinkProvider } from "@/lib/providers/deep-link-provider" - -const fontSans = FontSans({ - subsets: ["latin"], - variable: "--font-sans" -}) +import { GeistMono, GeistSans } from "./fonts" export const metadata: Metadata = { title: "Learn Anything", @@ -29,7 +23,7 @@ export default function RootLayout({ - + diff --git a/web/components/routes/public/Autocomplete.tsx b/web/components/routes/public/Autocomplete.tsx index fae825d5..f17adcbf 100644 --- a/web/components/routes/public/Autocomplete.tsx +++ b/web/components/routes/public/Autocomplete.tsx @@ -103,7 +103,7 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl return ( a.default) @@ -38,10 +40,13 @@ export function PublicHomeRoute() { filter_query={filterQuery} /> -
+
{ return new RegExp(escapedChars.join(".*"), "i") } +export function shuffleArray(array: T[]): T[] { + const shuffled = [...array] + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]] + } + return shuffled +} + export * from "./urls" export * from "./slug" export * from "./keyboard" diff --git a/web/package.json b/web/package.json index 61fff4b0..0009e03b 100644 --- a/web/package.json +++ b/web/package.json @@ -69,6 +69,7 @@ "cmdk": "^1.0.0", "date-fns": "^3.6.0", "framer-motion": "^11.5.4", + "geist": "^1.3.1", "jazz-react": "0.7.35-unique.2", "jazz-react-auth-clerk": "0.7.33-new-auth.1", "jazz-tools": "0.7.35-unique.2", diff --git a/web/tailwind.config.ts b/web/tailwind.config.ts index c0309f60..3db76af8 100644 --- a/web/tailwind.config.ts +++ b/web/tailwind.config.ts @@ -79,7 +79,9 @@ const config = { // learn anything custom fontFamily: { - sans: ["var(--font-sans)", ...fontFamily.sans] + // sans: ["var(--font-sans)", ...fontFamily.sans], + sans: ["var(--font-geist-sans)"], + mono: ["var(--font-geist-mono)"] }, backgroundImage: { From dc8ad3ebed0f5bf4d4de9ed2641b5ed64938b426 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sat, 7 Sep 2024 01:50:56 +0700 Subject: [PATCH 020/124] chore: blur out a bit for topic --- web/components/routes/public/Autocomplete.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/routes/public/Autocomplete.tsx b/web/components/routes/public/Autocomplete.tsx index f17adcbf..4315cdbf 100644 --- a/web/components/routes/public/Autocomplete.tsx +++ b/web/components/routes/public/Autocomplete.tsx @@ -147,7 +147,7 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl className="min-h-10 rounded-none px-3 py-1.5" > {topic.prettyName} - + {topic.connectedTopics.length > 0 && topic.connectedTopics.join(", ")} From e61aae02d50848526597fcb343e179a2910d0824 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sat, 7 Sep 2024 01:55:43 +0700 Subject: [PATCH 021/124] chore: update package json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3eee142..900ca126 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "web" ], "dependencies": { - "@tauri-apps/cli": "^2.0.0-rc.11", + "@tauri-apps/cli": "^2.0.0-rc.12", "@tauri-apps/plugin-fs": "^2.0.0-rc.2", "jazz-nodejs": "0.7.35-unique.2", "react-icons": "^5.3.0" From 844b1ae3347460f2153a3ca8847c11318967521a Mon Sep 17 00:00:00 2001 From: Anselm Eickhoff Date: Fri, 6 Sep 2024 21:11:43 +0100 Subject: [PATCH 022/124] feat: guest auth (#141) * feat: Start using guest auth * feat: Implement more functionality to work as guest * chore: update package and tweak public route * chore: update root package json * chore: update web package json --------- Co-authored-by: Aslam H --- bun.lockb | Bin 404552 -> 398912 bytes package.json | 2 +- web/app/(pages)/layout.tsx | 42 ++++--- web/app/layout.tsx | 3 +- web/components/custom/sidebar/sidebar.tsx | 19 +++- web/components/routes/public/Autocomplete.tsx | 13 +-- .../routes/public/PublicHomeRoute.tsx | 16 +-- .../routes/topics/detail/Header.tsx | 27 +++-- .../routes/topics/detail/TopicDetailRoute.tsx | 13 +-- .../topics/detail/partials/link-item.tsx | 26 +++-- .../routes/topics/detail/partials/section.tsx | 19 +--- .../topics/detail/partials/topic-sections.tsx | 10 -- web/hooks/use-topic-data.ts | 15 +++ web/lib/providers/jazz-provider.tsx | 103 +++--------------- web/middleware.ts | 28 +++-- web/package.json | 7 +- 16 files changed, 138 insertions(+), 205 deletions(-) create mode 100644 web/hooks/use-topic-data.ts diff --git a/bun.lockb b/bun.lockb index 033042b27ffa78e3a4fca159ab68d26fae342e74..90ac2ee464e5bca91b6c2337559c607c526491a6 100755 GIT binary patch delta 79368 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 QdWC(z{%@p{3VUw-KWIhgk^lez delta 82339 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 diff --git a/package.json b/package.json index 900ca126..7c66e8cf 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dependencies": { "@tauri-apps/cli": "^2.0.0-rc.12", "@tauri-apps/plugin-fs": "^2.0.0-rc.2", - "jazz-nodejs": "0.7.35-unique.2", + "jazz-nodejs": "0.7.35-guest-auth.5", "react-icons": "^5.3.0" }, "devDependencies": { diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index c4d3ec97..ef9144e7 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -1,32 +1,30 @@ -import { SignedInClient } from "@/components/custom/clerk/signed-in-client" +"use client" + import { Sidebar } from "@/components/custom/sidebar/sidebar" -import { JazzClerkAuth, JazzProvider } from "@/lib/providers/jazz-provider" -import { currentUser } from "@clerk/nextjs/server" import { CommandPalette } from "@/components/custom/command-palette/command-palette" +import { useAccountOrGuest } from "@/lib/providers/jazz-provider" +import { usePathname } from "next/navigation" +import PublicHomeRoute from "@/components/routes/public/PublicHomeRoute" -export default async function PageLayout({ children }: { children: React.ReactNode }) { - const user = await currentUser() +export default function PageLayout({ children }: { children: React.ReactNode }) { + const { me } = useAccountOrGuest() + const pathname = usePathname() - if (!user) { - return children + if (me._type === "Anonymous" && pathname === "/") { + return } return ( - - - -
- - +
+ -
-
- {children} -
-
-
- - - + {me._type !== "Anonymous" && } + +
+
+ {children} +
+
+
) } diff --git a/web/app/layout.tsx b/web/app/layout.tsx index d2e3d43a..488b22b5 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -8,6 +8,7 @@ import { Toaster } from "@/components/ui/sonner" import { ConfirmProvider } from "@/lib/providers/confirm-provider" import { DeepLinkProvider } from "@/lib/providers/deep-link-provider" import { GeistMono, GeistSans } from "./fonts" +import { JazzAndAuth } from "@/lib/providers/jazz-provider" export const metadata: Metadata = { title: "Learn Anything", @@ -27,7 +28,7 @@ export default function RootLayout({ - {children} + {children} diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index ec947915..54c4d1c7 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -14,6 +14,8 @@ import { LinkSection } from "./partial/link-section" import { PageSection } from "./partial/page-section" import { TopicSection } from "./partial/topic-section" import { ProfileSection } from "./partial/profile-section" +import { useAccountOrGuest } from "@/lib/providers/jazz-provider" +import { SignInButton } from "@clerk/nextjs" interface SidebarContextType { isCollapsed: boolean @@ -109,7 +111,9 @@ const LogoAndSearch: React.FC = React.memo(() => { LogoAndSearch.displayName = "LogoAndSearch" const SidebarContent: React.FC = React.memo(() => { + const { me } = useAccountOrGuest() const pathname = usePathname() + return ( <>
- - - + {me._type === "Account" && } + {me._type === "Account" && } + {me._type === "Account" && }
- + + {me._type === "Account" ? ( + + ) : ( +
+ Fake profile section +
+ )} ) }) diff --git a/web/components/routes/public/Autocomplete.tsx b/web/components/routes/public/Autocomplete.tsx index 4315cdbf..7c022a83 100644 --- a/web/components/routes/public/Autocomplete.tsx +++ b/web/components/routes/public/Autocomplete.tsx @@ -13,7 +13,7 @@ interface GraphNode { interface AutocompleteProps { topics: GraphNode[] - onSelect: (topic: GraphNode) => void + onSelect: (topic: string) => void onInputChange: (value: string) => void } @@ -46,18 +46,16 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl const handleSelect = useCallback( (topic: GraphNode) => { - setInputValue(topic.prettyName) + // setInputValue(topicPrettyName) setOpen(false) - onSelect(topic) + onSelect(topic.name) }, [onSelect] ) const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { - if (e.key === "Enter" && filteredTopics.length > 0) { - handleSelect(filteredTopics[0]) - } else if ((e.key === "Backspace" || e.key === "Delete") && inputRef.current?.value === "") { + if ((e.key === "Backspace" || e.key === "Delete") && inputRef.current?.value === "") { setOpen(true) setIsInitialOpen(true) } @@ -65,7 +63,7 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl setHasInteracted(true) } }, - [filteredTopics, handleSelect, hasInteracted] + [hasInteracted] ) const handleInputChange = useCallback( @@ -143,6 +141,7 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl {filteredTopics.map((topic, index) => ( handleSelect(topic)} className="min-h-10 rounded-none px-3 py-1.5" > diff --git a/web/components/routes/public/PublicHomeRoute.tsx b/web/components/routes/public/PublicHomeRoute.tsx index 8f9e6344..ed694a66 100644 --- a/web/components/routes/public/PublicHomeRoute.tsx +++ b/web/components/routes/public/PublicHomeRoute.tsx @@ -23,8 +23,8 @@ export function PublicHomeRoute() { const raw_graph_data = React.use(graph_data_promise) as GraphNode[] const [filterQuery, setFilterQuery] = React.useState("") - const handleTopicSelect = (topicName: string) => { - router.push(`/${topicName}`) + const handleTopicSelect = (topic: string) => { + router.replace(`/${topic}`) } const handleInputChange = (value: string) => { @@ -34,11 +34,7 @@ export function PublicHomeRoute() { return ( <>
- handleTopicSelect(val)} - filter_query={filterQuery} - /> +
@@ -53,11 +49,7 @@ export function PublicHomeRoute() { > I want to learn - handleTopicSelect(topic.name)} - onInputChange={handleInputChange} - /> +
diff --git a/web/components/routes/topics/detail/Header.tsx b/web/components/routes/topics/detail/Header.tsx index 0b9a9834..244dbc06 100644 --- a/web/components/routes/topics/detail/Header.tsx +++ b/web/components/routes/topics/detail/Header.tsx @@ -4,15 +4,16 @@ import * as React from "react" import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header" import { ListOfTopics, Topic } from "@/lib/schema" import { LearningStateSelector } from "@/components/custom/learning-state-selector" -import { useAccount } from "@/lib/providers/jazz-provider" +import { useAccountOrGuest } from "@/lib/providers/jazz-provider" import { LearningStateValue } from "@/lib/constants" +import { toast } from "sonner" interface TopicDetailHeaderProps { topic: Topic } export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic }: TopicDetailHeaderProps) { - const { me } = useAccount({ + const { me } = useAccountOrGuest({ root: { topicsWantToLearn: [], topicsLearning: [], @@ -26,34 +27,44 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic } learningState: LearningStateValue } | null = null - const wantToLearnIndex = me?.root.topicsWantToLearn.findIndex(t => t?.id === topic.id) ?? -1 + const wantToLearnIndex = + me?._type === "Anonymous" ? -1 : (me?.root.topicsWantToLearn.findIndex(t => t?.id === topic.id) ?? -1) if (wantToLearnIndex !== -1) { p = { index: wantToLearnIndex, - topic: me?.root.topicsWantToLearn[wantToLearnIndex], + // TODO: fix this type error by doing better conditionals on both index and p + topic: me && me._type !== "Anonymous" ? me.root.topicsWantToLearn[wantToLearnIndex] : undefined, learningState: "wantToLearn" } } - const learningIndex = me?.root.topicsLearning.findIndex(t => t?.id === topic.id) ?? -1 + const learningIndex = + me?._type === "Anonymous" ? -1 : (me?.root.topicsLearning.findIndex(t => t?.id === topic.id) ?? -1) if (learningIndex !== -1) { p = { index: learningIndex, - topic: me?.root.topicsLearning[learningIndex], + topic: me && me._type !== "Anonymous" ? me?.root.topicsLearning[learningIndex] : undefined, learningState: "learning" } } - const learnedIndex = me?.root.topicsLearned.findIndex(t => t?.id === topic.id) ?? -1 + const learnedIndex = + me?._type === "Anonymous" ? -1 : (me?.root.topicsLearned.findIndex(t => t?.id === topic.id) ?? -1) if (learnedIndex !== -1) { p = { index: learnedIndex, - topic: me?.root.topicsLearned[learnedIndex], + topic: me && me._type !== "Anonymous" ? me?.root.topicsLearned[learnedIndex] : undefined, learningState: "learned" } } const handleAddToProfile = (learningState: LearningStateValue) => { + if (me?._type === "Anonymous") { + // TODO: handle better + toast.error("You need to sign in to add links to your personal list.") + return + } + const topicLists: Record = { wantToLearn: me?.root.topicsWantToLearn, learning: me?.root.topicsLearning, diff --git a/web/components/routes/topics/detail/TopicDetailRoute.tsx b/web/components/routes/topics/detail/TopicDetailRoute.tsx index 8688ceea..1bd5f42c 100644 --- a/web/components/routes/topics/detail/TopicDetailRoute.tsx +++ b/web/components/routes/topics/detail/TopicDetailRoute.tsx @@ -4,9 +4,8 @@ import React, { useMemo, useRef } from "react" import { TopicDetailHeader } from "./Header" import { TopicSections } from "./partials/topic-sections" import { atom } from "jotai" -import { useAccount, useCoState } from "@/lib/providers/jazz-provider" -import { Topic } from "@/lib/schema" -import { JAZZ_GLOBAL_GROUP_ID } from "@/lib/constants" +import { useAccount, useAccountOrGuest } from "@/lib/providers/jazz-provider" +import { useTopicData } from "@/hooks/use-topic-data" interface TopicDetailRouteProps { topicName: string @@ -15,10 +14,8 @@ interface TopicDetailRouteProps { export const openPopoverForIdAtom = atom(null) export function TopicDetailRoute({ topicName }: TopicDetailRouteProps) { - const { me } = useAccount({ root: { personalLinks: [] } }) - - const topicID = useMemo(() => me && Topic.findUnique({ topicName }, JAZZ_GLOBAL_GROUP_ID, me), [topicName, me]) - const topic = useCoState(Topic, topicID, { latestGlobalGuide: { sections: [{ links: [] }] } }) + const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) + const { topic } = useTopicData(topicName, me) // const { activeIndex, setActiveIndex, containerRef, linkRefs } = useLinkNavigation(allLinks) const linksRefDummy = useRef<(HTMLLIElement | null)[]>([]) const containerRefDummy = useRef(null) @@ -37,8 +34,6 @@ export function TopicDetailRoute({ topicName }: TopicDetailRouteProps) { setActiveIndex={() => {}} linkRefs={linksRefDummy} containerRef={containerRefDummy} - me={me} - personalLinks={me.root.personalLinks} />
) diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index 1e119bd3..4758f01e 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -13,6 +13,7 @@ import { cn, ensureUrlProtocol, generateUniqueSlug } from "@/lib/utils" import { LaAccount, Link as LinkSchema, PersonalLink, PersonalLinkLists, Topic, UserRoot } from "@/lib/schema" import { openPopoverForIdAtom } from "../TopicDetailRoute" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" +import { useAccountOrGuest } from "@/lib/providers/jazz-provider" interface LinkItemProps { topic: Topic @@ -20,23 +21,24 @@ interface LinkItemProps { isActive: boolean index: number setActiveIndex: (index: number) => void - me: { - root: { - personalLinks: PersonalLinkLists - } & UserRoot - } & LaAccount - personalLinks: PersonalLinkLists } export const LinkItem = React.memo( React.forwardRef( - ({ topic, link, isActive, index, setActiveIndex, me, personalLinks }, ref) => { + ({ topic, link, isActive, index, setActiveIndex }, ref) => { const router = useRouter() const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) const [isPopoverOpen, setIsPopoverOpen] = useState(false) + const { me } = useAccountOrGuest({ root: { personalLinks: [] } }); + + const personalLinks = useMemo(() => { + if (!me || me._type === "Anonymous") return undefined; + return me?.root?.personalLinks || [] + }, [me]) + const personalLink = useMemo(() => { - return personalLinks.find(pl => pl?.link?.id === link.id) + return personalLinks?.find(pl => pl?.link?.id === link.id) }, [personalLinks, link.id]) const selectedLearningState = useMemo(() => { @@ -53,6 +55,14 @@ export const LinkItem = React.memo( const handleSelectLearningState = useCallback( (learningState: LearningStateValue) => { + if (!personalLinks || !me || me?._type === "Anonymous") { + if (me?._type === "Anonymous") { + // TODO: handle better + toast.error("You need to sign in to add links to your personal list.") + } + return + }; + const defaultToast = { duration: 5000, position: "bottom-right" as const, diff --git a/web/components/routes/topics/detail/partials/section.tsx b/web/components/routes/topics/detail/partials/section.tsx index f4b8398d..7f816432 100644 --- a/web/components/routes/topics/detail/partials/section.tsx +++ b/web/components/routes/topics/detail/partials/section.tsx @@ -11,24 +11,9 @@ interface SectionProps { startIndex: number linkRefs: React.MutableRefObject<(HTMLLIElement | null)[]> setActiveIndex: (index: number) => void - me: { - root: { - personalLinks: PersonalLinkLists - } & UserRoot - } & LaAccount - personalLinks: PersonalLinkLists } -export function Section({ - topic, - section, - activeIndex, - setActiveIndex, - startIndex, - linkRefs, - me, - personalLinks -}: SectionProps) { +export function Section({ topic, section, activeIndex, setActiveIndex, startIndex, linkRefs }: SectionProps) { const [nLinksToLoad, setNLinksToLoad] = useState(10) const linksToLoad = useMemo(() => { @@ -55,8 +40,6 @@ export function Section({ ref={el => { linkRefs.current[startIndex + index] = el }} - me={me} - personalLinks={personalLinks} /> ) : ( diff --git a/web/components/routes/topics/detail/partials/topic-sections.tsx b/web/components/routes/topics/detail/partials/topic-sections.tsx index b4c13dc7..a8b6ed5f 100644 --- a/web/components/routes/topics/detail/partials/topic-sections.tsx +++ b/web/components/routes/topics/detail/partials/topic-sections.tsx @@ -9,12 +9,6 @@ interface TopicSectionsProps { setActiveIndex: (index: number) => void linkRefs: React.MutableRefObject<(HTMLLIElement | null)[]> containerRef: React.RefObject - me: { - root: { - personalLinks: PersonalLinkLists - } & UserRoot - } & LaAccount - personalLinks: PersonalLinkLists } export function TopicSections({ @@ -24,8 +18,6 @@ export function TopicSections({ setActiveIndex, linkRefs, containerRef, - me, - personalLinks }: TopicSectionsProps) { return (
@@ -42,8 +34,6 @@ export function TopicSections({ setActiveIndex={setActiveIndex} startIndex={sections.slice(0, sectionIndex).reduce((acc, s) => acc + (s?.links?.length || 0), 0)} linkRefs={linkRefs} - me={me} - personalLinks={personalLinks} /> ) )} diff --git a/web/hooks/use-topic-data.ts b/web/hooks/use-topic-data.ts new file mode 100644 index 00000000..8280762b --- /dev/null +++ b/web/hooks/use-topic-data.ts @@ -0,0 +1,15 @@ +import { useMemo } from "react" +import { useCoState } from "@/lib/providers/jazz-provider" +import { PublicGlobalGroup } from "@/lib/schema/master/public-group" +import { Account, AnonymousJazzAgent, ID } from "jazz-tools" +import { Link, Topic } from "@/lib/schema" + +const GLOBAL_GROUP_ID = process.env.NEXT_PUBLIC_JAZZ_GLOBAL_GROUP as ID + +export function useTopicData(topicName: string, me: Account | AnonymousJazzAgent | undefined) { + const topicID = useMemo(() => me && Topic.findUnique({ topicName }, GLOBAL_GROUP_ID, me), [topicName, me]) + + const topic = useCoState(Topic, topicID, { latestGlobalGuide: { sections: [{ links: [] }] } }) + + return { topic } +} diff --git a/web/lib/providers/jazz-provider.tsx b/web/lib/providers/jazz-provider.tsx index e5b8fb80..3ca9ca9a 100644 --- a/web/lib/providers/jazz-provider.tsx +++ b/web/lib/providers/jazz-provider.tsx @@ -3,104 +3,27 @@ import { createJazzReactApp } from "jazz-react" import { LaAccount } from "@/lib/schema" import { useClerk } from "@clerk/nextjs" -import { createContext, useMemo, useState } from "react" -import { AuthMethodCtx } from "jazz-react" +import { useJazzClerkAuth } from "jazz-react-auth-clerk" const Jazz = createJazzReactApp({ AccountSchema: LaAccount }) -export const { useAccount, useCoState, useAcceptInvite } = Jazz +export const { useAccount, useAccountOrGuest, useCoState, useAcceptInvite } = Jazz -export function JazzProvider({ children }: { children: React.ReactNode }) { - return {children} -} - -export const JazzClerkAuthCtx = createContext<{ - errors: string[] -}>({ - errors: [] -}) - -export function JazzClerkAuth({ children }: { children: React.ReactNode }) { +export function JazzAndAuth({ children }: { children: React.ReactNode }) { const clerk = useClerk() - const [errors, setErrors] = useState([]) - const authMethod = useMemo(() => { - return new BrowserClerkAuth( - { - onError: error => { - void clerk.signOut() - setErrors(errors => [...errors, error.toString()]) - } - }, - clerk - ) - }, [clerk]) + const [auth, state] = useJazzClerkAuth(clerk) return ( - - {children} - + <> + {state.errors.map((error) => ( +
{error}
+ ))} + + {children} + + ) -} - -import { Account, AuthMethod, AuthResult, ID } from "jazz-tools" -import type { LoadedClerk } from "@clerk/types" -import { AgentSecret } from "cojson" - -export class BrowserClerkAuth implements AuthMethod { - constructor( - public driver: BrowserClerkAuth.Driver, - private readonly clerkClient: LoadedClerk - ) {} - - async start(): Promise { - if (this.clerkClient.user) { - const storedCredentials = this.clerkClient.user.unsafeMetadata - if (storedCredentials.jazzAccountID) { - if (!storedCredentials.jazzAccountSecret) { - throw new Error("No secret for existing user") - } - return { - type: "existing", - credentials: { - accountID: storedCredentials.jazzAccountID as ID, - secret: storedCredentials.jazzAccountSecret as AgentSecret - }, - onSuccess: () => {}, - onError: (error: string | Error) => { - this.driver.onError(error) - } - } - } else { - return { - type: "new", - creationProps: { - name: this.clerkClient.user.fullName || this.clerkClient.user.username || this.clerkClient.user.id - }, - saveCredentials: async (credentials: { accountID: ID; secret: AgentSecret }) => { - await this.clerkClient.user?.update({ - unsafeMetadata: { - jazzAccountID: credentials.accountID, - jazzAccountSecret: credentials.secret - } - }) - }, - onSuccess: () => {}, - onError: (error: string | Error) => { - this.driver.onError(error) - } - } - } - } else { - throw new Error("Not signed in") - } - } -} - -export namespace BrowserClerkAuth { - export interface Driver { - onError: (error: string | Error) => void - } -} +} \ No newline at end of file diff --git a/web/middleware.ts b/web/middleware.ts index 3059c4a8..92bdc125 100644 --- a/web/middleware.ts +++ b/web/middleware.ts @@ -1,19 +1,23 @@ -import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server" +import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' -const publicRoutes = ["/", "/sign-in(.*)", "/sign-up(.*)"] -const isPublicRoute = createRouteMatcher(publicRoutes) +const isPublicRoute = createRouteMatcher([ + '/sign-in(.*)', + '/sign-up(.*)', + '/', + '/:topicName(.*)' +]) export default clerkMiddleware((auth, request) => { - if (!isPublicRoute(request)) { - auth().protect() - } + if (!isPublicRoute(request)) { + auth().protect() + } }) export const config = { - matcher: [ - // Skip Next.js internals and all static files, unless found in search params - "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", - // Always run for API routes - "/(api|trpc)(.*)" - ] + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', + // Always run for API routes + '/(api|trpc)(.*)' + ] } diff --git a/web/package.json b/web/package.json index 0009e03b..50c1db55 100644 --- a/web/package.json +++ b/web/package.json @@ -70,9 +70,10 @@ "date-fns": "^3.6.0", "framer-motion": "^11.5.4", "geist": "^1.3.1", - "jazz-react": "0.7.35-unique.2", - "jazz-react-auth-clerk": "0.7.33-new-auth.1", - "jazz-tools": "0.7.35-unique.2", + "jazz-react": "0.7.35-guest-auth.5", + "jazz-browser-auth-clerk": "0.7.35-guest-auth.5", + "jazz-react-auth-clerk": "0.7.35-guest-auth.5", + "jazz-tools": "0.7.35-guest-auth.5", "jotai": "^2.9.3", "lowlight": "^3.1.0", "lucide-react": "^0.429.0", From 833e5eff8274374c2b9df95f2f0cbbf8759c0449 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Sat, 7 Sep 2024 15:00:17 +0300 Subject: [PATCH 023/124] removed toolbar --- bun.lockb | Bin 398912 -> 398520 bytes web/components/routes/link/bottom-bar.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bun.lockb b/bun.lockb index 90ac2ee464e5bca91b6c2337559c607c526491a6..08d679eea4c620fb131a205e263207eb71f6421d 100755 GIT binary patch delta 73806 zcmeF4d7O?_AOG)r#?4$bgk&ip+0qnBGh>$9zBVCQ#!?2u48|}sVybCI%Y#bWl@3Xz z4N0C-F=|3usHBpj(%utA`(kM7_x@bxjK}l5di{RS|G(~+?|jbpea?0E>s-sseQbHG z$;Q=9ZacTb6J57{_k4ES&Cmao@O|$&_m-HNr`LLYUrh5imb@{g?kndOwm+q2NS`Nf zAK9XG>dM%_u|MjDLS-`v)1#Lh50i=p>JYr z{JCVPbf4lYey`&@QRTY@jYro@O9?zoKn350)Y=Og2`S&>;NW3y^PxV#6=Tx8} z`U<}IoI+~GCk<4ij`%xu$9#w(|(9_Wy z(B|kgv>BR#>NqMLRAe^IQOh==>Z$3dDtMgx`@-!qzdqIGGP7|g)Rfq7(bLfPQ2v#@ zOZ+M5%jn7IGfp3IdY97$sJiPKRLw5(Gh^D7HmAkvj0{?;x@@H4%C!v@SL%s{h1rD@ zL!pP^N;fmVum~3FMng0jJ>bf1M%Ik{+)1I(S@>#0aaP`Rni*P*uUgGVm0z*bg6zWl z=~rZzhSt+_>a^9UGFpZzVNO|$y0yI>+YeAprYb*NqeZKg9ct(3A z;x?yqa&jl;5T9F^JvSRY^E_LZUr^;UGpi^kVOn-^f=mBwr=Vq}WqG+Xa%W~07M?|f znmVhqJ@93;F8*nbUrzHi$d{nXaM1ZS{TZnG{8y*n5PuT>{0r#rX5-R=bla#? zqqfunRH<^D)FXAhG)1LMONP(*~&dQ?Vnb{#U zp{O7`+vPvCEN{|`gh^RNS$9({HE#i`f{IaPl!Iy#zR}x`;t+gg*d5iV?x*?c#MArQ zij|_w{j!r##lJzk@_E|tT&qRtmH~FLd&KD?RLfS8)3K;>@9i`JRql;ZR;IF8lnl#$ z>u=}EM^2|?SbsOZ=E@FK-PGY?>u2^5PS2k+J$Fh@(YhhlpOk&Y^!%Bj(4_2Hv$Lj$LJz@}e%VkPKQ+H7E0=t}WeVx| zi!QO@^0 z0@S}3GB_kDdtij^_f(*IDrQtD)E2!1ZHZn2Z&gZQW+vx2deulfbN+>Dz-}YKY3LjH zs!*pf9Dr`XZ;Y-$mGLcTL-eh&w&l&n*_pT%Uln=`)l59+GCQGKJ6#NKu8eZYKr`$z zR9!c5df{BPtd~E_Yh3#2cspNa&zO`uk)GU%ulCK%Dk#kE%C&VyQEpLgcH!gj7Vw3r z`fCU09$ixQAii>)l%F@dD0h0{RrumQs_M1lGNk#~9My5#Q4Lg{O;@TjC1FlZR?#RH zLnU|@W$Kho$j_fXf#o-AZf<^IC=|M9vdb7%eBui5 zOq`ZmbRFqcku9~syjxHmFb1xUz1Q(&Og~ld66S>l zEFD!(ttXxQdrG;OjCR= zPN~MUD0k+};&Z1KCd{^7H=2ku;AyCCncAXi*&md0I(h(A3wNTL!cRKA8&yw~pgL}X zQ62wQG5zlmI7Ywd!1qy|16xroQ>&cbjH=*Ss0tj7szT|GpM~n4?oY
yf{pZ>K-zRbP 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⁣1=mzNAuF!k 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 JYdKv{{~sMglR*Fg diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index 06d6a944..dfc09e7d 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -191,7 +191,7 @@ export const LinkBottomBar: React.FC = () => { ref={plusBtnRef} /> )} - + {/* */} )} From 044f3abb2f0165f6f663b2f39380f19b29e26679 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Sat, 7 Sep 2024 15:31:44 +0300 Subject: [PATCH 024/124] edit profile --- .../(pages)/profile/_components/wrapper.tsx | 88 ++++++++++++------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/web/app/(pages)/profile/_components/wrapper.tsx b/web/app/(pages)/profile/_components/wrapper.tsx index a236a42e..725583d0 100644 --- a/web/app/(pages)/profile/_components/wrapper.tsx +++ b/web/app/(pages)/profile/_components/wrapper.tsx @@ -1,12 +1,12 @@ "use client" import { useAccount } from "@/lib/providers/jazz-provider" +import { useState, useRef, useCallback } from "react" import { useParams } from "next/navigation" -import { useState, useRef } from "react" -import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" import { Input } from "@/components/ui/input" -import Link from "next/link" import { Button } from "@/components/ui/button" +import Link from "next/link" +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" interface ProfileStatsProps { number: number @@ -30,31 +30,46 @@ export const ProfileWrapper = () => { const [isEditing, setIsEditing] = useState(false) const [newName, setNewName] = useState(account.me?.profile?.name || "") - const [originalName, setOriginalName] = useState(account.me?.profile?.name || "") + const [error, setError] = useState("") const editProfileClicked = () => { - setIsEditing(!isEditing) + setIsEditing(true) + setError("") } const changeName = (e: React.ChangeEvent) => { setNewName(e.target.value) + setError("") } + const validateName = useCallback((name: string) => { + if (name.trim().length < 2) { + return "Name must be at least 2 characters long" + } + if (name.trim().length > 40) { + return "Name must not exceed 40 characters" + } + return "" + }, []) + const saveProfile = () => { - if (newName.trim() !== "") { - if (account.me && account.me.profile) { - account.me.profile.name = newName.trim() - } - setOriginalName(newName.trim()) - setIsEditing(false) + const validationError = validateName(newName) + if (validationError) { + setError(validationError) + return } + + if (account.me && account.me.profile) { + account.me.profile.name = newName.trim() + console.log("Updating name to:", newName.trim()) + } + setIsEditing(false) } - const clickOutside = () => { - if (isEditing) { - setNewName(originalName) - setIsEditing(false) - } + const cancelEditing = () => { + setNewName(account.me?.profile?.name || "") + setIsEditing(false) + setError("") } const editAvatar = (event: React.ChangeEvent) => { @@ -75,7 +90,7 @@ export const ProfileWrapper = () => {

The link you followed may be broken, or the page may have been removed. Go back to - homepage + homepage .

@@ -87,7 +102,7 @@ export const ProfileWrapper = () => { return (
-

Profile

+

Profile

{username}

@@ -102,22 +117,35 @@ export const ProfileWrapper = () => {
{isEditing ? ( - + <> + + {error &&

{error}

} + ) : (

{account.me?.profile?.name}

)}
- + {isEditing ? ( +
+ + +
+ ) : ( + + )}
From 8da0828e6a207ceee97cf45cd87a7648fa3251dc Mon Sep 17 00:00:00 2001 From: Kisuyo Date: Sat, 7 Sep 2024 14:01:32 +0200 Subject: [PATCH 025/124] chore: front page glow --- bun.lockb | Bin 398520 -> 404520 bytes .../custom/text-blur-transition.tsx | 26 +++++++++ .../routes/public/PublicHomeRoute.tsx | 54 ++++++++++++------ 3 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 web/components/custom/text-blur-transition.tsx diff --git a/bun.lockb b/bun.lockb index 08d679eea4c620fb131a205e263207eb71f6421d..b58462d52e237c109b8dc8c9386f2f48f22061ed 100755 GIT binary patch delta 89009 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 + {words.map((word, index) => { + return ( + + {word} + + ) + })} + + ) +} diff --git a/web/components/routes/public/PublicHomeRoute.tsx b/web/components/routes/public/PublicHomeRoute.tsx index ed694a66..e95c36b6 100644 --- a/web/components/routes/public/PublicHomeRoute.tsx +++ b/web/components/routes/public/PublicHomeRoute.tsx @@ -7,6 +7,8 @@ import { Autocomplete } from "./Autocomplete" import { useRouter } from "next/navigation" import { cn } from "@/lib/utils" import { raleway } from "@/app/fonts" +import { useAccount } from "@/lib/providers/jazz-provider" +import TextBlurTransition from "@/components/custom/text-blur-transition" let graph_data_promise = import("./graph-data.json").then(a => a.default) @@ -33,25 +35,41 @@ export function PublicHomeRoute() { return ( <> -
- +
+ handleTopicSelect(val)} + filter_query={filterQuery} + /> -
- - - I want to learn - - - -
+ +
+ + I want to learn + + handleTopicSelect(topic.name)} + onInputChange={handleInputChange} + /> +
) From 45c4a381735820fc19c729da089c63af37595c9a Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sat, 7 Sep 2024 23:51:09 +0700 Subject: [PATCH 026/124] fix(jazz-auth): wait until clerk is loaded then pass to jazz --- web/lib/providers/jazz-provider.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/web/lib/providers/jazz-provider.tsx b/web/lib/providers/jazz-provider.tsx index 3ca9ca9a..2fa33758 100644 --- a/web/lib/providers/jazz-provider.tsx +++ b/web/lib/providers/jazz-provider.tsx @@ -2,7 +2,7 @@ import { createJazzReactApp } from "jazz-react" import { LaAccount } from "@/lib/schema" -import { useClerk } from "@clerk/nextjs" +import { useAuth, useClerk } from "@clerk/nextjs" import { useJazzClerkAuth } from "jazz-react-auth-clerk" const Jazz = createJazzReactApp({ @@ -13,17 +13,19 @@ export const { useAccount, useAccountOrGuest, useCoState, useAcceptInvite } = Ja export function JazzAndAuth({ children }: { children: React.ReactNode }) { const clerk = useClerk() + const { isLoaded } = useAuth() + const [authMethod, state] = useJazzClerkAuth(clerk) - const [auth, state] = useJazzClerkAuth(clerk) + if (!isLoaded) return null return ( <> - {state.errors.map((error) => ( + {state.errors.map(error => (
{error}
))} - + {children} ) -} \ No newline at end of file +} From 3bff1c4b21284e3c9f7a4b1189af718e507a2f28 Mon Sep 17 00:00:00 2001 From: Kisuyo Date: Sat, 7 Sep 2024 19:12:25 +0200 Subject: [PATCH 027/124] fix: front page font --- web/components/custom/text-blur-transition.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/custom/text-blur-transition.tsx b/web/components/custom/text-blur-transition.tsx index a48a82cf..477bd706 100644 --- a/web/components/custom/text-blur-transition.tsx +++ b/web/components/custom/text-blur-transition.tsx @@ -5,7 +5,7 @@ export default function TextBlurTransition(props: { children: string; className? const words = props.children.split(" ") return ( - + {words.map((word, index) => { return ( Date: Sun, 8 Sep 2024 00:31:04 +0700 Subject: [PATCH 028/124] chore: revert --- .../routes/public/PublicHomeRoute.tsx | 45 +++++-------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/web/components/routes/public/PublicHomeRoute.tsx b/web/components/routes/public/PublicHomeRoute.tsx index e95c36b6..699e6d43 100644 --- a/web/components/routes/public/PublicHomeRoute.tsx +++ b/web/components/routes/public/PublicHomeRoute.tsx @@ -7,8 +7,6 @@ import { Autocomplete } from "./Autocomplete" import { useRouter } from "next/navigation" import { cn } from "@/lib/utils" import { raleway } from "@/app/fonts" -import { useAccount } from "@/lib/providers/jazz-provider" -import TextBlurTransition from "@/components/custom/text-blur-transition" let graph_data_promise = import("./graph-data.json").then(a => a.default) @@ -34,44 +32,23 @@ export function PublicHomeRoute() { } return ( - <> -
- handleTopicSelect(val)} - filter_query={filterQuery} - /> +
+ - -
- + + I want to learn - - handleTopicSelect(topic.name)} - onInputChange={handleInputChange} - /> + +
- +
) } From f1390275b012d1f59661159353a13772bc978fe0 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 00:43:48 +0700 Subject: [PATCH 029/124] chore: remove wait on clerk if it base --- web/lib/providers/jazz-provider.tsx | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/web/lib/providers/jazz-provider.tsx b/web/lib/providers/jazz-provider.tsx index 2fa33758..c7d27305 100644 --- a/web/lib/providers/jazz-provider.tsx +++ b/web/lib/providers/jazz-provider.tsx @@ -4,6 +4,7 @@ import { createJazzReactApp } from "jazz-react" import { LaAccount } from "@/lib/schema" import { useAuth, useClerk } from "@clerk/nextjs" import { useJazzClerkAuth } from "jazz-react-auth-clerk" +import { usePathname } from "next/navigation" const Jazz = createJazzReactApp({ AccountSchema: LaAccount @@ -11,7 +12,18 @@ const Jazz = createJazzReactApp({ export const { useAccount, useAccountOrGuest, useCoState, useAcceptInvite } = Jazz -export function JazzAndAuth({ children }: { children: React.ReactNode }) { +const JAZZ_PEER_URL = "wss://mesh.jazz.tools/?key=example@gmail.com" + +interface ChildrenProps { + children: React.ReactNode +} + +export function JazzAndAuth({ children }: ChildrenProps) { + const pathname = usePathname() + return pathname === "/" ? {children} : {children} +} + +export function JazzAuth({ children }: ChildrenProps) { const clerk = useClerk() const { isLoaded } = useAuth() const [authMethod, state] = useJazzClerkAuth(clerk) @@ -20,12 +32,20 @@ export function JazzAndAuth({ children }: { children: React.ReactNode }) { return ( <> - {state.errors.map(error => ( + {state.errors.map((error: string) => (
{error}
))} - + {children} ) } + +export function JazzGuest({ children }: ChildrenProps) { + return ( + + {children} + + ) +} From 83af8cc81982a4654fc635ac01f9c2bdd99b1e48 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 01:11:17 +0700 Subject: [PATCH 030/124] chore: update fancy switch version --- web/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/package.json b/web/package.json index 50c1db55..f5775f62 100644 --- a/web/package.json +++ b/web/package.json @@ -13,10 +13,10 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@hookform/resolvers": "^3.9.0", - "@nothing-but/force-graph": "^0.9.3", + "@nothing-but/force-graph": "^0.9.4", "@nothing-but/utils": "^0.16.0", "@omit/react-confirm-dialog": "^1.1.5", - "@omit/react-fancy-switch": "^0.1.1", + "@omit/react-fancy-switch": "^0.1.2", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-context-menu": "^2.2.1", From e78d4fce7a07b03cee2446dd055e8bbf593a0e4b Mon Sep 17 00:00:00 2001 From: Aslam Date: Sun, 8 Sep 2024 03:55:57 +0700 Subject: [PATCH 031/124] chore: refactor clerk middleware and add readme for route protection (#147) --- web/middleware.ts | 41 +++++++++++++++++++++++++---------------- web/readme.md | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/web/middleware.ts b/web/middleware.ts index 92bdc125..90c011b9 100644 --- a/web/middleware.ts +++ b/web/middleware.ts @@ -1,23 +1,32 @@ -import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' +import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server" -const isPublicRoute = createRouteMatcher([ - '/sign-in(.*)', - '/sign-up(.*)', - '/', - '/:topicName(.*)' -]) +const ROUTE_PATTERNS = { + public: ["/sign-in(.*)", "/sign-up(.*)", "/", "/:topicName(.*)"], + protected: [ + "/edit-profile(.*)", + "/links(.*)", + "/pages(.*)", + "/profile(.*)", + "/search(.*)", + "/settings(.*)", + "/tauri(.*)" + ] +} + +const isPublicRoute = createRouteMatcher(ROUTE_PATTERNS.public) +const isProtectedRoute = createRouteMatcher(ROUTE_PATTERNS.protected) export default clerkMiddleware((auth, request) => { - if (!isPublicRoute(request)) { - auth().protect() - } + if (isProtectedRoute(request) || !isPublicRoute(request)) { + auth().protect() + } }) export const config = { - matcher: [ - // Skip Next.js internals and all static files, unless found in search params - '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', - // Always run for API routes - '/(api|trpc)(.*)' - ] + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", + // Always run for API routes + "/(api|trpc)(.*)" + ] } diff --git a/web/readme.md b/web/readme.md index 8f4fcc61..44e0516b 100644 --- a/web/readme.md +++ b/web/readme.md @@ -1,5 +1,43 @@ -### LA Editor +## LA Editor This folder should not contain any changes. It is a copy of the original LA Editor component from the [LA Editor](https://github.com/learn-anything/la-editor) repository. > This component is a temporary solution until the original repository publish the component to npm. + +## Clerk Middleware for Route Protection + +This middleware manages authentication and protects routes in our Next.js application using Clerk. + +## Key Concepts + +- **Public Routes**: Accessible to all users +- **Protected Routes**: Require authentication + +## Important: Route Registration + +The middleware uses a catch-all public route `"/:topicName(.*)"`. This means: + +1. New routes are public by default +2. Protected routes MUST be explicitly registered + +## How to Register Routes + +Update the `ROUTE_PATTERNS` object in the middleware file: + +```typescript +const ROUTE_PATTERNS = { + public: ["/sign-in(.*)", "/sign-up(.*)", "/", "/:topicName(.*)"], + protected: [ + "/edit-profile(.*)", + "/links(.*)", + // Add new protected routes here + "/new-protected-feature(.*)" + ] +} +``` + +## Best Practices + +1. Always add new protected routes to `ROUTE_PATTERNS.protected` +2. Regularly review routes to ensure proper protection +3. Be cautious when modifying the `"/:topicName(.*)"` catch-all route From aea243193466f02bcb3cbcdec6bdc5a703d51a53 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 03:57:02 +0700 Subject: [PATCH 032/124] chore: remove listed errors from jazz provider --- web/lib/providers/jazz-provider.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/web/lib/providers/jazz-provider.tsx b/web/lib/providers/jazz-provider.tsx index c7d27305..467d2e8e 100644 --- a/web/lib/providers/jazz-provider.tsx +++ b/web/lib/providers/jazz-provider.tsx @@ -26,19 +26,14 @@ export function JazzAndAuth({ children }: ChildrenProps) { export function JazzAuth({ children }: ChildrenProps) { const clerk = useClerk() const { isLoaded } = useAuth() - const [authMethod, state] = useJazzClerkAuth(clerk) + const [authMethod] = useJazzClerkAuth(clerk) if (!isLoaded) return null return ( - <> - {state.errors.map((error: string) => ( -
{error}
- ))} - - {children} - - + + {children} + ) } From 9e4544894cd20414896d7ea0a4e88eb70640b9a7 Mon Sep 17 00:00:00 2001 From: Aslam Date: Sun, 8 Sep 2024 04:00:48 +0700 Subject: [PATCH 033/124] fix: remove old logic for defining the return layout (#148) --- web/app/(pages)/layout.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index ef9144e7..4b11bfbc 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -3,16 +3,9 @@ import { Sidebar } from "@/components/custom/sidebar/sidebar" import { CommandPalette } from "@/components/custom/command-palette/command-palette" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" -import { usePathname } from "next/navigation" -import PublicHomeRoute from "@/components/routes/public/PublicHomeRoute" export default function PageLayout({ children }: { children: React.ReactNode }) { const { me } = useAccountOrGuest() - const pathname = usePathname() - - if (me._type === "Anonymous" && pathname === "/") { - return - } return (
From cb9e8f46da290e18683fc980381d6b6136eea005 Mon Sep 17 00:00:00 2001 From: Aslam Date: Sun, 8 Sep 2024 04:30:11 +0700 Subject: [PATCH 034/124] fix: topic for guest auth (#149) * wip * chore: redirect instead of toast * refactor: remove useRouter --- .../custom/sidebar/partial/link-section.tsx | 1 - .../sidebar/partial/profile-section.tsx | 132 +++----- web/components/custom/sidebar/sidebar.tsx | 9 +- .../routes/topics/detail/Header.tsx | 12 +- .../topics/detail/partials/link-item.tsx | 317 +++++++++--------- 5 files changed, 215 insertions(+), 256 deletions(-) diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index e4639ccc..46a6e359 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -6,7 +6,6 @@ import { cn } from "@/lib/utils" import { PersonalLinkLists } from "@/lib/schema/personal-link" import { useQueryState, parseAsStringLiteral } from "nuqs" import { LEARNING_STATES } from "@/lib/constants" -import { useRouter } from "next/navigation" export const LinkSection: React.FC<{ pathname: string }> = ({ pathname }) => { const { me } = useAccount({ diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 68f6bc2c..2f0809eb 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -1,7 +1,6 @@ -import { useAccount } from "@/lib/providers/jazz-provider" -import { LaIcon } from "../../la-icon" +import { LaIcon } from "@/components/custom/la-icon" import { useState } from "react" -import { useAuth } from "@clerk/nextjs" +import { SignInButton, useAuth, useUser } from "@clerk/nextjs" import { DropdownMenu, DropdownMenuContent, @@ -11,97 +10,68 @@ import { } from "@/components/ui/dropdown-menu" import { Avatar, AvatarImage } from "@/components/ui/avatar" import Link from "next/link" +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { usePathname } from "next/navigation" -const MenuItem = ({ - icon, - text, - href, - onClick, - onClose -}: { - icon: string - text: string - href?: string - onClick?: () => void - onClose: () => void -}) => { - const handleClick = () => { - onClose() - if (onClick) { - onClick() - } +export const ProfileSection: React.FC = () => { + const { user, isSignedIn } = useUser() + const { signOut } = useAuth() + const [menuOpen, setMenuOpen] = useState(false) + const pathname = usePathname() + + if (!isSignedIn) { + return ( +
+ + + +
+ ) } return ( -
- - {href ? ( - - {text} - - ) : ( - - {text} - - )} -
- ) -} -export const ProfileSection: React.FC = () => { - const { me } = useAccount({ - profile: true - }) - const { signOut, isSignedIn } = useAuth() - const [menuOpen, setMenuOpen] = useState(false) - - const closeMenu = () => setMenuOpen(false) - - return ( -
+
- + - {isSignedIn ? ( - <> - - - - - - - - - - - - - ) : ( - - - - )} + + +
+ + My profile +
+ +
+ + signOut()}> +
+ + Log out +
+
@@ -109,7 +79,3 @@ export const ProfileSection: React.FC = () => {
) } - -/* - - */ diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index 54c4d1c7..3679128a 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -15,7 +15,6 @@ import { PageSection } from "./partial/page-section" import { TopicSection } from "./partial/topic-section" import { ProfileSection } from "./partial/profile-section" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" -import { SignInButton } from "@clerk/nextjs" interface SidebarContextType { isCollapsed: boolean @@ -126,15 +125,9 @@ const SidebarContent: React.FC = React.memo(() => { {me._type === "Account" && } {me._type === "Account" && }
- - {me._type === "Account" ? ( - ) : ( -
- Fake profile section -
- )} + ) }) diff --git a/web/components/routes/topics/detail/Header.tsx b/web/components/routes/topics/detail/Header.tsx index 244dbc06..461f3176 100644 --- a/web/components/routes/topics/detail/Header.tsx +++ b/web/components/routes/topics/detail/Header.tsx @@ -6,13 +6,16 @@ import { ListOfTopics, Topic } from "@/lib/schema" import { LearningStateSelector } from "@/components/custom/learning-state-selector" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" import { LearningStateValue } from "@/lib/constants" -import { toast } from "sonner" +import { useClerk } from "@clerk/nextjs" +import { usePathname } from "next/navigation" interface TopicDetailHeaderProps { topic: Topic } export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic }: TopicDetailHeaderProps) { + const clerk = useClerk() + const pathname = usePathname() const { me } = useAccountOrGuest({ root: { topicsWantToLearn: [], @@ -32,7 +35,6 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic } if (wantToLearnIndex !== -1) { p = { index: wantToLearnIndex, - // TODO: fix this type error by doing better conditionals on both index and p topic: me && me._type !== "Anonymous" ? me.root.topicsWantToLearn[wantToLearnIndex] : undefined, learningState: "wantToLearn" } @@ -60,9 +62,9 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic } const handleAddToProfile = (learningState: LearningStateValue) => { if (me?._type === "Anonymous") { - // TODO: handle better - toast.error("You need to sign in to add links to your personal list.") - return + return clerk.redirectToSignIn({ + redirectUrl: pathname + }) } const topicLists: Record = { diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index 4758f01e..f6a80579 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useMemo, useState } from "react" import Link from "next/link" -import { useRouter } from "next/navigation" +import { usePathname, useRouter } from "next/navigation" import { useAtom } from "jotai" import { toast } from "sonner" @@ -10,10 +10,11 @@ import { Button } from "@/components/ui/button" import { LearningStateSelectorContent } from "@/components/custom/learning-state-selector" import { cn, ensureUrlProtocol, generateUniqueSlug } from "@/lib/utils" -import { LaAccount, Link as LinkSchema, PersonalLink, PersonalLinkLists, Topic, UserRoot } from "@/lib/schema" +import { Link as LinkSchema, PersonalLink, Topic } from "@/lib/schema" import { openPopoverForIdAtom } from "../TopicDetailRoute" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" +import { useClerk } from "@clerk/nextjs" interface LinkItemProps { topic: Topic @@ -24,185 +25,183 @@ interface LinkItemProps { } export const LinkItem = React.memo( - React.forwardRef( - ({ topic, link, isActive, index, setActiveIndex }, ref) => { - const router = useRouter() - const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) - const [isPopoverOpen, setIsPopoverOpen] = useState(false) + React.forwardRef(({ topic, link, isActive, index, setActiveIndex }, ref) => { + const clerk = useClerk() + const pathname = usePathname() + const router = useRouter() + const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) + const [isPopoverOpen, setIsPopoverOpen] = useState(false) - const { me } = useAccountOrGuest({ root: { personalLinks: [] } }); + const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) - const personalLinks = useMemo(() => { - if (!me || me._type === "Anonymous") return undefined; - return me?.root?.personalLinks || [] - }, [me]) + const personalLinks = useMemo(() => { + if (!me || me._type === "Anonymous") return undefined + return me?.root?.personalLinks || [] + }, [me]) - const personalLink = useMemo(() => { - return personalLinks?.find(pl => pl?.link?.id === link.id) - }, [personalLinks, link.id]) + const personalLink = useMemo(() => { + return personalLinks?.find(pl => pl?.link?.id === link.id) + }, [personalLinks, link.id]) - const selectedLearningState = useMemo(() => { - return LEARNING_STATES.find(ls => ls.value === personalLink?.learningState) - }, [personalLink?.learningState]) + const selectedLearningState = useMemo(() => { + return LEARNING_STATES.find(ls => ls.value === personalLink?.learningState) + }, [personalLink?.learningState]) - const handleClick = useCallback( - (e: React.MouseEvent) => { - e.preventDefault() - setActiveIndex(index) - }, - [index, setActiveIndex] - ) + const handleClick = useCallback( + (e: React.MouseEvent) => { + e.preventDefault() + setActiveIndex(index) + }, + [index, setActiveIndex] + ) - const handleSelectLearningState = useCallback( - (learningState: LearningStateValue) => { - if (!personalLinks || !me || me?._type === "Anonymous") { - if (me?._type === "Anonymous") { - // TODO: handle better - toast.error("You need to sign in to add links to your personal list.") - } - return - }; + const handleSelectLearningState = useCallback( + (learningState: LearningStateValue) => { + if (!personalLinks || !me || me?._type === "Anonymous") { + return clerk.redirectToSignIn({ + redirectUrl: pathname + }) + } - const defaultToast = { - duration: 5000, - position: "bottom-right" as const, - closeButton: true, - action: { - label: "Go to list", - onClick: () => router.push("/") - } + const defaultToast = { + duration: 5000, + position: "bottom-right" as const, + closeButton: true, + action: { + label: "Go to list", + onClick: () => router.push("/") } + } - if (personalLink) { - if (personalLink.learningState === learningState) { - personalLink.learningState = undefined - toast.error("Link learning state removed", defaultToast) - } else { - personalLink.learningState = learningState - toast.success("Link learning state updated", defaultToast) - } + if (personalLink) { + if (personalLink.learningState === learningState) { + personalLink.learningState = undefined + toast.error("Link learning state removed", defaultToast) } else { - const slug = generateUniqueSlug(personalLinks.toJSON(), link.title) - const newPersonalLink = PersonalLink.create( - { - url: link.url, - title: link.title, - slug, - link, - learningState, - sequence: personalLinks.length + 1, - completed: false, - topic, - createdAt: new Date(), - updatedAt: new Date() - }, - { owner: me } - ) - - personalLinks.push(newPersonalLink) - - toast.success("Link added.", { - ...defaultToast, - description: `${link.title} has been added to your personal link.` - }) + personalLink.learningState = learningState + toast.success("Link learning state updated", defaultToast) } + } else { + const slug = generateUniqueSlug(personalLinks.toJSON(), link.title) + const newPersonalLink = PersonalLink.create( + { + url: link.url, + title: link.title, + slug, + link, + learningState, + sequence: personalLinks.length + 1, + completed: false, + topic, + createdAt: new Date(), + updatedAt: new Date() + }, + { owner: me } + ) - setOpenPopoverForId(null) - setIsPopoverOpen(false) - }, - [personalLink, personalLinks, me, link, router, setOpenPopoverForId, topic] - ) + personalLinks.push(newPersonalLink) - const handlePopoverOpenChange = useCallback( - (open: boolean) => { - setIsPopoverOpen(open) - setOpenPopoverForId(open ? link.id : null) - }, - [link.id, setOpenPopoverForId] - ) + toast.success("Link added.", { + ...defaultToast, + description: `${link.title} has been added to your personal link.` + }) + } - return ( -
  • -
    -
    - - - - - e.preventDefault()} + setOpenPopoverForId(null) + setIsPopoverOpen(false) + }, + [personalLink, personalLinks, me, link, router, setOpenPopoverForId, topic] + ) + + const handlePopoverOpenChange = useCallback( + (open: boolean) => { + setIsPopoverOpen(open) + setOpenPopoverForId(open ? link.id : null) + }, + [link.id, setOpenPopoverForId] + ) + + return ( +
  • +
    +
    + + + + + e.preventDefault()} + > + handleSelectLearningState(value as LearningStateValue)} + /> + + + +
    +
    +

    + {link.title} +

    + +
    +
    -
    -
  • - ) - } - ) +
    +
    + + ) + }) ) LinkItem.displayName = "LinkItem" From a87c27d91fcd3c7e1d4bd7c3f2439d1f456857df Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 04:37:28 +0700 Subject: [PATCH 035/124] fix: push to new link route --- web/components/routes/topics/detail/partials/link-item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index f6a80579..24baeb69 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -69,7 +69,7 @@ export const LinkItem = React.memo( closeButton: true, action: { label: "Go to list", - onClick: () => router.push("/") + onClick: () => router.push("/links") } } From f005101d91b0dbdef55b97b5ba5741cb394bd110 Mon Sep 17 00:00:00 2001 From: Aslam Date: Sun, 8 Sep 2024 05:43:44 +0700 Subject: [PATCH 036/124] feat: navigation for command palette (#150) * feat: add dotenv to dev dep * chore: jest use env * feat * feat: command palette navigation --- .../custom/command-palette/command-data.ts | 108 ++++++++++++++++-- .../custom/command-palette/command-items.tsx | 7 +- .../command-palette/command-palette.tsx | 5 +- web/jest.config.ts | 6 +- web/lib/utils/htmlLikeElementUtil.test.tsx | 45 ++++++++ web/lib/utils/htmlLikeElementUtil.ts | 21 ++++ web/lib/utils/index.ts | 1 + web/package.json | 5 +- 8 files changed, 185 insertions(+), 13 deletions(-) create mode 100644 web/lib/utils/htmlLikeElementUtil.test.tsx create mode 100644 web/lib/utils/htmlLikeElementUtil.ts diff --git a/web/components/custom/command-palette/command-data.ts b/web/components/custom/command-palette/command-data.ts index e6d7a3d1..80217afd 100644 --- a/web/components/custom/command-palette/command-data.ts +++ b/web/components/custom/command-palette/command-data.ts @@ -1,12 +1,14 @@ import { icons } from "lucide-react" import { useCommandActions } from "./hooks/use-command-actions" import { LaAccount } from "@/lib/schema" +import { HTMLLikeElement } from "@/lib/utils" export type CommandAction = string | (() => void) export type CommandItemType = { icon?: keyof typeof icons - label: string + value: string + label: HTMLLikeElement | string action: CommandAction payload?: any shortcut?: string @@ -25,9 +27,16 @@ export const createCommandGroups = ( { heading: "General", items: [ - { icon: "SunMoon", label: "Change Theme...", action: "CHANGE_PAGE", payload: "changeTheme" }, + { + icon: "SunMoon", + value: "Change Theme...", + label: "Change Theme...", + action: "CHANGE_PAGE", + payload: "changeTheme" + }, { icon: "Copy", + value: "Copy Current URL", label: "Copy Current URL", action: actions.copyCurrentURL } @@ -36,20 +45,88 @@ export const createCommandGroups = ( { heading: "Personal Links", items: [ - { icon: "TextSearch", label: "Search Links...", action: "CHANGE_PAGE", payload: "searchLinks" }, - { icon: "Plus", label: "Create New Link...", action: () => actions.navigateTo("/") } + { + icon: "TextSearch", + value: "Search Links...", + label: "Search Links...", + action: "CHANGE_PAGE", + payload: "searchLinks" + }, + { + icon: "Plus", + value: "Create New Link...", + label: "Create New Link...", + action: () => actions.navigateTo("/") + } ] }, { heading: "Personal Pages", items: [ - { icon: "FileSearch", label: "Search Pages...", action: "CHANGE_PAGE", payload: "searchPages" }, + { + icon: "FileSearch", + value: "Search Pages...", + label: "Search Pages...", + action: "CHANGE_PAGE", + payload: "searchPages" + }, { icon: "Plus", + value: "Create New Page...", label: "Create New Page...", action: () => actions.createNewPage(me) } ] + }, + { + heading: "Navigation", + items: [ + { + icon: "ArrowRight", + value: "Go to Links", + label: { + tag: "span", + children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["links"] }] + }, + action: () => actions.navigateTo("/links") + }, + { + icon: "ArrowRight", + value: "Go to Pages", + label: { + tag: "span", + children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["pages"] }] + }, + action: () => actions.navigateTo("/pages") + }, + { + icon: "ArrowRight", + value: "Go to Search", + label: { + tag: "span", + children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["search"] }] + }, + action: () => actions.navigateTo("/search") + }, + { + icon: "ArrowRight", + value: "Go to Profile", + label: { + tag: "span", + children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["profile"] }] + }, + action: () => actions.navigateTo("/profile") + }, + { + icon: "ArrowRight", + value: "Go to Settings", + label: { + tag: "span", + children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["settings"] }] + }, + action: () => actions.navigateTo("/settings") + } + ] } ], searchLinks: [], @@ -58,9 +135,24 @@ export const createCommandGroups = ( changeTheme: [ { items: [ - { icon: "Moon", label: "Change Theme to Dark", action: () => actions.changeTheme("dark") }, - { icon: "Sun", label: "Change Theme to Light", action: () => actions.changeTheme("light") }, - { icon: "Monitor", label: "Change Theme to System", action: () => actions.changeTheme("system") } + { + icon: "Moon", + value: "Change Theme to Dark", + label: "Change Theme to Dark", + action: () => actions.changeTheme("dark") + }, + { + icon: "Sun", + value: "Change Theme to Light", + label: "Change Theme to Light", + action: () => actions.changeTheme("light") + }, + { + icon: "Monitor", + value: "changeThemeToSystem", + label: "Change Theme to System", + action: () => actions.changeTheme("system") + } ] } ] diff --git a/web/components/custom/command-palette/command-items.tsx b/web/components/custom/command-palette/command-items.tsx index 0404a01e..64b09602 100644 --- a/web/components/custom/command-palette/command-items.tsx +++ b/web/components/custom/command-palette/command-items.tsx @@ -2,16 +2,21 @@ import { Command } from "cmdk" import { CommandSeparator, CommandShortcut } from "@/components/ui/command" import { LaIcon } from "../la-icon" import { CommandItemType, CommandAction } from "./command-data" +import { HTMLLikeElement, renderHTMLLikeElement } from "@/lib/utils" export interface CommandItemProps extends Omit { action: CommandAction handleAction: (action: CommandAction, payload?: any) => void } +const HTMLLikeRenderer: React.FC<{ content: HTMLLikeElement | string }> = ({ content }) => { + return <>{renderHTMLLikeElement(content)} +} + export const CommandItem: React.FC = ({ icon, label, action, payload, shortcut, handleAction }) => ( handleAction(action, payload)}> {icon && } - {label} + {shortcut && {shortcut}} ) diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index a6b5386a..4986d557 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -14,7 +14,7 @@ import { useCommandActions } from "./hooks/use-command-actions" let graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) const filterItems = (items: CommandItemType[], searchRegex: RegExp) => - items.filter(item => searchRegex.test(item.label)).slice(0, 6) + items.filter(item => searchRegex.test(item.value)).slice(0, 6) export function CommandPalette() { const { me } = useAccount({ root: { personalLinks: [], personalPages: [] } }) @@ -81,6 +81,7 @@ export function CommandPalette() { heading: "Topics", items: raw_graph_data.map(topic => ({ icon: "Circle" as const, + value: topic?.prettyName || "", label: topic?.prettyName || "", action: () => actions.navigateTo(`/${topic?.name}`) })) @@ -94,6 +95,7 @@ export function CommandPalette() { items: me?.root.personalLinks?.map(link => ({ icon: "Link" as const, + value: link?.title || "Untitled", label: link?.title || "Untitled", action: () => actions.openLinkInNewTab(link?.url || "#") })) || [] @@ -107,6 +109,7 @@ export function CommandPalette() { items: me?.root.personalPages?.map(page => ({ icon: "FileText" as const, + value: page?.title || "Untitled", label: page?.title || "Untitled", action: () => actions.navigateTo(`/pages/${page?.id}`) })) || [] diff --git a/web/jest.config.ts b/web/jest.config.ts index 694d51c8..2e971f9f 100644 --- a/web/jest.config.ts +++ b/web/jest.config.ts @@ -1,11 +1,14 @@ import type { Config } from "jest" import nextJest from "next/jest.js" +import dotenv from "dotenv" const createJestConfig = nextJest({ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment dir: "./" }) +dotenv.config({ path: ".env.local" }) + // Add any custom config to be passed to Jest const config: Config = { coverageProvider: "v8", @@ -13,7 +16,8 @@ const config: Config = { // Add more setup options before each test is run // setupFilesAfterEnv: ['/jest.setup.ts'], // Automatically clear mock calls, instances, contexts and results before every test - clearMocks: true + clearMocks: true, + moduleDirectories: ["node_modules", __dirname] } // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async diff --git a/web/lib/utils/htmlLikeElementUtil.test.tsx b/web/lib/utils/htmlLikeElementUtil.test.tsx new file mode 100644 index 00000000..3da4d3ff --- /dev/null +++ b/web/lib/utils/htmlLikeElementUtil.test.tsx @@ -0,0 +1,45 @@ +import React from "react" +import { render } from "@testing-library/react" +import { HTMLLikeElement, renderHTMLLikeElement } from "./htmlLikeElementUtil" + +const HTMLLikeRenderer: React.FC<{ content: HTMLLikeElement | string }> = ({ content }) => { + return <>{renderHTMLLikeElement(content)} +} + +describe("HTML-like Element Utility", () => { + test("HTMLLikeRenderer renders simple string content", () => { + const { getByText } = render() + expect(getByText("Hello, World!")).toBeTruthy() + }) + + test("HTMLLikeRenderer renders HTML-like structure", () => { + const content: HTMLLikeElement = { + tag: "div", + attributes: { className: "test-class" }, + children: ["Hello, ", { tag: "strong", children: ["World"] }, "!"] + } + const { container, getByText } = render() + expect(container.firstChild).toHaveProperty("className", "test-class") + const strongElement = getByText("World") + expect(strongElement.tagName.toLowerCase()).toBe("strong") + }) + + test("HTMLLikeRenderer handles multiple attributes", () => { + const content: HTMLLikeElement = { + tag: "div", + attributes: { + className: "test-class", + id: "test-id", + "data-testid": "custom-element", + style: { color: "red", fontSize: "16px" } + }, + children: ["Test Content"] + } + const { getByTestId } = render() + const element = getByTestId("custom-element") + expect(element.className).toBe("test-class") + expect(element.id).toBe("test-id") + expect(element.style.color).toBe("red") + expect(element.style.fontSize).toBe("16px") + }) +}) diff --git a/web/lib/utils/htmlLikeElementUtil.ts b/web/lib/utils/htmlLikeElementUtil.ts new file mode 100644 index 00000000..6246c5c0 --- /dev/null +++ b/web/lib/utils/htmlLikeElementUtil.ts @@ -0,0 +1,21 @@ +import React from "react" + +export type HTMLAttributes = React.HTMLAttributes & { + [key: string]: any +} + +export type HTMLLikeElement = { + tag: keyof JSX.IntrinsicElements + attributes?: HTMLAttributes + children?: (HTMLLikeElement | string)[] +} + +export const renderHTMLLikeElement = (element: HTMLLikeElement | string): React.ReactNode => { + if (typeof element === "string") { + return element + } + + const { tag, attributes = {}, children = [] } = element + + return React.createElement(tag, attributes, ...children.map(child => renderHTMLLikeElement(child))) +} diff --git a/web/lib/utils/index.ts b/web/lib/utils/index.ts index 92a6b8e9..7e4dfbe3 100644 --- a/web/lib/utils/index.ts +++ b/web/lib/utils/index.ts @@ -37,3 +37,4 @@ export function shuffleArray(array: T[]): T[] { export * from "./urls" export * from "./slug" export * from "./keyboard" +export * from "./htmlLikeElementUtil" diff --git a/web/package.json b/web/package.json index f5775f62..de3bc5d0 100644 --- a/web/package.json +++ b/web/package.json @@ -70,8 +70,8 @@ "date-fns": "^3.6.0", "framer-motion": "^11.5.4", "geist": "^1.3.1", - "jazz-react": "0.7.35-guest-auth.5", "jazz-browser-auth-clerk": "0.7.35-guest-auth.5", + "jazz-react": "0.7.35-guest-auth.5", "jazz-react-auth-clerk": "0.7.35-guest-auth.5", "jazz-tools": "0.7.35-guest-auth.5", "jotai": "^2.9.3", @@ -91,7 +91,6 @@ "streaming-markdown": "^0.0.14", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", - "ts-node": "^10.9.2", "zod": "^3.23.8", "zsa": "^0.6.0" }, @@ -102,6 +101,7 @@ "@types/node": "^22.5.4", "@types/react": "^18.3.5", "@types/react-dom": "^18.3.0", + "dotenv": "^16.4.5", "eslint": "^8.57.0", "eslint-config-next": "14.2.5", "jest": "^29.7.0", @@ -110,6 +110,7 @@ "prettier-plugin-tailwindcss": "^0.6.6", "tailwindcss": "^3.4.10", "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", "typescript": "^5.5.4" } } From 8daa17334a4039d5f9124baeab18b71427ea6ff5 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 06:04:37 +0700 Subject: [PATCH 037/124] chore: make delete icon color red for bottom bar --- web/components/routes/link/bottom-bar.tsx | 51 +++++++++++++---------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index dfc09e7d..151815b9 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -15,34 +15,36 @@ import { ID } from "jazz-tools" import { globalLinkFormExceptionRefsAtom } from "./partials/form/link-form" import { toast } from "sonner" -interface ToolbarButtonProps { +interface ToolbarButtonProps extends React.ComponentPropsWithoutRef { icon: keyof typeof icons onClick?: (e: React.MouseEvent) => void tooltip?: string } -const ToolbarButton = React.forwardRef(({ icon, onClick, tooltip }, ref) => { - const button = ( - - ) - - if (tooltip) { - return ( - - - {button} - -

    {tooltip}

    -
    -
    -
    +const ToolbarButton = React.forwardRef( + ({ icon, onClick, tooltip, ...props }, ref) => { + const button = ( + ) - } - return button -}) + if (tooltip) { + return ( + + + {button} + +

    {tooltip}

    +
    +
    +
    + ) + } + + return button + } +) ToolbarButton.displayName = "ToolbarButton" @@ -168,7 +170,12 @@ export const LinkBottomBar: React.FC = () => { transition={{ duration: 0.1 }} > setEditId(null)} /> - + )} From b9b61ef423a339973e6454f93dd278b09be3bfb0 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 06:09:21 +0700 Subject: [PATCH 038/124] refactor: use muted insteadof secondary for fancyS --- web/components/routes/link/header.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/components/routes/link/header.tsx b/web/components/routes/link/header.tsx index 527894db..6a7a2ed2 100644 --- a/web/components/routes/link/header.tsx +++ b/web/components/routes/link/header.tsx @@ -123,8 +123,8 @@ const LearningTab = React.memo(() => { handleTabChange(value as string) }} options={ALL_STATES} - className="bg-secondary flex rounded-lg" - highlighterClassName="bg-secondary-foreground/10 rounded-lg" + className="bg-muted flex rounded-lg" + highlighterClassName="bg-muted-foreground/10 rounded-md" radioClassName={cn( "relative mx-2 flex h-8 cursor-pointer items-center justify-center rounded-full px-1 text-sm text-secondary-foreground/60 data-[checked]:text-secondary-foreground font-medium transition-colors focus:outline-none" )} From f612662a447972f4fcbe87a021dc7bccf80d5757 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 06:10:59 +0700 Subject: [PATCH 039/124] chore: update bun type --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c66e8cf..55478bce 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "react-icons": "^5.3.0" }, "devDependencies": { - "bun-types": "^1.1.26" + "bun-types": "^1.1.27" }, "prettier": { "plugins": [ From 57289326db5aecc836b934b31885af92abdf0e22 Mon Sep 17 00:00:00 2001 From: Aslam Date: Sun, 8 Sep 2024 06:29:55 +0700 Subject: [PATCH 040/124] chore: project config and docs (#152) * refactor(eslint): improve config readability and structure * chore(tsconfig): group options and add explanatory comments * docs(readme): restructure for clearer setup and contribution guide --- eslint.config.js | 24 +++++++++++----------- readme.md | 44 +++++++++++++++++++++++++++------------- tsconfig.json | 53 +++++++++++++++++++++++++++++++----------------- 3 files changed, 76 insertions(+), 45 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 22c9826a..c74755ce 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -3,22 +3,22 @@ import js from "@eslint/js" const compat = new FlatCompat() +const typescriptConfig = compat.extends( + "eslint:recommended", + "plugin:@typescript-eslint/recommended-type-checked", + "plugin:@typescript-eslint/stylistic-type-checked", + "prettier" +) + +const javascriptConfig = js.configs.recommended + export default [ { - ...compat - .extends( - "eslint:recommended", - "plugin:@typescript-eslint/recommended-type-checked", - "plugin:@typescript-eslint/stylistic-type-checked", - "prettier" - ) - .map(c => ({ - ...c, - files: ["**/*.{ts,tsx,mts}"] - })) + files: ["**/*.{ts,tsx,mts}"], + ...typescriptConfig }, { files: ["**/*.{js,jsx,cjs,mjs}"], - ...js.configs.recommended + ...javascriptConfig } ] diff --git a/readme.md b/readme.md index 04ee1085..9483e4d5 100644 --- a/readme.md +++ b/readme.md @@ -1,33 +1,49 @@ -# [Learn-Anything.xyz](https://learn-anything.xyz) +# Learn-Anything.xyz + +[![X](https://img.shields.io/badge/learnanything-100000?logo=X&color=black)](https://x.com/learnanything_) + +[Learn-Anything.xyz](https://learn-anything.xyz) is an open-source learning platform. + +Visit the website at: https://learn-anything.xyz ## Setup -Using [Bun](https://bun.sh). +[Bun](https://bun.sh) is required to run this project. -``` +Install dependencies: + +```bash bun i ``` -[Jazz](https://jazz.tools/) is used for all global/local state management. +## Run Website -## Run website +To start the website locally: -``` +```bash bun web ``` -## Contribute +## Development -Currently things are unstable but will improve. +- [Jazz](https://jazz.tools/) is used for all global/local state management. +- The project is currently in an unstable state but actively improving. -If you want to help contribute to code, ask for help on [Discord](https://discord.gg/bxtD8x6aNF)'s `#dev` channel. You will be onboarded and unblocked fast. +## Contributing -Can always submit draft PRs with good ideas/fixes. We will help along the way to make it merge ready. +We welcome contributions! Here's how you can get involved: -## Chat +1. Join our [Discord](https://discord.gg/bxtD8x6aNF) and ask for help in the `#dev` channel. +2. Submit draft PRs with your ideas or fixes. We'll guide you through the process. -Community chat in [Discord server](https://discord.gg/bxtD8x6aNF). +## Community -Internal dev chat in Telegram (can email `join@learn-anything.xyz` to join core team). We will reach out with offer to join if you contribute to repo in form of PRs too. +- **Public Chat**: Join our [Discord server](https://discord.gg/bxtD8x6aNF). +- **Internal Dev Chat**: Available on Telegram. + - Email `join@learn-anything.xyz` to join the core team. + - Active contributors may receive invitations to join. -[![X](https://img.shields.io/badge/learnanything-100000?logo=X&color=black)](https://x.com/learnanything_) +## Notes + +- The project is in active development. Expect frequent changes. +- We prioritize quick onboarding and unblocking of contributors. diff --git a/tsconfig.json b/tsconfig.json index 1bb3943b..ba35efc4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,29 +1,44 @@ { "compilerOptions": { + // Project Structure "rootDirs": [".", ".next-types"], - "target": "esnext", - "module": "esnext", - "moduleResolution": "bundler", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], "baseUrl": ".", "paths": { "@/*": ["./*"] }, - "types": ["bun-types"] + + // Module Settings + "target": "esnext", + "module": "esnext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "resolveJsonModule": true, + + // Compilation Behavior + "noEmit": true, + "incremental": true, + "isolatedModules": true, + + // Type Checking + "strict": true, + "skipLibCheck": true, + + // JavaScript Support + "allowJs": true, + + // React Support + "jsx": "preserve", + + // Libraries and Types + "lib": ["dom", "dom.iterable", "esnext"], + "types": ["bun-types"], + + // Plugins + "plugins": [ + { + "name": "next" + } + ] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "**/*.(mts|ts)"], "exclude": ["node_modules"] From 74fc53c3266ea398f26f7d3d875c2f283f52b82f Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 06:35:18 +0700 Subject: [PATCH 041/124] docs(readme): restructure for clearer readme --- readme.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/readme.md b/readme.md index 9483e4d5..30f011be 100644 --- a/readme.md +++ b/readme.md @@ -4,8 +4,6 @@ [Learn-Anything.xyz](https://learn-anything.xyz) is an open-source learning platform. -Visit the website at: https://learn-anything.xyz - ## Setup [Bun](https://bun.sh) is required to run this project. @@ -31,19 +29,14 @@ bun web ## Contributing -We welcome contributions! Here's how you can get involved: +Currently things are unstable but will improve. -1. Join our [Discord](https://discord.gg/bxtD8x6aNF) and ask for help in the `#dev` channel. -2. Submit draft PRs with your ideas or fixes. We'll guide you through the process. +If you want to help contribute to code, ask for help on [Discord](https://discord.gg/bxtD8x6aNF)'s `#dev` channel. You will be onboarded and unblocked fast. -## Community +Can always submit draft PRs with good ideas/fixes. We will help along the way to make it merge ready. -- **Public Chat**: Join our [Discord server](https://discord.gg/bxtD8x6aNF). -- **Internal Dev Chat**: Available on Telegram. - - Email `join@learn-anything.xyz` to join the core team. - - Active contributors may receive invitations to join. +## Chat -## Notes +Community chat in [Discord server](https://discord.gg/bxtD8x6aNF). -- The project is in active development. Expect frequent changes. -- We prioritize quick onboarding and unblocking of contributors. +Internal dev chat in Telegram (can email `join@learn-anything.xyz` to join core team). We will reach out with offer to join if you contribute to repo in form of PRs too. From 34b8df25d2b42539b5b440c7d4df2571749aa9e5 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 06:37:54 +0700 Subject: [PATCH 042/124] docs(readme): restructure for clearer readme --- readme.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/readme.md b/readme.md index 30f011be..3fdeacae 100644 --- a/readme.md +++ b/readme.md @@ -8,16 +8,12 @@ [Bun](https://bun.sh) is required to run this project. -Install dependencies: - ```bash bun i ``` ## Run Website -To start the website locally: - ```bash bun web ``` From 713f198120ead36c32e4db544ffca37911af4827 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 06:41:36 +0700 Subject: [PATCH 043/124] refactor(command): improve command data --- .../custom/command-palette/command-data.ts | 73 ++++++------------- 1 file changed, 24 insertions(+), 49 deletions(-) diff --git a/web/components/custom/command-palette/command-data.ts b/web/components/custom/command-palette/command-data.ts index 80217afd..2a36b515 100644 --- a/web/components/custom/command-palette/command-data.ts +++ b/web/components/custom/command-palette/command-data.ts @@ -5,7 +5,7 @@ import { HTMLLikeElement } from "@/lib/utils" export type CommandAction = string | (() => void) -export type CommandItemType = { +export interface CommandItemType { icon?: keyof typeof icons value: string label: HTMLLikeElement | string @@ -14,10 +14,25 @@ export type CommandItemType = { shortcut?: string } -export type CommandGroupType = { +export type CommandGroupType = Array<{ heading?: string items: CommandItemType[] -}[] +}> + +const createNavigationItem = ( + icon: keyof typeof icons, + value: string, + path: string, + actions: ReturnType +): CommandItemType => ({ + icon, + value: `Go to ${value}`, + label: { + tag: "span", + children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: [value] }] + }, + action: () => actions.navigateTo(path) +}) export const createCommandGroups = ( actions: ReturnType, @@ -81,51 +96,11 @@ export const createCommandGroups = ( { heading: "Navigation", items: [ - { - icon: "ArrowRight", - value: "Go to Links", - label: { - tag: "span", - children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["links"] }] - }, - action: () => actions.navigateTo("/links") - }, - { - icon: "ArrowRight", - value: "Go to Pages", - label: { - tag: "span", - children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["pages"] }] - }, - action: () => actions.navigateTo("/pages") - }, - { - icon: "ArrowRight", - value: "Go to Search", - label: { - tag: "span", - children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["search"] }] - }, - action: () => actions.navigateTo("/search") - }, - { - icon: "ArrowRight", - value: "Go to Profile", - label: { - tag: "span", - children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["profile"] }] - }, - action: () => actions.navigateTo("/profile") - }, - { - icon: "ArrowRight", - value: "Go to Settings", - label: { - tag: "span", - children: ["Go to ", { tag: "span", attributes: { className: "font-semibold" }, children: ["settings"] }] - }, - action: () => actions.navigateTo("/settings") - } + createNavigationItem("ArrowRight", "Links", "/links", actions), + createNavigationItem("ArrowRight", "Pages", "/pages", actions), + createNavigationItem("ArrowRight", "Search", "/search", actions), + createNavigationItem("ArrowRight", "Profile", "/profile", actions), + createNavigationItem("ArrowRight", "Settings", "/settings", actions) ] } ], @@ -149,7 +124,7 @@ export const createCommandGroups = ( }, { icon: "Monitor", - value: "changeThemeToSystem", + value: "Change Theme to System", label: "Change Theme to System", action: () => actions.changeTheme("system") } From 3b9dc809c0e8dd7d5af5684bd468310b32f92f11 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 06:54:14 +0700 Subject: [PATCH 044/124] fix(topic): handleSelectLearningState missing depth --- web/components/routes/topics/detail/partials/link-item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index 24baeb69..8273b86c 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -110,7 +110,7 @@ export const LinkItem = React.memo( setOpenPopoverForId(null) setIsPopoverOpen(false) }, - [personalLink, personalLinks, me, link, router, setOpenPopoverForId, topic] + [personalLink, personalLinks, me, link, router, setOpenPopoverForId, topic, clerk, pathname] ) const handlePopoverOpenChange = useCallback( From 1cd606376880c74cb7c900961f2fac114ede24c1 Mon Sep 17 00:00:00 2001 From: Aslam Date: Sun, 8 Sep 2024 08:32:10 +0700 Subject: [PATCH 045/124] chore: improve link accesibility and keybind (#153) * fix(topic): handleSelectLearningState missing depth * fix(link): use active index instead of native focus * chore(palette): use atom for maintain state * chore(link): prevent keydown if command palette active --- .../command-palette/command-palette.tsx | 9 +- web/components/routes/link/LinkRoute.tsx | 3 +- web/components/routes/link/list.tsx | 89 ++++++++----------- .../routes/link/partials/link-item.tsx | 34 +++---- .../topics/detail/partials/link-item.tsx | 2 +- 5 files changed, 57 insertions(+), 80 deletions(-) diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index 4986d557..e6a45aa9 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -10,18 +10,21 @@ import { useAccount } from "@/lib/providers/jazz-provider" import { searchSafeRegExp, toTitleCase } from "@/lib/utils" import { GraphNode } from "@/components/routes/public/PublicHomeRoute" import { useCommandActions } from "./hooks/use-command-actions" +import { atom, useAtom } from "jotai" let graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) const filterItems = (items: CommandItemType[], searchRegex: RegExp) => items.filter(item => searchRegex.test(item.value)).slice(0, 6) +export const commandPaletteOpenAtom = atom(false) + export function CommandPalette() { const { me } = useAccount({ root: { personalLinks: [], personalPages: [] } }) const dialogRef = React.useRef(null) const [inputValue, setInputValue] = React.useState("") const [activePage, setActivePage] = React.useState("home") - const [open, setOpen] = React.useState(false) + const [open, setOpen] = useAtom(commandPaletteOpenAtom) const actions = useCommandActions() const commandGroups = React.useMemo(() => me && createCommandGroups(actions, me), [actions, me]) @@ -38,7 +41,7 @@ export function CommandPalette() { document.addEventListener("keydown", down) return () => document.removeEventListener("keydown", down) - }, []) + }, [setOpen]) const bounce = React.useCallback(() => { if (dialogRef.current) { @@ -177,7 +180,7 @@ export function CommandPalette() { closeDialog() } }, - [bounce] + [bounce, setOpen] ) const filteredCommands = React.useMemo(() => getFilteredCommands(), [getFilteredCommands]) diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index 1505b45d..32e3605d 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -21,7 +21,8 @@ export function LinkRoute() {
    - + {/* Refresh list everytime editId is changed */} +
    ) diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 0173a8cb..b9b58350 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -1,5 +1,4 @@ -"use client" - +import React, { useCallback, useEffect, useMemo, useState } from "react" import { DndContext, closestCenter, @@ -11,6 +10,7 @@ import { DragStartEvent, UniqueIdentifier } from "@dnd-kit/core" +import { Primitive } from "@radix-ui/react-primitive" import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from "@dnd-kit/sortable" import { useAccount } from "@/lib/providers/jazz-provider" import { PersonalLinkLists } from "@/lib/schema/personal-link" @@ -18,15 +18,17 @@ import { useAtom } from "jotai" import { linkSortAtom } from "@/store/link" import { useKey } from "react-use" import { LinkItem } from "./partials/link-item" -import { useRef, useState, useCallback, useEffect, useMemo } from "react" -import { learningStateAtom } from "./header" import { useQueryState } from "nuqs" +import { learningStateAtom } from "./header" +import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" interface LinkListProps {} const LinkList: React.FC = () => { + const [isCommandPalettePpen] = useAtom(commandPaletteOpenAtom) const [editId, setEditId] = useQueryState("editId") const [activeLearningState] = useAtom(learningStateAtom) + const [activeItemIndex, setActiveItemIndex] = useState(null) const { me } = useAccount({ root: { personalLinks: [] } @@ -34,9 +36,7 @@ const LinkList: React.FC = () => { const personalLinks = useMemo(() => me?.root?.personalLinks || [], [me?.root?.personalLinks]) const [sort] = useAtom(linkSortAtom) - const [focusedId, setFocusedId] = useState(null) const [draggingId, setDraggingId] = useState(null) - const linkRefs = useRef<{ [key: string]: HTMLLIElement | null }>({}) const filteredLinks = useMemo( () => @@ -67,10 +67,6 @@ const LinkList: React.FC = () => { }) ) - const registerRef = useCallback((id: string, ref: HTMLLIElement | null) => { - linkRefs.current[id] = ref - }, []) - useKey("Escape", () => { if (editId) { setEditId(null) @@ -87,57 +83,40 @@ const LinkList: React.FC = () => { useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { - if (!me?.root?.personalLinks || sortedLinks.length === 0 || editId !== null) return - - const currentIndex = sortedLinks.findIndex(link => link?.id === focusedId) + if (isCommandPalettePpen || !me?.root?.personalLinks || sortedLinks.length === 0 || editId !== null) return if (e.key === "ArrowUp" || e.key === "ArrowDown") { e.preventDefault() - const newIndex = - e.key === "ArrowUp" ? Math.max(0, currentIndex - 1) : Math.min(sortedLinks.length - 1, currentIndex + 1) + setActiveItemIndex(prevIndex => { + if (prevIndex === null) return 0 + const newIndex = + e.key === "ArrowUp" ? Math.max(0, prevIndex - 1) : Math.min(sortedLinks.length - 1, prevIndex + 1) - if (e.metaKey && sort === "manual") { - const currentLink = me.root.personalLinks[currentIndex] - if (!currentLink) return + if (e.metaKey && sort === "manual") { + const linksArray = [...me.root.personalLinks] + const newLinks = arrayMove(linksArray, prevIndex, newIndex) - const linksArray = [...me.root.personalLinks] - const newLinks = arrayMove(linksArray, currentIndex, newIndex) - - while (me.root.personalLinks.length > 0) { - me.root.personalLinks.pop() - } - - newLinks.forEach(link => { - if (link) { - me.root.personalLinks.push(link) + while (me.root.personalLinks.length > 0) { + me.root.personalLinks.pop() } - }) - updateSequences(me.root.personalLinks) - - const newFocusedLink = me.root.personalLinks[newIndex] - if (newFocusedLink) { - setFocusedId(newFocusedLink.id) - - requestAnimationFrame(() => { - linkRefs.current[newFocusedLink.id]?.focus() + newLinks.forEach(link => { + if (link) { + me.root.personalLinks.push(link) + } }) + + updateSequences(me.root.personalLinks) } - } else { - const newFocusedLink = sortedLinks[newIndex] - if (newFocusedLink) { - setFocusedId(newFocusedLink.id) - requestAnimationFrame(() => { - linkRefs.current[newFocusedLink.id]?.focus() - }) - } - } + + return newIndex + }) } } window.addEventListener("keydown", handleKeyDown) return () => window.removeEventListener("keydown", handleKeyDown) - }, [me?.root?.personalLinks, sortedLinks, focusedId, editId, sort, updateSequences]) + }, [me?.root?.personalLinks, sortedLinks, editId, sort, updateSequences, isCommandPalettePpen]) const handleDragStart = useCallback( (event: DragStartEvent) => { @@ -185,6 +164,7 @@ const LinkList: React.FC = () => { }) updateSequences(me.root.personalLinks) + setActiveItemIndex(newIndex) } catch (error) { console.error("Error during link reordering:", error) } @@ -194,7 +174,10 @@ const LinkList: React.FC = () => { } return ( -
    + = () => { item?.id || "") || []} strategy={verticalListSortingStrategy}>
      {sortedLinks.map( - linkItem => + (linkItem, index) => linkItem && ( = () => { setEditId={setEditId} personalLink={linkItem} disabled={sort !== "manual" || editId !== null} - registerRef={registerRef} isDragging={draggingId === linkItem.id} - isFocused={focusedId === linkItem.id} - setFocusedId={setFocusedId} + isActive={activeItemIndex === index} + setActiveItemIndex={setActiveItemIndex} + index={index} /> ) )}
    -
    + ) } diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index 6f1a0a09..2c8c6901 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -1,5 +1,3 @@ -"use client" - import React, { useCallback, useMemo } from "react" import Image from "next/image" import Link from "next/link" @@ -15,7 +13,7 @@ import { PersonalLink } from "@/lib/schema/personal-link" import { LinkForm } from "./form/link-form" import { cn } from "@/lib/utils" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" -import { linkOpenPopoverForIdAtom, linkShowCreateAtom } from "@/store/link" +import { linkOpenPopoverForIdAtom } from "@/store/link" interface LinkItemProps { personalLink: PersonalLink @@ -23,9 +21,9 @@ interface LinkItemProps { isEditing: boolean setEditId: (id: string | null) => void isDragging: boolean - isFocused: boolean - setFocusedId: (id: string | null) => void - registerRef: (id: string, ref: HTMLLIElement | null) => void + isActive: boolean + setActiveItemIndex: (index: number | null) => void + index: number } export const LinkItem: React.FC = ({ @@ -34,9 +32,9 @@ export const LinkItem: React.FC = ({ personalLink, disabled = false, isDragging, - isFocused, - setFocusedId, - registerRef + isActive, + setActiveItemIndex, + index }) => { const [openPopoverForId, setOpenPopoverForId] = useAtom(linkOpenPopoverForIdAtom) const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: personalLink.id, disabled }) @@ -50,14 +48,6 @@ export const LinkItem: React.FC = ({ [transform, transition, isDragging] ) - const refCallback = useCallback( - (node: HTMLLIElement | null) => { - setNodeRef(node) - registerRef(personalLink.id, node) - }, - [setNodeRef, registerRef, personalLink.id] - ) - const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === "Enter") { @@ -92,17 +82,17 @@ export const LinkItem: React.FC = ({ return (
  • setFocusedId(personalLink.id)} - onBlur={() => setFocusedId(null)} + onFocus={() => setActiveItemIndex(index)} + onBlur={() => setActiveItemIndex(null)} onKeyDown={handleKeyDown} className={cn("relative flex h-14 cursor-default items-center outline-none xl:h-11", { - "bg-muted-foreground/10": isFocused, - "hover:bg-muted/50": !isFocused + "bg-muted-foreground/10": isActive, + "hover:bg-muted/50": !isActive })} onDoubleClick={handleRowDoubleClick} > diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index 24baeb69..8273b86c 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -110,7 +110,7 @@ export const LinkItem = React.memo( setOpenPopoverForId(null) setIsPopoverOpen(false) }, - [personalLink, personalLinks, me, link, router, setOpenPopoverForId, topic] + [personalLink, personalLinks, me, link, router, setOpenPopoverForId, topic, clerk, pathname] ) const handlePopoverOpenChange = useCallback( From 7100ded35a6d42001d539368fb42aa76e9607034 Mon Sep 17 00:00:00 2001 From: Aslam Date: Sun, 8 Sep 2024 13:38:23 +0700 Subject: [PATCH 046/124] fix(link): improve UX, maintain state (#154) * fix(topic): handleSelectLearningState missing depth * fix(link): use active index instead of native focus * chore(palette): use atom for maintain state * chore(link): prevent keydown if command palette active * fix: responsive link item * chore: add active item index state to LinkRoute * fix: ability to press enter go to edit mode --- web/components/routes/link/LinkRoute.tsx | 6 +- web/components/routes/link/list.tsx | 40 +++++- .../routes/link/partials/link-item.tsx | 134 +++++++++--------- 3 files changed, 101 insertions(+), 79 deletions(-) diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index 32e3605d..bd88d6ab 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -4,7 +4,7 @@ import { LinkHeader } from "@/components/routes/link/header" import { LinkList } from "@/components/routes/link/list" import { LinkManage } from "@/components/routes/link/manage" import { useQueryState } from "nuqs" -import { useEffect } from "react" +import { useEffect, useState } from "react" import { useAtom } from "jotai" import { linkEditIdAtom } from "@/store/link" import { LinkBottomBar } from "./bottom-bar" @@ -12,6 +12,7 @@ import { LinkBottomBar } from "./bottom-bar" export function LinkRoute() { const [, setEditId] = useAtom(linkEditIdAtom) const [nuqsEditId] = useQueryState("editId") + const [activeItemIndex, setActiveItemIndex] = useState(null) useEffect(() => { setEditId(nuqsEditId) @@ -21,8 +22,7 @@ export function LinkRoute() {
    - {/* Refresh list everytime editId is changed */} - +
    ) diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index b9b58350..3de70727 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useState } from "react" +import React, { useCallback, useEffect, useMemo } from "react" import { DndContext, closestCenter, @@ -22,13 +22,16 @@ import { useQueryState } from "nuqs" import { learningStateAtom } from "./header" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" -interface LinkListProps {} +interface LinkListProps { + activeItemIndex: number | null + setActiveItemIndex: React.Dispatch> +} -const LinkList: React.FC = () => { +const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex }) => { const [isCommandPalettePpen] = useAtom(commandPaletteOpenAtom) const [editId, setEditId] = useQueryState("editId") const [activeLearningState] = useAtom(learningStateAtom) - const [activeItemIndex, setActiveItemIndex] = useState(null) + const [draggingId, setDraggingId] = React.useState(null) const { me } = useAccount({ root: { personalLinks: [] } @@ -36,7 +39,6 @@ const LinkList: React.FC = () => { const personalLinks = useMemo(() => me?.root?.personalLinks || [], [me?.root?.personalLinks]) const [sort] = useAtom(linkSortAtom) - const [draggingId, setDraggingId] = useState(null) const filteredLinks = useMemo( () => @@ -73,6 +75,16 @@ const LinkList: React.FC = () => { } }) + // on mounted, if editId is set, set activeItemIndex to the index of the item with the editId + useEffect(() => { + if (editId) { + const index = sortedLinks.findIndex(link => link?.id === editId) + if (index !== -1) { + setActiveItemIndex(index) + } + } + }, [editId, sortedLinks, setActiveItemIndex]) + const updateSequences = useCallback((links: PersonalLinkLists) => { links.forEach((link, index) => { if (link) { @@ -111,12 +123,28 @@ const LinkList: React.FC = () => { return newIndex }) + } else if (e.key === "Enter" && activeItemIndex !== null) { + e.preventDefault() + const activeLink = sortedLinks[activeItemIndex] + if (activeLink) { + setEditId(activeLink.id) + } } } window.addEventListener("keydown", handleKeyDown) return () => window.removeEventListener("keydown", handleKeyDown) - }, [me?.root?.personalLinks, sortedLinks, editId, sort, updateSequences, isCommandPalettePpen]) + }, [ + me?.root?.personalLinks, + sortedLinks, + editId, + sort, + updateSequences, + isCommandPalettePpen, + activeItemIndex, + setEditId, + setActiveItemIndex + ]) const handleDragStart = useCallback( (event: DragStartEvent) => { diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index 2c8c6901..ffab129e 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -90,82 +90,76 @@ export const LinkItem: React.FC = ({ onFocus={() => setActiveItemIndex(index)} onBlur={() => setActiveItemIndex(null)} onKeyDown={handleKeyDown} - className={cn("relative flex h-14 cursor-default items-center outline-none xl:h-11", { - "bg-muted-foreground/10": isActive, - "hover:bg-muted/50": !isActive - })} + className={cn( + "relative cursor-default outline-none", + "grid grid-cols-[auto_1fr_auto] items-center gap-x-2 px-2 py-2 sm:px-4 sm:py-2", + { + "bg-muted-foreground/10": isActive, + "hover:bg-muted/50": !isActive + } + )} onDoubleClick={handleRowDoubleClick} > -
    -
    - setOpenPopoverForId(open ? personalLink.id : null)} - > - - - - e.preventDefault()} - > - - - + setOpenPopoverForId(open ? personalLink.id : null)} + > + + + + e.preventDefault()} + > + + + - {personalLink.icon && ( - {personalLink.title} - )} -
    -
    -

    - {personalLink.title} -

    - {personalLink.url && ( -
    -
    - )} +
    + {personalLink.icon && ( + {personalLink.title} + )} +
    +

    {personalLink.title}

    + {personalLink.url && ( +
    +
    -
    + )}
    +
    -
    - {personalLink.topic && {personalLink.topic.prettyName}} -
    +
    + {personalLink.topic && {personalLink.topic.prettyName}}
  • ) From eb7e912843974d5740047f70c66a176b601e2ca2 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 14:02:50 +0700 Subject: [PATCH 047/124] fix(link): enter conflict with command palette --- web/components/routes/link/LinkRoute.tsx | 25 +++++++++++++-- web/components/routes/link/header.tsx | 41 ------------------------ web/components/routes/link/list.tsx | 16 +++++---- 3 files changed, 32 insertions(+), 50 deletions(-) diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index bd88d6ab..31a74a0a 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -1,28 +1,47 @@ "use client" +import React, { useEffect, useState, useCallback } from "react" import { LinkHeader } from "@/components/routes/link/header" import { LinkList } from "@/components/routes/link/list" import { LinkManage } from "@/components/routes/link/manage" import { useQueryState } from "nuqs" -import { useEffect, useState } from "react" import { useAtom } from "jotai" import { linkEditIdAtom } from "@/store/link" import { LinkBottomBar } from "./bottom-bar" +import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" -export function LinkRoute() { +export function LinkRoute(): React.ReactElement { const [, setEditId] = useAtom(linkEditIdAtom) const [nuqsEditId] = useQueryState("editId") const [activeItemIndex, setActiveItemIndex] = useState(null) + const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) + const [disableEnterKey, setDisableEnterKey] = useState(false) useEffect(() => { setEditId(nuqsEditId) }, [nuqsEditId, setEditId]) + const handleCommandPaletteClose = useCallback(() => { + setDisableEnterKey(true) + setTimeout(() => setDisableEnterKey(false), 100) + }, []) + + useEffect(() => { + if (!isCommandPaletteOpen) { + handleCommandPaletteClose() + } + }, [isCommandPaletteOpen, handleCommandPaletteClose]) + return (
    - +
    ) diff --git a/web/components/routes/link/header.tsx b/web/components/routes/link/header.tsx index 6a7a2ed2..4c3fd992 100644 --- a/web/components/routes/link/header.tsx +++ b/web/components/routes/link/header.tsx @@ -24,11 +24,6 @@ export const learningStateAtom = atom("all") export const LinkHeader = React.memo(() => { const isTablet = useMedia("(max-width: 1024px)") - const [activeState, setActiveState] = useQueryState( - "state", - parseAsStringLiteral(["all", "ToLearn", "learning", "learned"]).withDefault("all") - ) - return ( <> @@ -57,42 +52,6 @@ export const LinkHeader = React.memo(() => { LinkHeader.displayName = "LinkHeader" -// const LearningTab = React.memo(() => { -// const [activeTab, setActiveTab] = useAtom(learningStateAtom) -// const [activeState, setActiveState] = useQueryState( -// "state", -// parseAsStringLiteral(Object.values(ALL_STATES_STRING)).withDefault(ALL_STATES_STRING[0]) -// ) - -// const handleTabChange = React.useCallback( -// (value: string) => { -// setActiveTab(value) -// setActiveState(value) -// }, -// [setActiveTab, setActiveState] -// ) - -// React.useEffect(() => { -// setActiveTab(activeState) -// }, [activeState, setActiveTab]) - -// return ( -// { -// handleTabChange(value as string) -// }} -// options={ALL_STATES} -// className="bg-secondary flex rounded-lg" -// highlighterClassName="bg-secondary-foreground/10 rounded-lg" -// radioClassName={cn( -// "relative mx-2 flex h-8 cursor-pointer items-center justify-center rounded-full px-1 text-sm text-secondary-foreground/60 data-[checked]:text-secondary-foreground font-medium transition-colors focus:outline-none" -// )} -// highlighterIncludeMargin={true} -// /> -// ) -// }) - const LearningTab = React.memo(() => { const [activeTab, setActiveTab] = useAtom(learningStateAtom) const [activeState, setActiveState] = useQueryState( diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 3de70727..1a5fb372 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -25,9 +25,10 @@ import { commandPaletteOpenAtom } from "@/components/custom/command-palette/comm interface LinkListProps { activeItemIndex: number | null setActiveItemIndex: React.Dispatch> + disableEnterKey: boolean } -const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex }) => { +const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex, disableEnterKey }) => { const [isCommandPalettePpen] = useAtom(commandPaletteOpenAtom) const [editId, setEditId] = useQueryState("editId") const [activeLearningState] = useAtom(learningStateAtom) @@ -123,11 +124,13 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex return newIndex }) - } else if (e.key === "Enter" && activeItemIndex !== null) { + } else if (e.key === "Enter" && !disableEnterKey) { e.preventDefault() - const activeLink = sortedLinks[activeItemIndex] - if (activeLink) { - setEditId(activeLink.id) + if (activeItemIndex !== null) { + const activeLink = sortedLinks[activeItemIndex] + if (activeLink) { + setEditId(activeLink.id) + } } } } @@ -143,7 +146,8 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex isCommandPalettePpen, activeItemIndex, setEditId, - setActiveItemIndex + setActiveItemIndex, + disableEnterKey ]) const handleDragStart = useCallback( From b27fc3014e0c2e433e58a863c3d248fa29a84106 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Sun, 8 Sep 2024 15:50:31 +0700 Subject: [PATCH 048/124] fix(link): ensure url contain protocol --- web/components/routes/link/partials/link-item.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index ffab129e..0ff605b7 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -11,7 +11,7 @@ import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover import { LearningStateSelectorContent } from "@/components/custom/learning-state-selector" import { PersonalLink } from "@/lib/schema/personal-link" import { LinkForm } from "./form/link-form" -import { cn } from "@/lib/utils" +import { cn, ensureUrlProtocol } from "@/lib/utils" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { linkOpenPopoverForIdAtom } from "@/store/link" @@ -144,7 +144,7 @@ export const LinkItem: React.FC = ({
    - {open && ( + {showDropdown && hasInteracted && ( From c87ae5c32938531b455284f46915d4f8ae246023 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Sun, 8 Sep 2024 18:55:46 +0300 Subject: [PATCH 052/124] clerk auth dark mode --- bun.lockb | Bin 404520 -> 401256 bytes package.json | 1 + .../(auth)/sign-in/[[...sign-in]]/page.tsx | 14 +++++++-- .../custom/clerk/clerk-provider-client.tsx | 28 ++++++++++++++++-- .../custom/clerk/sign-in-client.tsx | 19 ++++++++---- 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/bun.lockb b/bun.lockb index b58462d52e237c109b8dc8c9386f2f48f22061ed..4d280d4d424b4906a3f288189513d9dde208a0b3 100755 GIT binary patch delta 90486 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 B_?!R$ delta 90409 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 +// ) +// } + +import { SignUpClient } from "@/components/custom/clerk/sign-up-client" export default async function Page() { return (
    - +
    ) } diff --git a/web/components/custom/clerk/clerk-provider-client.tsx b/web/components/custom/clerk/clerk-provider-client.tsx index 6edee0ca..769a8c3f 100644 --- a/web/components/custom/clerk/clerk-provider-client.tsx +++ b/web/components/custom/clerk/clerk-provider-client.tsx @@ -1,7 +1,31 @@ "use client" - import { ClerkProvider } from "@clerk/nextjs" +import { dark } from "@clerk/themes" +import { useEffect, useState } from "react" export const ClerkProviderClient = ({ children }: { children: React.ReactNode }) => { - return {children} + const [darkMode, setDarkMode] = useState(false) + + useEffect(() => { + const updateTheme = () => { + setDarkMode(document.documentElement.classList.contains("dark")) + } + updateTheme() + + const observer = new MutationObserver(updateTheme) + observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] }) + + return () => observer.disconnect() + }, []) + + return ( + + {children} + + ) } diff --git a/web/components/custom/clerk/sign-in-client.tsx b/web/components/custom/clerk/sign-in-client.tsx index a789546a..aae99d82 100644 --- a/web/components/custom/clerk/sign-in-client.tsx +++ b/web/components/custom/clerk/sign-in-client.tsx @@ -1,7 +1,16 @@ -"use client" +import { SignUp } from "@clerk/nextjs" -import { SignIn } from "@clerk/nextjs" - -export const SignInClient = () => { - return +export const SignUpClient = () => { + return ( +
    + +
    + ) } From 9032601c77ea7efcb897d37582c42028b6fc50b9 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Sun, 8 Sep 2024 19:02:05 +0300 Subject: [PATCH 053/124] clerk sign in --- bun.lockb | Bin 401256 -> 401256 bytes .../(auth)/sign-in/[[...sign-in]]/page.tsx | 14 ++------------ .../custom/clerk/sign-in-client.tsx | 6 +++--- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/bun.lockb b/bun.lockb index 4d280d4d424b4906a3f288189513d9dde208a0b3..1cc4225034ce52d3aa4bce0dda15b3c77d49214c 100755 GIT binary patch delta 38 scmaE{PvXTsiG~)&7N#xCf8TL1#u@1u>KU20v%Y5rVwUZ!?^%-#05t9nc>n+a delta 38 ocmaE{PvXTsiG~)&7N#xCf8TL1F+e~&>w9J(X4%gAo;BG302w0;^Z)<= diff --git a/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx b/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx index aa9403c5..0b5feeb9 100644 --- a/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx +++ b/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx @@ -1,19 +1,9 @@ -// import { SignInClient } from "@/components/custom/clerk/sign-in-client" - -// export default async function Page() { -// return ( -//
    -// -//
    -// ) -// } - -import { SignUpClient } from "@/components/custom/clerk/sign-up-client" +import { SignInClient } from "@/components/custom/clerk/sign-in-client" export default async function Page() { return (
    - +
    ) } diff --git a/web/components/custom/clerk/sign-in-client.tsx b/web/components/custom/clerk/sign-in-client.tsx index aae99d82..cd4f6223 100644 --- a/web/components/custom/clerk/sign-in-client.tsx +++ b/web/components/custom/clerk/sign-in-client.tsx @@ -1,9 +1,9 @@ -import { SignUp } from "@clerk/nextjs" +import { SignIn } from "@clerk/nextjs" -export const SignUpClient = () => { +export const SignInClient = () => { return (
    - Date: Sun, 8 Sep 2024 19:42:15 +0300 Subject: [PATCH 054/124] readme --- package.json | 3 ++- readme.md | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8dbb6a9e..eb489b5d 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,10 @@ "dev": "bun web", "web": "cd web && bun dev", "web:build": "bun run --filter '*' build", - "cli": "bun run --watch scripts/run.ts", + "ts": "bun run --watch scripts/run.ts", "seed": "bun --watch scripts/seed.ts", "tauri": "tauri", + "app": "tauri dev", "app:build": "bun tauri build -b dmg -v" }, "workspaces": [ diff --git a/readme.md b/readme.md index 397b8f3d..56c3be6e 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# [Learn-Anything.xyz](https://learn-anything.xyz) +# [learn-anything.xyz](https://learn-anything.xyz) > Organize world's knowledge, explore connections and curate learning paths @@ -7,7 +7,7 @@ - [api](api) - http services (using TS/[Encore](https://encore.dev/)) - [app](app) - desktop app (wrapping the [website](web) with desktop specific logic) (using [Tauri](https://v2.tauri.app/)) - [cli](cli) - cli (using [Go](https://go.dev)) -- [docs](https://github.com/learn-anything/docs) - public docs hosted on [docs.learn-anything.xyz](https://docs.learn-anything.xyz/) (separate repo, to be managed by LA itself soon) +- [docs](https://github.com/learn-anything/docs) - public docs hosted on [docs.learn-anything.xyz](https://docs.learn-anything.xyz/) - [lib](lib) - shared utility functions in TS - [nix](nix) - shared nix code - [scripts](scripts) - utility scripts in TS @@ -24,7 +24,8 @@ Using [Bun](https://bun.sh): bun i ``` -> [!NOTE] > `bun setup` is not yet done but will be a command to fully bootstrap a local working env for the project, without it, running `bun web` is impossible yet +> [!NOTE] +> bun setup is not yet done but will be a command to fully bootstrap a local working env for the project, without it, running `bun web` is impossible yet ``` bun setup From 2c53acbac53a2597fbb254a4cf569d1ae633144b Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 07:18:44 +0700 Subject: [PATCH 055/124] chore: tweak responsive header and link --- web/components/routes/link/header.tsx | 4 ++-- web/components/routes/link/partials/link-item.tsx | 2 +- web/components/routes/topics/detail/Header.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/components/routes/link/header.tsx b/web/components/routes/link/header.tsx index 4c3fd992..3fa3d863 100644 --- a/web/components/routes/link/header.tsx +++ b/web/components/routes/link/header.tsx @@ -26,11 +26,11 @@ export const LinkHeader = React.memo(() => { return ( <> - +
    - Links + Links
    diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index 0ff605b7..e79f5d7e 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -92,7 +92,7 @@ export const LinkItem: React.FC = ({ onKeyDown={handleKeyDown} className={cn( "relative cursor-default outline-none", - "grid grid-cols-[auto_1fr_auto] items-center gap-x-2 px-2 py-2 sm:px-4 sm:py-2", + "grid grid-cols-[auto_1fr_auto] items-center gap-x-2 py-2 max-lg:px-4 sm:px-5 sm:py-2", { "bg-muted-foreground/10": isActive, "hover:bg-muted/50": !isActive diff --git a/web/components/routes/topics/detail/Header.tsx b/web/components/routes/topics/detail/Header.tsx index 461f3176..0987e9a0 100644 --- a/web/components/routes/topics/detail/Header.tsx +++ b/web/components/routes/topics/detail/Header.tsx @@ -93,7 +93,7 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic }
    - {topic.prettyName} + {topic.prettyName}
    From 99816efa262fb7312cb68a05c3c805d3605938ea Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 07:20:07 +0700 Subject: [PATCH 056/124] feat(ui): add drawer component --- web/components/ui/drawer.tsx | 118 +++++++++++++++++++++++++++++++++++ web/package.json | 1 + 2 files changed, 119 insertions(+) create mode 100644 web/components/ui/drawer.tsx diff --git a/web/components/ui/drawer.tsx b/web/components/ui/drawer.tsx new file mode 100644 index 00000000..6a0ef53d --- /dev/null +++ b/web/components/ui/drawer.tsx @@ -0,0 +1,118 @@ +"use client" + +import * as React from "react" +import { Drawer as DrawerPrimitive } from "vaul" + +import { cn } from "@/lib/utils" + +const Drawer = ({ + shouldScaleBackground = true, + ...props +}: React.ComponentProps) => ( + +) +Drawer.displayName = "Drawer" + +const DrawerTrigger = DrawerPrimitive.Trigger + +const DrawerPortal = DrawerPrimitive.Portal + +const DrawerClose = DrawerPrimitive.Close + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName + +const DrawerContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + +
    + {children} + + +)) +DrawerContent.displayName = "DrawerContent" + +const DrawerHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
    +) +DrawerHeader.displayName = "DrawerHeader" + +const DrawerFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
    +) +DrawerFooter.displayName = "DrawerFooter" + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerTitle.displayName = DrawerPrimitive.Title.displayName + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerDescription.displayName = DrawerPrimitive.Description.displayName + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +} diff --git a/web/package.json b/web/package.json index de3bc5d0..b1fe92a6 100644 --- a/web/package.json +++ b/web/package.json @@ -91,6 +91,7 @@ "streaming-markdown": "^0.0.14", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.2", "zod": "^3.23.8", "zsa": "^0.6.0" }, From 1c3ffcf92fe7f9e4f42dc480fcb57641268eaf30 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 10:34:00 +0700 Subject: [PATCH 057/124] fix(palette): responsive and improve performance --- web/app/command-palette.css | 4 +-- .../custom/command-palette/command-items.tsx | 32 ++++++++++++------- .../command-palette/command-palette.tsx | 2 +- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/web/app/command-palette.css b/web/app/command-palette.css index 808aa88b..f4e67bc7 100644 --- a/web/app/command-palette.css +++ b/web/app/command-palette.css @@ -69,14 +69,14 @@ [la-dialog][cmdk-dialog] { top: 15%; transform: translateX(-50%); - width: 640px; + max-width: 640px; background: var(--cmdk-bg); box-shadow: var(--cmdk-shadow); transform-origin: left; animation: scaleIn 0.2s ease; transition: transform 0.1s ease; border: 0.5px solid var(--cmdk-border-color); - @apply fixed left-1/2 z-50 overflow-hidden rounded-lg outline-none; + @apply fixed left-1/2 z-50 w-full overflow-hidden rounded-lg outline-none; } [la-dialog][cmdk-dialog][data-state="closed"] { diff --git a/web/components/custom/command-palette/command-items.tsx b/web/components/custom/command-palette/command-items.tsx index 64b09602..9555ad16 100644 --- a/web/components/custom/command-palette/command-items.tsx +++ b/web/components/custom/command-palette/command-items.tsx @@ -1,6 +1,7 @@ +import * as React from "react" import { Command } from "cmdk" import { CommandSeparator, CommandShortcut } from "@/components/ui/command" -import { LaIcon } from "../la-icon" +import { LaIcon } from "@/components/custom/la-icon" import { CommandItemType, CommandAction } from "./command-data" import { HTMLLikeElement, renderHTMLLikeElement } from "@/lib/utils" @@ -9,17 +10,24 @@ export interface CommandItemProps extends Omit { handleAction: (action: CommandAction, payload?: any) => void } -const HTMLLikeRenderer: React.FC<{ content: HTMLLikeElement | string }> = ({ content }) => { +const HTMLLikeRenderer: React.FC<{ content: HTMLLikeElement | string }> = React.memo(({ content }) => { return <>{renderHTMLLikeElement(content)} -} +}) -export const CommandItem: React.FC = ({ icon, label, action, payload, shortcut, handleAction }) => ( - handleAction(action, payload)}> - {icon && } - - {shortcut && {shortcut}} - +HTMLLikeRenderer.displayName = "HTMLLikeRenderer" + +export const CommandItem: React.FC = React.memo( + ({ icon, label, action, payload, shortcut, handleAction }) => ( + handleAction(action, payload)}> + {icon && } + + {shortcut && {shortcut}} + + ) ) + +CommandItem.displayName = "CommandItem" + export interface CommandGroupProps { heading?: string items: CommandItemType[] @@ -27,7 +35,7 @@ export interface CommandGroupProps { isLastGroup: boolean } -export const CommandGroup: React.FC = ({ heading, items, handleAction, isLastGroup }) => { +export const CommandGroup: React.FC = React.memo(({ heading, items, handleAction, isLastGroup }) => { return ( <> {heading ? ( @@ -44,4 +52,6 @@ export const CommandGroup: React.FC = ({ heading, items, hand {!isLastGroup && } ) -} +}) + +CommandGroup.displayName = "CommandGroup" diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index e6a45aa9..56f3eef5 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -12,7 +12,7 @@ import { GraphNode } from "@/components/routes/public/PublicHomeRoute" import { useCommandActions } from "./hooks/use-command-actions" import { atom, useAtom } from "jotai" -let graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) +const graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) const filterItems = (items: CommandItemType[], searchRegex: RegExp) => items.filter(item => searchRegex.test(item.value)).slice(0, 6) From 94aac134eabeb00dcf1547835cdf754c26667129 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 10:56:34 +0700 Subject: [PATCH 058/124] fix(palette): searched items should belongs to own group --- .../command-palette/command-palette.tsx | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index 56f3eef5..ac4033ff 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -1,5 +1,3 @@ -"use client" - import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" import { Command } from "cmdk" @@ -70,15 +68,6 @@ export function CommandPalette() { [activePage, inputValue, bounce] ) - const allCommands = React.useMemo(() => { - if (!commandGroups) return [] - - return Object.entries(commandGroups).map(([key, value]) => ({ - heading: toTitleCase(key), - items: value.flatMap(subgroup => subgroup.items) - })) - }, [commandGroups]) - const topics = React.useMemo( () => ({ heading: "Topics", @@ -126,14 +115,22 @@ export function CommandPalette() { const searchRegex = searchSafeRegExp(inputValue) if (activePage === "home") { - if (!inputValue) return commandGroups.home + const filteredGroups = Object.entries(commandGroups) + .map(([key, groups]) => { + return groups.map(group => ({ + heading: group.heading, + items: filterItems(group.items, searchRegex) + })) + }) + .flat() - return [...allCommands, personalLinks, personalPages, topics] - .map(group => ({ - heading: group.heading, - items: filterItems(group.items, searchRegex) - })) - .filter(group => group.items.length > 0) + const additionalGroups = [ + { heading: personalLinks.heading, items: filterItems(personalLinks.items, searchRegex) }, + { heading: personalPages.heading, items: filterItems(personalPages.items, searchRegex) }, + { heading: topics.heading, items: filterItems(topics.items, searchRegex) } + ] + + return [...filteredGroups, ...additionalGroups].filter(group => group.items.length > 0) } switch (activePage) { @@ -151,7 +148,7 @@ export function CommandPalette() { })) .filter(group => group.items.length > 0) } - }, [inputValue, activePage, allCommands, personalLinks, personalPages, commandGroups, topics]) + }, [inputValue, activePage, commandGroups, personalLinks, personalPages, topics]) const handleAction = React.useCallback( (action: CommandAction, payload?: any) => { From 9e7f4e80bd8cbf43a97bcdec77ece0c2f649bbd5 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 11:00:34 +0700 Subject: [PATCH 059/124] fix(palette): slice 10 and default to show only home --- .../command-palette/command-palette.tsx | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index ac4033ff..4e2fd40c 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -5,7 +5,7 @@ import { Dialog, DialogPortal, DialogHeader, DialogTitle, DialogDescription } fr import { CommandGroup } from "./command-items" import { CommandAction, CommandItemType, createCommandGroups } from "./command-data" import { useAccount } from "@/lib/providers/jazz-provider" -import { searchSafeRegExp, toTitleCase } from "@/lib/utils" +import { searchSafeRegExp } from "@/lib/utils" import { GraphNode } from "@/components/routes/public/PublicHomeRoute" import { useCommandActions } from "./hooks/use-command-actions" import { atom, useAtom } from "jotai" @@ -13,7 +13,7 @@ import { atom, useAtom } from "jotai" const graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) const filterItems = (items: CommandItemType[], searchRegex: RegExp) => - items.filter(item => searchRegex.test(item.value)).slice(0, 6) + items.filter(item => searchRegex.test(item.value)).slice(0, 10) export const commandPaletteOpenAtom = atom(false) @@ -115,24 +115,23 @@ export function CommandPalette() { const searchRegex = searchSafeRegExp(inputValue) if (activePage === "home") { - const filteredGroups = Object.entries(commandGroups) - .map(([key, groups]) => { - return groups.map(group => ({ - heading: group.heading, - items: filterItems(group.items, searchRegex) - })) - }) - .flat() + if (!inputValue) { + // Only show items from the home object when there's no search input + return commandGroups.home + } - const additionalGroups = [ - { heading: personalLinks.heading, items: filterItems(personalLinks.items, searchRegex) }, - { heading: personalPages.heading, items: filterItems(personalPages.items, searchRegex) }, - { heading: topics.heading, items: filterItems(topics.items, searchRegex) } - ] + // When there's a search input, search across all categories + const allGroups = [...Object.values(commandGroups).flat(), personalLinks, personalPages, topics] - return [...filteredGroups, ...additionalGroups].filter(group => group.items.length > 0) + return allGroups + .map(group => ({ + heading: group.heading, + items: filterItems(group.items, searchRegex) + })) + .filter(group => group.items.length > 0) } + // Handle other active pages (searchLinks, searchPages, etc.) switch (activePage) { case "searchLinks": return [...commandGroups.searchLinks, { items: filterItems(personalLinks.items, searchRegex) }] From 875c87a2a378f87fa783bf0e2a5bac12a8f8a493 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 13:02:58 +0700 Subject: [PATCH 060/124] chore: update fancy switch version --- web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/package.json b/web/package.json index b1fe92a6..5aec67fd 100644 --- a/web/package.json +++ b/web/package.json @@ -16,7 +16,7 @@ "@nothing-but/force-graph": "^0.9.4", "@nothing-but/utils": "^0.16.0", "@omit/react-confirm-dialog": "^1.1.5", - "@omit/react-fancy-switch": "^0.1.2", + "@omit/react-fancy-switch": "^0.1.3", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-context-menu": "^2.2.1", From f683dcfe0d08293279e18b7d470bdb6c8e5ead9e Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 15:38:07 +0700 Subject: [PATCH 061/124] chore(layout): correct provider sequence --- web/app/layout.tsx | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 488b22b5..4a63cc2f 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -15,6 +15,20 @@ export const metadata: Metadata = { description: "Organize world's knowledge, explore connections and curate learning paths" } +const Providers = ({ children }: { children: React.ReactNode }) => ( + + + + + + {children} + + + + + +) + export default function RootLayout({ children }: Readonly<{ @@ -22,20 +36,12 @@ export default function RootLayout({ }>) { return ( - - - - - - - {children} - - - - - - - + + + {children} + + + ) } From 45620c442459458382a1bd2b910a7bf1a536f7d8 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 15:38:44 +0700 Subject: [PATCH 062/124] chore(clerk): use theme from next theme insteadof manually detect it --- .../custom/clerk/clerk-provider-client.tsx | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/web/components/custom/clerk/clerk-provider-client.tsx b/web/components/custom/clerk/clerk-provider-client.tsx index 769a8c3f..69a5acdc 100644 --- a/web/components/custom/clerk/clerk-provider-client.tsx +++ b/web/components/custom/clerk/clerk-provider-client.tsx @@ -1,31 +1,22 @@ "use client" + import { ClerkProvider } from "@clerk/nextjs" import { dark } from "@clerk/themes" -import { useEffect, useState } from "react" +import { useTheme } from "next-themes" -export const ClerkProviderClient = ({ children }: { children: React.ReactNode }) => { - const [darkMode, setDarkMode] = useState(false) - - useEffect(() => { - const updateTheme = () => { - setDarkMode(document.documentElement.classList.contains("dark")) - } - updateTheme() - - const observer = new MutationObserver(updateTheme) - observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] }) - - return () => observer.disconnect() - }, []) - - return ( - - {children} - - ) +interface ClerkProviderClientProps { + children: React.ReactNode +} + +export const ClerkProviderClient: React.FC = ({ children }) => { + const { theme, systemTheme } = useTheme() + + const isDarkTheme = theme === "dark" || (theme === "system" && systemTheme === "dark") + + const appearance = { + baseTheme: isDarkTheme ? dark : undefined, + variables: { colorPrimary: isDarkTheme ? "#dddddd" : "#2e2e2e" } + } + + return {children} } From 66c96efad824aab98a8fc985dcc8c070ece7ce9b Mon Sep 17 00:00:00 2001 From: olya Date: Mon, 9 Sep 2024 12:49:46 +0300 Subject: [PATCH 063/124] Onboarding (#155) * onboarding layout * edit profile avatar * onboarding tasks --- web/app/(pages)/onboarding/page.tsx | 5 ++ .../(pages)/profile/_components/wrapper.tsx | 77 ++++------------ .../sidebar/partial/profile-section.tsx | 9 ++ web/components/routes/OnboardingRoute.tsx | 89 +++++++++++++++++++ 4 files changed, 122 insertions(+), 58 deletions(-) create mode 100644 web/app/(pages)/onboarding/page.tsx create mode 100644 web/components/routes/OnboardingRoute.tsx diff --git a/web/app/(pages)/onboarding/page.tsx b/web/app/(pages)/onboarding/page.tsx new file mode 100644 index 00000000..b286035c --- /dev/null +++ b/web/app/(pages)/onboarding/page.tsx @@ -0,0 +1,5 @@ +import OnboardingRoute from "@/components/routes/OnboardingRoute" + +export default function EditProfilePage() { + return +} diff --git a/web/app/(pages)/profile/_components/wrapper.tsx b/web/app/(pages)/profile/_components/wrapper.tsx index 725583d0..c1f2c445 100644 --- a/web/app/(pages)/profile/_components/wrapper.tsx +++ b/web/app/(pages)/profile/_components/wrapper.tsx @@ -1,12 +1,13 @@ "use client" import { useAccount } from "@/lib/providers/jazz-provider" +import { useUser } from "@clerk/nextjs" import { useState, useRef, useCallback } from "react" import { useParams } from "next/navigation" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" import Link from "next/link" -import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" +import { Avatar, AvatarImage } from "@/components/ui/avatar" interface ProfileStatsProps { number: number @@ -26,8 +27,20 @@ export const ProfileWrapper = () => { const account = useAccount() const params = useParams() const username = params.username as string + const { user, isSignedIn } = useUser() const avatarInputRef = useRef(null) + const editAvatar = (event: React.ChangeEvent) => { + const file = event.target.files?.[0] + if (file) { + console.log("File selected:", file) + const imageUrl = URL.createObjectURL(file) + if (account.me && account.me.profile) { + account.me.profile.avatarUrl = imageUrl + } + } + } + const [isEditing, setIsEditing] = useState(false) const [newName, setNewName] = useState(account.me?.profile?.name || "") const [error, setError] = useState("") @@ -72,13 +85,6 @@ export const ProfileWrapper = () => { setError("") } - const editAvatar = (event: React.ChangeEvent) => { - const file = event.target.files?.[0] - if (file) { - console.log("File selected:", file) - } - } - if (!account.me || !account.me.profile) { return (
    @@ -107,14 +113,12 @@ export const ProfileWrapper = () => {

    {username}

    - - - avatarInputRef.current?.click()} className="cursor-pointer"> - {account.me?.profile?.name?.charAt(0)} - - + -
    {isEditing ? ( <> @@ -161,46 +165,3 @@ export const ProfileWrapper = () => {
    ) } - -{ - // interface ProfileLinksProps { - // linklabel?: string - // link?: string - // topic?: string - // } - // interface ProfilePagesProps { - // topic?: string - // } - // const ProfileLinks: React.FC = ({ linklabel, link, topic }) => { - // return ( - //
    - //
    - //

    {linklabel || "Untitled"}

    - //
    - // - //

    {link || "#"}

    - //
    - //
    - //
    {topic || "Uncategorized"}
    - //
    - // ) - // } - // const ProfilePages: React.FC = ({ topic }) => { - // return ( - //
    - //
    {topic || "Uncategorized"}
    - //
    - // ) - // } - /*
    - -

    {account.me.root?.website}

    -
    */ - /* */ -} diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 2f0809eb..76c8653e 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -66,6 +66,15 @@ export const ProfileSection: React.FC = () => { + + +
    + + Onboarding +
    + +
    + signOut()}>
    diff --git a/web/components/routes/OnboardingRoute.tsx b/web/components/routes/OnboardingRoute.tsx new file mode 100644 index 00000000..017c856c --- /dev/null +++ b/web/components/routes/OnboardingRoute.tsx @@ -0,0 +1,89 @@ +"use client" + +import { LaIcon } from "../custom/la-icon" +import { useState } from "react" + +const steps = [ + { + number: 1, + title: "Create Link", + description: + "Links are essentially bookmarks of things from internet. You can create a link by pressing Links button in left sidebar. Then pressing + button on the bottom.", + task: "create any Link with any title or description (for example, you can add https://learn-anything.xyz as link)" + }, + { + number: 2, + title: "Create Page", + description: + "Pages are things with content inside (images, text, anything). You can think of them as Notion pages. To create page, press the + button next to pages, then create title and put some content.", + task: "create any Page with any content inside" + }, + { + number: 3, + title: "Start tracking Learning status of some Topic", + description: + "What makes Learn Anything different from Notion and other tools is notion of topics. A topic is anything after learn-anything.xyz/, for example learn-anything.xyz/typescript. You can go to the page, then on top right corner where it says add to my profile, press it and change the state of the topic to I want to learn, Learning or Learned.", + task: "go to any Topic, and mark it as I want to learn" + }, + { + number: 4, + title: "Add a Link from a Topic into personal link collection", + description: + "If you noticed, there are links attached to topics as a list. This is the topic's study guide. It will be improved greatly in future and we will allow any user to edit these study guides too (Reddit style). You can click on the circle to left of the links and add a link to your personal collection with learning status too.", + task: "add any Link from topic typescript into your personal collection" + } +] + +const StepItem = ({ + number, + title, + description, + task, + done +}: { + number: number + title: string + description: string + task: string + done: boolean +}) => ( +
    +
    + {number} +
    +
    +

    {title}

    +

    {description}

    +
    + +

    {task}

    +
    +
    +
    +) + +export default function OnboardingRoute() { + const [completedSteps, setCompletedSteps] = useState([]) + + const stepComplete = (stepNumber: number) => { + setCompletedSteps(prev => + prev.includes(stepNumber) ? prev.filter(num => num !== stepNumber) : [...prev, stepNumber] + ) + } + + return ( +
    +
    +

    Onboarding

    +
    +
    +

    Complete the steps below to get started

    +
    + {steps.map(step => ( + + ))} +
    +
    +
    + ) +} From 5f537d5618462d6c580a084f06d2722580083e6b Mon Sep 17 00:00:00 2001 From: Aslam Date: Mon, 9 Sep 2024 17:35:15 +0700 Subject: [PATCH 064/124] feat: pages (#151) * wip * wip * wip * wwip * wip * wip * fix(util): rmeove checking to existing in slug * wip * chore: handle create page * chore: handle page title untitled --- scripts/past-seed.ts | 4 +- web/app/(pages)/pages/page.tsx | 5 + .../routes/link/partials/form/link-form.tsx | 3 +- web/components/routes/page/PageRoute.tsx | 35 ++++++ .../routes/page/detail/PageDetailRoute.tsx | 3 +- web/components/routes/page/header.tsx | 54 ++++++++++ .../routes/page/hooks/use-column-styles.ts | 16 +++ .../page/hooks/use-keyboard-navigation.ts | 69 ++++++++++++ web/components/routes/page/list.tsx | 100 ++++++++++++++++++ .../routes/page/partials/column.tsx | 38 +++++++ .../routes/page/partials/page-item.tsx | 59 +++++++++++ .../topics/detail/partials/link-item.tsx | 2 +- web/lib/utils/slug.test.ts | 29 +++++ web/lib/utils/slug.ts | 42 ++------ 14 files changed, 420 insertions(+), 39 deletions(-) create mode 100644 web/app/(pages)/pages/page.tsx create mode 100644 web/components/routes/page/PageRoute.tsx create mode 100644 web/components/routes/page/header.tsx create mode 100644 web/components/routes/page/hooks/use-column-styles.ts create mode 100644 web/components/routes/page/hooks/use-keyboard-navigation.ts create mode 100644 web/components/routes/page/list.tsx create mode 100644 web/components/routes/page/partials/column.tsx create mode 100644 web/components/routes/page/partials/page-item.tsx create mode 100644 web/lib/utils/slug.test.ts diff --git a/scripts/past-seed.ts b/scripts/past-seed.ts index 07ec79e5..d821546e 100644 --- a/scripts/past-seed.ts +++ b/scripts/past-seed.ts @@ -27,11 +27,11 @@ async function devSeed() { const pageOneTitle = "Physics" const pageTwoTitle = "Karabiner" const page1 = PersonalPage.create( - { title: pageOneTitle, slug: generateUniqueSlug([], pageOneTitle), content: "Physics is great" }, + { title: pageOneTitle, slug: generateUniqueSlug(pageOneTitle), content: "Physics is great" }, { owner: user } ) const page2 = PersonalPage.create( - { title: pageTwoTitle, slug: generateUniqueSlug([], pageTwoTitle), content: "Karabiner is great" }, + { title: pageTwoTitle, slug: generateUniqueSlug(pageTwoTitle), content: "Karabiner is great" }, { owner: user } ) user.root.personalPages?.push(page1) diff --git a/web/app/(pages)/pages/page.tsx b/web/app/(pages)/pages/page.tsx new file mode 100644 index 00000000..edc1ae2c --- /dev/null +++ b/web/app/(pages)/pages/page.tsx @@ -0,0 +1,5 @@ +import { PageRoute } from "@/components/routes/page/PageRoute" + +export default function Page() { + return +} diff --git a/web/components/routes/link/partials/form/link-form.tsx b/web/components/routes/link/partials/form/link-form.tsx index 0381527b..cf5b9fa9 100644 --- a/web/components/routes/link/partials/form/link-form.tsx +++ b/web/components/routes/link/partials/form/link-form.tsx @@ -147,8 +147,7 @@ export const LinkForm: React.FC = ({ if (isFetching || !me) return try { - const personalLinks = me.root?.personalLinks?.toJSON() || [] - const slug = generateUniqueSlug(personalLinks, values.title) + const slug = generateUniqueSlug(values.title) if (selectedLink) { const { topic, ...diffValues } = values diff --git a/web/components/routes/page/PageRoute.tsx b/web/components/routes/page/PageRoute.tsx new file mode 100644 index 00000000..f01892a5 --- /dev/null +++ b/web/components/routes/page/PageRoute.tsx @@ -0,0 +1,35 @@ +"use client" + +import { useCallback, useEffect, useState } from "react" +import { PageHeader } from "./header" +import { PageList } from "./list" +import { useAtom } from "jotai" +import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" + +export function PageRoute() { + const [activeItemIndex, setActiveItemIndex] = useState(null) + const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) + const [disableEnterKey, setDisableEnterKey] = useState(false) + + const handleCommandPaletteClose = useCallback(() => { + setDisableEnterKey(true) + setTimeout(() => setDisableEnterKey(false), 100) + }, []) + + useEffect(() => { + if (!isCommandPaletteOpen) { + handleCommandPaletteClose() + } + }, [isCommandPaletteOpen, handleCommandPaletteClose]) + + return ( +
    + + +
    + ) +} diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index a1bd5776..ca075c36 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -126,8 +126,7 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => { const newTitle = editor.getText() if (newTitle !== page.title) { - const personalPages = me?.root?.personalPages?.toJSON() || [] - const slug = generateUniqueSlug(personalPages, page.slug || "") + const slug = generateUniqueSlug(page.title?.toString() || "") page.title = newTitle page.slug = slug page.updatedAt = new Date() diff --git a/web/components/routes/page/header.tsx b/web/components/routes/page/header.tsx new file mode 100644 index 00000000..af1c1b22 --- /dev/null +++ b/web/components/routes/page/header.tsx @@ -0,0 +1,54 @@ +"use client" + +import * as React from "react" +import { Button } from "@/components/ui/button" +import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header" +import { LaIcon } from "@/components/custom/la-icon" +import { useAccount } from "@/lib/providers/jazz-provider" +import { useRouter } from "next/navigation" +import { PersonalPage } from "@/lib/schema" +import { toast } from "sonner" + +export const PageHeader = React.memo(() => { + const { me } = useAccount() + const router = useRouter() + + if (!me) return null + + const handleClick = () => { + try { + const newPersonalPage = PersonalPage.create( + { public: false, createdAt: new Date(), updatedAt: new Date() }, + { owner: me._owner } + ) + me.root?.personalPages?.push(newPersonalPage) + router.push(`/pages/${newPersonalPage.id}`) + } catch (error) { + toast.error("Failed to create page") + } + } + + return ( + +
    + +
    + Pages +
    +
    + +
    + +
    +
    + +
    +
    +
    + ) +}) + +PageHeader.displayName = "PageHeader" diff --git a/web/components/routes/page/hooks/use-column-styles.ts b/web/components/routes/page/hooks/use-column-styles.ts new file mode 100644 index 00000000..30f1171e --- /dev/null +++ b/web/components/routes/page/hooks/use-column-styles.ts @@ -0,0 +1,16 @@ +import { useMedia } from "react-use" + +export const useColumnStyles = () => { + const isTablet = useMedia("(max-width: 640px)") + + return { + title: { + "--width": "69px", + "--min-width": "200px", + "--max-width": isTablet ? "none" : "auto" + }, + content: { "--width": "auto", "--min-width": "200px", "--max-width": "200px" }, + topic: { "--width": "65px", "--min-width": "120px", "--max-width": "120px" }, + updated: { "--width": "82px", "--min-width": "82px", "--max-width": "82px" } + } +} diff --git a/web/components/routes/page/hooks/use-keyboard-navigation.ts b/web/components/routes/page/hooks/use-keyboard-navigation.ts new file mode 100644 index 00000000..3878b653 --- /dev/null +++ b/web/components/routes/page/hooks/use-keyboard-navigation.ts @@ -0,0 +1,69 @@ +import React, { useEffect, useRef, useCallback } from "react" +import { PersonalPage, PersonalPageLists } from "@/lib/schema" + +interface UseKeyboardNavigationProps { + personalPages?: PersonalPageLists | null + activeItemIndex: number | null + setActiveItemIndex: React.Dispatch> + isCommandPaletteOpen: boolean + disableEnterKey: boolean + onEnter?: (selectedPage: PersonalPage) => void +} + +export const useKeyboardNavigation = ({ + personalPages, + activeItemIndex, + setActiveItemIndex, + isCommandPaletteOpen, + disableEnterKey, + onEnter +}: UseKeyboardNavigationProps) => { + const listRef = useRef(null) + const itemRefs = useRef<(HTMLAnchorElement | null)[]>([]) + const itemCount = personalPages?.length || 0 + + const scrollIntoView = useCallback((index: number) => { + if (itemRefs.current[index]) { + itemRefs.current[index]?.scrollIntoView({ + block: "nearest" + }) + } + }, []) + + useEffect(() => { + if (activeItemIndex !== null) { + scrollIntoView(activeItemIndex) + } + }, [activeItemIndex, scrollIntoView]) + + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (isCommandPaletteOpen) return + + if (e.key === "ArrowUp" || e.key === "ArrowDown") { + e.preventDefault() + setActiveItemIndex(prevIndex => { + if (prevIndex === null) return 0 + const newIndex = e.key === "ArrowUp" ? (prevIndex - 1 + itemCount) % itemCount : (prevIndex + 1) % itemCount + return newIndex + }) + } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null && personalPages) { + e.preventDefault() + const selectedPage = personalPages[activeItemIndex] + if (selectedPage) onEnter?.(selectedPage) + } + }, + [itemCount, isCommandPaletteOpen, activeItemIndex, setActiveItemIndex, disableEnterKey, personalPages, onEnter] + ) + + useEffect(() => { + window.addEventListener("keydown", handleKeyDown) + return () => window.removeEventListener("keydown", handleKeyDown) + }, [handleKeyDown]) + + const setItemRef = useCallback((el: HTMLAnchorElement | null, index: number) => { + itemRefs.current[index] = el + }, []) + + return { listRef, setItemRef } +} diff --git a/web/components/routes/page/list.tsx b/web/components/routes/page/list.tsx new file mode 100644 index 00000000..d5796a73 --- /dev/null +++ b/web/components/routes/page/list.tsx @@ -0,0 +1,100 @@ +import React, { useMemo, useCallback } from "react" +import { Primitive } from "@radix-ui/react-primitive" +import { useAccount } from "@/lib/providers/jazz-provider" +import { useAtom } from "jotai" +import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" +import { PageItem } from "./partials/page-item" +import { useKeyboardNavigation } from "./hooks/use-keyboard-navigation" +import { useMedia } from "react-use" +import { Column } from "./partials/column" +import { useColumnStyles } from "./hooks/use-column-styles" +import { PersonalPage, PersonalPageLists } from "@/lib/schema" +import { useRouter } from "next/navigation" + +interface PageListProps { + activeItemIndex: number | null + setActiveItemIndex: React.Dispatch> + disableEnterKey: boolean +} + +export const PageList: React.FC = ({ activeItemIndex, setActiveItemIndex, disableEnterKey }) => { + const isTablet = useMedia("(max-width: 640px)") + const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) + const { me } = useAccount({ root: { personalPages: [] } }) + const personalPages = useMemo(() => me?.root?.personalPages, [me?.root?.personalPages]) + const router = useRouter() + + const handleEnter = useCallback( + (selectedPage: PersonalPage) => { + router.push(`/pages/${selectedPage.id}`) + }, + [router] + ) + + const { listRef, setItemRef } = useKeyboardNavigation({ + personalPages, + activeItemIndex, + setActiveItemIndex, + isCommandPaletteOpen, + disableEnterKey, + onEnter: handleEnter + }) + + return ( +
    + {!isTablet && } + +
    + ) +} + +export const ColumnHeader: React.FC = () => { + const columnStyles = useColumnStyles() + + return ( +
    + + Title + + + Topic + + + Updated + +
    + ) +} + +interface PageListItemsProps { + listRef: React.RefObject + setItemRef: (el: HTMLAnchorElement | null, index: number) => void + personalPages?: PersonalPageLists | null + activeItemIndex: number | null +} + +const PageListItems: React.FC = ({ listRef, setItemRef, personalPages, activeItemIndex }) => ( + + {personalPages?.map( + (page, index) => + page?.id && ( + setItemRef(el, index)} + page={page} + isActive={index === activeItemIndex} + /> + ) + )} + +) diff --git a/web/components/routes/page/partials/column.tsx b/web/components/routes/page/partials/column.tsx new file mode 100644 index 00000000..79c8b03d --- /dev/null +++ b/web/components/routes/page/partials/column.tsx @@ -0,0 +1,38 @@ +import React from "react" +import { cn } from "@/lib/utils" + +interface ColumnWrapperProps extends React.HTMLAttributes { + style?: { [key: string]: string } +} + +interface ColumnTextProps extends React.HTMLAttributes {} + +const ColumnWrapper = React.forwardRef( + ({ children, className, style, ...props }, ref) => ( +
    + {children} +
    + ) +) + +const ColumnText = React.forwardRef(({ children, className, ...props }, ref) => ( + + {children} + +)) + +export const Column = { + Wrapper: ColumnWrapper, + Text: ColumnText +} diff --git a/web/components/routes/page/partials/page-item.tsx b/web/components/routes/page/partials/page-item.tsx new file mode 100644 index 00000000..c7f42379 --- /dev/null +++ b/web/components/routes/page/partials/page-item.tsx @@ -0,0 +1,59 @@ +import React from "react" +import Link from "next/link" +import { cn } from "@/lib/utils" +import { PersonalPage } from "@/lib/schema" +import { Badge } from "@/components/ui/badge" +import { Column } from "./column" +import { useMedia } from "react-use" +import { useColumnStyles } from "../hooks/use-column-styles" + +interface PageItemProps { + page: PersonalPage + isActive: boolean +} + +export const PageItem = React.forwardRef(({ page, isActive }, ref) => { + const isTablet = useMedia("(max-width: 640px)") + const columnStyles = useColumnStyles() + + return ( + +
    + + {page.title || "Untitled"} + + + {!isTablet && ( + <> + {/* + {page.slug} + */} + + {page.topic && {page.topic.prettyName}} + + + )} + + + {page.updatedAt.toLocaleDateString()} + +
    + + ) +}) + +PageItem.displayName = "PageItem" diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index 8273b86c..0c99fd01 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -82,7 +82,7 @@ export const LinkItem = React.memo( toast.success("Link learning state updated", defaultToast) } } else { - const slug = generateUniqueSlug(personalLinks.toJSON(), link.title) + const slug = generateUniqueSlug(link.title) const newPersonalLink = PersonalLink.create( { url: link.url, diff --git a/web/lib/utils/slug.test.ts b/web/lib/utils/slug.test.ts new file mode 100644 index 00000000..573e72b3 --- /dev/null +++ b/web/lib/utils/slug.test.ts @@ -0,0 +1,29 @@ +import { generateUniqueSlug } from "./slug" + +describe("generateUniqueSlug", () => { + it("should generate a slug with the correct format", () => { + const title = "This is a test title" + const slug = generateUniqueSlug(title) + expect(slug).toMatch(/^this-is-a-test-title-[a-f0-9]{8}$/) + }) + + it("should respect the maxLength parameter", () => { + const title = "This is a very long title that should be truncated" + const maxLength = 30 + const slug = generateUniqueSlug(title, maxLength) + expect(slug.length).toBe(maxLength) + }) + + it("should generate different slugs for the same title", () => { + const title = "Same Title" + const slug1 = generateUniqueSlug(title) + const slug2 = generateUniqueSlug(title) + expect(slug1).not.toBe(slug2) + }) + + it("should handle empty strings", () => { + const title = "" + const slug = generateUniqueSlug(title) + expect(slug).toMatch(/^-[a-f0-9]{8}$/) + }) +}) diff --git a/web/lib/utils/slug.ts b/web/lib/utils/slug.ts index 9c07d145..20e37ece 100644 --- a/web/lib/utils/slug.ts +++ b/web/lib/utils/slug.ts @@ -1,36 +1,14 @@ import slugify from "slugify" +import crypto from "crypto" -type SlugLikeProperty = string | undefined +export function generateUniqueSlug(title: string, maxLength: number = 60): string { + const baseSlug = slugify(title, { + lower: true, + strict: true + }) + const randomSuffix = crypto.randomBytes(4).toString("hex") -interface Data { - [key: string]: any -} - -export function generateUniqueSlug( - existingItems: Data[], - title: string, - slugProperty: string = "slug", - maxLength: number = 50 -): string { - const baseSlug = slugify(title, { lower: true, strict: true }) - let uniqueSlug = baseSlug.slice(0, maxLength) - let num = 1 - - if (!existingItems || existingItems.length === 0) { - return uniqueSlug - } - - const isSlugTaken = (slug: string) => - existingItems.some(item => { - const itemSlug = item[slugProperty] as SlugLikeProperty - return itemSlug === slug - }) - - while (isSlugTaken(uniqueSlug)) { - const suffix = `-${num}` - uniqueSlug = `${baseSlug.slice(0, maxLength - suffix.length)}${suffix}` - num++ - } - - return uniqueSlug + const truncatedSlug = baseSlug.slice(0, Math.min(maxLength, 75) - 9) + + return `${truncatedSlug}-${randomSuffix}` } From 84ee3d05175112e2e4890fa1dd3339de5be157fc Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 17:50:29 +0700 Subject: [PATCH 065/124] fix: missing display name --- web/components/routes/page/partials/column.tsx | 4 ++++ web/components/routes/page/partials/page-item.tsx | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/web/components/routes/page/partials/column.tsx b/web/components/routes/page/partials/column.tsx index 79c8b03d..d1482f93 100644 --- a/web/components/routes/page/partials/column.tsx +++ b/web/components/routes/page/partials/column.tsx @@ -26,12 +26,16 @@ const ColumnWrapper = React.forwardRef( ) ) +ColumnWrapper.displayName = "ColumnWrapper" + const ColumnText = React.forwardRef(({ children, className, ...props }, ref) => ( {children} )) +ColumnText.displayName = "ColumnText" + export const Column = { Wrapper: ColumnWrapper, Text: ColumnText diff --git a/web/components/routes/page/partials/page-item.tsx b/web/components/routes/page/partials/page-item.tsx index c7f42379..1f9c3d29 100644 --- a/web/components/routes/page/partials/page-item.tsx +++ b/web/components/routes/page/partials/page-item.tsx @@ -30,7 +30,6 @@ export const PageItem = React.forwardRef(({ pa )} href={`/pages/${page.id}`} role="listitem" - aria-selected={isActive} >
    From 407ef129e38489e56e2fdcf55aea87bb16d20b4a Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 18:10:19 +0700 Subject: [PATCH 066/124] chore: navigate to pages and disable topic for now --- web/components/custom/sidebar/partial/page-section.tsx | 7 ++++--- web/components/custom/sidebar/partial/topic-section.tsx | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/web/components/custom/sidebar/partial/page-section.tsx b/web/components/custom/sidebar/partial/page-section.tsx index d8213c05..8fe4e59c 100644 --- a/web/components/custom/sidebar/partial/page-section.tsx +++ b/web/components/custom/sidebar/partial/page-section.tsx @@ -78,15 +78,16 @@ const PageSectionHeader: React.FC = ({ pageCount }) => (
    - + +
    diff --git a/web/components/custom/sidebar/partial/topic-section.tsx b/web/components/custom/sidebar/partial/topic-section.tsx index 0a2300b8..0e1155f7 100644 --- a/web/components/custom/sidebar/partial/topic-section.tsx +++ b/web/components/custom/sidebar/partial/topic-section.tsx @@ -118,7 +118,7 @@ const ListItem: React.FC = ({ label, value, href, count, isActive
    Date: Mon, 9 Sep 2024 18:20:42 +0700 Subject: [PATCH 067/124] chore: jazz peer url setup --- web/.env.example | 5 ++++- web/lib/providers/jazz-provider.tsx | 13 ++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/web/.env.example b/web/.env.example index c775b455..e9474cf4 100644 --- a/web/.env.example +++ b/web/.env.example @@ -7,4 +7,7 @@ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= CLERK_SECRET_KEY= NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in -NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up \ No newline at end of file +NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up + +NEXT_PUBLIC_JAZZ_PEER_URL="wss://" +# IGNORE_BUILD_ERRORS=true \ No newline at end of file diff --git a/web/lib/providers/jazz-provider.tsx b/web/lib/providers/jazz-provider.tsx index 467d2e8e..1e010ded 100644 --- a/web/lib/providers/jazz-provider.tsx +++ b/web/lib/providers/jazz-provider.tsx @@ -12,7 +12,18 @@ const Jazz = createJazzReactApp({ export const { useAccount, useAccountOrGuest, useCoState, useAcceptInvite } = Jazz -const JAZZ_PEER_URL = "wss://mesh.jazz.tools/?key=example@gmail.com" +function assertPeerUrl(url: string | undefined): asserts url is `wss://${string}` | `ws://${string}` { + if (!url) { + throw new Error("NEXT_PUBLIC_JAZZ_PEER_URL is not defined") + } + if (!url.startsWith("wss://") && !url.startsWith("ws://")) { + throw new Error("NEXT_PUBLIC_JAZZ_PEER_URL must start with wss:// or ws://") + } +} + +const rawUrl = process.env.NEXT_PUBLIC_JAZZ_PEER_URL +assertPeerUrl(rawUrl) +const JAZZ_PEER_URL = rawUrl interface ChildrenProps { children: React.ReactNode From 1b084d239df5f18b5fd15902be22ddbef1105cf1 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 18:36:08 +0700 Subject: [PATCH 068/124] chore: onboarding set to storage --- web/components/routes/OnboardingRoute.tsx | 62 +++++++++++++++++++---- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/web/components/routes/OnboardingRoute.tsx b/web/components/routes/OnboardingRoute.tsx index 017c856c..fff5daea 100644 --- a/web/components/routes/OnboardingRoute.tsx +++ b/web/components/routes/OnboardingRoute.tsx @@ -1,7 +1,15 @@ "use client" +import React, { useEffect } from "react" +import { atomWithStorage } from "jotai/utils" import { LaIcon } from "../custom/la-icon" -import { useState } from "react" +import { useAccount } from "@/lib/providers/jazz-provider" +import { useAtom } from "jotai" + +const isCreateLinkDoneAtom = atomWithStorage("isCreateLinkDone", false) +const isCreatePageDoneAtom = atomWithStorage("isCreatePageDone", false) +const isStartTrackingDoneAtom = atomWithStorage("isStartTrackingDone", false) +const isAddLinkDoneAtom = atomWithStorage("isAddLinkDone", false) const steps = [ { @@ -55,7 +63,7 @@ const StepItem = ({

    {title}

    {description}

    - +

    {task}

    @@ -63,13 +71,40 @@ const StepItem = ({ ) export default function OnboardingRoute() { - const [completedSteps, setCompletedSteps] = useState([]) + const { me } = useAccount({ + root: { + personalPages: [], + personalLinks: [], + topicsWantToLearn: [] + } + }) - const stepComplete = (stepNumber: number) => { - setCompletedSteps(prev => - prev.includes(stepNumber) ? prev.filter(num => num !== stepNumber) : [...prev, stepNumber] - ) - } + const [isCreateLinkDone, setIsCreateLinkDone] = useAtom(isCreateLinkDoneAtom) + const [isCreatePageDone, setIsCreatePageDone] = useAtom(isCreatePageDoneAtom) + const [isStartTrackingDone, setIsStartTrackingDone] = useAtom(isStartTrackingDoneAtom) + const [isAddLinkDone, setIsAddLinkDone] = useAtom(isAddLinkDoneAtom) + + useEffect(() => { + if (!me) return + + if (me.root.personalLinks.length > 0 && !isCreateLinkDone) { + setIsCreateLinkDone(true) + } + + if (me.root.personalPages.length > 0 && !isCreatePageDone) { + setIsCreatePageDone(true) + } + + if (me.root.topicsWantToLearn.length > 0 && !isStartTrackingDone) { + setIsStartTrackingDone(true) + } + + if (me.root.personalLinks.some(link => link?.topic?.name === "typescript") && !isAddLinkDone) { + setIsAddLinkDone(true) + } + }, [me, isCreateLinkDone, isCreatePageDone, setIsCreateLinkDone, setIsCreatePageDone]) + + const completedSteps = [isCreateLinkDone, isCreatePageDone, isStartTrackingDone, isAddLinkDone].filter(Boolean).length return (
    @@ -78,9 +113,16 @@ export default function OnboardingRoute() {

    Complete the steps below to get started

    +

    + Completed {completedSteps} out of {steps.length} steps +

    - {steps.map(step => ( - + {steps.map((step, index) => ( + ))}
    From 1def5eca2065347c470e8a18d3abe1be0a144023 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 19:04:32 +0700 Subject: [PATCH 069/124] feat(link): command+backspace to handle delete link --- web/components/routes/link/list.tsx | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 1a5fb372..63e6a38f 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -21,6 +21,8 @@ import { LinkItem } from "./partials/link-item" import { useQueryState } from "nuqs" import { learningStateAtom } from "./header" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" +import { useConfirm } from "@omit/react-confirm-dialog" +import { toast } from "sonner" interface LinkListProps { activeItemIndex: number | null @@ -33,6 +35,7 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex const [editId, setEditId] = useQueryState("editId") const [activeLearningState] = useAtom(learningStateAtom) const [draggingId, setDraggingId] = React.useState(null) + const confirm = useConfirm() const { me } = useAccount({ root: { personalLinks: [] } @@ -76,6 +79,55 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex } }) + useKey( + event => (event.metaKey || event.ctrlKey) && event.key === "Backspace", + async () => { + if (activeItemIndex !== null) { + const activeLink = sortedLinks[activeItemIndex] + if (activeLink) { + console.log("Delete link", activeLink.toJSON()) + + const result = await confirm({ + title: `Delete "${activeLink.title}"?`, + description: "This action cannot be undone.", + alertDialogTitle: { + className: "text-base" + }, + cancelButton: { + variant: "outline" + }, + confirmButton: { + variant: "destructive" + } + }) + + if (result) { + if (!me?.root.personalLinks) return + + const index = me.root.personalLinks.findIndex(item => item?.id === activeLink.id) + if (index === -1) { + console.error("Delete operation fail", { index, activeLink }) + return + } + + toast.success("Link deleted.", { + position: "bottom-right", + description: ( + + {activeLink.title} has been deleted. + + ) + }) + + me.root.personalLinks.splice(index, 1) + setEditId(null) + } + } + } + }, + { event: "keydown" } + ) + // on mounted, if editId is set, set activeItemIndex to the index of the item with the editId useEffect(() => { if (editId) { From e841a1c2cc56c673114985550dc4df3e9a006a2d Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 19:25:01 +0700 Subject: [PATCH 070/124] fix(link): delete link should disabled all key outside confirm --- web/components/routes/link/LinkRoute.tsx | 9 ++++- web/components/routes/link/bottom-bar.tsx | 23 +++---------- .../routes/link/hooks/use-link-actions.ts | 30 +++++++++++++++++ web/components/routes/link/list.tsx | 33 +++++++------------ 4 files changed, 54 insertions(+), 41 deletions(-) create mode 100644 web/components/routes/link/hooks/use-link-actions.ts diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index 31a74a0a..a3a645d7 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -5,16 +5,19 @@ import { LinkHeader } from "@/components/routes/link/header" import { LinkList } from "@/components/routes/link/list" import { LinkManage } from "@/components/routes/link/manage" import { useQueryState } from "nuqs" -import { useAtom } from "jotai" +import { atom, useAtom } from "jotai" import { linkEditIdAtom } from "@/store/link" import { LinkBottomBar } from "./bottom-bar" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" +export const isDeleteConfirmShownAtom = atom(false) + export function LinkRoute(): React.ReactElement { const [, setEditId] = useAtom(linkEditIdAtom) const [nuqsEditId] = useQueryState("editId") const [activeItemIndex, setActiveItemIndex] = useState(null) const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) + const [isDeleteConfirmShown] = useAtom(isDeleteConfirmShownAtom) const [disableEnterKey, setDisableEnterKey] = useState(false) useEffect(() => { @@ -32,6 +35,10 @@ export function LinkRoute(): React.ReactElement { } }, [isCommandPaletteOpen, handleCommandPaletteClose]) + useEffect(() => { + setDisableEnterKey(isDeleteConfirmShown || isCommandPaletteOpen) + }, [isDeleteConfirmShown, isCommandPaletteOpen]) + return (
    diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index 151815b9..aaae2665 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -13,7 +13,7 @@ import { useAccount, useCoState } from "@/lib/providers/jazz-provider" import { PersonalLink } from "@/lib/schema" import { ID } from "jazz-tools" import { globalLinkFormExceptionRefsAtom } from "./partials/form/link-form" -import { toast } from "sonner" +import { useLinkActions } from "./hooks/use-link-actions" interface ToolbarButtonProps extends React.ComponentPropsWithoutRef { icon: keyof typeof icons @@ -66,6 +66,7 @@ export const LinkBottomBar: React.FC = () => { const plusBtnRef = useRef(null) const plusMoreBtnRef = useRef(null) + const { deleteLink } = useLinkActions() const confirm = useConfirm() useEffect(() => { @@ -107,24 +108,8 @@ export const LinkBottomBar: React.FC = () => { }) if (result) { - if (!me?.root.personalLinks) return - - const index = me.root.personalLinks.findIndex(item => item?.id === personalLink.id) - if (index === -1) { - console.error("Delete operation fail", { index, personalLink }) - return - } - - toast.success("Link deleted.", { - position: "bottom-right", - description: ( - - {personalLink.title} has been deleted. - - ) - }) - - me.root.personalLinks.splice(index, 1) + if (!me) return + deleteLink(me, personalLink) setEditId(null) } } diff --git a/web/components/routes/link/hooks/use-link-actions.ts b/web/components/routes/link/hooks/use-link-actions.ts new file mode 100644 index 00000000..98fb4428 --- /dev/null +++ b/web/components/routes/link/hooks/use-link-actions.ts @@ -0,0 +1,30 @@ +import * as React from "react" +import { toast } from "sonner" +import { LaAccount, PersonalLink } from "@/lib/schema" + +export const useLinkActions = () => { + const deleteLink = React.useCallback((me: LaAccount, link: PersonalLink) => { + if (!me.root?.personalLinks) return + + try { + const index = me.root.personalLinks.findIndex(item => item?.id === link.id) + if (index === -1) { + console.error("Delete operation fail", { index, link }) + return + } + + toast.success("Link deleted.", { + position: "bottom-right", + description: `${link.title} has been deleted.` + }) + + me.root.personalLinks.splice(index, 1) + } catch (error) { + toast.error("Failed to delete link") + } + }, []) + + return { + deleteLink + } +} diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 63e6a38f..f7cefd82 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -22,7 +22,8 @@ import { useQueryState } from "nuqs" import { learningStateAtom } from "./header" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { useConfirm } from "@omit/react-confirm-dialog" -import { toast } from "sonner" +import { useLinkActions } from "./hooks/use-link-actions" +import { isDeleteConfirmShownAtom } from "./LinkRoute" interface LinkListProps { activeItemIndex: number | null @@ -32,9 +33,12 @@ interface LinkListProps { const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex, disableEnterKey }) => { const [isCommandPalettePpen] = useAtom(commandPaletteOpenAtom) + const [, setIsDeleteConfirmShown] = useAtom(isDeleteConfirmShownAtom) const [editId, setEditId] = useQueryState("editId") const [activeLearningState] = useAtom(learningStateAtom) const [draggingId, setDraggingId] = React.useState(null) + + const { deleteLink } = useLinkActions() const confirm = useConfirm() const { me } = useAccount({ @@ -83,10 +87,9 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex event => (event.metaKey || event.ctrlKey) && event.key === "Backspace", async () => { if (activeItemIndex !== null) { + setIsDeleteConfirmShown(true) const activeLink = sortedLinks[activeItemIndex] if (activeLink) { - console.log("Delete link", activeLink.toJSON()) - const result = await confirm({ title: `Delete "${activeLink.title}"?`, description: "This action cannot be undone.", @@ -102,25 +105,12 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex }) if (result) { - if (!me?.root.personalLinks) return + if (!me) return + deleteLink(me, activeLink) - const index = me.root.personalLinks.findIndex(item => item?.id === activeLink.id) - if (index === -1) { - console.error("Delete operation fail", { index, activeLink }) - return - } - - toast.success("Link deleted.", { - position: "bottom-right", - description: ( - - {activeLink.title} has been deleted. - - ) - }) - - me.root.personalLinks.splice(index, 1) - setEditId(null) + setIsDeleteConfirmShown(false) + } else { + setIsDeleteConfirmShown(false) } } } @@ -177,6 +167,7 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex return newIndex }) } else if (e.key === "Enter" && !disableEnterKey) { + console.log("Enter key pressed") e.preventDefault() if (activeItemIndex !== null) { const activeLink = sortedLinks[activeItemIndex] From 4765447ccda46c77dfdf5caf197b7153790cc114 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 19:31:40 +0700 Subject: [PATCH 071/124] fix(onboarding): missing depth --- web/components/routes/OnboardingRoute.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/web/components/routes/OnboardingRoute.tsx b/web/components/routes/OnboardingRoute.tsx index fff5daea..87cf0dc6 100644 --- a/web/components/routes/OnboardingRoute.tsx +++ b/web/components/routes/OnboardingRoute.tsx @@ -102,7 +102,17 @@ export default function OnboardingRoute() { if (me.root.personalLinks.some(link => link?.topic?.name === "typescript") && !isAddLinkDone) { setIsAddLinkDone(true) } - }, [me, isCreateLinkDone, isCreatePageDone, setIsCreateLinkDone, setIsCreatePageDone]) + }, [ + me, + isCreateLinkDone, + isCreatePageDone, + setIsCreateLinkDone, + setIsCreatePageDone, + isAddLinkDone, + setIsAddLinkDone, + isStartTrackingDone, + setIsStartTrackingDone + ]) const completedSteps = [isCreateLinkDone, isCreatePageDone, isStartTrackingDone, isAddLinkDone].filter(Boolean).length From 4347037589d5534a3d11bb961568d1c04b13f3d3 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Mon, 9 Sep 2024 19:38:43 +0700 Subject: [PATCH 072/124] fix: deleting unnecessary log --- web/app/(pages)/profile/_components/wrapper.tsx | 2 -- web/components/la-editor/extensions/link/link.ts | 1 - web/components/routes/SettingsRoute.tsx | 1 - web/components/routes/link/list.tsx | 1 - web/components/routes/link/partials/form/link-form.tsx | 1 - web/components/routes/tauri/TauriRoute.tsx | 2 -- web/components/routes/topics/detail/use-link-navigation.ts | 1 - web/lib/schema/index.ts | 2 -- 8 files changed, 11 deletions(-) diff --git a/web/app/(pages)/profile/_components/wrapper.tsx b/web/app/(pages)/profile/_components/wrapper.tsx index c1f2c445..cc7257de 100644 --- a/web/app/(pages)/profile/_components/wrapper.tsx +++ b/web/app/(pages)/profile/_components/wrapper.tsx @@ -33,7 +33,6 @@ export const ProfileWrapper = () => { const editAvatar = (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (file) { - console.log("File selected:", file) const imageUrl = URL.createObjectURL(file) if (account.me && account.me.profile) { account.me.profile.avatarUrl = imageUrl @@ -74,7 +73,6 @@ export const ProfileWrapper = () => { if (account.me && account.me.profile) { account.me.profile.name = newName.trim() - console.log("Updating name to:", newName.trim()) } setIsEditing(false) } diff --git a/web/components/la-editor/extensions/link/link.ts b/web/components/la-editor/extensions/link/link.ts index 26eef782..b737c2ec 100644 --- a/web/components/la-editor/extensions/link/link.ts +++ b/web/components/la-editor/extensions/link/link.ts @@ -49,7 +49,6 @@ export const Link = TiptapLink.extend({ * This will move the cursor to the end of the link. */ if (event.key === "Escape" && selection.empty !== true) { - console.log("Link handleKeyDown") editor.commands.focus(selection.to, { scrollIntoView: false }) } diff --git a/web/components/routes/SettingsRoute.tsx b/web/components/routes/SettingsRoute.tsx index 26a3f9e2..ccefbf2e 100644 --- a/web/components/routes/SettingsRoute.tsx +++ b/web/components/routes/SettingsRoute.tsx @@ -97,7 +97,6 @@ export const SettingsRoute = () => { const [topInboxHotkey, setTopInboxHotkey] = useState("") const saveSettings = () => { - console.log("Saving settings:", { inboxHotkey, topInboxHotkey }) toast.success("Settings saved", { description: "Your hotkey settings have been updated." }) diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index f7cefd82..ac68e6bc 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -167,7 +167,6 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex return newIndex }) } else if (e.key === "Enter" && !disableEnterKey) { - console.log("Enter key pressed") e.preventDefault() if (activeItemIndex !== null) { const activeLink = sortedLinks[activeItemIndex] diff --git a/web/components/routes/link/partials/form/link-form.tsx b/web/components/routes/link/partials/form/link-form.tsx index cf5b9fa9..8e65a2ea 100644 --- a/web/components/routes/link/partials/form/link-form.tsx +++ b/web/components/routes/link/partials/form/link-form.tsx @@ -135,7 +135,6 @@ export const LinkForm: React.FC = ({ shouldValidate: true }) form.setFocus("title") - console.log(form.formState.isValid, "form state after....") } catch (err) { console.error("Failed to fetch metadata", err) } finally { diff --git a/web/components/routes/tauri/TauriRoute.tsx b/web/components/routes/tauri/TauriRoute.tsx index beaf550a..d55caa73 100644 --- a/web/components/routes/tauri/TauriRoute.tsx +++ b/web/components/routes/tauri/TauriRoute.tsx @@ -5,7 +5,5 @@ import { useAccount } from "@/lib/providers/jazz-provider" export default function TauriRoute() { const { me } = useAccount() - console.log({ pages: me?.root?.personalPages?.toJSON() }) - return
    {JSON.stringify(me?.root?.personalPages)}
    } diff --git a/web/components/routes/topics/detail/use-link-navigation.ts b/web/components/routes/topics/detail/use-link-navigation.ts index 5726626a..5e72054b 100644 --- a/web/components/routes/topics/detail/use-link-navigation.ts +++ b/web/components/routes/topics/detail/use-link-navigation.ts @@ -27,7 +27,6 @@ export function useLinkNavigation(allLinks: (LinkSchema | null)[]) { const handleKeyDown = useCallback( (e: KeyboardEvent) => { - console.log("handleKeyDown") if (e.key === "ArrowDown") { e.preventDefault() setActiveIndex(prevIndex => { diff --git a/web/lib/schema/index.ts b/web/lib/schema/index.ts index 3ba49c10..12ebd60e 100644 --- a/web/lib/schema/index.ts +++ b/web/lib/schema/index.ts @@ -44,8 +44,6 @@ export class LaAccount extends Account { // so just do default profile create provided by jazz-tools super.migrate(creationProps) - console.log("In migration", this._refs.root, creationProps) - if (!this._refs.root && creationProps) { this.root = UserRoot.create( { From 3dc440632f94cef773dda34d830c0683d8114b54 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Mon, 9 Sep 2024 20:05:36 +0300 Subject: [PATCH 073/124] pages active sidebar background --- .../custom/sidebar/partial/page-section.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/web/components/custom/sidebar/partial/page-section.tsx b/web/components/custom/sidebar/partial/page-section.tsx index 8fe4e59c..94216769 100644 --- a/web/components/custom/sidebar/partial/page-section.tsx +++ b/web/components/custom/sidebar/partial/page-section.tsx @@ -57,7 +57,8 @@ export const PageSection: React.FC<{ pathname?: string }> = ({ pathname }) => { const [show, setShow] = useAtom(pageShowAtom) const pageCount = me?.root.personalPages?.length || 0 - const isActive = pathname ? pathname.startsWith("/pages") : false + // const isActive = pathname ? pathname.startsWith("/pages") : false + const isActive = pathname === "/pages" if (!me) return null @@ -74,21 +75,24 @@ interface PageSectionHeaderProps { isActive: boolean } -const PageSectionHeader: React.FC = ({ pageCount }) => ( +const PageSectionHeader: React.FC = ({ pageCount, isActive }) => (
    -

    +

    Pages {pageCount > 0 && {pageCount}}

    -
    +
    From 3f6ff82063a73620e1381b1a55b088ad70b13c92 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Mon, 9 Sep 2024 20:17:13 +0300 Subject: [PATCH 074/124] date format --- web/components/routes/page/list.tsx | 2 +- web/components/routes/page/partials/page-item.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/web/components/routes/page/list.tsx b/web/components/routes/page/list.tsx index d5796a73..149cb9ce 100644 --- a/web/components/routes/page/list.tsx +++ b/web/components/routes/page/list.tsx @@ -64,7 +64,7 @@ export const ColumnHeader: React.FC = () => { Topic - + Updated
    diff --git a/web/components/routes/page/partials/page-item.tsx b/web/components/routes/page/partials/page-item.tsx index 1f9c3d29..6b484eed 100644 --- a/web/components/routes/page/partials/page-item.tsx +++ b/web/components/routes/page/partials/page-item.tsx @@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/badge" import { Column } from "./column" import { useMedia } from "react-use" import { useColumnStyles } from "../hooks/use-column-styles" +import { format } from "date-fns" interface PageItemProps { page: PersonalPage @@ -47,8 +48,8 @@ export const PageItem = React.forwardRef(({ pa )} - - {page.updatedAt.toLocaleDateString()} + + {format(new Date(page.updatedAt), "d MMM yyyy")}
    From 6f77158aacb56b3e4e48447b2d836c1c851355c2 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Mon, 9 Sep 2024 20:48:29 +0300 Subject: [PATCH 075/124] link isActive sidebar --- .../custom/sidebar/partial/link-section.tsx | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index 46a6e359..0ac854aa 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -1,6 +1,6 @@ import React from "react" import Link from "next/link" -import { usePathname } from "next/navigation" +import { usePathname, useRouter } from "next/navigation" import { useAccount } from "@/lib/providers/jazz-provider" import { cn } from "@/lib/utils" import { PersonalLinkLists } from "@/lib/schema/personal-link" @@ -32,28 +32,31 @@ interface LinkSectionHeaderProps { isActive: boolean } -const LinkSectionHeader: React.FC = ({ linkCount, isActive }) => ( -
    - -

    - Links - {linkCount > 0 && {linkCount}} -

    - -
    -) +const LinkSectionHeader: React.FC = ({ linkCount, isActive }) => { + const pathname = usePathname() + const [state] = useQueryState("state", parseAsStringLiteral(LEARNING_STATES.map(ls => ls.value))) + const isLinksActive = pathname.startsWith("/links") && !state + + return ( +
    + +

    + Links + {linkCount > 0 && {linkCount}} +

    + +
    + ) +} const ALL_STATES = ["all", ...LEARNING_STATES.map(ls => ls.value)] interface ListProps { @@ -62,31 +65,26 @@ interface ListProps { const List: React.FC = ({ personalLinks }) => { const pathname = usePathname() - const [activeState] = useQueryState("state", parseAsStringLiteral(ALL_STATES)) + const [state] = useQueryState("state", parseAsStringLiteral(LEARNING_STATES.map(ls => ls.value))) + const toLearnCount = personalLinks.filter(link => link?.learningState === "wantToLearn").length const learningCount = personalLinks.filter(link => link?.learningState === "learning").length const learnedCount = personalLinks.filter(link => link?.learningState === "learned").length + const isActive = (checkState: string) => { + return pathname === "/links" && state === checkState + } + return (
    - - + +
    ) } @@ -104,7 +102,10 @@ const ListItem: React.FC = ({ label, href, count, isActive }) =>

    {label}

    From 522a4e13376fc66ac1005124f8a233a291d10a51 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Tue, 10 Sep 2024 03:08:12 +0700 Subject: [PATCH 076/124] fix(link): listen on the list and fix timeout --- web/components/routes/link/LinkRoute.tsx | 36 ++++++++++++------- web/components/routes/link/list.tsx | 1 + .../routes/link/partials/link-item.tsx | 11 ------ 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index a3a645d7..e5bde331 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -1,43 +1,53 @@ "use client" -import React, { useEffect, useState, useCallback } from "react" +import React, { useEffect, useState, useCallback, useRef } from "react" import { LinkHeader } from "@/components/routes/link/header" import { LinkList } from "@/components/routes/link/list" import { LinkManage } from "@/components/routes/link/manage" import { useQueryState } from "nuqs" import { atom, useAtom } from "jotai" -import { linkEditIdAtom } from "@/store/link" import { LinkBottomBar } from "./bottom-bar" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" export const isDeleteConfirmShownAtom = atom(false) export function LinkRoute(): React.ReactElement { - const [, setEditId] = useAtom(linkEditIdAtom) const [nuqsEditId] = useQueryState("editId") const [activeItemIndex, setActiveItemIndex] = useState(null) const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) const [isDeleteConfirmShown] = useAtom(isDeleteConfirmShownAtom) const [disableEnterKey, setDisableEnterKey] = useState(false) - - useEffect(() => { - setEditId(nuqsEditId) - }, [nuqsEditId, setEditId]) + const timeoutRef = useRef(null) const handleCommandPaletteClose = useCallback(() => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + setDisableEnterKey(true) - setTimeout(() => setDisableEnterKey(false), 100) + timeoutRef.current = setTimeout(() => { + setDisableEnterKey(false) + timeoutRef.current = null + }, 100) }, []) useEffect(() => { - if (!isCommandPaletteOpen) { + if (isDeleteConfirmShown || isCommandPaletteOpen) { + setDisableEnterKey(true) + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + timeoutRef.current = null + } + } else if (!isCommandPaletteOpen) { handleCommandPaletteClose() } - }, [isCommandPaletteOpen, handleCommandPaletteClose]) - useEffect(() => { - setDisableEnterKey(isDeleteConfirmShown || isCommandPaletteOpen) - }, [isDeleteConfirmShown, isCommandPaletteOpen]) + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + } + }, [isDeleteConfirmShown, isCommandPaletteOpen, handleCommandPaletteClose]) return (
    diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index ac68e6bc..f15a9ca6 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -167,6 +167,7 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex return newIndex }) } else if (e.key === "Enter" && !disableEnterKey) { + console.log("Enter key pressed", { activeItemIndex, sortedLinks, disableEnterKey }) e.preventDefault() if (activeItemIndex !== null) { const activeLink = sortedLinks[activeItemIndex] diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index e79f5d7e..43b53127 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -48,16 +48,6 @@ export const LinkItem: React.FC = ({ [transform, transition, isDragging] ) - const handleKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (e.key === "Enter") { - e.preventDefault() - setEditId(personalLink.id) - } - }, - [setEditId, personalLink.id] - ) - const handleSuccess = useCallback(() => setEditId(null), [setEditId]) const handleOnClose = useCallback(() => setEditId(null), [setEditId]) const handleRowDoubleClick = useCallback(() => setEditId(personalLink.id), [setEditId, personalLink.id]) @@ -89,7 +79,6 @@ export const LinkItem: React.FC = ({ tabIndex={0} onFocus={() => setActiveItemIndex(index)} onBlur={() => setActiveItemIndex(null)} - onKeyDown={handleKeyDown} className={cn( "relative cursor-default outline-none", "grid grid-cols-[auto_1fr_auto] items-center gap-x-2 py-2 max-lg:px-4 sm:px-5 sm:py-2", From 421bd9b0b6e8d21c3f1109ff0230bd13aeb9cf42 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Tue, 10 Sep 2024 03:09:58 +0700 Subject: [PATCH 077/124] refactor(link): remove log on enter pressed --- web/components/routes/link/list.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index f15a9ca6..ac68e6bc 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -167,7 +167,6 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex return newIndex }) } else if (e.key === "Enter" && !disableEnterKey) { - console.log("Enter key pressed", { activeItemIndex, sortedLinks, disableEnterKey }) e.preventDefault() if (activeItemIndex !== null) { const activeLink = sortedLinks[activeItemIndex] From 5466965fa633566e607f85078c34efdb4d8852be Mon Sep 17 00:00:00 2001 From: Aslam H Date: Tue, 10 Sep 2024 10:57:23 +0700 Subject: [PATCH 078/124] fix(link-sb): mismatch size when active --- .../custom/sidebar/partial/link-section.tsx | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index 0ac854aa..a2c88f39 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -1,6 +1,6 @@ import React from "react" import Link from "next/link" -import { usePathname, useRouter } from "next/navigation" +import { usePathname } from "next/navigation" import { useAccount } from "@/lib/providers/jazz-provider" import { cn } from "@/lib/utils" import { PersonalLinkLists } from "@/lib/schema/personal-link" @@ -33,23 +33,18 @@ interface LinkSectionHeaderProps { } const LinkSectionHeader: React.FC = ({ linkCount, isActive }) => { - const pathname = usePathname() - const [state] = useQueryState("state", parseAsStringLiteral(LEARNING_STATES.map(ls => ls.value))) - const isLinksActive = pathname.startsWith("/links") && !state - return ( -
    +
    -

    +

    Links {linkCount > 0 && {linkCount}}

    @@ -58,7 +53,6 @@ const LinkSectionHeader: React.FC = ({ linkCount, isActi ) } -const ALL_STATES = ["all", ...LEARNING_STATES.map(ls => ls.value)] interface ListProps { personalLinks: PersonalLinkLists } From 4ddb64adddd4966c00ee40659ed4df9ef8381bc8 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Tue, 10 Sep 2024 12:49:32 +0700 Subject: [PATCH 079/124] fix(link): force cache url fetching --- web/components/routes/link/partials/form/link-form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/routes/link/partials/form/link-form.tsx b/web/components/routes/link/partials/form/link-form.tsx index 8e65a2ea..e1f95ead 100644 --- a/web/components/routes/link/partials/form/link-form.tsx +++ b/web/components/routes/link/partials/form/link-form.tsx @@ -118,7 +118,7 @@ export const LinkForm: React.FC = ({ const fetchMetadata = async (url: string) => { setIsFetching(true) try { - const res = await fetch(`/api/metadata?url=${encodeURIComponent(url)}`, { cache: "no-cache" }) + const res = await fetch(`/api/metadata?url=${encodeURIComponent(url)}`, { cache: "force-cache" }) const data = await res.json() setUrlFetched(data.url) form.setValue("url", data.url, { From 0b1ea7bc0d7817a324712291448e164b6cafde93 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Tue, 10 Sep 2024 13:08:30 +0700 Subject: [PATCH 080/124] chore: register tooltip provider --- web/app/layout.tsx | 9 ++-- .../components/ui/toolbar-button.tsx | 50 +++++++++---------- web/components/routes/link/bottom-bar.tsx | 16 +++--- .../routes/link/partials/form/url-input.tsx | 48 +++++++++--------- 4 files changed, 60 insertions(+), 63 deletions(-) diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 4a63cc2f..b9dd669c 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -9,6 +9,7 @@ import { ConfirmProvider } from "@/lib/providers/confirm-provider" import { DeepLinkProvider } from "@/lib/providers/deep-link-provider" import { GeistMono, GeistSans } from "./fonts" import { JazzAndAuth } from "@/lib/providers/jazz-provider" +import { TooltipProvider } from "@/components/ui/tooltip" export const metadata: Metadata = { title: "Learn Anything", @@ -20,9 +21,11 @@ const Providers = ({ children }: { children: React.ReactNode }) => ( - - {children} - + + + {children} + + diff --git a/web/components/la-editor/components/ui/toolbar-button.tsx b/web/components/la-editor/components/ui/toolbar-button.tsx index 8556a35c..7a4371d7 100644 --- a/web/components/la-editor/components/ui/toolbar-button.tsx +++ b/web/components/la-editor/components/ui/toolbar-button.tsx @@ -1,4 +1,4 @@ -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { Toggle } from "@/components/ui/toggle" import * as React from "react" @@ -16,31 +16,29 @@ const ToolbarButton = React.forwardRef(fu ref ) { return ( - - - - - {children} - - - {tooltip && ( - -
    {tooltip}
    -
    - )} -
    -
    + + + + {children} + + + {tooltip && ( + +
    {tooltip}
    +
    + )} +
    ) }) diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index aaae2665..995c7ad0 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from "react" import { motion, AnimatePresence } from "framer-motion" import { icons } from "lucide-react" import { Button } from "@/components/ui/button" -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { getSpecialShortcut, formatShortcut, isMacOS } from "@/lib/utils" import { LaIcon } from "@/components/custom/la-icon" import { useAtom } from "jotai" @@ -31,14 +31,12 @@ const ToolbarButton = React.forwardRef( if (tooltip) { return ( - - - {button} - -

    {tooltip}

    -
    -
    -
    + + {button} + +

    {tooltip}

    +
    +
    ) } diff --git a/web/components/routes/link/partials/form/url-input.tsx b/web/components/routes/link/partials/form/url-input.tsx index a90c4eb6..83878fb9 100644 --- a/web/components/routes/link/partials/form/url-input.tsx +++ b/web/components/routes/link/partials/form/url-input.tsx @@ -4,7 +4,7 @@ import { FormField, FormItem, FormControl, FormLabel, FormMessage } from "@/comp import { Input } from "@/components/ui/input" import { cn } from "@/lib/utils" import { LinkFormValues } from "./schema" -import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip" +import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip" import { TooltipArrow } from "@radix-ui/react-tooltip" interface UrlInputProps { @@ -38,30 +38,28 @@ export const UrlInput: React.FC = ({ urlFetched, fetchMetadata, i > Url - - - - setIsFocused(true)} - onBlur={() => setIsFocused(false)} - /> - - - - - Press Enter to fetch metadata - - - - + + + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + + + + + Press Enter to fetch metadata + + + From 27763ae2305547c183ffe311db682b80a6cdcc3b Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Tue, 10 Sep 2024 11:35:02 +0300 Subject: [PATCH 081/124] sidebar: fixed link active state, /docs link --- bun.lockb | Bin 401256 -> 401672 bytes .../custom/sidebar/partial/link-section.tsx | 25 +++++++++++------- .../sidebar/partial/profile-section.tsx | 11 ++++++++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/bun.lockb b/bun.lockb index 1cc4225034ce52d3aa4bce0dda15b3c77d49214c..a9aec6684faaf853296f72a5a6aa58994ac9561e 100755 GIT binary patch delta 76546 zcmeFadwkFJ|NsAby?C(~7RzZWp@T?sDB759bYPoCPMIVPW5bNiVIyriv9U_ecu*-S zMN+ARN@rzim3k|bN~Nf`&WcL?uD9povE}{#_4!=B-^=Cq$L|lX%O1Nu?$^ip_!d|bcfEdVgcfHPjtK;A zg)82)!nn!#W3vP4)RwxIzJsb_{m-!l$D_*kLbw{x7FAC)b6Q8Lguk3^0}i0-vX9Yv z=)cbP9u9RZiRustL}PZKT;a)Clk;=O5*1q`R-BdRk6;P5@}G(-n=wucvJ3Mk&BzYS zjSB?mtJ0aM^2tRNFDI)of9B*s;MtD0oR%(pO76H~2C^g&0jL2(U54qXIw-|yZeiT` zN%^A#&vmvLcY~{dJ7?FROyANsT>LRP`4e-;W)+Mp z%$|`wd0JsXcGlRjxdl_EUuY{5A8+S)*5u;6tdePCatf!9Dab0!4h&DQJuxM>AZr>; z$eTr}yb`**$)A%wSrusQ><(x&1*ejZ=~wD^6Y+oVVdv^MsD^A)qD{Zf>CBwmF*(G` zEzB+|$;Ok?)3)H`B%9%stZ6xM6SIpyB?Aq`k8r<-awq3b$to=T04{rVvQ2jkRTsbI z_>vS`a2cu!Uf;{cABt+|;(A*>qZi|^W&b=tlle(h1%63H=|dOU41PhC@Orok{)deG zd^%oYEArp@q|if3*A7)V)r+b0Qt#dHg(ZcPrjJ+0e&cqj=TdF-wWvCF398*?A*yOj zLu1hqXbV(pCXNNno;4|#sdpsZR{QRLwmL^prTf-t1*#b{8$FpdQ2H$i6d^ssCRlYz!3Y=xdcwJlyX$V;!=vE-+rfxs!4yPdv>sskQ!dMm1W z&2)MVs_fEH?VcB+s{gsD#y!UAox`nN2fIG}k0G}BtFE;6PuR4lap{!&!f7-*FnW67 z==|b9AbW&YFRJaJ&#toR$7au%ls_d97@IwHdKTN(2XN*0)<_#ZA%9v{ZUE5K_UkBA z^^1(M;rXcFucN%%quQ2KUkv{HB2j(y`tOU$KVJm?zDQJGH2(YIQhkl6zQ~kP2Zq0n zz1ny;Mb0c~lVeA4`sA^>V`xG&wt8|(RzYEQH#%bSwA^XA*@fSZx7%lTU}x0l z*y@R~`FYc)KT)xAC$Hizknnm^ao z=_U0G?2P)3QPfO*3swE@nrcVvW5++FEMd~~Dv(t$-XDWB zm+nGTOZN?CXEb^iwh9V3ecm-_OhNXfY2W&rXABsG9%qe4E+r zsJdsC(`;1TF#uJ(mgTm)XQ8TfE*j(IMW1>~r-gPWXo_kJ_+x<`sDCf;o{8=l-TGGB zZL`qRykpTFg4cWPPwG@s+p`%BKvlp122;(Qc!%W|p|USnY%5s3a}`b-8<#zKMxbzJ z0i6^Gj3l0>)c~h2FR}LN#!kZ~sD@)~{+HrEdOZO^M4NJs~hApQ|$$ zy?8Rxg6N29tK_^jA-ix|!2d;o@psw<@D{3g&F`}Il!@cxxS9vXU3I) zo{BzCJY}PkL)N6iz+=w77*+LlK5VB$b+xKj*?(8>zpmFMrPUYg`^ZT3d!CAD8Bef> z+`t`hwQoAw3eA1gRwx72^=1gX4O)HjD9)P1m33NR`(w628y>gQi6e8?q^z;otFcwi z9cU9ByB{Hgx5*u7|3{Khh&wiJzjX;2CZtzwBja z5U0j5)8cXq?;~6-t3WkR>po|D?k$&Y66rKlzrvNzztL`*kkw;VI4P?zhbi?K2^zwy zn4;2Ys21{r&)c54$LoD^tCH|bcA_M|jwzW#S!&;uxJkKrxzhr3x7)I^i?YXL5vko9cEO&CYIqu=>W46@h585a zRfqh^+0(f41-Sg>j)@yLc?9{YZC$Hu`rlXB^ht1y%ILTJX;D(s;R67=p5$dsNb`#U|oNcPi=Sw`KlGVuEdR*l=}l*rB5ly&zmx>FmS^@KVR?HrZMf4K67J>YBH>% zOmz{-rsoC%f#NO+8nS@#x-@GQQ@!N;6BKl!m(`4|=YUPq2UTk)6msMv^p1meqbfnQ z5mq1g^03vLBb=tAs(*KFe=r-=4*FHAUaNV$Ly*UXdO~6T`KHY==l68W7Wn4m*LM*L4UeWa}`?_ zu^D%1U<;~1wWP*rQD=?G*;CJkH$%02ABhh7SHm-0yuy=${xYfEQ01~6Hm`3h@G}AG z(Sw%bJokdQx^^Ahs&7pMtc^?|1rB>!5!YdDwN)f?>iNhSHCM;8YI zub*MZn9H#Lo@DBocI@9HVQUgjAY)}vedwuvKWs2uElNVwrK`>k`W0DH8URX zU{l4MXR9AZ)qlUhHNbnYl|HK|J8leHSei?hf@)?oqZz7RRYx1|DU^Oax3v1b;N<{C ztUkluimigjqw1dl-lCT0lnglEPTVux$)q}VC0r}&DOAm=F7|$GHD@h#)G!ugkL6vD zKp?7{t^N>HrT*U4_UfekvHr=oI-f0Yg}a-XP+;AY-A}(am|2wb>KP5A0#jzkCjOQ? zZ^*bopFcS9j?s@@(dg)7*G7MtcINzNAAC1<=8p4DX?k|d+%pHixn<$ceVTk%{95;4 zrZuQv>@B+K)9XsEX|evjq?-q9Tfg+O%s%(-efYlS&mQrj&gp#38>f7KR(@0SQ1Ea; z-FZiPeR#@-$~v>R4Lmk`&WCT0Z}R%XeMjsI-1NrHcXV4{ul@yROuq2;vzI)*Gp+Zq zH3O%PxwPRg^FCP9>hmpK*Z%ff+nmEwXY~o(_+_Kki|fXO9@;Rb*DEKzwZ?nAZEVy% zCr#QhKmFO;pWM~z`&x}2NqD?z^3@-Y>a};n#8>V(FX(-L=ISR`oK+h1I=8*tn}2R2 zZ)w|{sOk)#Og$%|miOYht-S;1w(~l7Xx-?m@>8!0j%wR<*3fZv7mbQp=oNM7_2i2k z8ng^|ruPEg!1Va=8_dQ;ucBvSs9|%jJSRDl&V)%PIOxUniI2R*C|`tivNy12e54km z)x)=9#>I#FxA4+uCr3Qsg}_?gj$ZMh$`)Svs^rLQcEn_xOLBbp3#{|KiV=yCX6z4_ z_=Y4M@u zXM5@C$)V!5UO95Gt+zcrIh@PA@F1@uB{A|WJ~f4;SZK{-X;uba;{gNksRrJuD!qZ>kyfV)zPmJ zlOwze>pU;I57`pAe{!g42XA}-V}@d4qp0z z@sSj)-h_p{9sLucFw}WWP=gveKhE2Jd2(b$Tp%zG$b3$X57(hGl$4qn zx~QX9J}@~l51Sbx zH$e4A&>P6|Z-rMa3Hx(oWLK|zSaRsGuHN=x$&t?pQ&X7WPbLJrdFh$Vu5Nr>=T-Gj zjBLW^mfPq=ufg!1kt8A~5Wki;kg0luv*^u?_{g*W5%v?7>P{=7THp|dhV6K!?1#Mjk>zF zdMKaBJt5uRM+@Ct(EUW~7g)B?*%10%KeoykYk4lbQNY-jL14x%l#?o*` zyqMwfk&Re3Kh2c-DgMSVAjL*uR~{7~c@Rr240t<8e!zuA`IUR5*Ku$&8Z?B}k56Sryn$E5hn~O0E6+}j z`~g&kb}%oz)T=ixIkfmvFMV8c1ax{RI>W1%mmJE> z@Y0br8D4o_a^wiD97L45eg%j3w_U}0A?qixE+>o%V$~X8s~a*q>ID;OV3$CUs9z5BACnl0&-(d)o_= zBNq&@o$7Bwp@~Dh^upxO%R{{K!sKu#X7ezwqA)RhbB%8=zM(!le^|9|9=^do+f(C< zWB3O8>^gh{d|ws5e)5H4hkNyAB!@-~_tIx1M;;t*=L<80Eo|R#Z#%H{mDLkM$F6x; zn%M!3MR+S#H?LxP&)}8b_L<3%E+c9tAsfh4EUgcJY{KiYI{OoDA3mE0YcO>FRbIW~ zc&@l+h-+*FTI++ z_cD?a!>jO(^?gl8aS-u+^YD$6FLDT<60;xoj1RYExYB)oJw9!f)L(lA77M<#zPF=y zd?Y%{UUTimI|=J@ol3$L`1*JiX^D~d@M#w!^_YaH(RLOQoqe$oYl01;?t8JQpCV!< zj;Wq7#0=ksHCh=*j^Wdavax!Pt;w8WFjzypD%SEh_*fAxR(IY4qSnq@hGl2DhHgJr zx}T=gIGcukoe>|IgLP3&nisG-W3f69Bt&5_b2RoN;v;>>R|n~0F%#=TKbfq~Id%!L z)p1pyk>i!$lpI+LWHGrb#qU_!uk3ZBQ?4C-P8e*x?SG@WSIp1=D=W?=R4WA3;tAvn=1pPw9QGRaO?u6~p= z1j{eSENT%<&}Y@knTro$d$3%MK2C_rv)igYDh$&tf^sa{-yhQvqG@~hLS1r=DznijBZw&r{33zH)!O{wmV z9XaulL0F2yaAB2Wsf&FpbXI|vUXdJmxS)C_a`_GYT;Of5NDd_xdi8Ee4&7GhrQebq z*lU znug_)GbiR@Y1!IS%w{Y*R_epUf2U)u#T8dyJO&ar2g|M<9WZxc*>%7*D}2fP*LvlPlOs<9 z*~1uIC2u?@5V*2BK{yZV3csIT!IxRhBd1&*p~mW8kg9H+L&XoYI2Wk z!TLL0V8-_Qzmmh@`TicNn+kkAs(Iu` zEIX4lJA0KKZ{32GS`+hqtiFC*bij`*_qMN0jw~#g2Gtbx6PBOOUqBaE*d=3Q&c$-8lnU>}(m2y(7DAg_ zY=zlyd56Fm{-rXq4a@enB6hvi?p^-25nh7Dq+QXowmkNjbz5~uDEe(!s=2Mf9xT<> zKYB#YTU6bSft=^>sj)~G{Mjp8lM?LeRjx^il-zE2KQ{bg?s~9#lZQR&H}mXO-EP_0 zSQit<0f9GkR%7+^Ee+65SczC+|IYHsJM6tWcNVO~BUlP!dQFIr^j>VsV`ON-O;{Rq z4!K;^U%G8pLys~vEk#kHwPR+j>4KPt=_A(I}gHAn7zB1kJS~6>ophE=dhBkMY`XxRCoI>LDyyW zFxApiZU4aHcD@vgmfUiuy)4qlxd~Ai%EaDUoq1RF4UqQMF<7>6-8n3)vDC%;v6Rlg zY(&ny+gjYblP(J@!4Ff+Wmw9E&7WIWjPrd%Sq7Ke5v5-9<0Cg?4fn&eV}61)0LvZ+ zQ}5wO>{|oLaygdz+fNt##!GuDC3N1sUinkWky-cJ$=y`v#_-En=X=qdwn8iHfYtTi zMCpTNYpD8OhZS$Lqu|vm1A!4WR;~N&dbU+e$I_&;9hr82AdrLQPx#<3USwlRMkGaFka`jJqA9=eYlP3+p`cV2#nR->|e${VQF#&uWI$%g9TNyp6Ah zhfZDNrEg9S&h^SRr-XN}VaOCI)Z$^U-ZRO;fnM4(Dd9yA(_SxQb7FWuzKi4wowL@f z_iS=x_}c1yL$iDh)+H3l>R?VC!cxcB>2UTV)ssVaK4Y*hB8;QVEZ$(l;_aJx`GpSwuJNKbCWdc(ls(3;*+=-U^nDjTrsF)Jk@xXw z8Z(pFKN~&n)$5ZS?Bk`qkP><0@#FWjcI&Ha%1jDg<3(Of3EuCey_gdInUJf!ip`0^ zL0;vHDUqd5RBvM(FoPd^X)mROM?a~}j5y&<_)5KumlA^sUgYJJ@T#W*ftg6N{j z5Zg!TvR-FH0^{wBc zy!glxEW7RK_UTQm?&LzuN%7%^FKO24_NgB}U3S?E$=$=U(?-j#3hN5OnA6PLgqLl} z_DWHX)yq$(Q_gm0Q7hV2?-iR1?Mh}gRHZ}~Lwed$sLKwl&RA?BT*Xd%wdTT4*jOy} zmw%rSxerV0h^q`?dz{7fVMKiRfc zShL=;Cna0xyI7iV90$1Sw0qmnfxGt^`2N1mJc@M*u{aVjeSXAB#bUCt@%DMAdZ;u2 zH)AO#Z#UBA+p*MKF70n6)ZU>Lp!XvN;nOGu{7c96SZ)t^Bq0hz&89x2{|n15*F}pG zqTW5e83kCj@3?;pKH-&pm=ZkXReqQfY5QJHytN5Y7&Sc+toVt#<0Zilvs@bIG4re=m+3cJXc+VRk&<$I?vX==UP`@Vo8Y zV+J#&CSfVe#@vkM>d97EZ;u@t7EXCW6h=(};dimP$@r>g?GH5r`1C0<5noM&$jex+ zu9On>(eb@>F_u;h8{JfH^|8*wV$4S*C}K|xeGnBNX}GugUMyx#LKKE(7|W4^=u=p3 z1+nq{h?PW`-wG~si$IceWZ*S&VmN|S%@RjXQt`-^b!D(LQ zH!0z@u%TYY-kwnhY{Dk?jccsFWW!uz*IR*g0ag?L-OQ&B)|?I5cuxDe`Vyq=YciId z1+t#Py3FQ+75K(l_MNiJu+nS7mN?6PMDjJ3x|fZP9i#2P?V7YF@DePIyxsVp!%}(n zP!~F6cN6>O`z2Vt{1SGg#D|y3@+xwBdS&0IL^^$IV{+LVoDhX^iJztB;07#BI~JX; z30NJmn7YOBkyF2`&Qi@Af#t4cS8+3drHa+}uea}GX*BIQqR#hP@>Qb}Ba`uIeC+=I z1lEPVrOovK7AF9+7z|~ltZ7de)Em*dG1A8WTWk06`k9d_or$pj@wDZ7T z??++TSZb4TVgB7!xDqQvZ>a|}y~r;qkr>1{_)>{suUnsEX>iGdnzcG= zw+7qN!B}c3rS*;v--4CwzYG5+KJ^o^s72^EI|`gYxV-eoQW%#jHl7<@7;~JMJDg>= z%$VP+2VLzPjHOM9i|}m;UfS;|!56%;-%}#LInD(tpU>|8up_~$- z)tbb}pZL^w_N?0HFWW!1e0zS0hy+yC4=~aX=ZC7KuXnoi|dnu6p`eK6>z)Wk^LNYK4a7PBs)HU@W~9MvDdm*U4B_(FW}B2!j3 zm=eC5?>ySiRX)R~E@Qm7AwGpKRobfZURgL3i}#($`!#%5)l~J2lU(ZXe0+S%gwu31 zU%R9p97OtBUut4_E@`i!{P$UbugB1X%4YPjpyBLEXDHAIpGhnF89wr9hx#~aFz(~G31#@LyCF==${h9 z-{KqM7kW`M`ohmZ`)D~ac?bHR2nc%mF&=YJ6Ij_NL_?EEXGQ+CBJe zt#g?3PSC~kXU`pfxpq2^y3UlXutgF4M^u$P4z7?wOw%3zM zShmhOGVR3DZp}~}Nr-A$Gs_7o#2W1f@g`bur-?M9j23)hRNYPOE>AFJLTf?lSlgJN zu+p&D4EgAzXDho`P;_>Du)vfxC&5d=D~ZAm&W<;>1VL6Z2R?6owXm#M|+dZ*;?`?{D_+5~KxX8{deppQ#kpWCZ=DQX7xN-UdGiuqwsXC#*ttD!|HIZ?Nx@5w-m0!vP=74`0NVP!R2(m z+3F?+3wjcky=28aln{ks%j8BbdV)zTZtS5WHn{aR(($Uha0!BYF{`RAZ_u`b3s)&H_e(+h067!woW zy(_F9`uL3rQQ4LaYd^pq>)P;@TP33l*bK&EX*DsksOIZf8dZB8Zqm&bXRnr5WA!xA zjTi=ensNTL4t``R+fl&D7y8$U`H7LS_|(S?KP^~+W%mrmCiw2g%J#u$gC*kvfxz9k zAZ|HMFKI2@3a9s@3V#51GVW<-Z$v}5%{aY|*Sgp*;%ehw#p!jtD*xB{+l4?F&zm?E zP=(V=s)Rdm;_u>=U?)xme&Fmq&i)8hKKpQWaNNcC@whedZBE~Q`X5nMjPs)({yXLC z+tdLL8h!*$1O7{@f+DC;J$^MqTRYB9NP%6KqX)atUZ@RPpHzi85KtGq@ zGM7LaGF6>|%^4d`)-HIU(?L!Lqk8?5YAR&9aH+h*`K5M^aQ0P#yrkk+J3h*B)YR`B zjFdzXINm|2yg5#D9hWMTiKymRf#b)kI&nH&K{K4rbn*U4HIL`;$0dmG@e8P)E>dN9 zz02qZmyuN78=ZZ;s)9E=e!L3J43zsT>Ie%Q@zsdos(QT*-_E3HTcj%W>apUac zRcI-{q|02mRPpb2e7WON@fFTqQQhJiP#xeR-jAxLs~rEoQKfsxrIR+o-stRVEvfeW zlPZJFB&dhJ;u1>Lg4a-mZ*%;36{_Tybi30xoW3c@OUh44pvncjjH_R_>HuX?t5RGb;G)DOe zG)xK~#s(f0Z>gqF{o`Wh~oU^;2%CBpP@*Csn>jL_@fc~f^U8dt#p~`rq3%>@{ z#+8Gr;C#mmoK8no;A|IuJ*o=bjOry-e)B?Z{m%o`2i)!=9IuM#xdeB(aH+gY95;?j zRnT3iT6mAsl_)=f`}w8(9&oymojf>hSXxs)FA`mGS#dccaR1kJArPz5YoR?<1G)6I4<6Ir|G#J#@hG zlEBvr3RL_cs_?KPYyQil$rO!> zHR6{d#>n_D6>8$bn>uagw7Jt*RAryy>{FfH(%GjwZ6($GYmJAWz?u5xR24W2TN$-= z+719&vV+*X=fMS1y##0bUeZF?oNB4{`Du(fu1NoffW66s*A<|Y-KbAJqaD{ z!lm+NJIz5AFVERiP&J?c)k~@d6rx(Tvue@*N>Bo5fi6Xr;7(LSa-XvwK=qPFqw7)a z8?T~DUx_N??WmUVPE_aFZ&1ZQf@(hrG4Ku0lThW~tTz3x0%HNHQ7cpt+oO6(mGQYw zJD>{c#4i=x6;=8Ro$}_RUxABVcq*!V2cUZWzqEt^Di;{!B1o0-FvqWST&jYua(cDX zYfx1%3)Sm?(}sjkA)fM|?$VdcbQu)645Z4i*hQG_xK#XFr*oWM=fb60t~WYcs`xiK zz1itJ7cM2de_*}?Qnj$$1_Ty3E^P^4?ZO{+T-pTwJgQ0h7TVA(?;dRB&*_g{wBuDB zz0dJ~QjOFB7cN!&Z%|e15UQXb9RFDcFR9vj*x6`_4}Nh0zq$aaA|647{%~BX0{%ob zGPU?6JM6SBD&Bx!N*^u5sj`T{Rz;gQ-XyG*@gD%gtm_eM#hHI=kKj!InsXu1RZIe^ zY6X33zw=om!P`E%y3*P-rw2(pe6c4f|AQ*Q9LJ@~=sHxq)bZn0o6ABMuL4z% zEpqnlF8=K$cvSFG2c*h)nbSKRmx|xz^lrzGSD}^sQcE9j@m9HbQVq?c&aT!Hd6e;E zF5q|-f824YTJV&!rK%1_`!XMc<;pHBpxs>k+OyTtb>;THe}eCZ+{ zuPW%@jvueuw||DKfWt2Qc-6jNpTtTZ!!N}<*=b`zUGwEp1)8C1;VFKARq>Y2mMXjr zs*0Y8s!Pv9m2n)Z*YT=*gp1$`O68Z*rMq}i|IB$A9%Yc>0;I9n*{BN4bzG`lZjQ61 zYV`G}(%;~?RO#k9TdMH+&X%g@ZbLQy0*hUQB~A^hnl3}t19zj!a0RNDRI~dLRQu$U zsN!#M;m51OpL6^lw8Ssq1sCCXRgGVStKe5r*;}2y=Hg4mUq|&`)Go)Rs`!VfhUg1t zf91lZr@}*Z-TDtvO=VQeX>C=T*YT?GFkCII>*7VBDyY7Tf4nMQG+Ygcap6+&#?Eeh zNww!6R0TG1T&jf4Q1RxDOKY3x-n@mRdYtaCRMA>F`*;;^?fCJk!%_!$TXdicKVDV* zV8^8@?+Rz5wkMAReg%fPh*zR2c!bldT*Bj3BbDvK$Dt}P$HkK>eXg^mvL`ruf<{s` z%5wpeQT2R*iy&2kLT4YZDrmY3pW(u#nl0BlTdH{1p~|Pk@fxl1{C9v$P>L#p>z#dr z(;J=MglY(GLv{0YAF2jCi0buEs){^BxEfyaFdl)mE`d}5k2+hb@W-4!j%xL9aN)=`@RGo9cvQgePX9pll8PU5wp1+(vI(gIbx?)ZbsFU~ zf~v>rJG%j@msAxw+407{&G@&#qYO_+Rl_z;&vJSWs)9Q>yCbRsIy>GK)k~^)2~K-D zE*0~gPb9XP{movFD1BNhEv6R09(2WRs6LsTq^#Ev!zP^sN;{J8tSJUe>%#RA9xy| zOTp_dpb}L^+fluwYWZ8J?q5GbmC-&_^YZ|zYunGL;vGR%@NcMI|D?+QcfETq&mT^Y zxdc-2KT)mgMr?D+@MNb=Q0;sz`K5TL%lI!9Zw1$B{(MyFyP~RSFH}QYa= z3VGrKyAvPS{lf=!{wjf=_`uHZiN8O=b02CnM)mf~i4W{-|D5>1?!*Un`k+oPsXlo) z@qyim5A05SVE6x*5A4$azx%*$V{vxyftX%*U6VK{(Bu!V*@AGJrM1()f3j1|;mPb`X34w;*F1G^=NCFZ+B&bc896f; zYx3?3_6v41o9_z_GqFX%XtU*LuwN)XVA5aYZ-TdpbbF2{-Ob$Rh*DGx7`c`xi2-xx zFGOiK3$k-7QIgC{_Y-B8K-_CYNeP%)uMuVbY{*`b-T`y&>yU(NAr-GfE)JMIA_qhk zZ-ZPKFoO<3md=4}I0We%F!%i%l6oB^ERsh3M2?6=e+%gsFjsyHSzQ9zB9cMrKSDA~ zAey; zB7SWXm{SB8ZYl+eZUn>?14fwQVnDl_0DA>Sn)b5*y96p`0Y;hK0`qSMB+Uk7nX=h{ zgn58N0%J_~YXJuYmR}3VHU|Zk&Ie@70gN}x<^WR50LKJ!P1<#UBLeHL157kW1y+{> zMwS5b%$gEF<^n);DInhrF9k#`1Z)wQY9ez1n*{Ra0t(G$ft(6J%j*HtP44x$Y#0^)82%r?b00@~dM*efu{w7&_kOQ7N=K#AEcFn1M!OQ+6{T;da0wfg4Qsd4K}~%jW@ZG6w~gdVq}ifO%%wd_d|QfMWt>Canx`L||PR zV1YR*uzE3IWI3S1tSJX%E&)U@0NiSZF91Xtz!rf;CbAH)Ng!__z%!c#a+U&GRsa^8 z+zLSKGC-ApF)eNZY!jGs3t*Y46ezkA5O*u!E>nCfpxs@7y#mWk``ZA!1S)O=+-r6V z%)c9uvhq}>5H zBCzfbz*=)uVD$>X$i;wlX3b(i=1M^H62N0-_!2rKP}HVNbzz>{XPK+gSu zmP-K}Ozu)Z>;r%*fsLlcGQc*0Im-Z>O{GB5DnQ(wfM-qdoq%=^0`>}QG41aH>=LNB z3-E&3EinHfK+@fSmrU8+fP~e6Ljtdu?#lrO1ePxcY&8c3maYM0+yi*sEV~Dg`Y_;_ zK&45$7jQ&i-MxS}%u#{WYXKuy0IJNI6@bh~0MRP}Z=2yO0a5D!TLgBPi1y7*0xjPBZlZK{;_8{OxGxtG2yY+zm z0((uDhXA_-7C!{|#OwnEKQ-M~Bm2xE$!F%E4ec}67a6-dj?keHyEuo<#Rq)Mc5(46uNB{dwJvykRNGwWH% zHj%v|ErMpxbC9CfAW2*3kW)?B7J8`N>wrT7Elv050lNg2KM!bS4hqcQ2FQ2;(8er# z0gzA$I3{qWS@|O1fI#>qz}aTtOMs=@0UHF)F~OGssc!(XUk0={>jjPoG<^lo!DPJx zSp6not3aHIc@>aZ1(^OSpp$t?AnGl^**X?HH@&w5wh64-4(M)v5GdLK82Sbv(X4y}(C%G8_)S2P8Tck(m%s*r6celh z%zqD%T?OcE)(a&3D|nJ=_Es=Gc(KWP3vl3H#Mt^4F)lSRZv&R@1WbP$(AT^qkorF0 z>~{cZX6ieDBLX`G`k6L60INR$+_VFbVRi^)?gDgs7cjugeHRe58?awspy~1+V3WY& z_W*;={fMw#F)!2FK^*}DK)X1zedCxE8A0b@+oZomP7tpeF5W)EQLr-12u z0OQR|0;&4|XMYIDHB&zX91++lFwwO62(bDyz)c?k^2`o_%+CSc_5$+F+`WLPF97=m zrkXAv12zdP{uoeb_6g+d2VD9IV7giK2_W`Mz+r)zruV0SZ33%41r(bf1d6@_4BZEq zZC36Bv^xL@e+HOi27U(EC9pxD!~{PF%s&Xo{v0sZtQSc58qo9$zzrtr3%~(^tpYch znEimI-vFlX2h1}s38elTaQ2sgGBfo{z!8C+0t-x=uK=qL0dD#VP+@imWPS_ib^vgz znR@^b^&Mcpz#`M-AYhZg;)4Ls>=VfO9&qW`fW>Cf*MQg`0EY#P>HQ60o4~4X0L#n| z0!2Rpntlhk%Vd2AX!jFftH5#-^F3gfK-KqvdrgZU0P}wa%=rPZ(o_m090tU3Smw~N z`zOEwfux@St4!I?fTh0x4hcMD4ju-i{tC$W1+d2a@C)FGz|da-Yt71E0jrMy!bbq> z%)ldn%%gw}0*{&CQ9#shfb63H4jsP%HVNeY2H?;kkn=mB&=0!7CFamN50I*tL_{R!ABz@g($z%GG`KLH#%1m^z*Ncs!Fq2sUMu*C@> z8Y}yi#oa^079R*jFJ2zvz_IvXh^`0{ni0h2yb%PXh5*L|IB$diM+DY|0Gu}jR@VZI ztOek_Q45e+8xUO^z$vj^>l&$XkfVe2Yho(3R&@KYlE3ntJj{tTFR73!unB4;N>j9GL z0rr`)dVqxbfI|YGo9^`i2LzVa2kbWo1(r4dWHbPLWtKGnq(%dd2^=(O(SRcY>!JbQ zn4@R!*w zFuy4vsVN|6%9;WangI?8)H2Y8QE0jaToV*(MA77I8c zur3x*-y9WK-2yPO1t8k2X#vPQ1rU7-prILl3LxrKz!rfRo-+m5B#?J1pt0F3kaHTK z40{v0DA>mn)a;#y96p) z0a}^e0`pq~l3D}Wn6lP@gf@Uf0%w};Z2$)ZmbU?%Z4L@7Jp+(&2H+gC>0%V>Ih&~(8$qYXm5Y-m2MWBm`v;}Mu$ZHF@ zz-$)CIS0`496&dddk!GB9iU1e-n3{3*d{Qi9iTf8KLQlB2gJ1pB%0#(fOh8s_6j7K z_U8h22~?a5NHMzw=63)jbpZ4>WgP$s=K&50Tx`0Z2RI_m(Vv!)Xuvoj#NGhl!j-Wd?p1+Yb6 zpow$=Y!b-p0vK#I3*?*+Xn8(hsL4Ga5PJcjN?@32aRFePz?=&J!%d|?QCC1*SHK8U z+!fHS8(^=%NYlO>V3$BeH^3;fTVVc$fTRlnS*GklKmz~V%p4LJW4gx!4hSrd2V|Rr z0!y{qW+VW{n`H@r)b4;|0=XuQ{ns23Sl1me(Hs?6-2*VP2O!U^=>f=01VkqS^3Cu> zKvYk_7J;cI(i5;rAg?E&&}43XTaXO$~Kfqpr<)(c(;Zh`ri0g^5QtTbhp0TMC*hXn37-7^3O z1eRw2R+)nWOZx*d`U4&^%lZRS2LO% z1hCmu3KR_m#0>>JYl?>g+Fb$IE3n11zXGsJpyCR^3ud>#{9%BkVStxR*)TvtCg703 zE2euU;DEsLOu$xiP+;kBK*n&u>t@+-KO5g+2 zViaJTz?@Nl-KJ8Y=o&!WHGmII@il;US%AF)drkW+z%GG`EWjsbx4`_-fTYoYeWq+Q zAYlyPkih4r`xw9hf#qWW`^`atrDFjZV*y{8Wn%%U*??mL2TfWw;E2GwY`{0>sKDxR zfRW<>hs>IBfXwlL=<$H>%<%Dms2so+fgemH2e3&XF9-0G*({Kg3uu`OIBarr0kIPR zRRX`7783y51m;Wt95s~!MH2yW69K=Q;)#HElK^`Kj+yq80J{V#CIS93y9MUw0h00n zK~t6oNSF*bBv8wApA0x4uzWHgYz_)6%?D)U1L~S(`GC|ZfMWs?lQso#L}1+%Kz(ym zVD(hM$fa6n*rG2m=-P+;jSK*lVT3ZbuLZ=JHP-?%=K!MT06LlBa{y7-0k#NqF_G&4n*{Q%16*J>3*?jlT9yF1 zncNaUY$>2hAl|en1#A0^;TZ5>4@3K)dS!dj*nA`|AO_1S+lvq?p|T z^KSqo-2muq%5DH8+z2=%aIxurBjA9*@*4q{nu7vMZvtf81n6s)-2_Oz8E{M>&7|E7 zI3lp_WSn)VfdT>=#q zfKg_*!2DYPNw)y9OxZ1fgj)fJ1jd-|w*n3bEWZ_yZ4L@7y$z6Y8(_Ryb{imd5#X3W zu1Q-2I3loa5n!S@DzN%?z{uMHd1lS+fJ_e%?E&)5a1Ri52Vjf9R1>)au;~uYbd&Gk zOjl?&3*;;&M$5&-m~L_x17eo|ssv`57E1uz1m-LO6q`zcA_IssfZ3+l0NO1D>=l?} z+Ajs{5~x@TC^5SQ<}U*zEd$IoWy=5wcLELx++e!j2{<6I{7%44=AgjRy8sz?0p^)y zcL7rG1{@P8Gii4NjtH!~8?e9}6i$h-#-eGlMPGyEPv)V+W$0*g%K zUce@Syn6wj*({K=0?={=V6n+v0f=1*s1h)y#Y(_7fjKJy%S@#}(S3lp`v7;D;`;#Y z?g#7@SZ>nZz2x^HVNcC40zIP7RXr(Xt@@!!Q`$5#6AM364+>3JQ5lf+_Z7(BcV~X%{8Be z9{JB-x0!by52c2)&f^z^ea*E}71E>zLOoW3!1Wpsa@5z8~3 zN;A<$ifTfB`qs~cqQd9)`H%FEc#V3V=7*m4JfRzg@HmIRbN{b4{EucYzuNcCBx}lj z7;XH2mC4@&fgp-#bRrpwNdX}bL}|68x6V}8savfH>UgEqaIHsPw*e8siWBU5> zrH)N9F^`gko|vXw^~J(87c|*1eWB_)$MPN1UssnpHpOHqi|U7~>3I?hT#z2Lrfl^s zXV0-|j_J8*T^yTk){{la3>T!wGj(!7Gab_-wyt!4)LjJQ$N!uAD;%5c;_1)bTf6T^ zUhCrNPuSZyrbm`2tsZJ~hGW;;v?c!IHP3PI1`GY?xap~7dfn()bNo{sy9uVLrKjf< zI5y8QJ)}s_gVC$Zr9B1zM~;;{rsu-!t>d01u)rlg4fv^xSm7e-L1gSI8HC)*g5#u`@}wVPsNqAJVtf30j;DrMkLA-;VtWpp<=BHR9(!}( zb64qy96Jv-mV}x+t6|ET{W@@uRsgSuT|7NeWJTCLO=7Ksntan*n@89*4JYEm6!*n2Oe_p2_mZ27vk19_LNH; z4_o58dV`C{23FnJR-|L$~fi_4H}R^TqjUT`c4cDG|M zxeWQo0D*fQd)cuR*b2v9fyvtox6-lKU4Fe`dLE$4uX&W>Mfhj9hx2_l7 zaO@J;caFX3m^L>(D_XBA$NJ#UgSA56f~i;g;;JA2^p2hdsWhp$D_p@lT;ep?W}IH{ zI+l+A8K3YVGROL1Kj+xL9J>sAi(`5&q{_&^T}TbuqwhP`U+qtD@B;@25TU1IyBxb5 zmh9MW#|FZBJGKXgSN|3xaFJslxp;$N?O<9#dmZD95%{+ntp5Ml!J+um+%oyZu`6KR z9sAU=VX)zD_V05n6Skh2u2t}vW5e+;hiMgj?%0+1H@Ttv0_HzAk$*)H80_GF7x60C z6^?!B*htuL$G(E8#aH7-J9f~;8wKm?*w-%JHL%)_{oBRMQu{Z$aX;kXXd;%o7Juv5 z7}#r$edpL%Sf*p&JC+T*(y<>L8wbY|8?I0L^P&8FU{=NJdvK=p%4>pNBi)3Y+w@&=9-3*sI+PyMk)%f*l?%0j^RfyupI@V5`JFkV68&BF)>)jx?fqOUC z>YJqJ=S;v)BG;U?qqae5|?PFi|Kk4{&5X3s|Q*o4!w+RigmpQ%+6oq@MMZUF9b zbL^Q~%}dV4+ZLxIR6CrGO;d3?78T-j6w=XWFHR>Lon&-!(WCQr*E^W z_-{-8Nk%6Zom3j*n&6t^n&VpF_GZo{PC-eyWLyePC!j94SUt;E z5BAlB(FDp z6i(0geggL-P7fuYk1NBK<8(Azh^xTeg1Z%W8*UL!Pkbn8&M!URN)LSh3->YZQ`|n> zXE>eDzQFCreTn-DcK~+~_ciVt+`n;$aC(S_Hm4Q1ck#c6(?1bdja!3Ti+cpO4)-YT zu^>f1j%PjY3EY#WVoR+vOY{UOJp^hRP7jHiihF}#J#lIjZVYZY%T;H>dvPmpdKPUm zPS5k3g41*O=HT`+auZQKKCcuv7pI2;_9H(%L{N`q!gF6Fv7OpMs99(>%fH0m2zM#H*#|cfHwiZxmyer* zn~EESy9PHJmyH{T8;rX`!*m&*Ubx=4GjOeO{F^NQd>D_WQrR@zWw^mO9T9c7(Xmj+ zKpp#Z%o_q5iW`9&iPPiW^~`!bZ~ix&9xwkkP7jCB!}B-bp4KB-^#uM0aS!2E<5uAG z*#1U1-Qww=B6;-!UR1wJVLf8QfXNCRKL-}7&ump!{XJ3BkOJ3G63a&7`J1K>vk z`6<|)Kq5f@@&{lyun}kk(7kL1(50jc*@1sy+7S=Gqt$-^TLC}BO91p8O9K^wFrX+< z4EO{Z`j8gjIm^aUCM{3cf|APM<&8$I7x3M2u&Fc6<|>39bG1w03wkjS5K;Lkd20#*R0z~BVHUtl=|(8uJ#1%G?y8JJdv z;QR~BwutkStKS2Afpx$J;Ah|na1=NWoB&P&=YZJr_~!!fD{uq23ETqi0QZ33fcwBB z;4$z7cnUlN{sNu@FMyW-zv+1s2n5OioGNBO0WtzvfouSs-yOgh&i~<%dJQ6L0sP6a zT|DCa#_)E4U+V4;3;+fJgMrCNp8|{q#sGAh&jROw^T02_1%OWSRe&E-jsWN&_XP$5 z{9yJppsoWZC_mNA?@{xE@wb3?K!`;q9tOnKr>wyhG4rnGPfqMF(ag6{v%~9y;Xy7E`M*$lY#;>O~haxF%x`E?QQH+>=bO;nN%w1ohEccnPM zUtHla5|54Y05_4h9SA}h?n!yF(*X_Z2=K_LJb30q=nMG8;-A@IJO@||@Oz*9y6I9> zG#Sii0mA`W(KkqQ0r<7`c;GiYy93#PUZ}7G&`2zqi zRB8j3La>1VH?sRtK}+PFLKA-m4gvdtQE0$0pa;+iXbdy~Y65-$H?91ne?^)|e$aU= znDGOya}e^Q;@3gY17QqM4VVhz>3|K-Z;>|{&$oeCpdIiXz}*!2a39$U&p+Y$FNDv5 z7r;csCj(Of7tlKbnaSXHWbzB>k$C3s0Pr^hxc}pRuPZPF`G=9;1|hd`{BZ^fx)7KL zuwwqcz)*zkfL8#^vlYYM!8^#iPxxT*-evD3Nk{B2x8ac@ z&p3JZxe^EkDNh5rYj^;q(bu5QydPl_z+D0lbj~7;hc0daU4-2L-3Gc3yMRPshx~OE zNAWyx4p5du0z3fph@CsaoInoX9pX+1-y(!+?EVI52wnp(fb+mP3>kG&oMn2?0x>!S!)D;+v4E2(bbd7+tl^W&A)F{V!KqGV#I03`~J%PT! zLLeTP3`}DGPsGD$fagFX0cwQjJHvoJKnxHKv;@8ZS^&*~D1fJCWXuW2W4dslJJ1d2 z3UmQF1D$}5KnI{b&<+_+5&9=GU*LaFrIL+a#l7J7!0sAj1K_% z13Y*d2n+&-0CB(wU=%>QvA`H$955c30L)=)rvX!eDF8JQ2h0Sf12cd*z${=kumB(< z($53p0BUM3AOI_v4=|0-M}Z^2N?--B7)SsX0n32c<@jd_uoPgz7GMXk71#u<237&z z0c(Jbzq;0f>;cm!Mpt^k*TOTb0o0!P~~cqk7T z3>Y#_<_~}}z#qVGz#ZT^a1Ai%Z{ztE@GEc=xRDBX@q7=s5Bv`N2|NUz0nB@q1^>JR z$m~B7eahag)*C!#H)$3$E5a;5W*`%g5pV!3fCAV9X21m40iO_j1U>-oyG zpd3&d2<5JiCp%0m3y_%46?7PdnMY$bp0(68 z^nVjP)C1}Q-1KngULR-xaEC^hhdvduV9<5f^LimH3q%01 zJ@HQuAY4a!6_8OBkeaz6D4F*K7_#-$!Vq9EFbL=ma7@t77@8b_H2pbN-JYaMP>HE- z14bdE3ygFl9Gk){xtr3XHTZlUqW?)ux75g>Wt)*IZd7E1f;^)|jnS%_fZ<`*!}EOP z*F{(xs0GvjssZzmo_1E%LjC_pTnxelz+g-!)qy&|BBU<_7656PG2fVyMm_~)`Lwi4 zP@Z|J{a=R2QXOm&3sB;;h0+ zY+XZuEl96oqgAOj!b;K#XwXq03d73T4Tbq$Lc$afRx|CiKFVyet&HCsU~eNC{ODaRIJ)ZM#aVmPQ7Lu-Lq3~nX%hE4W>q3YPE(So&3@pABo`c8hLvV zav@VUc)Rt;UWCjrm}&$Dq4uW88RVY^3ZZZk!k>V{z>feO6QgVqJR3Ad+9A*!1P%bq z+mF!LV0>~!gl3b(EPP5QVkJKVCxN5D5#Sg=;g0L!2|Z*QZzOqY%(JU+fTzgg3330c{!e4+3z$M@ca2dD?Tm!BHzX5(Ie;46x z;3jYbxTS}85Z(jm*xyI^J1_`!pge8IYzyrkD7}7J30=GkYTQi2LFCJL=V)J z1@IT}3}9D10iFY|fmgtP0E3TFmgQLH4ZvrnlcpDX)9yVUJ}?p5LxRf01cFaU`v`D= z@fyn>U}54Yl8*rnNTYB}<7ILtfI?7MHj3$n;0&FSp9{zaWC5P=$FQ>E!Gg@32y+10 z0p6cc7!n$lx#5{i|G$Q`rvEELjs*?@qq~dYxhTMa%seBVN1=RX8pkZhsxe#w5a&>d zWk2}iArLSK6F|uE#R93v7zZo|V||1i2Rv$KHoUw7{CVc2$~1aoPj)qE(35z z^2M~WKzRUTCswV11A>KlLR|x>s>iD#+>Q#WBdiD10cr!xuLbbHm+5tNG(^|{Xay`t z)d*vDwm>>33lEFgkjA`26%cajvc(LWA#4h8&Ql}J@jM3U1`{%9iL`Hl)<7G8WvS}a zvyCUy9rV182*ZKyKsVq!@S~v3xSQz;bOAa88io3!h^-~B0YHBs1n3L&0b&iK!-}E-c0m*n39xTzggAE};+c6I52?G0CYtFn04*is z{Q&Y}c?!?5G7xbJMnQ+-c?f`p#3qkcX2c;ebqi@hDK&+prc$?bEaIe{4j39?*Gxft z5je!6)07F`i{B-~&UIEY^EJ2vsCM-aF20+2c$5`B`^7)9*11Q9| zD9hPJL&U~VXb2l?w-^sbg$a0O>lXp6)CC0$RnuZot!zD2N`V&wsastbaq?nWmNOd0 zK}s|zK2=TN-=K#{E zaE^NxF9X)>)q{w;BfO2U8^Yfa{s!CwNOK3_E#Oz69^%&#UInfImw~e!31{%|GjJL> z2^lEyaxUQyaZ|huK>2fke<@A1#f{j z01M9tdV$eTEaMGR{gmcLLa~z$O{w!@&oyR0zg54>4U(# zFv3Cr>np&)#EhZXG1vZlmge()5(m?YXe3<*1>b?{v$orM?Qoh z2zlk@2<1rO%Yw`#GiFfdWKKn=`S(@;Yl0d5f)a$3;|;yg}G*A;n&AcoLPOHC8Y3dt~S2GiIYj>NWL9D$IF zW^aUsnd^(^KEMzlCRO?XJhMz3gzAsyetc2wfdsb5 zP|+yF$*dc|IY$eXT4mH2CpxVSHN^SN0cXr@&g-#24S+mJ!$CL>;6VM17yI8}G9J&g z92`K?0S>Hbz*L|*P#@sP=j@#Ta4Jp#CIgdzi2#|CA9?VZ;Vgg_jPZDY`Evk{t5{Y- z<{WHfJ{w4FZJHvUx>T-bHv<&FXKlNhYaJe%2 zhkdgYY3%{3eld^$aFJMqa3PSoy9|LiC5^#MfjD(36f3UWj(@mG!EYZ zYk?m?P!uIxkhu$S5+(vWfgQkhU^B1{*aB)x78{(kr$GI_a6eNdP~++|5E6lmZsNmUW= zhp-XCIQRHwN?Y?Rck!;KxLam{k)NEkDY<33JL)O#A?w{i=H@9;wGsMx%Ea1WQ^iyE zG2X;eF4tCyn%g43fa31h))TG;8u7iS%q*Z(HUF%oGe4}L;w`R)l$-{!k!X8qg{z1{ zUh}Ts8o1l#fv%yF@Y$>~v|tMR{093sXjvANFkgy}lS%=_Qc7q7`pS<56t}b$nFr>U zedU#^DXq*Uak&+nquOe)(^V~(t16yyE02;hEoDZ7QsT;j1a#F6)um-(E}2KNv`A02 zX(~m}I8}ry)6ggL)6xS}d6E;2L17fuS{5K@8YsmLiP8$6TsZB>`kaZoxkg^uQzb?6 z9My>?FT<0ClEPc+brLlx|koGK#&bZOGciaH^XgMFvm-YXJ~;l`4@p8*e`$_rXeR6avHojxMPpfaXxZ_z#DUGS-v@^Fcq@r7qbar zwtNy|Qns6aDIkwbsPjevY=lrSyiQM-5;G^nu}*DL{!u`@&8YMhC|GH=JLM1WS$Ovc zm7=5{44XqiEob<&Z71?%^Ylr{aV;plS+<}~k)eFnn-f=*E2&dJ86n7V2G^gmytw<; z`r|OZ{cyuA`)Mo%S?JYzBJc)+;Wh2=44dv0y53Z-gs zSw4AbuLRgK`D((goUkNc(fZ3OfP&hDo^$(3Wd&7-`^t0$y*|KK9^F7V!dDI{Q15O( zX_yG}=VVcWY&kFldC7Y$iFoJ}I zu~NE$aDhwWL(egLdV;W|Um#gm43#Ar!MbLsJQ3*iQAX&sCCE8;9yaPUq~WB(!&P!s zR`1eME)y6QgWT-m#uF}X^lDMt5fu7#2rVs9nUtcABS69S>Lp}PCdD)46kguqkW2C` z;IO-0`&%d$s5SADo|DIXr){GrPoj`h8r=|tgO@Ur#Su#Wvy8-@LHMqWgduL*P*$@8 z4TneUxxQuFdXy-ojiFs-rLQB}RI!|_afG^ZmX}wKO3@0JabbsrDegH!O7ZN_u&9NX zPMk+cfAlAY-T+%j2tq*s zMaCnCqh!sXkCA~(td$h1*q=ttCrs97hQQUrG>X694(WcN#q5=4lOIypEzQE@7ARu- zBF?ecx#htF3E7ue%%*lo!7*aoNQ4x*+4)EL2S+-!RO?kO=3?ZqC;T1%`m<AyYVNv8zl=!SQ7W%-zeHmZf+hkS8ux+?I|!@V3|&epTfZC~a}oH0|B~ zcyeRmpsaHt7Y!V`6P9iIAT%=|$S`4ECGFBRbE+tsnX7C2yN1?1R8?S%V~dX*jxCQPGcx@;X7f5V$KMaeC9J0O zMp<*?nle5+#%-&bvIlWnUzC6&7MJ%#!M%?UWwxo}!z5rIQd6F>ws=r*c*JM+JUMHl z=S!7B9k1(jIbId_w|Oq{t)bSY=Ip5{6`Yj$X2Wzpb5cS=_Se>`%KB@$2X?`QUV)qg zAG*AP99D6Ee|P7YSCu#=IL#o$liJcc2gLrnw)D>dPjyHgxq*1dlsZ~Nk7jDK>0!G^ zLv&uSe+!Yr@m;q-DYvQp{xH@gjDjb1q-P3NbotqVZ&0$VrMx_a!IZ~vS>XKVxHptCqGBO1%_T(EG47rM2vY(+e^n@K0w zh+FnH!5N#}&ZW56j3S*o^zGu?wRfNLGpAPlP-))>CzDT8Y441_U)WS;J1b?(FPh33 zXEgGCQ>_W+RnLxf?Y;{xF4qLKz|NtWI8DJ2_V>5T(M-Ht(3+ynw0Rvkv(w=Him$r5s( z1_}U8o={wt&%GuT4R3}jD zZ{@iwdZ634;^n4z(-EP|n7$*Tj_NM0q^}!F-IWM)rgl=(1B*JSo+aInrnFCQXQ=0M zN9dSashYfFMEaS$sp7`oPNxrfh3?IXhP^G={XfxztJ`sRs7_?N*~rO-oDQQ8)>$3X z&NU@R-4{BGkC)<=?(R}uq3y6IZ4L2lUKk!6Au|ia@4Qf}Qu7o(S_1=Yt5K5;6g67i zwi|PN?CcZ@Y-MkFLCx5i=FFqC{aVAZ0oj-*RYTJ@TyL=L%gxgD{S|SvI@C?j3Ge?| z9M~#%_Ty*$p^Ig7_J6Hh+s1uG5nthe-D1~57>(Qret|<;+0w?6bBKJrTuOjv{Y z=b4`ow5jk{`u+14BBVHv?b|xrTw)=~Rheic9hZ!}TsI=;KQk{uF z^cmD0fwH!=>Q*VRMB}a@N|um&x~)_9%xSr6GQ&$_*(WG#ei|kFN+MhkEn)r$kzNjQ z+qFKLcaeX4aNUZD3U@+m6Z8R!D3;J`x20QqDeA@Gt%vDTsyq#fbmdhYH(fqkPV_=f zXfks7ka4{VHkhuq$dHTLFj^FnE-#gGTYr7Ka(Q^-mU|=5scl2+k|q9%q>BfLQ-Icb z>Q5>MU|oJcKn4b2b~y}`^#L#}UsX-&2B$xl)SXJYYE{dgijtv$=%V9;G!HI(!IS2T zrN4QK4?t~~OUhUL;ZKW;Ey z)+5u_WxQtYvN(3BVC$aeH9aPEuGkrezq+vWE7TKY?R?faNp`ml1_zp@J+D{38FaPf zh7=An$IH+#4D+?)B|a=20nbwQ13? zPNs3YLpVS~xXju$Kn@St#vd4ZXn)g#^EvQvE{dEsvZ%Gcc=aU!PC;|8_i%e~eOt*s0Cbb-DZ2hg~ zaUJRX?Yssj3k}v+cC5l(he%9YtnB&XC84d-)cR%V4@lN_N}$bIG{>}6tzjJ>*PXEq ztazdv1lK{Nbvq@_oMo=uXs6UgYl^j30`aBwcJ0x&sp3CPagnL*6)*fizy>6lZxoOt z?IHC_d55U=z&uUJ?QguUPLE62=!xs%-yKz!k`44*xFc8wd-HasQDdQ@Ws0$L08V1eat#V|V4`G&J!kO3`6Ellx^SyXBb% zrIdOnrs>dF#-&m}9t*C^QW?4cq0drj*dJkurIKwB#4Il}x`N$FX#i?dU0FO4K`Y5S z2|I1n*yEEpOw#mntpIIX51ZMRI$g5Hqc5O*iN0UOkIZFZk0-UE0IR0`VzNcNYU1%cUhg**yZ+^ywT;Qb;H_Q+U1eECBQH^-GE>P%!e=Dr!Z|SyHV6SOfPyf_~o-VIr2YEAy_U? zNpKDX`N3iD#CcvrFZ`f25XalWb_XQ;d^FH+l~m}70fPzI%h0)psi{oriPHU6X_{?0 zc*WlTxZK#NvJc={#BBKy1g2%Of1ctj*?TJ%>n0HM4ySU1W)131nYGU#E@`(9spS5O_VmK_BzF|LBz&VbVk3vos&-{{(UY2%IS_Td>B`o+S8KeBh57sBF&O+a z#X5hJW(a<=<@&hjL3Vee;{xo~OWtS{IVNQhn4g!IZ=#jD)_>lljN7bfV7BZ9p>>TO zO>{qe{mhw<_rQ=RC;p-Qnp8B5qwz9?@!KDfb5Cao1FZMMv_&S>Z8qQ2z-pu}nSq$6sqL&a!yau&|5E-x8v@KPu-W3G z6au@a5z?Xu8H-z5`~65h%}HgmtH{#V9t={Jl0Vja@2P$3D zESc#q4$146zGBUhN`uf_s=@Fut&+WSQL{F0+;z9c8vdiURxaA~_e*JiEeSRAohQ`X zPb!U7T)nUmi~|jBSK{m+l&ZJF4nK0Hy~qNWsix!@j2aT87y@(VB&j|adrjvg8O%^~ zOzN5~`{h025W2(=P^(5rn^jsJ7SxgNfD9j^1X$B~TSIrxG}=6%P3K(-o?MhN-lf3L zM0pH_J9Vjycqi~46Mt`UNEYHP3v-r3vU4cZnIvb|LSnqn5jqU6p6c8sJ0$2cdLgFC z@^~1CbY}!!U*6#mUsvujX*OJ`r1I6& zNz`{O+CTdAc6x_bDuH?g{JGyFNwFBMtL0iOs?U2=Ud2LtnvD8j)ZLKJmUKfH{PfnF zIEeoqzwM@YgjkMi4sYSVZanSd_KX(_dJli$Jr}Is(96&fu-;#8QTF4qnG{xVr}P8k;w`*x zBxsb<-RLC!J(7QVi$(`&HQ92V(j=Qzr_{!+q2)bMll~%+_bKT;8Vif&-&y@Y=!7O* z+gE4#y{X+>gOE2aP(uaTJsLg!x##!g7Br9?q%$Y=!)+s(PKcUPsMZ{ z{KPRz-mko6px1#Zm36E!rC{%P&7#f|RSHZfZ2!h$Td0u-X#Tf{Tt}L<%o%O84xH3? zZ>~pgr>H%r&V0Qcns}~oIn5(Fa9}Wi2^Ac^Vphule@YFZZL?FJ|o9JSSjIcasql zpbKrDSfkEsb?=%n^k(HH19;G{zoYc|T+{aswn3ohKz6O0<>~f)L+BdlTMc8kVC)V) z+ZF_mMLCY1To-)&R%}^7$4y@i;`N;DcjpxJDcPLsgg(?)$&4wGG*PZi{vU?dXYDhT zq;E&E{-T{KEx&N2@QbbIO0X{UC49|r$&5;@yFic`V%B`LeR=DLtFd39HgL0Nr`r|X z=INE_F-<~SmW!G$|IFg;k$Gf+0C3m$jJjk0Yjg6Sxn4;7-=C{Gp*qrHVw>e+ZJmfWz7k8hH&gxBO+Deu0l5C!e`Fu%^PF1=Y zhB8ez>(={!+bLovXyt}6P1o!F_qkoDeOX(>TW#9?{i)rTRliN!tG1T2+(-77KN+Ok zjh~GOTfQq=XN~@}BjKI9OKORBv}XpInlf>?afPYRG*cM?|7Oxm)S&n1@A4yJ zh9hE)zpBmI?!oO=l_>sr4w&k1Ag906Tq3PzftkM1*7c^>YxIr2aW>Y(b&_K?itLkO z47B!2+u68XF*?;yWGlHn8wUYvWf;66U0~Tf2dkbw@-#o`Dl-5|c@w-%M#O_- zdOf6Xe5K}iRFgdF)svRb)oC4YU0b9UZk;sQKZ_*?4r;g}z;0EM#ezem=OD<9fn9du zvg-kP-|Sb%BBlg9BJFKg{8VFxx!4sMzJtEhl(?ZC2>C32{x+seg*_~%xsGAd1{+G< zN&Cl960V%Mvj+co`3qj0=0iD4o_W|h&IMCf2$N-IwWc{nU&af0#(~X7sW=aF{}2f1 zzvZ4$yLYE0vj(IP=%)#))z>+vPEtKJ53|!PxjqjI+LsRm;H6V~t*p0tIFau5Qq9qu zx={wqPoAx5kG}pAUmSgX<@`rG8#7LD^d&mom303CrP=4*o!l!sq{~7$N~2`nLU?CB z*ay&BefI9E>YeEae13N{3$!VG-Gvjm_8GVE!T4y0G*|>Tek3@gd6$-NG@{$|H(kwm z^E6=*&KNb<)IB&IcUON$JRK)j-3H<>@1$1(bkp-U&E&s#H8)$+@lbE)!_eBcbK6wg)kY7OH0RsQ0{wIp>-#Rjd;4yM|z@uD^82Hn@ z#Stkv8ShJl#TXL#?rTSazjqt@+q+Y%ucuH{l<36}qOD9qU>y!RZ*Y3rpn3O(qn_fs zlg1cLkRL%1vL6KbKyb__cJK?|0i#j~?&=K173^x}4_l$;E|OSUDrF4s52=8VOp z{}Oo0=~-ASzwF80t>b*q@rn_8LP3hU5cfsx@=ggJg}Ze8TRL7_qhj} zUGbt%z;zF9&UM+c47OE&fyVk92pvH3iGKa(X#BrZxz2fFQa|llf)5*6&TJzLiv5 zg%0c`xmSQL7IYj%W2+|(85bB41v=dYm@SP~z$AWI7X7^?os*nf0rPtczk!RH*IV+g zL>*d8das15iob4|xDw|iqaSO!PYCJMV9TLyeNiD_M zGyZp;75uemsYp7lcpHVU0Tp>6>sO=3@}Qu>eZ6^h4f|I8`6f{aj!fYmH<6pG6_3~+ zAmF|DH}merWw#})!9|JwPV92zaIST|QgLfD-%z}*J`yQB`ut7L`KIraK`lSI;Ou-4 zQi>qwBSOx?E|YVWoq2p)M^wi@u?PXDms;6pM>qO9`#A7LltAc62CPv^n93xsUZb=$ z!6`6ttE*caYdp$usJdEO=B}T6)KRl3)GwGWncWg;KtkPA+1BcW&92-omNQQs?aoDY z-Zzw$gtgGaKmV*m-C1IKXxnSkcKH(=tx4d-sn`2W>A&msXl#X;LE4ql3CXh#1YdrE zT|dmeD!o8w*8VESMSgBZtZ?Uq5P4?lA|-smjD3Q}_q9kn-yxOMeh}fuM|kou-~6ZmtIbeJ>Q9EzuxA zm6O)vj_RBwu2=G=XTUixg7GH(Yt1xP7~g2wn|2XH!HAO++PNZGH^5sPCDS&*2dXT^ zHsVxI?;rf6Gkqw&7vzaUiwzrLJlqy&`g`r!2_MsgnH)^M{)wRqj1ZhzW1sBaNfO#CfgNxMOkcPqvU z{#3BsR*c@sAmpfY$yV#zPTen+QMFo%JvUb(w}Mj<%sXcgIQ;fJS2KI({wWL3O4$nn zTNZo%Ocpq}FDmY8K3M@A=qBPE%F|x5Y=hQI$j+uO{w$) z8i3wtFJ-Sld!`KXaR*w~PNIKGE@J%coONRc?bn!d+djn3So=FV(~vV!D*b}?PyIo0 z<6i(7Y_uxn{!Wbcc4A6QRfCpgu+}TIzQp$nazW?M630)Um+?34xLmaC(LC86P(=FG(%<=HMQ z&gC3U>biI)!x@s}Cf#c-%1*lxpyfp!IT!gYK33u8B*y42d7;4)k>kk+bdTTdIs~=D*MgN}I zpS40?;O?}_mzPqb)55z_yv$Wq0bKngM z4*sFU)a$=J3r)bg}LG;emUd1(qG|Vg=C@f^f2nsh7z2t+q)5KB9T|L zIT9|@DA!;qu?H7>6Trp=Y}Pydz#PFXZMOU}N#mMo|~U#G*$>XRjYFFM;_ zK3)7r-%HoM>Sb--A2qQx)k2S__k#6$$()2BRL&kk&|M-wr|Kh(_8}eC41x7-Hk0ao zWxiAPVVzksvZ?~8*6gJ$27y`Awu=|6l9FAUTTA9ShUEEvGH)xXR&=u{c>08jZxL$rUco2J_-IM6K%%)PsF+@`if-f8nAzni0OV_ zakDRJr`tTWh^vAw*G>9c4L_*T!HlYRSM$dRF?$31$txk8LP=7K0l?j8B(StRjXA=)fHo&G&+tk{^e$? zGsYFe%I=tyxQLYwuE!TfIgE!sjCH}(n5#_sS;?)LaQ)a@kle)IQbCtuB<_!`kYjF9GuLD2~d) zwZBi7uE&%hV<4tgD)0Yp;pYF-F}3;WDqg4I+MRBldhhA;KxJrH2g5J;Ogeqx(M^)O z>^Xrys#)Nrd0SqA)jRw#c#iQM0o19Te7Q)5lS&D*)muVOD&E$G-kRl}ajlkV>)H0$ zj^aHK`g&NjX23r+otG&m5xkJqCzS>1Tn6%WDJU6dAZHG~krZ+uG zIQMnW;I`~m8SNNgj8p4~h|#8nn!TsHp#Kee@c-q{z$?nhGZ6L*?kJW;&G|Id66;ww z2F;}bT+!ez1vF3Ufy1dk-Es}(=8@|O$iMvO&jV!Y5#sP8Tsl{4&eji`XA%Zi@9L=$$}+e$~>Z4FkpS=TVjF64b*6r`{#>VWlFP z%lEO(wB~=FtA0tZ7&7WDO>MQ4og>|%Zl15C>w9DpMife0^ z(~D6~ZUrONkE~$7g;nFYEGDVeO5qZ&8{hzI4iw6SCX6tJOfFEl_FP?kn3SpAP<0hj zJyDOZERnkNc2o9ZE2KGxZuWsj8}-qU)_2esWA1HqFWhV?E^Dsh;8ORcCEGQ{Gx#b5 zW#4?WYRB}_4s&+ueFNjT53asC>>Gc-xM9J;we%beCrx!TIfq`>QBHp+{9r?vC>iop*9B7?vDQhX!pB~w;z-_$P;&?dOD^PIJ{ zT~tBnSHCW(C^_!n%wjtTT%g|x?;gf_d53IMMRB0AWTBUDN}o|joceu;?b7Kksr%b zjXomF?tYtUo;jw{t0z>cU;OoOeD<4y#T(yV{awo4Q)b8R@zD-{@F!!HBR1E`Srx{H z$K=Kbp3>&`?c>`!vU3-^(Jex{<~VYr?c@9!{hXb-H@lA8T;OlSIoX?bZQ9y#^!5X$ z0@^)Y?1D0v{y0!?l^5dVKj{0Ng&UoC7JIn}61dy0U%C0Im;r^QJcRJ6Ez@>Q+A-3Eunbm$yD(6?WBiKv+HUg3Si`*)EC z6)ZOSxuT_zw2wh)C;l_2BGPvApC>3SPs&>|Nn9n1vv~EeCmaKZ%mh7k^Q^)_URN884)3ax?5_?^wMBcw}Pdd_EfcuWkP&0L&Ca!EWWa`s-=dd zt$&DLsFbf}36u=gEa4KG-x81*Rn78)N#Z;mED~MAQkuxlwrLrK8X!JD8&mTzV)$$nGz%NTf`x;bTP{Zv&7W1SQC5u zSx#q`$UhW|1ix3D67Q6;95K%+<6xDzo|YW)w7ez19A6Dt)SiHl&Z4}w_-N5L6)gRv z$V@F)ZD5ehIiuw|YYqGmW*L==CqjDx4T>FCtmAm8E;D5)XOs1lo%alnP-yiF_xK>#W69Vk7biN zQTki5tagmb;{a=c4-$JUJkqeAJ{!$G5`Po delta 75788 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 Cv`~fs diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index a2c88f39..a469aaf2 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -1,6 +1,6 @@ import React from "react" import Link from "next/link" -import { usePathname } from "next/navigation" +import { usePathname, useRouter } from "next/navigation" import { useAccount } from "@/lib/providers/jazz-provider" import { cn } from "@/lib/utils" import { PersonalLinkLists } from "@/lib/schema/personal-link" @@ -32,19 +32,24 @@ interface LinkSectionHeaderProps { isActive: boolean } -const LinkSectionHeader: React.FC = ({ linkCount, isActive }) => { +const LinkSectionHeader: React.FC = ({ linkCount }) => { + const pathname = usePathname() + const [state] = useQueryState("state", parseAsStringLiteral(LEARNING_STATES.map(ls => ls.value))) + const isLinksActive = pathname.startsWith("/links") && !state + return ( -

    +
    -

    +

    Links {linkCount > 0 && {linkCount}}

    diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 76c8653e..5ac924ab 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -75,6 +75,17 @@ export const ProfileSection: React.FC = () => { + + + +
    + + Docs +
    + +
    + + signOut()}>
    From ddcff3c6e4551e194d542c6fb67e4b4218998bc1 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Tue, 10 Sep 2024 11:48:12 +0300 Subject: [PATCH 082/124] sidebar: GitHub link --- .../custom/sidebar/partial/profile-section.tsx | 10 ++++++++++ web/components/routes/OnboardingRoute.tsx | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 5ac924ab..d4670f13 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -86,6 +86,16 @@ export const ProfileSection: React.FC = () => { + + +
    + + GitHub +
    + +
    + + signOut()}>
    diff --git a/web/components/routes/OnboardingRoute.tsx b/web/components/routes/OnboardingRoute.tsx index 87cf0dc6..359bd610 100644 --- a/web/components/routes/OnboardingRoute.tsx +++ b/web/components/routes/OnboardingRoute.tsx @@ -64,7 +64,7 @@ const StepItem = ({

    {description}

    -

    {task}

    +

    {task}

    @@ -117,9 +117,9 @@ export default function OnboardingRoute() { const completedSteps = [isCreateLinkDone, isCreatePageDone, isStartTrackingDone, isAddLinkDone].filter(Boolean).length return ( -
    +
    -

    Onboarding

    +

    Onboarding

    Complete the steps below to get started

    From e89374012263b26a1e402053ac5b587eb68201ee Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Tue, 10 Sep 2024 12:22:55 +0300 Subject: [PATCH 083/124] delete empty page --- .../custom/sidebar/partial/page-section.tsx | 2 +- .../routes/page/detail/PageDetailRoute.tsx | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/web/components/custom/sidebar/partial/page-section.tsx b/web/components/custom/sidebar/partial/page-section.tsx index 94216769..2a0edad1 100644 --- a/web/components/custom/sidebar/partial/page-section.tsx +++ b/web/components/custom/sidebar/partial/page-section.tsx @@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button" import { LaIcon } from "@/components/custom/la-icon" import { toast } from "sonner" import Link from "next/link" +import { useEffect } from "react" import { DropdownMenu, DropdownMenuContent, @@ -57,7 +58,6 @@ export const PageSection: React.FC<{ pathname?: string }> = ({ pathname }) => { const [show, setShow] = useAtom(pageShowAtom) const pageCount = me?.root.personalPages?.length || 0 - // const isActive = pathname ? pathname.startsWith("/pages") : false const isActive = pathname === "/pages" if (!me) return null diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index ca075c36..204fb5f3 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -23,12 +23,44 @@ import { useRouter } from "next/navigation" const TITLE_PLACEHOLDER = "Untitled" +const emptyPage = (page: PersonalPage): boolean => { + return (!page.title || page.title.trim() === "") && (!page.content || Object.keys(page.content).length === 0) +} + +export const deleteEmptyPage = (currentPageId: string | null) => { + const router = useRouter() + const { me } = useAccount({ + root: { + personalPages: [] + } + }) + + useEffect(() => { + const handleRouteChange = () => { + if (!currentPageId || !me?.root?.personalPages) return + + const currentPage = me.root.personalPages.find(page => page?.id === currentPageId) + if (currentPage && emptyPage(currentPage)) { + const index = me.root.personalPages.findIndex(page => page?.id === currentPageId) + if (index !== -1) { + me.root.personalPages.splice(index, 1) + } + } + } + + return () => { + handleRouteChange() + } + }, [currentPageId, me, router]) +} + export function PageDetailRoute({ pageId }: { pageId: string }) { const { me } = useAccount({ root: { personalLinks: [] } }) const isMobile = useMedia("(max-width: 770px)") const page = useCoState(PersonalPage, pageId as ID) const router = useRouter() const confirm = useConfirm() + deleteEmptyPage(pageId) const handleDelete = async () => { const result = await confirm({ From ae3f79767b0d56d92f5a2fdc3441e7dca06e7de5 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Tue, 10 Sep 2024 12:26:38 +0300 Subject: [PATCH 084/124] fixed router call --- web/components/routes/page/detail/PageDetailRoute.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index 204fb5f3..cb827571 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -22,13 +22,13 @@ import { toast } from "sonner" import { useRouter } from "next/navigation" const TITLE_PLACEHOLDER = "Untitled" +const router = useRouter() const emptyPage = (page: PersonalPage): boolean => { return (!page.title || page.title.trim() === "") && (!page.content || Object.keys(page.content).length === 0) } export const deleteEmptyPage = (currentPageId: string | null) => { - const router = useRouter() const { me } = useAccount({ root: { personalPages: [] From bdb8109b8bc7ebf20fda3f7904dea35d8b329815 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Tue, 10 Sep 2024 12:29:18 +0300 Subject: [PATCH 085/124] fixed router call --- web/components/routes/page/detail/PageDetailRoute.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index cb827571..9bf9df60 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -1,6 +1,6 @@ "use client" -import React, { useCallback, useRef, useEffect } from "react" +import { useCallback, useRef, useEffect } from "react" import { ID } from "jazz-tools" import { PersonalPage } from "@/lib/schema" import { LAEditor, LAEditorRef } from "@/components/la-editor" @@ -22,13 +22,13 @@ import { toast } from "sonner" import { useRouter } from "next/navigation" const TITLE_PLACEHOLDER = "Untitled" -const router = useRouter() const emptyPage = (page: PersonalPage): boolean => { return (!page.title || page.title.trim() === "") && (!page.content || Object.keys(page.content).length === 0) } export const deleteEmptyPage = (currentPageId: string | null) => { + const router = useRouter() const { me } = useAccount({ root: { personalPages: [] From 4ea3a179e0662209c0bc66fb214ca8825377cd7c Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Tue, 10 Sep 2024 12:42:00 +0300 Subject: [PATCH 086/124] cursor pointer for actions section --- .../custom/sidebar/partial/profile-section.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index d4670f13..9ac3b814 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -58,7 +58,7 @@ export const ProfileSection: React.FC = () => { - +
    My profile @@ -67,7 +67,7 @@ export const ProfileSection: React.FC = () => { - +
    Onboarding @@ -77,7 +77,7 @@ export const ProfileSection: React.FC = () => { - +
    Docs @@ -87,7 +87,7 @@ export const ProfileSection: React.FC = () => { - +
    GitHub @@ -97,7 +97,7 @@ export const ProfileSection: React.FC = () => { signOut()}> -
    +
    Log out
    From 711fe35e1ad4ba4ec6bb07a3c82b2ba6e72d7ea8 Mon Sep 17 00:00:00 2001 From: Aslam Date: Tue, 10 Sep 2024 17:58:58 +0700 Subject: [PATCH 087/124] feat: feedback (#156) * minimal tiptap * wip * img edit block * wip * fix --- web/.env.example | 2 + web/.gitignore | 2 + web/.npmrc | 2 + web/app/actions.ts | 71 +++++++ .../custom/sidebar/partial/feedback.tsx | 137 +++++++++++++ .../sidebar/partial/profile-section.tsx | 3 + web/components/custom/spinner.tsx | 17 ++ .../bubble-menu/image-bubble-menu.tsx | 39 ++++ .../bubble-menu/link-bubble-menu.tsx | 106 ++++++++++ .../components/image/image-edit-block.tsx | 98 +++++++++ .../components/image/image-edit-dialog.tsx | 48 +++++ .../components/image/image-popover-block.tsx | 21 ++ .../components/link/link-edit-block.tsx | 75 +++++++ .../components/link/link-edit-popover.tsx | 68 +++++++ .../components/link/link-popover-block.tsx | 62 ++++++ .../components/section/five.tsx | 84 ++++++++ .../components/section/four.tsx | 73 +++++++ .../minimal-tiptap/components/section/one.tsx | 137 +++++++++++++ .../components/section/three.tsx | 191 ++++++++++++++++++ .../minimal-tiptap/components/section/two.tsx | 100 +++++++++ .../components/shortcut-key.tsx | 33 +++ .../components/toolbar-button.tsx | 38 ++++ .../components/toolbar-section.tsx | 112 ++++++++++ .../code-block-lowlight.ts | 17 ++ .../extensions/code-block-lowlight/index.ts | 1 + .../minimal-tiptap/extensions/color/color.ts | 20 ++ .../minimal-tiptap/extensions/color/index.ts | 1 + .../horizontal-rule/horizontal-rule.ts | 18 ++ .../extensions/horizontal-rule/index.ts | 1 + .../image/components/image-view-block.tsx | 45 +++++ .../minimal-tiptap/extensions/image/image.ts | 9 + .../minimal-tiptap/extensions/image/index.ts | 1 + .../minimal-tiptap/extensions/index.ts | 8 + .../minimal-tiptap/extensions/link/index.ts | 1 + .../minimal-tiptap/extensions/link/link.ts | 89 ++++++++ .../extensions/reset-marks-on-enter/index.ts | 1 + .../reset-marks-on-enter.ts | 25 +++ .../extensions/selection/index.ts | 1 + .../extensions/selection/selection.ts | 36 ++++ .../extensions/unset-all-marks/index.ts | 1 + .../unset-all-marks/unset-all-marks.ts | 9 + .../minimal-tiptap/hooks/use-image-load.ts | 15 ++ .../hooks/use-minimal-tiptap.ts | 107 ++++++++++ .../minimal-tiptap/hooks/use-theme.ts | 25 +++ .../minimal-tiptap/hooks/use-throttle.ts | 34 ++++ web/components/minimal-tiptap/index.ts | 1 + .../minimal-tiptap/minimal-tiptap.tsx | 95 +++++++++ .../minimal-tiptap/styles/index.css | 182 +++++++++++++++++ .../minimal-tiptap/styles/partials/code.css | 86 ++++++++ .../minimal-tiptap/styles/partials/lists.css | 82 ++++++++ .../styles/partials/placeholder.css | 4 + .../styles/partials/typography.css | 27 +++ web/components/minimal-tiptap/types.ts | 28 +++ web/components/minimal-tiptap/utils.ts | 81 ++++++++ .../routes/page/detail/PageDetailRoute.tsx | 1 - web/components/ui/dialog.tsx | 3 +- web/components/ui/switch.tsx | 29 +++ web/components/ui/toggle-group.tsx | 61 ++++++ web/lib/utils/auth-procedure.ts | 13 ++ web/middleware.ts | 3 +- web/package.json | 12 +- web/tsconfig.json | 3 +- 62 files changed, 2689 insertions(+), 6 deletions(-) create mode 100644 web/.npmrc create mode 100644 web/app/actions.ts create mode 100644 web/components/custom/sidebar/partial/feedback.tsx create mode 100644 web/components/custom/spinner.tsx create mode 100644 web/components/minimal-tiptap/components/bubble-menu/image-bubble-menu.tsx create mode 100644 web/components/minimal-tiptap/components/bubble-menu/link-bubble-menu.tsx create mode 100644 web/components/minimal-tiptap/components/image/image-edit-block.tsx create mode 100644 web/components/minimal-tiptap/components/image/image-edit-dialog.tsx create mode 100644 web/components/minimal-tiptap/components/image/image-popover-block.tsx create mode 100644 web/components/minimal-tiptap/components/link/link-edit-block.tsx create mode 100644 web/components/minimal-tiptap/components/link/link-edit-popover.tsx create mode 100644 web/components/minimal-tiptap/components/link/link-popover-block.tsx create mode 100644 web/components/minimal-tiptap/components/section/five.tsx create mode 100644 web/components/minimal-tiptap/components/section/four.tsx create mode 100644 web/components/minimal-tiptap/components/section/one.tsx create mode 100644 web/components/minimal-tiptap/components/section/three.tsx create mode 100644 web/components/minimal-tiptap/components/section/two.tsx create mode 100644 web/components/minimal-tiptap/components/shortcut-key.tsx create mode 100644 web/components/minimal-tiptap/components/toolbar-button.tsx create mode 100644 web/components/minimal-tiptap/components/toolbar-section.tsx create mode 100644 web/components/minimal-tiptap/extensions/code-block-lowlight/code-block-lowlight.ts create mode 100644 web/components/minimal-tiptap/extensions/code-block-lowlight/index.ts create mode 100644 web/components/minimal-tiptap/extensions/color/color.ts create mode 100644 web/components/minimal-tiptap/extensions/color/index.ts create mode 100644 web/components/minimal-tiptap/extensions/horizontal-rule/horizontal-rule.ts create mode 100644 web/components/minimal-tiptap/extensions/horizontal-rule/index.ts create mode 100644 web/components/minimal-tiptap/extensions/image/components/image-view-block.tsx create mode 100644 web/components/minimal-tiptap/extensions/image/image.ts create mode 100644 web/components/minimal-tiptap/extensions/image/index.ts create mode 100644 web/components/minimal-tiptap/extensions/index.ts create mode 100644 web/components/minimal-tiptap/extensions/link/index.ts create mode 100644 web/components/minimal-tiptap/extensions/link/link.ts create mode 100644 web/components/minimal-tiptap/extensions/reset-marks-on-enter/index.ts create mode 100644 web/components/minimal-tiptap/extensions/reset-marks-on-enter/reset-marks-on-enter.ts create mode 100644 web/components/minimal-tiptap/extensions/selection/index.ts create mode 100644 web/components/minimal-tiptap/extensions/selection/selection.ts create mode 100644 web/components/minimal-tiptap/extensions/unset-all-marks/index.ts create mode 100644 web/components/minimal-tiptap/extensions/unset-all-marks/unset-all-marks.ts create mode 100644 web/components/minimal-tiptap/hooks/use-image-load.ts create mode 100644 web/components/minimal-tiptap/hooks/use-minimal-tiptap.ts create mode 100644 web/components/minimal-tiptap/hooks/use-theme.ts create mode 100644 web/components/minimal-tiptap/hooks/use-throttle.ts create mode 100644 web/components/minimal-tiptap/index.ts create mode 100644 web/components/minimal-tiptap/minimal-tiptap.tsx create mode 100644 web/components/minimal-tiptap/styles/index.css create mode 100644 web/components/minimal-tiptap/styles/partials/code.css create mode 100644 web/components/minimal-tiptap/styles/partials/lists.css create mode 100644 web/components/minimal-tiptap/styles/partials/placeholder.css create mode 100644 web/components/minimal-tiptap/styles/partials/typography.css create mode 100644 web/components/minimal-tiptap/types.ts create mode 100644 web/components/minimal-tiptap/utils.ts create mode 100644 web/components/ui/switch.tsx create mode 100644 web/components/ui/toggle-group.tsx create mode 100644 web/lib/utils/auth-procedure.ts diff --git a/web/.env.example b/web/.env.example index e9474cf4..8587bc0c 100644 --- a/web/.env.example +++ b/web/.env.example @@ -10,4 +10,6 @@ NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_JAZZ_PEER_URL="wss://" + +RONIN_TOKEN= # IGNORE_BUILD_ERRORS=true \ No newline at end of file diff --git a/web/.gitignore b/web/.gitignore index fd3dbb57..5083fca0 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -34,3 +34,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +.ronin \ No newline at end of file diff --git a/web/.npmrc b/web/.npmrc new file mode 100644 index 00000000..2806c9c2 --- /dev/null +++ b/web/.npmrc @@ -0,0 +1,2 @@ +[install.scopes] +ronin = { url = "https://ronin.supply", token = "$RONIN_TOKEN" } \ No newline at end of file diff --git a/web/app/actions.ts b/web/app/actions.ts new file mode 100644 index 00000000..b3d3d04d --- /dev/null +++ b/web/app/actions.ts @@ -0,0 +1,71 @@ +"use server" + +import { authedProcedure } from "@/lib/utils/auth-procedure" +import { create } from "ronin" +import { z } from "zod" +import { ZSAError } from "zsa" + +const MAX_FILE_SIZE = 1 * 1024 * 1024 +const ALLOWED_FILE_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"] + +export const sendFeedback = authedProcedure + .input( + z.object({ + content: z.string() + }) + ) + .handler(async ({ input, ctx }) => { + const { clerkUser } = ctx + const { content } = input + + try { + await create.feedback.with({ + message: content, + emailFrom: clerkUser?.emailAddresses[0].emailAddress + }) + } catch (error) { + console.error(error) + throw new ZSAError("ERROR", "Failed to send feedback") + } + }) + +export const storeImage = authedProcedure + .input( + z.object({ + file: z.custom(file => { + if (!(file instanceof File)) { + throw new Error("Not a file") + } + if (!ALLOWED_FILE_TYPES.includes(file.type)) { + throw new Error("Invalid file type. Only JPEG, PNG, GIF, and WebP images are allowed.") + } + if (file.size > MAX_FILE_SIZE) { + throw new Error("File size exceeds the maximum limit of 1 MB.") + } + return true + }) + }), + { type: "formData" } + ) + .handler(async ({ ctx, input }) => { + const { file } = input + const { clerkUser } = ctx + + if (!clerkUser?.id) { + throw new ZSAError("NOT_AUTHORIZED", "You are not authorized to upload files") + } + + try { + const fileModel = await create.image.with({ + content: file, + name: file.name, + type: file.type, + size: file.size + }) + + return { fileModel } + } catch (error) { + console.error(error) + throw new ZSAError("ERROR", "Failed to store image") + } + }) diff --git a/web/components/custom/sidebar/partial/feedback.tsx b/web/components/custom/sidebar/partial/feedback.tsx new file mode 100644 index 00000000..038c253c --- /dev/null +++ b/web/components/custom/sidebar/partial/feedback.tsx @@ -0,0 +1,137 @@ +"use client" + +import { Button, buttonVariants } from "@/components/ui/button" +import { + Dialog, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogPortal, + DialogOverlay, + DialogPrimitive +} from "@/components/ui/dialog" +import { LaIcon } from "@/components/custom/la-icon" +import { MinimalTiptapEditor, MinimalTiptapEditorRef } from "@/components/minimal-tiptap" +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form" +import { useRef, useState } from "react" +import { cn } from "@/lib/utils" +import { sendFeedback } from "@/app/actions" +import { useServerAction } from "zsa-react" +import { z } from "zod" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import { toast } from "sonner" +import { Spinner } from "@/components/custom/spinner" + +const formSchema = z.object({ + content: z.string().min(1, { + message: "Feedback cannot be empty" + }) +}) + +export function Feedback() { + const [open, setOpen] = useState(false) + const editorRef = useRef(null) + const { isPending, execute } = useServerAction(sendFeedback) + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + content: "" + } + }) + + async function onSubmit(values: z.infer) { + const [, err] = await execute(values) + + if (err) { + toast.error("Failed to send feedback") + console.error(err) + return + } + + form.reset({ content: "" }) + editorRef.current?.editor?.commands.clearContent() + setOpen(false) + toast.success("Feedback sent") + } + + return ( + + + + + + + + +
    + + + Share feedback + + Your feedback helps us improve. Please share your thoughts, ideas, and suggestions + + + + ( + + Content + + + + + + )} + /> + + + Cancel + + + + +
    +
    +
    + ) +} diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 9ac3b814..e87e9877 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -13,6 +13,7 @@ import Link from "next/link" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { usePathname } from "next/navigation" +import { Feedback } from "./feedback" export const ProfileSection: React.FC = () => { const { user, isSignedIn } = useUser() @@ -105,6 +106,8 @@ export const ProfileSection: React.FC = () => {
    + +
    ) diff --git a/web/components/custom/spinner.tsx b/web/components/custom/spinner.tsx new file mode 100644 index 00000000..cf164b81 --- /dev/null +++ b/web/components/custom/spinner.tsx @@ -0,0 +1,17 @@ +import * as React from "react" +import { cn } from "@/lib/utils" + +interface SpinnerProps extends React.SVGAttributes {} + +export const Spinner = React.forwardRef(({ className, ...props }, ref) => ( + + + + +)) + +Spinner.displayName = "Spinner" diff --git a/web/components/minimal-tiptap/components/bubble-menu/image-bubble-menu.tsx b/web/components/minimal-tiptap/components/bubble-menu/image-bubble-menu.tsx new file mode 100644 index 00000000..cae3243e --- /dev/null +++ b/web/components/minimal-tiptap/components/bubble-menu/image-bubble-menu.tsx @@ -0,0 +1,39 @@ +import type { Editor } from '@tiptap/react' +import { BubbleMenu } from '@tiptap/react' +import { ImagePopoverBlock } from '../image/image-popover-block' +import { ShouldShowProps } from '../../types' + +const ImageBubbleMenu = ({ editor }: { editor: Editor }) => { + const shouldShow = ({ editor, from, to }: ShouldShowProps) => { + if (from === to) { + return false + } + + const img = editor.getAttributes('image') + + if (img.src) { + return true + } + + return false + } + + const unSetImage = () => { + editor.commands.deleteSelection() + } + + return ( + + + + ) +} + +export { ImageBubbleMenu } diff --git a/web/components/minimal-tiptap/components/bubble-menu/link-bubble-menu.tsx b/web/components/minimal-tiptap/components/bubble-menu/link-bubble-menu.tsx new file mode 100644 index 00000000..53aa107b --- /dev/null +++ b/web/components/minimal-tiptap/components/bubble-menu/link-bubble-menu.tsx @@ -0,0 +1,106 @@ +import React, { useState, useCallback } from 'react' +import { Editor } from '@tiptap/react' +import { BubbleMenu } from '@tiptap/react' +import { LinkEditBlock } from '../link/link-edit-block' +import { LinkPopoverBlock } from '../link/link-popover-block' +import { ShouldShowProps } from '../../types' + +interface LinkBubbleMenuProps { + editor: Editor +} + +interface LinkAttributes { + href: string + target: string +} + +export const LinkBubbleMenu: React.FC = ({ editor }) => { + const [showEdit, setShowEdit] = useState(false) + const [linkAttrs, setLinkAttrs] = useState({ href: '', target: '' }) + const [selectedText, setSelectedText] = useState('') + + const updateLinkState = useCallback(() => { + const { from, to } = editor.state.selection + const { href, target } = editor.getAttributes('link') + const text = editor.state.doc.textBetween(from, to, ' ') + + setLinkAttrs({ href, target }) + setSelectedText(text) + }, [editor]) + + const shouldShow = useCallback( + ({ editor, from, to }: ShouldShowProps) => { + if (from === to) { + return false + } + const { href } = editor.getAttributes('link') + + if (href) { + updateLinkState() + return true + } + return false + }, + [updateLinkState] + ) + + const handleEdit = useCallback(() => { + setShowEdit(true) + }, []) + + const onSetLink = useCallback( + (url: string, text?: string, openInNewTab?: boolean) => { + editor + .chain() + .focus() + .extendMarkRange('link') + .insertContent({ + type: 'text', + text: text || url, + marks: [ + { + type: 'link', + attrs: { + href: url, + target: openInNewTab ? '_blank' : '' + } + } + ] + }) + .setLink({ href: url, target: openInNewTab ? '_blank' : '' }) + .run() + setShowEdit(false) + updateLinkState() + }, + [editor, updateLinkState] + ) + + const onUnsetLink = useCallback(() => { + editor.chain().focus().extendMarkRange('link').unsetLink().run() + setShowEdit(false) + updateLinkState() + }, [editor, updateLinkState]) + + return ( + setShowEdit(false) + }} + > + {showEdit ? ( + + ) : ( + + )} + + ) +} diff --git a/web/components/minimal-tiptap/components/image/image-edit-block.tsx b/web/components/minimal-tiptap/components/image/image-edit-block.tsx new file mode 100644 index 00000000..f8786b8b --- /dev/null +++ b/web/components/minimal-tiptap/components/image/image-edit-block.tsx @@ -0,0 +1,98 @@ +import type { Editor } from "@tiptap/react" +import React, { useRef, useState } from "react" +import { Button } from "@/components/ui/button" +import { Label } from "@/components/ui/label" +import { Input } from "@/components/ui/input" +import { cn } from "@/lib/utils" + +import { storeImage } from "@/app/actions" + +interface ImageEditBlockProps extends React.HTMLAttributes { + editor: Editor + close: () => void +} + +const ImageEditBlock = ({ editor, className, close, ...props }: ImageEditBlockProps) => { + const fileInputRef = useRef(null) + const [link, setLink] = useState("") + const [isUploading, setIsUploading] = useState(false) + const [error, setError] = useState(null) + + const handleClick = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() + fileInputRef.current?.click() + } + + const handleLink = () => { + editor.chain().focus().setImage({ src: link }).run() + close() + } + + const handleFile = async (e: React.ChangeEvent) => { + const files = e.target.files + if (!files || files.length === 0) return + + setIsUploading(true) + setError(null) + + const formData = new FormData() + formData.append("file", files[0]) + + try { + const [response, err] = await storeImage(formData) + if (response?.fileModel) { + editor.chain().setImage({ src: response.fileModel.content.src }).focus().run() + close() + } else { + throw new Error("Failed to upload image") + } + } catch (error) { + console.error("Error uploading file:", error) + setError(error instanceof Error ? error.message : "An unknown error occurred") + } finally { + setIsUploading(false) + } + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + handleLink() + } + + return ( +
    +
    +
    + +
    + setLink(e.target.value)} + /> + +
    +
    + + + {error &&
    {error}
    } +
    +
    + ) +} + +export { ImageEditBlock } diff --git a/web/components/minimal-tiptap/components/image/image-edit-dialog.tsx b/web/components/minimal-tiptap/components/image/image-edit-dialog.tsx new file mode 100644 index 00000000..68b10e07 --- /dev/null +++ b/web/components/minimal-tiptap/components/image/image-edit-dialog.tsx @@ -0,0 +1,48 @@ +import type { Editor } from '@tiptap/react' +import { useState } from 'react' +import { ImageIcon } from '@radix-ui/react-icons' +import { ToolbarButton } from '../toolbar-button' +import { + Dialog, + DialogContent, + DialogHeader, + DialogDescription, + DialogTitle, + DialogTrigger +} from '@/components/ui/dialog' +import { ImageEditBlock } from './image-edit-block' +import type { VariantProps } from 'class-variance-authority' +import type { toggleVariants } from '@/components/ui/toggle' + +interface ImageEditDialogProps extends VariantProps { + editor: Editor +} + +const ImageEditDialog = ({ editor, size, variant }: ImageEditDialogProps) => { + const [open, setOpen] = useState(false) + + return ( + + + + + + + + + Select image + Upload an image from your computer + + setOpen(false)} /> + + + ) +} + +export { ImageEditDialog } diff --git a/web/components/minimal-tiptap/components/image/image-popover-block.tsx b/web/components/minimal-tiptap/components/image/image-popover-block.tsx new file mode 100644 index 00000000..28fef8d7 --- /dev/null +++ b/web/components/minimal-tiptap/components/image/image-popover-block.tsx @@ -0,0 +1,21 @@ +import { ToolbarButton } from '../toolbar-button' +import { TrashIcon } from '@radix-ui/react-icons' + +const ImagePopoverBlock = ({ onRemove }: { onRemove: (e: React.MouseEvent) => void }) => { + const handleRemove = (e: React.MouseEvent) => { + e.preventDefault() + onRemove(e) + } + + return ( +
    +
    + + + +
    +
    + ) +} + +export { ImagePopoverBlock } diff --git a/web/components/minimal-tiptap/components/link/link-edit-block.tsx b/web/components/minimal-tiptap/components/link/link-edit-block.tsx new file mode 100644 index 00000000..d42bc8c1 --- /dev/null +++ b/web/components/minimal-tiptap/components/link/link-edit-block.tsx @@ -0,0 +1,75 @@ +import * as React from 'react' +import { Button } from '@/components/ui/button' +import { Label } from '@/components/ui/label' +import { Switch } from '@/components/ui/switch' +import { Input } from '@/components/ui/input' +import { cn } from '@/lib/utils' + +export interface LinkEditorProps extends React.HTMLAttributes { + defaultUrl?: string + defaultText?: string + defaultIsNewTab?: boolean + onSave: (url: string, text?: string, isNewTab?: boolean) => void +} + +export const LinkEditBlock = React.forwardRef( + ({ onSave, defaultIsNewTab, defaultUrl, defaultText, className }, ref) => { + const formRef = React.useRef(null) + const [url, setUrl] = React.useState(defaultUrl || '') + const [text, setText] = React.useState(defaultText || '') + const [isNewTab, setIsNewTab] = React.useState(defaultIsNewTab || false) + + const handleSave = React.useCallback( + (e: React.FormEvent) => { + e.preventDefault() + if (formRef.current) { + const isValid = Array.from(formRef.current.querySelectorAll('input')).every(input => input.checkValidity()) + + if (isValid) { + onSave(url, text, isNewTab) + } else { + formRef.current.querySelectorAll('input').forEach(input => { + if (!input.checkValidity()) { + input.reportValidity() + } + }) + } + } + }, + [onSave, url, text, isNewTab] + ) + + React.useImperativeHandle(ref, () => formRef.current as HTMLDivElement) + + return ( +
    +
    +
    + + setUrl(e.target.value)} /> +
    + +
    + + setText(e.target.value)} /> +
    + +
    + + +
    + +
    + +
    +
    +
    + ) + } +) + +LinkEditBlock.displayName = 'LinkEditBlock' + +export default LinkEditBlock diff --git a/web/components/minimal-tiptap/components/link/link-edit-popover.tsx b/web/components/minimal-tiptap/components/link/link-edit-popover.tsx new file mode 100644 index 00000000..90fc0467 --- /dev/null +++ b/web/components/minimal-tiptap/components/link/link-edit-popover.tsx @@ -0,0 +1,68 @@ +import type { Editor } from '@tiptap/react' +import * as React from 'react' +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' +import { Link2Icon } from '@radix-ui/react-icons' +import { ToolbarButton } from '../toolbar-button' +import { LinkEditBlock } from './link-edit-block' +import type { VariantProps } from 'class-variance-authority' +import type { toggleVariants } from '@/components/ui/toggle' + +interface LinkEditPopoverProps extends VariantProps { + editor: Editor +} + +const LinkEditPopover = ({ editor, size, variant }: LinkEditPopoverProps) => { + const [open, setOpen] = React.useState(false) + + const { from, to } = editor.state.selection + const text = editor.state.doc.textBetween(from, to, ' ') + + const onSetLink = React.useCallback( + (url: string, text?: string, openInNewTab?: boolean) => { + editor + .chain() + .focus() + .extendMarkRange('link') + .insertContent({ + type: 'text', + text: text || url, + marks: [ + { + type: 'link', + attrs: { + href: url, + target: openInNewTab ? '_blank' : '' + } + } + ] + }) + .setLink({ href: url }) + .run() + + editor.commands.enter() + }, + [editor] + ) + + return ( + + + + + + + + + + + ) +} + +export { LinkEditPopover } diff --git a/web/components/minimal-tiptap/components/link/link-popover-block.tsx b/web/components/minimal-tiptap/components/link/link-popover-block.tsx new file mode 100644 index 00000000..7d495439 --- /dev/null +++ b/web/components/minimal-tiptap/components/link/link-popover-block.tsx @@ -0,0 +1,62 @@ +import React, { useState, useCallback } from 'react' +import { Separator } from '@/components/ui/separator' +import { ToolbarButton } from '../toolbar-button' +import { CopyIcon, ExternalLinkIcon, LinkBreak2Icon } from '@radix-ui/react-icons' + +interface LinkPopoverBlockProps { + url: string + onClear: () => void + onEdit: (e: React.MouseEvent) => void +} + +export const LinkPopoverBlock: React.FC = ({ url, onClear, onEdit }) => { + const [copyTitle, setCopyTitle] = useState('Copy') + + const handleCopy = useCallback( + (e: React.MouseEvent) => { + e.preventDefault() + navigator.clipboard + .writeText(url) + .then(() => { + setCopyTitle('Copied!') + setTimeout(() => setCopyTitle('Copy'), 1000) + }) + .catch(console.error) + }, + [url] + ) + + const handleOpenLink = useCallback(() => { + window.open(url, '_blank', 'noopener,noreferrer') + }, [url]) + + return ( +
    +
    + + Edit link + + + + + + + + + + + { + if (e.target === e.currentTarget) e.preventDefault() + } + }} + > + + +
    +
    + ) +} diff --git a/web/components/minimal-tiptap/components/section/five.tsx b/web/components/minimal-tiptap/components/section/five.tsx new file mode 100644 index 00000000..606485c0 --- /dev/null +++ b/web/components/minimal-tiptap/components/section/five.tsx @@ -0,0 +1,84 @@ +import * as React from 'react' +import type { Editor } from '@tiptap/react' +import { CaretDownIcon, CodeIcon, DividerHorizontalIcon, PlusIcon, QuoteIcon } from '@radix-ui/react-icons' +import { LinkEditPopover } from '../link/link-edit-popover' +import { ImageEditDialog } from '../image/image-edit-dialog' +import type { FormatAction } from '../../types' +import { ToolbarSection } from '../toolbar-section' +import type { toggleVariants } from '@/components/ui/toggle' +import type { VariantProps } from 'class-variance-authority' + +type InsertElementAction = 'codeBlock' | 'blockquote' | 'horizontalRule' +interface InsertElement extends FormatAction { + value: InsertElementAction +} + +const formatActions: InsertElement[] = [ + { + value: 'codeBlock', + label: 'Code block', + icon: , + action: editor => editor.chain().focus().toggleCodeBlock().run(), + isActive: editor => editor.isActive('codeBlock'), + canExecute: editor => editor.can().chain().focus().toggleCodeBlock().run(), + shortcuts: ['mod', 'alt', 'C'] + }, + { + value: 'blockquote', + label: 'Blockquote', + icon: , + action: editor => editor.chain().focus().toggleBlockquote().run(), + isActive: editor => editor.isActive('blockquote'), + canExecute: editor => editor.can().chain().focus().toggleBlockquote().run(), + shortcuts: ['mod', 'shift', 'B'] + }, + { + value: 'horizontalRule', + label: 'Divider', + icon: , + action: editor => editor.chain().focus().setHorizontalRule().run(), + isActive: () => false, + canExecute: editor => editor.can().chain().focus().setHorizontalRule().run(), + shortcuts: ['mod', 'alt', '-'] + } +] + +interface SectionFiveProps extends VariantProps { + editor: Editor + activeActions?: InsertElementAction[] + mainActionCount?: number +} + +export const SectionFive: React.FC = ({ + editor, + activeActions = formatActions.map(action => action.value), + mainActionCount = 0, + size, + variant +}) => { + return ( + <> + + + + + + + } + dropdownTooltip="Insert elements" + size={size} + variant={variant} + /> + + ) +} + +SectionFive.displayName = 'SectionFive' + +export default SectionFive diff --git a/web/components/minimal-tiptap/components/section/four.tsx b/web/components/minimal-tiptap/components/section/four.tsx new file mode 100644 index 00000000..2686c635 --- /dev/null +++ b/web/components/minimal-tiptap/components/section/four.tsx @@ -0,0 +1,73 @@ +import * as React from 'react' +import type { Editor } from '@tiptap/react' +import { CaretDownIcon, ListBulletIcon } from '@radix-ui/react-icons' +import type { FormatAction } from '../../types' +import { ToolbarSection } from '../toolbar-section' +import type { toggleVariants } from '@/components/ui/toggle' +import type { VariantProps } from 'class-variance-authority' + +type ListItemAction = 'orderedList' | 'bulletList' +interface ListItem extends FormatAction { + value: ListItemAction +} + +const formatActions: ListItem[] = [ + { + value: 'orderedList', + label: 'Numbered list', + icon: ( + + + + ), + isActive: editor => editor.isActive('orderedList'), + action: editor => editor.chain().focus().toggleOrderedList().run(), + canExecute: editor => editor.can().chain().focus().toggleOrderedList().run(), + shortcuts: ['mod', 'shift', '7'] + }, + { + value: 'bulletList', + label: 'Bullet list', + icon: , + isActive: editor => editor.isActive('bulletList'), + action: editor => editor.chain().focus().toggleBulletList().run(), + canExecute: editor => editor.can().chain().focus().toggleBulletList().run(), + shortcuts: ['mod', 'shift', '8'] + } +] + +interface SectionFourProps extends VariantProps { + editor: Editor + activeActions?: ListItemAction[] + mainActionCount?: number +} + +export const SectionFour: React.FC = ({ + editor, + activeActions = formatActions.map(action => action.value), + mainActionCount = 0, + size, + variant +}) => { + return ( + + + + + } + dropdownTooltip="Lists" + size={size} + variant={variant} + /> + ) +} + +SectionFour.displayName = 'SectionFour' + +export default SectionFour diff --git a/web/components/minimal-tiptap/components/section/one.tsx b/web/components/minimal-tiptap/components/section/one.tsx new file mode 100644 index 00000000..85c41d8a --- /dev/null +++ b/web/components/minimal-tiptap/components/section/one.tsx @@ -0,0 +1,137 @@ +import type { Editor } from '@tiptap/react' +import type { Level } from '@tiptap/extension-heading' +import { cn } from '@/lib/utils' +import { CaretDownIcon, LetterCaseCapitalizeIcon } from '@radix-ui/react-icons' +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' +import { ToolbarButton } from '../toolbar-button' +import { ShortcutKey } from '../shortcut-key' +import React, { useCallback, useMemo } from 'react' +import type { FormatAction } from '../../types' +import type { VariantProps } from 'class-variance-authority' +import type { toggleVariants } from '@/components/ui/toggle' + +interface TextStyle extends Omit { + element: keyof JSX.IntrinsicElements + level?: Level + className: string +} + +const formatActions: TextStyle[] = [ + { + label: 'Normal Text', + element: 'span', + className: 'grow', + shortcuts: ['mod', 'alt', '0'] + }, + { + label: 'Heading 1', + element: 'h1', + level: 1, + className: 'm-0 grow text-3xl font-extrabold', + shortcuts: ['mod', 'alt', '1'] + }, + { + label: 'Heading 2', + element: 'h2', + level: 2, + className: 'm-0 grow text-xl font-bold', + shortcuts: ['mod', 'alt', '2'] + }, + { + label: 'Heading 3', + element: 'h3', + level: 3, + className: 'm-0 grow text-lg font-semibold', + shortcuts: ['mod', 'alt', '3'] + }, + { + label: 'Heading 4', + element: 'h4', + level: 4, + className: 'm-0 grow text-base font-semibold', + shortcuts: ['mod', 'alt', '4'] + }, + { + label: 'Heading 5', + element: 'h5', + level: 5, + className: 'm-0 grow text-sm font-normal', + shortcuts: ['mod', 'alt', '5'] + }, + { + label: 'Heading 6', + element: 'h6', + level: 6, + className: 'm-0 grow text-sm font-normal', + shortcuts: ['mod', 'alt', '6'] + } +] + +interface SectionOneProps extends VariantProps { + editor: Editor + activeLevels?: Level[] +} + +export const SectionOne: React.FC = React.memo( + ({ editor, activeLevels = [1, 2, 3, 4, 5, 6], size, variant }) => { + const filteredActions = useMemo( + () => formatActions.filter(action => !action.level || activeLevels.includes(action.level)), + [activeLevels] + ) + + const handleStyleChange = useCallback( + (level?: Level) => { + if (level) { + editor.chain().focus().toggleHeading({ level }).run() + } else { + editor.chain().focus().setParagraph().run() + } + }, + [editor] + ) + + const renderMenuItem = useCallback( + ({ label, element: Element, level, className, shortcuts }: TextStyle) => ( + handleStyleChange(level)} + className={cn('flex flex-row items-center justify-between gap-4', { + 'bg-accent': level ? editor.isActive('heading', { level }) : editor.isActive('paragraph') + })} + aria-label={label} + > + {label} + + + ), + [editor, handleStyleChange] + ) + + return ( + + + + + + + + + {filteredActions.map(renderMenuItem)} + + + ) + } +) + +SectionOne.displayName = 'SectionOne' + +export default SectionOne diff --git a/web/components/minimal-tiptap/components/section/three.tsx b/web/components/minimal-tiptap/components/section/three.tsx new file mode 100644 index 00000000..3b724569 --- /dev/null +++ b/web/components/minimal-tiptap/components/section/three.tsx @@ -0,0 +1,191 @@ +import * as React from 'react' +import type { Editor } from '@tiptap/react' +import { CaretDownIcon, CheckIcon } from '@radix-ui/react-icons' +import { ToolbarButton } from '../toolbar-button' +import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' +import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { useTheme } from '../../hooks/use-theme' +import type { toggleVariants } from '@/components/ui/toggle' +import type { VariantProps } from 'class-variance-authority' + +interface ColorItem { + cssVar: string + label: string + darkLabel?: string +} + +interface ColorPalette { + label: string + colors: ColorItem[] + inverse: string +} + +const COLORS: ColorPalette[] = [ + { + label: 'Palette 1', + inverse: 'hsl(var(--background))', + colors: [ + { cssVar: 'hsl(var(--foreground))', label: 'Default' }, + { cssVar: 'var(--mt-accent-bold-blue)', label: 'Bold blue' }, + { cssVar: 'var(--mt-accent-bold-teal)', label: 'Bold teal' }, + { cssVar: 'var(--mt-accent-bold-green)', label: 'Bold green' }, + { cssVar: 'var(--mt-accent-bold-orange)', label: 'Bold orange' }, + { cssVar: 'var(--mt-accent-bold-red)', label: 'Bold red' }, + { cssVar: 'var(--mt-accent-bold-purple)', label: 'Bold purple' } + ] + }, + { + label: 'Palette 2', + inverse: 'hsl(var(--background))', + colors: [ + { cssVar: 'var(--mt-accent-gray)', label: 'Gray' }, + { cssVar: 'var(--mt-accent-blue)', label: 'Blue' }, + { cssVar: 'var(--mt-accent-teal)', label: 'Teal' }, + { cssVar: 'var(--mt-accent-green)', label: 'Green' }, + { cssVar: 'var(--mt-accent-orange)', label: 'Orange' }, + { cssVar: 'var(--mt-accent-red)', label: 'Red' }, + { cssVar: 'var(--mt-accent-purple)', label: 'Purple' } + ] + }, + { + label: 'Palette 3', + inverse: 'hsl(var(--foreground))', + colors: [ + { cssVar: 'hsl(var(--background))', label: 'White', darkLabel: 'Black' }, + { cssVar: 'var(--mt-accent-blue-subtler)', label: 'Blue subtle' }, + { cssVar: 'var(--mt-accent-teal-subtler)', label: 'Teal subtle' }, + { cssVar: 'var(--mt-accent-green-subtler)', label: 'Green subtle' }, + { cssVar: 'var(--mt-accent-yellow-subtler)', label: 'Yellow subtle' }, + { cssVar: 'var(--mt-accent-red-subtler)', label: 'Red subtle' }, + { cssVar: 'var(--mt-accent-purple-subtler)', label: 'Purple subtle' } + ] + } +] + +const MemoizedColorButton = React.memo<{ + color: ColorItem + isSelected: boolean + inverse: string + onClick: (value: string) => void +}>(({ color, isSelected, inverse, onClick }) => { + const isDarkMode = useTheme() + const label = isDarkMode && color.darkLabel ? color.darkLabel : color.label + + return ( + + + ) => { + e.preventDefault() + onClick(color.cssVar) + }} + > + {isSelected && } + + + +

    {label}

    +
    +
    + ) +}) + +MemoizedColorButton.displayName = 'MemoizedColorButton' + +const MemoizedColorPicker = React.memo<{ + palette: ColorPalette + selectedColor: string + inverse: string + onColorChange: (value: string) => void +}>(({ palette, selectedColor, inverse, onColorChange }) => ( + { + if (value) onColorChange(value) + }} + className="gap-1.5" + > + {palette.colors.map((color, index) => ( + + ))} + +)) + +MemoizedColorPicker.displayName = 'MemoizedColorPicker' + +interface SectionThreeProps extends VariantProps { + editor: Editor +} + +export const SectionThree: React.FC = ({ editor, size, variant }) => { + const color = editor.getAttributes('textStyle')?.color || 'hsl(var(--foreground))' + const [selectedColor, setSelectedColor] = React.useState(color) + + const handleColorChange = React.useCallback( + (value: string) => { + setSelectedColor(value) + editor.chain().setColor(value).run() + }, + [editor] + ) + + React.useEffect(() => { + setSelectedColor(color) + }, [color]) + + return ( + + + + + + + + + + + + +
    + {COLORS.map((palette, index) => ( + + ))} +
    +
    +
    + ) +} + +SectionThree.displayName = 'SectionThree' + +export default SectionThree diff --git a/web/components/minimal-tiptap/components/section/two.tsx b/web/components/minimal-tiptap/components/section/two.tsx new file mode 100644 index 00000000..3515e630 --- /dev/null +++ b/web/components/minimal-tiptap/components/section/two.tsx @@ -0,0 +1,100 @@ +import * as React from 'react' +import type { Editor } from '@tiptap/react' +import { + CodeIcon, + DotsHorizontalIcon, + FontBoldIcon, + FontItalicIcon, + StrikethroughIcon, + TextNoneIcon +} from '@radix-ui/react-icons' +import type { FormatAction } from '../../types' +import { ToolbarSection } from '../toolbar-section' +import type { toggleVariants } from '@/components/ui/toggle' +import type { VariantProps } from 'class-variance-authority' + +type TextStyleAction = 'bold' | 'italic' | 'strikethrough' | 'code' | 'clearFormatting' + +interface TextStyle extends FormatAction { + value: TextStyleAction +} + +const formatActions: TextStyle[] = [ + { + value: 'bold', + label: 'Bold', + icon: , + action: editor => editor.chain().focus().toggleBold().run(), + isActive: editor => editor.isActive('bold'), + canExecute: editor => editor.can().chain().focus().toggleBold().run() && !editor.isActive('codeBlock'), + shortcuts: ['mod', 'B'] + }, + { + value: 'italic', + label: 'Italic', + icon: , + action: editor => editor.chain().focus().toggleItalic().run(), + isActive: editor => editor.isActive('italic'), + canExecute: editor => editor.can().chain().focus().toggleItalic().run() && !editor.isActive('codeBlock'), + shortcuts: ['mod', 'I'] + }, + { + value: 'strikethrough', + label: 'Strikethrough', + icon: , + action: editor => editor.chain().focus().toggleStrike().run(), + isActive: editor => editor.isActive('strike'), + canExecute: editor => editor.can().chain().focus().toggleStrike().run() && !editor.isActive('codeBlock'), + shortcuts: ['mod', 'shift', 'S'] + }, + { + value: 'code', + label: 'Code', + icon: , + action: editor => editor.chain().focus().toggleCode().run(), + isActive: editor => editor.isActive('code'), + canExecute: editor => editor.can().chain().focus().toggleCode().run() && !editor.isActive('codeBlock'), + shortcuts: ['mod', 'E'] + }, + { + value: 'clearFormatting', + label: 'Clear formatting', + icon: , + action: editor => editor.chain().focus().unsetAllMarks().run(), + isActive: () => false, + canExecute: editor => editor.can().chain().focus().unsetAllMarks().run() && !editor.isActive('codeBlock'), + shortcuts: ['mod', '\\'] + } +] + +interface SectionTwoProps extends VariantProps { + editor: Editor + activeActions?: TextStyleAction[] + mainActionCount?: number +} + +export const SectionTwo: React.FC = ({ + editor, + activeActions = formatActions.map(action => action.value), + mainActionCount = 2, + size, + variant +}) => { + return ( + } + dropdownTooltip="More formatting" + dropdownClassName="w-8" + size={size} + variant={variant} + /> + ) +} + +SectionTwo.displayName = 'SectionTwo' + +export default SectionTwo diff --git a/web/components/minimal-tiptap/components/shortcut-key.tsx b/web/components/minimal-tiptap/components/shortcut-key.tsx new file mode 100644 index 00000000..2691528d --- /dev/null +++ b/web/components/minimal-tiptap/components/shortcut-key.tsx @@ -0,0 +1,33 @@ +import * as React from 'react' +import { cn } from '@/lib/utils' +import { getShortcutKey } from '../utils' + +export interface ShortcutKeyProps extends React.HTMLAttributes { + keys: string[] +} + +export const ShortcutKey = React.forwardRef(({ className, keys, ...props }, ref) => { + const modifiedKeys = keys.map(key => getShortcutKey(key)) + const ariaLabel = modifiedKeys.map(shortcut => shortcut.readable).join(' + ') + + return ( + + {modifiedKeys.map(shortcut => ( + + {shortcut.symbol} + + ))} + + ) +}) + +ShortcutKey.displayName = 'ShortcutKey' diff --git a/web/components/minimal-tiptap/components/toolbar-button.tsx b/web/components/minimal-tiptap/components/toolbar-button.tsx new file mode 100644 index 00000000..5fd9cf40 --- /dev/null +++ b/web/components/minimal-tiptap/components/toolbar-button.tsx @@ -0,0 +1,38 @@ +import * as React from 'react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { Toggle } from '@/components/ui/toggle' +import { cn } from '@/lib/utils' +import type { TooltipContentProps } from '@radix-ui/react-tooltip' + +interface ToolbarButtonProps extends React.ComponentPropsWithoutRef { + isActive?: boolean + tooltip?: string + tooltipOptions?: TooltipContentProps +} + +export const ToolbarButton = React.forwardRef( + ({ isActive, children, tooltip, className, tooltipOptions, ...props }, ref) => { + const toggleButton = ( + + {children} + + ) + + if (!tooltip) { + return toggleButton + } + + return ( + + {toggleButton} + +
    {tooltip}
    +
    +
    + ) + } +) + +ToolbarButton.displayName = 'ToolbarButton' + +export default ToolbarButton diff --git a/web/components/minimal-tiptap/components/toolbar-section.tsx b/web/components/minimal-tiptap/components/toolbar-section.tsx new file mode 100644 index 00000000..e296fd17 --- /dev/null +++ b/web/components/minimal-tiptap/components/toolbar-section.tsx @@ -0,0 +1,112 @@ +import * as React from 'react' +import type { Editor } from '@tiptap/react' +import { cn } from '@/lib/utils' +import { CaretDownIcon } from '@radix-ui/react-icons' +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' +import { ToolbarButton } from './toolbar-button' +import { ShortcutKey } from './shortcut-key' +import { getShortcutKey } from '../utils' +import type { FormatAction } from '../types' +import type { VariantProps } from 'class-variance-authority' +import type { toggleVariants } from '@/components/ui/toggle' + +interface ToolbarSectionProps extends VariantProps { + editor: Editor + actions: FormatAction[] + activeActions?: string[] + mainActionCount?: number + dropdownIcon?: React.ReactNode + dropdownTooltip?: string + dropdownClassName?: string +} + +export const ToolbarSection: React.FC = ({ + editor, + actions, + activeActions = actions.map(action => action.value), + mainActionCount = 0, + dropdownIcon, + dropdownTooltip = 'More options', + dropdownClassName = 'w-12', + size, + variant +}) => { + const { mainActions, dropdownActions } = React.useMemo(() => { + const sortedActions = actions + .filter(action => activeActions.includes(action.value)) + .sort((a, b) => activeActions.indexOf(a.value) - activeActions.indexOf(b.value)) + + return { + mainActions: sortedActions.slice(0, mainActionCount), + dropdownActions: sortedActions.slice(mainActionCount) + } + }, [actions, activeActions, mainActionCount]) + + const renderToolbarButton = React.useCallback( + (action: FormatAction) => ( + action.action(editor)} + disabled={!action.canExecute(editor)} + isActive={action.isActive(editor)} + tooltip={`${action.label} ${action.shortcuts.map(s => getShortcutKey(s).symbol).join(' ')}`} + aria-label={action.label} + size={size} + variant={variant} + > + {action.icon} + + ), + [editor, size, variant] + ) + + const renderDropdownMenuItem = React.useCallback( + (action: FormatAction) => ( + action.action(editor)} + disabled={!action.canExecute(editor)} + className={cn('flex flex-row items-center justify-between gap-4', { + 'bg-accent': action.isActive(editor) + })} + aria-label={action.label} + > + {action.label} + + + ), + [editor] + ) + + const isDropdownActive = React.useMemo( + () => dropdownActions.some(action => action.isActive(editor)), + [dropdownActions, editor] + ) + + return ( + <> + {mainActions.map(renderToolbarButton)} + {dropdownActions.length > 0 && ( + + + + {dropdownIcon || } + + + + {dropdownActions.map(renderDropdownMenuItem)} + + + )} + + ) +} + +export default ToolbarSection diff --git a/web/components/minimal-tiptap/extensions/code-block-lowlight/code-block-lowlight.ts b/web/components/minimal-tiptap/extensions/code-block-lowlight/code-block-lowlight.ts new file mode 100644 index 00000000..54523668 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/code-block-lowlight/code-block-lowlight.ts @@ -0,0 +1,17 @@ +import { CodeBlockLowlight as TiptapCodeBlockLowlight } from '@tiptap/extension-code-block-lowlight' +import { common, createLowlight } from 'lowlight' + +export const CodeBlockLowlight = TiptapCodeBlockLowlight.extend({ + addOptions() { + return { + ...this.parent?.(), + lowlight: createLowlight(common), + defaultLanguage: null, + HTMLAttributes: { + class: 'block-node' + } + } + } +}) + +export default CodeBlockLowlight diff --git a/web/components/minimal-tiptap/extensions/code-block-lowlight/index.ts b/web/components/minimal-tiptap/extensions/code-block-lowlight/index.ts new file mode 100644 index 00000000..9ded0403 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/code-block-lowlight/index.ts @@ -0,0 +1 @@ +export * from './code-block-lowlight' diff --git a/web/components/minimal-tiptap/extensions/color/color.ts b/web/components/minimal-tiptap/extensions/color/color.ts new file mode 100644 index 00000000..9582d38b --- /dev/null +++ b/web/components/minimal-tiptap/extensions/color/color.ts @@ -0,0 +1,20 @@ +import { Color as TiptapColor } from '@tiptap/extension-color' +import { Plugin } from '@tiptap/pm/state' + +export const Color = TiptapColor.extend({ + addProseMirrorPlugins() { + return [ + ...(this.parent?.() || []), + new Plugin({ + props: { + handleKeyDown: (_, event) => { + if (event.key === 'Enter') { + this.editor.commands.unsetColor() + } + return false + } + } + }) + ] + } +}) diff --git a/web/components/minimal-tiptap/extensions/color/index.ts b/web/components/minimal-tiptap/extensions/color/index.ts new file mode 100644 index 00000000..1315dbcf --- /dev/null +++ b/web/components/minimal-tiptap/extensions/color/index.ts @@ -0,0 +1 @@ +export * from './color' diff --git a/web/components/minimal-tiptap/extensions/horizontal-rule/horizontal-rule.ts b/web/components/minimal-tiptap/extensions/horizontal-rule/horizontal-rule.ts new file mode 100644 index 00000000..c530d4f5 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/horizontal-rule/horizontal-rule.ts @@ -0,0 +1,18 @@ +/* + * Wrap the horizontal rule in a div element. + * Also add a keyboard shortcut to insert a horizontal rule. + */ +import { HorizontalRule as TiptapHorizontalRule } from '@tiptap/extension-horizontal-rule' + +export const HorizontalRule = TiptapHorizontalRule.extend({ + addKeyboardShortcuts() { + return { + 'Mod-Alt--': () => + this.editor.commands.insertContent({ + type: this.name + }) + } + } +}) + +export default HorizontalRule diff --git a/web/components/minimal-tiptap/extensions/horizontal-rule/index.ts b/web/components/minimal-tiptap/extensions/horizontal-rule/index.ts new file mode 100644 index 00000000..e6cb8015 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/horizontal-rule/index.ts @@ -0,0 +1 @@ +export * from './horizontal-rule' diff --git a/web/components/minimal-tiptap/extensions/image/components/image-view-block.tsx b/web/components/minimal-tiptap/extensions/image/components/image-view-block.tsx new file mode 100644 index 00000000..522569b4 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/image/components/image-view-block.tsx @@ -0,0 +1,45 @@ +import { isNumber, NodeViewProps, NodeViewWrapper } from '@tiptap/react' +import { useMemo } from 'react' +import { useImageLoad } from '../../../hooks/use-image-load' +import { cn } from '@/lib/utils' + +const ImageViewBlock = ({ editor, node, getPos }: NodeViewProps) => { + const imgSize = useImageLoad(node.attrs.src) + + const paddingBottom = useMemo(() => { + if (!imgSize.width || !imgSize.height) { + return 0 + } + + return (imgSize.height / imgSize.width) * 100 + }, [imgSize.width, imgSize.height]) + + return ( + +
    +
    +
    +
    +
    +
    + {node.attrs.alt} +
    +
    +
    +
    +
    +
    +
    + ) +} + +export { ImageViewBlock } diff --git a/web/components/minimal-tiptap/extensions/image/image.ts b/web/components/minimal-tiptap/extensions/image/image.ts new file mode 100644 index 00000000..12784fb5 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/image/image.ts @@ -0,0 +1,9 @@ +import { Image as TiptapImage } from '@tiptap/extension-image' +import { ReactNodeViewRenderer } from '@tiptap/react' +import { ImageViewBlock } from './components/image-view-block' + +export const Image = TiptapImage.extend({ + addNodeView() { + return ReactNodeViewRenderer(ImageViewBlock) + } +}) diff --git a/web/components/minimal-tiptap/extensions/image/index.ts b/web/components/minimal-tiptap/extensions/image/index.ts new file mode 100644 index 00000000..556dbfdf --- /dev/null +++ b/web/components/minimal-tiptap/extensions/image/index.ts @@ -0,0 +1 @@ +export * from './image' diff --git a/web/components/minimal-tiptap/extensions/index.ts b/web/components/minimal-tiptap/extensions/index.ts new file mode 100644 index 00000000..63f21fa7 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/index.ts @@ -0,0 +1,8 @@ +export * from './code-block-lowlight' +export * from './color' +export * from './horizontal-rule' +export * from './image' +export * from './link' +export * from './selection' +export * from './unset-all-marks' +export * from './reset-marks-on-enter' diff --git a/web/components/minimal-tiptap/extensions/link/index.ts b/web/components/minimal-tiptap/extensions/link/index.ts new file mode 100644 index 00000000..6bbafd20 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/link/index.ts @@ -0,0 +1 @@ +export * from './link' diff --git a/web/components/minimal-tiptap/extensions/link/link.ts b/web/components/minimal-tiptap/extensions/link/link.ts new file mode 100644 index 00000000..1db71b95 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/link/link.ts @@ -0,0 +1,89 @@ +import { mergeAttributes } from '@tiptap/core' +import TiptapLink from '@tiptap/extension-link' +import { EditorView } from '@tiptap/pm/view' +import { getMarkRange } from '@tiptap/core' +import { Plugin, TextSelection } from '@tiptap/pm/state' + +export const Link = TiptapLink.extend({ + /* + * Determines whether typing next to a link automatically becomes part of the link. + * In this case, we dont want any characters to be included as part of the link. + */ + inclusive: false, + + /* + * Match all elements that have an href attribute, except for: + * - elements with a data-type attribute set to button + * - elements with an href attribute that contains 'javascript:' + */ + parseHTML() { + return [{ tag: 'a[href]:not([data-type="button"]):not([href *= "javascript:" i])' }] + }, + + renderHTML({ HTMLAttributes }) { + return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0] + }, + + addOptions() { + return { + ...this.parent?.(), + openOnClick: false, + HTMLAttributes: { + class: 'link' + } + } + }, + + addProseMirrorPlugins() { + const { editor } = this + + return [ + ...(this.parent?.() || []), + new Plugin({ + props: { + handleKeyDown: (_: EditorView, event: KeyboardEvent) => { + const { selection } = editor.state + + /* + * Handles the 'Escape' key press when there's a selection within the link. + * This will move the cursor to the end of the link. + */ + if (event.key === 'Escape' && selection.empty !== true) { + editor.commands.focus(selection.to, { scrollIntoView: false }) + } + + return false + }, + handleClick(view, pos) { + /* + * Marks the entire link when the user clicks on it. + */ + + const { schema, doc, tr } = view.state + const range = getMarkRange(doc.resolve(pos), schema.marks.link) + + if (!range) { + return + } + + const { from, to } = range + const start = Math.min(from, to) + const end = Math.max(from, to) + + if (pos < start || pos > end) { + return + } + + const $start = doc.resolve(start) + const $end = doc.resolve(end) + const transaction = tr.setSelection(new TextSelection($start, $end)) + + view.dispatch(transaction) + } + } + }) + ] + } +}) + +export default Link diff --git a/web/components/minimal-tiptap/extensions/reset-marks-on-enter/index.ts b/web/components/minimal-tiptap/extensions/reset-marks-on-enter/index.ts new file mode 100644 index 00000000..f514cdd1 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/reset-marks-on-enter/index.ts @@ -0,0 +1 @@ +export * from './reset-marks-on-enter' diff --git a/web/components/minimal-tiptap/extensions/reset-marks-on-enter/reset-marks-on-enter.ts b/web/components/minimal-tiptap/extensions/reset-marks-on-enter/reset-marks-on-enter.ts new file mode 100644 index 00000000..e9770710 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/reset-marks-on-enter/reset-marks-on-enter.ts @@ -0,0 +1,25 @@ +import { Extension } from '@tiptap/core' + +export const ResetMarksOnEnter = Extension.create({ + name: 'resetMarksOnEnter', + + addKeyboardShortcuts() { + return { + Enter: ({ editor }) => { + if ( + editor.isActive('bold') || + editor.isActive('italic') || + editor.isActive('strike') || + editor.isActive('underline') || + editor.isActive('code') + ) { + editor.commands.splitBlock({ keepMarks: false }) + + return true + } + + return false + } + } + } +}) diff --git a/web/components/minimal-tiptap/extensions/selection/index.ts b/web/components/minimal-tiptap/extensions/selection/index.ts new file mode 100644 index 00000000..75df11a6 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/selection/index.ts @@ -0,0 +1 @@ +export * from './selection' diff --git a/web/components/minimal-tiptap/extensions/selection/selection.ts b/web/components/minimal-tiptap/extensions/selection/selection.ts new file mode 100644 index 00000000..7e28ac2f --- /dev/null +++ b/web/components/minimal-tiptap/extensions/selection/selection.ts @@ -0,0 +1,36 @@ +import { Extension } from '@tiptap/core' +import { Plugin, PluginKey } from '@tiptap/pm/state' +import { Decoration, DecorationSet } from '@tiptap/pm/view' + +export const Selection = Extension.create({ + name: 'selection', + + addProseMirrorPlugins() { + const { editor } = this + + return [ + new Plugin({ + key: new PluginKey('selection'), + props: { + decorations(state) { + if (state.selection.empty) { + return null + } + + if (editor.isFocused === true) { + return null + } + + return DecorationSet.create(state.doc, [ + Decoration.inline(state.selection.from, state.selection.to, { + class: 'selection' + }) + ]) + } + } + }) + ] + } +}) + +export default Selection diff --git a/web/components/minimal-tiptap/extensions/unset-all-marks/index.ts b/web/components/minimal-tiptap/extensions/unset-all-marks/index.ts new file mode 100644 index 00000000..50d26fbc --- /dev/null +++ b/web/components/minimal-tiptap/extensions/unset-all-marks/index.ts @@ -0,0 +1 @@ +export * from './unset-all-marks' diff --git a/web/components/minimal-tiptap/extensions/unset-all-marks/unset-all-marks.ts b/web/components/minimal-tiptap/extensions/unset-all-marks/unset-all-marks.ts new file mode 100644 index 00000000..6aa64469 --- /dev/null +++ b/web/components/minimal-tiptap/extensions/unset-all-marks/unset-all-marks.ts @@ -0,0 +1,9 @@ +import { Extension } from '@tiptap/core' + +export const UnsetAllMarks = Extension.create({ + addKeyboardShortcuts() { + return { + 'Mod-\\': () => this.editor.commands.unsetAllMarks() + } + } +}) diff --git a/web/components/minimal-tiptap/hooks/use-image-load.ts b/web/components/minimal-tiptap/hooks/use-image-load.ts new file mode 100644 index 00000000..463efb1e --- /dev/null +++ b/web/components/minimal-tiptap/hooks/use-image-load.ts @@ -0,0 +1,15 @@ +import * as React from 'react' + +export const useImageLoad = (src: string) => { + const [imgSize, setImgSize] = React.useState({ width: 0, height: 0 }) + + React.useEffect(() => { + const img = new Image() + img.src = src + img.onload = () => { + setImgSize({ width: img.width, height: img.height }) + } + }, [src]) + + return imgSize +} diff --git a/web/components/minimal-tiptap/hooks/use-minimal-tiptap.ts b/web/components/minimal-tiptap/hooks/use-minimal-tiptap.ts new file mode 100644 index 00000000..ed89b675 --- /dev/null +++ b/web/components/minimal-tiptap/hooks/use-minimal-tiptap.ts @@ -0,0 +1,107 @@ +import * as React from "react" +import { StarterKit } from "@tiptap/starter-kit" +import type { Content, UseEditorOptions } from "@tiptap/react" +import { useEditor } from "@tiptap/react" +import type { Editor } from "@tiptap/core" +import { Typography } from "@tiptap/extension-typography" +import { Placeholder } from "@tiptap/extension-placeholder" +import { TextStyle } from "@tiptap/extension-text-style" +import { + Link, + Image, + HorizontalRule, + CodeBlockLowlight, + Selection, + Color, + UnsetAllMarks, + ResetMarksOnEnter +} from "../extensions" +import { cn } from "@/lib/utils" +import { getOutput } from "../utils" +import { useThrottle } from "../hooks/use-throttle" + +export interface UseMinimalTiptapEditorProps extends UseEditorOptions { + value?: Content + output?: "html" | "json" | "text" + placeholder?: string + editorClassName?: string + throttleDelay?: number + onUpdate?: (content: Content) => void + onBlur?: (content: Content) => void +} + +const createExtensions = (placeholder: string) => [ + StarterKit.configure({ + horizontalRule: false, + codeBlock: false, + paragraph: { HTMLAttributes: { class: "text-node" } }, + heading: { HTMLAttributes: { class: "heading-node" } }, + blockquote: { HTMLAttributes: { class: "block-node" } }, + bulletList: { HTMLAttributes: { class: "list-node" } }, + orderedList: { HTMLAttributes: { class: "list-node" } }, + code: { HTMLAttributes: { class: "inline", spellcheck: "false" } }, + dropcursor: { width: 2, class: "ProseMirror-dropcursor border" } + }), + Link, + Image, + Color, + TextStyle, + Selection, + Typography, + UnsetAllMarks, + HorizontalRule, + ResetMarksOnEnter, + CodeBlockLowlight, + Placeholder.configure({ placeholder: () => placeholder }) +] + +export const useMinimalTiptapEditor = ({ + value, + output = "html", + placeholder = "", + editorClassName, + throttleDelay = 1000, + onUpdate, + onBlur, + ...props +}: UseMinimalTiptapEditorProps) => { + const throttledSetValue = useThrottle((value: Content) => onUpdate?.(value), throttleDelay) + + const handleUpdate = React.useCallback( + (editor: Editor) => { + throttledSetValue(getOutput(editor, output)) + }, + [output, throttledSetValue] + ) + + const handleCreate = React.useCallback( + (editor: Editor) => { + if (value && editor.isEmpty) { + editor.commands.setContent(value) + } + }, + [value] + ) + + const handleBlur = React.useCallback((editor: Editor) => onBlur?.(getOutput(editor, output)), [output, onBlur]) + + const editor = useEditor({ + extensions: createExtensions(placeholder!), + editorProps: { + attributes: { + autocomplete: "off", + autocorrect: "off", + autocapitalize: "off", + class: cn("focus:outline-none", editorClassName) + } + }, + onUpdate: ({ editor }) => handleUpdate(editor), + onCreate: ({ editor }) => handleCreate(editor), + onBlur: ({ editor }) => handleBlur(editor), + ...props + }) + + return editor +} + +export default useMinimalTiptapEditor diff --git a/web/components/minimal-tiptap/hooks/use-theme.ts b/web/components/minimal-tiptap/hooks/use-theme.ts new file mode 100644 index 00000000..9bb816b6 --- /dev/null +++ b/web/components/minimal-tiptap/hooks/use-theme.ts @@ -0,0 +1,25 @@ +import * as React from 'react' + +export const useTheme = () => { + const [isDarkMode, setIsDarkMode] = React.useState(false) + + React.useEffect(() => { + const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + setIsDarkMode(darkModeMediaQuery.matches) + + const handleChange = (e: MediaQueryListEvent) => { + const newDarkMode = e.matches + setIsDarkMode(newDarkMode) + } + + darkModeMediaQuery.addEventListener('change', handleChange) + + return () => { + darkModeMediaQuery.removeEventListener('change', handleChange) + } + }, []) + + return isDarkMode +} + +export default useTheme diff --git a/web/components/minimal-tiptap/hooks/use-throttle.ts b/web/components/minimal-tiptap/hooks/use-throttle.ts new file mode 100644 index 00000000..f3f88fba --- /dev/null +++ b/web/components/minimal-tiptap/hooks/use-throttle.ts @@ -0,0 +1,34 @@ +import { useRef, useCallback } from 'react' + +export function useThrottle void>( + callback: T, + delay: number +): (...args: Parameters) => void { + const lastRan = useRef(Date.now()) + const timeoutRef = useRef(null) + + return useCallback( + (...args: Parameters) => { + const handler = () => { + if (Date.now() - lastRan.current >= delay) { + callback(...args) + lastRan.current = Date.now() + } else { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + timeoutRef.current = setTimeout( + () => { + callback(...args) + lastRan.current = Date.now() + }, + delay - (Date.now() - lastRan.current) + ) + } + } + + handler() + }, + [callback, delay] + ) +} diff --git a/web/components/minimal-tiptap/index.ts b/web/components/minimal-tiptap/index.ts new file mode 100644 index 00000000..1532aab8 --- /dev/null +++ b/web/components/minimal-tiptap/index.ts @@ -0,0 +1 @@ +export * from './minimal-tiptap' diff --git a/web/components/minimal-tiptap/minimal-tiptap.tsx b/web/components/minimal-tiptap/minimal-tiptap.tsx new file mode 100644 index 00000000..668dde79 --- /dev/null +++ b/web/components/minimal-tiptap/minimal-tiptap.tsx @@ -0,0 +1,95 @@ +import * as React from "react" +import "./styles/index.css" + +import { EditorContent } from "@tiptap/react" +import type { Content, Editor } from "@tiptap/react" +import { Separator } from "@/components/ui/separator" +import { cn } from "@/lib/utils" +import { SectionOne } from "./components/section/one" +import { SectionTwo } from "./components/section/two" +import { SectionThree } from "./components/section/three" +import { SectionFour } from "./components/section/four" +import { SectionFive } from "./components/section/five" +import { LinkBubbleMenu } from "./components/bubble-menu/link-bubble-menu" +import { ImageBubbleMenu } from "./components/bubble-menu/image-bubble-menu" +import type { UseMinimalTiptapEditorProps } from "./hooks/use-minimal-tiptap" +import { useMinimalTiptapEditor } from "./hooks/use-minimal-tiptap" + +export interface MinimalTiptapProps extends Omit { + value?: Content + onChange?: (value: Content) => void + className?: string + editorContentClassName?: string +} + +const Toolbar = ({ editor }: { editor: Editor }) => ( +
    +
    + + + + + + + + + + + + + + + + + +
    +
    +) + +export type MinimalTiptapEditorRef = { + editor: Editor | null +} + +export const MinimalTiptapEditor = React.forwardRef( + ({ value, onChange, className, editorContentClassName, ...props }, ref) => { + const editor = useMinimalTiptapEditor({ + value, + onUpdate: onChange, + ...props + }) + + React.useImperativeHandle( + ref, + () => ({ + editor: editor || null + }), + [editor] + ) + + if (!editor) { + return null + } + + return ( +
    + + + + +
    + ) + } +) + +MinimalTiptapEditor.displayName = "MinimalTiptapEditor" + +export default MinimalTiptapEditor diff --git a/web/components/minimal-tiptap/styles/index.css b/web/components/minimal-tiptap/styles/index.css new file mode 100644 index 00000000..7121c31a --- /dev/null +++ b/web/components/minimal-tiptap/styles/index.css @@ -0,0 +1,182 @@ +@import './partials/code.css'; +@import './partials/placeholder.css'; +@import './partials/lists.css'; +@import './partials/typography.css'; + +:root { + --mt-font-size-regular: 0.9375rem; + + --mt-code-background: #082b781f; + --mt-code-color: #d4d4d4; + --mt-secondary: #9d9d9f; + --mt-pre-background: #ececec; + --mt-pre-border: #e0e0e0; + --mt-pre-color: #2f2f31; + --mt-hr: #dcdcdc; + --mt-drag-handle-hover: #5c5c5e; + + --mt-accent-bold-blue: #05c; + --mt-accent-bold-teal: #206a83; + --mt-accent-bold-green: #216e4e; + --mt-accent-bold-orange: #a54800; + --mt-accent-bold-red: #ae2e24; + --mt-accent-bold-purple: #5e4db2; + + --mt-accent-gray: #758195; + --mt-accent-blue: #1d7afc; + --mt-accent-teal: #2898bd; + --mt-accent-green: #22a06b; + --mt-accent-orange: #fea362; + --mt-accent-red: #c9372c; + --mt-accent-purple: #8270db; + + --mt-accent-blue-subtler: #cce0ff; + --mt-accent-teal-subtler: #c6edfb; + --mt-accent-green-subtler: #baf3db; + --mt-accent-yellow-subtler: #f8e6a0; + --mt-accent-red-subtler: #ffd5d2; + --mt-accent-purple-subtler: #dfd8fd; + + --hljs-string: #aa430f; + --hljs-title: #b08836; + --hljs-comment: #999999; + --hljs-keyword: #0c5eb1; + --hljs-attr: #3a92bc; + --hljs-literal: #c82b0f; + --hljs-name: #259792; + --hljs-selector-tag: #c8500f; + --hljs-number: #3da067; +} + +.dark { + --mt-font-size-regular: 0.9375rem; + + --mt-code-background: #ffffff13; + --mt-code-color: #2c2e33; + --mt-secondary: #595a5c; + --mt-pre-background: #080808; + --mt-pre-border: #23252a; + --mt-pre-color: #e3e4e6; + --mt-hr: #26282d; + --mt-drag-handle-hover: #969799; + + --mt-accent-bold-blue: #85b8ff; + --mt-accent-bold-teal: #9dd9ee; + --mt-accent-bold-green: #7ee2b8; + --mt-accent-bold-orange: #fec195; + --mt-accent-bold-red: #fd9891; + --mt-accent-bold-purple: #b8acf6; + + --mt-accent-gray: #738496; + --mt-accent-blue: #388bff; + --mt-accent-teal: #42b2d7; + --mt-accent-green: #2abb7f; + --mt-accent-orange: #a54800; + --mt-accent-red: #e2483d; + --mt-accent-purple: #8f7ee7; + + --mt-accent-blue-subtler: #09326c; + --mt-accent-teal-subtler: #164555; + --mt-accent-green-subtler: #164b35; + --mt-accent-yellow-subtler: #533f04; + --mt-accent-red-subtler: #5d1f1a; + --mt-accent-purple-subtler: #352c63; + + --hljs-string: #da936b; + --hljs-title: #f1d59d; + --hljs-comment: #aaaaaa; + --hljs-keyword: #6699cc; + --hljs-attr: #90cae8; + --hljs-literal: #f2777a; + --hljs-name: #5fc0a0; + --hljs-selector-tag: #e8c785; + --hljs-number: #b6e7b6; +} + +.minimal-tiptap-editor .ProseMirror { + @apply flex max-w-full flex-1 cursor-text flex-col; + @apply z-0 outline-0; +} + +.minimal-tiptap-editor .ProseMirror > div.editor { + @apply block flex-1 whitespace-pre-wrap; +} + +.minimal-tiptap-editor .ProseMirror .block-node:not(:last-child), +.minimal-tiptap-editor .ProseMirror .list-node:not(:last-child), +.minimal-tiptap-editor .ProseMirror .text-node:not(:last-child) { + @apply mb-2.5; +} + +.minimal-tiptap-editor .ProseMirror ol, +.minimal-tiptap-editor .ProseMirror ul { + @apply pl-6; +} + +.minimal-tiptap-editor .ProseMirror blockquote, +.minimal-tiptap-editor .ProseMirror dl, +.minimal-tiptap-editor .ProseMirror ol, +.minimal-tiptap-editor .ProseMirror p, +.minimal-tiptap-editor .ProseMirror pre, +.minimal-tiptap-editor .ProseMirror ul { + @apply m-0; +} + +.minimal-tiptap-editor .ProseMirror li { + @apply leading-7; +} + +.minimal-tiptap-editor .ProseMirror p { + @apply break-words; +} + +.minimal-tiptap-editor .ProseMirror li .text-node:has(+ .list-node), +.minimal-tiptap-editor .ProseMirror li > .list-node, +.minimal-tiptap-editor .ProseMirror li > .text-node, +.minimal-tiptap-editor .ProseMirror li p { + @apply mb-0; +} + +.minimal-tiptap-editor .ProseMirror blockquote { + @apply relative pl-3.5; +} + +.minimal-tiptap-editor .ProseMirror blockquote::before, +.minimal-tiptap-editor .ProseMirror blockquote.is-empty::before { + @apply absolute bottom-0 left-0 top-0 h-full w-1 rounded-sm bg-accent-foreground/15 content-['']; +} + +.minimal-tiptap-editor .ProseMirror hr { + @apply my-3 h-0.5 w-full border-none bg-[var(--mt-hr)]; +} + +.minimal-tiptap-editor .ProseMirror-focused hr.ProseMirror-selectednode { + @apply rounded-full outline outline-2 outline-offset-1 outline-muted-foreground; +} + +.minimal-tiptap-editor .ProseMirror .ProseMirror-gapcursor { + @apply pointer-events-none absolute hidden; +} + +.minimal-tiptap-editor .ProseMirror .ProseMirror-hideselection { + @apply caret-transparent; +} + +.minimal-tiptap-editor .ProseMirror.resize-cursor { + @apply cursor-col-resize; +} + +.minimal-tiptap-editor .ProseMirror .selection { + @apply inline-block; +} + +.minimal-tiptap-editor .ProseMirror .selection, +.minimal-tiptap-editor .ProseMirror *::selection, +::selection { + @apply bg-primary/25; +} + +/* Override native selection when custom selection is present */ +.minimal-tiptap-editor .ProseMirror .selection::selection { + background: transparent; +} diff --git a/web/components/minimal-tiptap/styles/partials/code.css b/web/components/minimal-tiptap/styles/partials/code.css new file mode 100644 index 00000000..b1d03ea7 --- /dev/null +++ b/web/components/minimal-tiptap/styles/partials/code.css @@ -0,0 +1,86 @@ +.minimal-tiptap-editor .ProseMirror code.inline { + @apply rounded border border-[var(--mt-code-color)] bg-[var(--mt-code-background)] px-1 py-0.5 text-sm; +} + +.minimal-tiptap-editor .ProseMirror pre { + @apply relative overflow-auto rounded border font-mono text-sm; + @apply border-[var(--mt-pre-border)] bg-[var(--mt-pre-background)] text-[var(--mt-pre-color)]; + @apply hyphens-none whitespace-pre text-left; +} + +.minimal-tiptap-editor .ProseMirror code { + @apply break-words leading-[1.7em]; +} + +.minimal-tiptap-editor .ProseMirror pre code { + @apply block overflow-x-auto p-3.5; +} + +.minimal-tiptap-editor .ProseMirror pre { + .hljs-keyword, + .hljs-operator, + .hljs-function, + .hljs-built_in, + .hljs-builtin-name { + color: var(--hljs-keyword); + } + + .hljs-attr, + .hljs-symbol, + .hljs-property, + .hljs-attribute, + .hljs-variable, + .hljs-template-variable, + .hljs-params { + color: var(--hljs-attr); + } + + .hljs-name, + .hljs-regexp, + .hljs-link, + .hljs-type, + .hljs-addition { + color: var(--hljs-name); + } + + .hljs-string, + .hljs-bullet { + color: var(--hljs-string); + } + + .hljs-title, + .hljs-subst, + .hljs-section { + color: var(--hljs-title); + } + + .hljs-literal, + .hljs-type, + .hljs-deletion { + color: var(--hljs-literal); + } + + .hljs-selector-tag, + .hljs-selector-id, + .hljs-selector-class { + color: var(--hljs-selector-tag); + } + + .hljs-number { + color: var(--hljs-number); + } + + .hljs-comment, + .hljs-meta, + .hljs-quote { + color: var(--hljs-comment); + } + + .hljs-emphasis { + @apply italic; + } + + .hljs-strong { + @apply font-bold; + } +} diff --git a/web/components/minimal-tiptap/styles/partials/lists.css b/web/components/minimal-tiptap/styles/partials/lists.css new file mode 100644 index 00000000..f4d75a68 --- /dev/null +++ b/web/components/minimal-tiptap/styles/partials/lists.css @@ -0,0 +1,82 @@ +.minimal-tiptap-editor div.tiptap p { + @apply text-[var(--mt-font-size-regular)]; +} + +.minimal-tiptap-editor .ProseMirror ol { + @apply list-decimal; +} + +.minimal-tiptap-editor .ProseMirror ol ol { + list-style: lower-alpha; +} + +.minimal-tiptap-editor .ProseMirror ol ol ol { + list-style: lower-roman; +} + +.minimal-tiptap-editor .ProseMirror ul { + list-style: disc; +} + +.minimal-tiptap-editor .ProseMirror ul ul { + list-style: circle; +} + +.minimal-tiptap-editor .ProseMirror ul ul ul { + list-style: square; +} + +.minimal-tiptap-editor .ProseMirror ul[data-type='taskList'] { + @apply list-none pl-1; +} + +.minimal-tiptap-editor .ProseMirror ul[data-type='taskList'] p { + @apply m-0; +} + +.minimal-tiptap-editor .ProseMirror ul[data-type='taskList'] li > label { + @apply mr-2 mt-0.5 flex-none select-none; +} + +.minimal-tiptap-editor .ProseMirror li[data-type='taskItem'] { + @apply flex flex-row items-start; +} + +.minimal-tiptap-editor .ProseMirror li[data-type='taskItem'] .taskItem-checkbox-container { + @apply relative pr-2; +} + +.minimal-tiptap-editor .ProseMirror .taskItem-drag-handle { + @apply absolute -left-5 top-1.5 h-[18px] w-[18px] cursor-move pl-0.5 text-[var(--mt-secondary)] opacity-0; +} + +.minimal-tiptap-editor + .ProseMirror + li[data-type='taskItem']:hover:not(:has(li:hover)) + > .taskItem-checkbox-container + > .taskItem-drag-handle { + @apply opacity-100; +} + +.minimal-tiptap-editor .ProseMirror .taskItem-drag-handle:hover { + @apply text-[var(--mt-drag-handle-hover)]; +} + +.minimal-tiptap-editor .ProseMirror .taskItem-checkbox { + fill-opacity: 0; + @apply h-3.5 w-3.5 flex-shrink-0 cursor-pointer select-none appearance-none rounded border border-solid border-[var(--mt-secondary)] bg-transparent bg-[1px_2px] p-0.5 align-middle transition-colors duration-75 ease-out; +} + +.minimal-tiptap-editor .ProseMirror .taskItem-checkbox:checked { + @apply border-primary bg-primary bg-no-repeat; + background-image: url('data:image/svg+xml;utf8,%3Csvg%20width=%2210%22%20height=%229%22%20viewBox=%220%200%2010%208%22%20xmlns=%22http://www.w3.org/2000/svg%22%20fill=%22%23fbfbfb%22%3E%3Cpath%20d=%22M3.46975%205.70757L1.88358%204.1225C1.65832%203.8974%201.29423%203.8974%201.06897%204.1225C0.843675%204.34765%200.843675%204.7116%201.06897%204.93674L3.0648%206.93117C3.29006%207.15628%203.65414%207.15628%203.8794%206.93117L8.93103%201.88306C9.15633%201.65792%209.15633%201.29397%208.93103%201.06883C8.70578%200.843736%208.34172%200.843724%208.11646%201.06879C8.11645%201.0688%208.11643%201.06882%208.11642%201.06883L3.46975%205.70757Z%22%20stroke-width=%220.2%22%20/%3E%3C/svg%3E'); +} + +.minimal-tiptap-editor .ProseMirror .taskItem-content { + @apply min-w-0 flex-1; +} + +.minimal-tiptap-editor .ProseMirror li[data-checked='true'] .taskItem-content > :not([data-type='taskList']), +.minimal-tiptap-editor .ProseMirror li[data-checked='true'] .taskItem-content .taskItem-checkbox { + @apply opacity-75; +} diff --git a/web/components/minimal-tiptap/styles/partials/placeholder.css b/web/components/minimal-tiptap/styles/partials/placeholder.css new file mode 100644 index 00000000..04bcfdf0 --- /dev/null +++ b/web/components/minimal-tiptap/styles/partials/placeholder.css @@ -0,0 +1,4 @@ +.minimal-tiptap-editor .ProseMirror > p.is-editor-empty::before { + content: attr(data-placeholder); + @apply pointer-events-none float-left h-0 text-[var(--mt-secondary)]; +} diff --git a/web/components/minimal-tiptap/styles/partials/typography.css b/web/components/minimal-tiptap/styles/partials/typography.css new file mode 100644 index 00000000..a1f753b7 --- /dev/null +++ b/web/components/minimal-tiptap/styles/partials/typography.css @@ -0,0 +1,27 @@ +.minimal-tiptap-editor .ProseMirror .heading-node { + @apply relative font-semibold; +} + +.minimal-tiptap-editor .ProseMirror .heading-node:first-child { + @apply mt-0; +} + +.minimal-tiptap-editor .ProseMirror h1 { + @apply mb-4 mt-[46px] text-[1.375rem] leading-7 tracking-[-0.004375rem]; +} + +.minimal-tiptap-editor .ProseMirror h2 { + @apply mb-3.5 mt-8 text-[1.1875rem] leading-7 tracking-[0.003125rem]; +} + +.minimal-tiptap-editor .ProseMirror h3 { + @apply mb-3 mt-6 text-[1.0625rem] leading-6 tracking-[0.00625rem]; +} + +.minimal-tiptap-editor .ProseMirror a.link { + @apply cursor-pointer text-primary; +} + +.minimal-tiptap-editor .ProseMirror a.link:hover { + @apply underline; +} diff --git a/web/components/minimal-tiptap/types.ts b/web/components/minimal-tiptap/types.ts new file mode 100644 index 00000000..fe06c8e0 --- /dev/null +++ b/web/components/minimal-tiptap/types.ts @@ -0,0 +1,28 @@ +import type { Editor } from '@tiptap/core' +import type { EditorView } from '@tiptap/pm/view' +import type { EditorState } from '@tiptap/pm/state' + +export interface LinkProps { + url: string + text?: string + openInNewTab?: boolean +} + +export interface ShouldShowProps { + editor: Editor + view: EditorView + state: EditorState + oldState?: EditorState + from: number + to: number +} + +export interface FormatAction { + label: string + icon?: React.ReactNode + action: (editor: Editor) => void + isActive: (editor: Editor) => boolean + canExecute: (editor: Editor) => boolean + shortcuts: string[] + value: string +} diff --git a/web/components/minimal-tiptap/utils.ts b/web/components/minimal-tiptap/utils.ts new file mode 100644 index 00000000..d8772d84 --- /dev/null +++ b/web/components/minimal-tiptap/utils.ts @@ -0,0 +1,81 @@ +import type { Editor } from '@tiptap/core' +import type { MinimalTiptapProps } from './minimal-tiptap' + +let isMac: boolean | undefined + +interface Navigator { + userAgentData?: { + brands: { brand: string; version: string }[] + mobile: boolean + platform: string + getHighEntropyValues: (hints: string[]) => Promise<{ + platform: string + platformVersion: string + uaFullVersion: string + }> + } +} + +function getPlatform(): string { + const nav = navigator as Navigator + + if (nav.userAgentData) { + if (nav.userAgentData.platform) { + return nav.userAgentData.platform + } + + nav.userAgentData.getHighEntropyValues(['platform']).then(highEntropyValues => { + if (highEntropyValues.platform) { + return highEntropyValues.platform + } + }) + } + + if (typeof navigator.platform === 'string') { + return navigator.platform + } + + return '' +} + +export function isMacOS() { + if (isMac === undefined) { + isMac = getPlatform().toLowerCase().includes('mac') + } + + return isMac +} + +interface ShortcutKeyResult { + symbol: string + readable: string +} + +export function getShortcutKey(key: string): ShortcutKeyResult { + const lowercaseKey = key.toLowerCase() + if (lowercaseKey === 'mod') { + return isMacOS() ? { symbol: '⌘', readable: 'Command' } : { symbol: 'Ctrl', readable: 'Control' } + } else if (lowercaseKey === 'alt') { + return isMacOS() ? { symbol: '⌥', readable: 'Option' } : { symbol: 'Alt', readable: 'Alt' } + } else if (lowercaseKey === 'shift') { + return isMacOS() ? { symbol: '⇧', readable: 'Shift' } : { symbol: 'Shift', readable: 'Shift' } + } else { + return { symbol: key, readable: key } + } +} + +export function getShortcutKeys(keys: string[]): ShortcutKeyResult[] { + return keys.map(key => getShortcutKey(key)) +} + +export function getOutput(editor: Editor, format: MinimalTiptapProps['output']) { + if (format === 'json') { + return editor.getJSON() + } + + if (format === 'html') { + return editor.getText() ? editor.getHTML() : '' + } + + return editor.getText() +} diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index 9bf9df60..5416e349 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -135,7 +135,6 @@ const SidebarActions = ({ page, handleDelete }: { page: PersonalPage; handleDele ) const DetailPageForm = ({ page }: { page: PersonalPage }) => { - const { me } = useAccount() const titleEditorRef = useRef(null) const contentEditorRef = useRef(null) const isTitleInitialMount = useRef(true) diff --git a/web/components/ui/dialog.tsx b/web/components/ui/dialog.tsx index 2c023914..dbc51d92 100644 --- a/web/components/ui/dialog.tsx +++ b/web/components/ui/dialog.tsx @@ -93,5 +93,6 @@ export { DialogHeader, DialogFooter, DialogTitle, - DialogDescription + DialogDescription, + DialogPrimitive } diff --git a/web/components/ui/switch.tsx b/web/components/ui/switch.tsx new file mode 100644 index 00000000..5f4117f0 --- /dev/null +++ b/web/components/ui/switch.tsx @@ -0,0 +1,29 @@ +"use client" + +import * as React from "react" +import * as SwitchPrimitives from "@radix-ui/react-switch" + +import { cn } from "@/lib/utils" + +const Switch = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +Switch.displayName = SwitchPrimitives.Root.displayName + +export { Switch } diff --git a/web/components/ui/toggle-group.tsx b/web/components/ui/toggle-group.tsx new file mode 100644 index 00000000..1c876bbe --- /dev/null +++ b/web/components/ui/toggle-group.tsx @@ -0,0 +1,61 @@ +"use client" + +import * as React from "react" +import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group" +import { type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +import { toggleVariants } from "@/components/ui/toggle" + +const ToggleGroupContext = React.createContext< + VariantProps +>({ + size: "default", + variant: "default", +}) + +const ToggleGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, size, children, ...props }, ref) => ( + + + {children} + + +)) + +ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName + +const ToggleGroupItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, children, variant, size, ...props }, ref) => { + const context = React.useContext(ToggleGroupContext) + + return ( + + {children} + + ) +}) + +ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName + +export { ToggleGroup, ToggleGroupItem } diff --git a/web/lib/utils/auth-procedure.ts b/web/lib/utils/auth-procedure.ts new file mode 100644 index 00000000..28347b64 --- /dev/null +++ b/web/lib/utils/auth-procedure.ts @@ -0,0 +1,13 @@ +import { currentUser } from "@clerk/nextjs/server" +import { createServerActionProcedure, ZSAError } from "zsa" + +export const authedProcedure = createServerActionProcedure() + .handler(async () => { + try { + const clerkUser = await currentUser() + return { clerkUser } + } catch { + throw new ZSAError("NOT_AUTHORIZED", "User not authenticated") + } + }) + .createServerAction() diff --git a/web/middleware.ts b/web/middleware.ts index 90c011b9..9081e846 100644 --- a/web/middleware.ts +++ b/web/middleware.ts @@ -9,7 +9,8 @@ const ROUTE_PATTERNS = { "/profile(.*)", "/search(.*)", "/settings(.*)", - "/tauri(.*)" + "/tauri(.*)", + "/onboarding(.*)" ] } diff --git a/web/package.json b/web/package.json index 5aec67fd..5ffce2d2 100644 --- a/web/package.json +++ b/web/package.json @@ -31,7 +31,9 @@ "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.0", "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@tanstack/react-virtual": "^3.10.7", "@tiptap/core": "^2.6.6", @@ -40,6 +42,7 @@ "@tiptap/extension-bullet-list": "^2.6.6", "@tiptap/extension-code": "^2.6.6", "@tiptap/extension-code-block-lowlight": "^2.6.6", + "@tiptap/extension-color": "^2.6.6", "@tiptap/extension-document": "^2.6.6", "@tiptap/extension-dropcursor": "^2.6.6", "@tiptap/extension-focus": "^2.6.6", @@ -48,6 +51,7 @@ "@tiptap/extension-heading": "^2.6.6", "@tiptap/extension-history": "^2.6.6", "@tiptap/extension-horizontal-rule": "^2.6.6", + "@tiptap/extension-image": "^2.6.6", "@tiptap/extension-italic": "^2.6.6", "@tiptap/extension-link": "^2.6.6", "@tiptap/extension-list-item": "^2.6.6", @@ -61,6 +65,7 @@ "@tiptap/extension-typography": "^2.6.6", "@tiptap/pm": "^2.6.6", "@tiptap/react": "^2.6.6", + "@tiptap/starter-kit": "^2.6.6", "@tiptap/suggestion": "^2.6.6", "axios": "^1.7.7", "cheerio": "1.0.0", @@ -86,6 +91,7 @@ "react-hook-form": "^7.53.0", "react-textarea-autosize": "^8.5.3", "react-use": "^17.5.1", + "ronin": "^4.3.1", "slugify": "^1.6.6", "sonner": "^1.5.0", "streaming-markdown": "^0.0.14", @@ -93,9 +99,11 @@ "tailwindcss-animate": "^1.0.7", "vaul": "^0.9.2", "zod": "^3.23.8", - "zsa": "^0.6.0" + "zsa": "^0.6.0", + "zsa-react": "^0.2.2" }, "devDependencies": { + "@ronin/learn-anything": "^0.0.0-3451915138150", "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.1", "@types/jest": "^29.5.12", @@ -112,6 +120,6 @@ "tailwindcss": "^3.4.10", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", - "typescript": "^5.5.4" + "typescript": "^5.6.2" } } diff --git a/web/tsconfig.json b/web/tsconfig.json index 581de8fa..bc715948 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -20,7 +20,8 @@ ], "paths": { "@/*": ["./*"] - } + }, + "types": ["@ronin/learn-anything"] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "types/**/*.d.ts"], "exclude": ["node_modules"] From 9e28aa5995ea87a63709dbbc3ac064e294783bfe Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 10 Sep 2024 14:18:37 +0300 Subject: [PATCH 088/124] Sentry (#157) * . * add sentry * prettier * up packages * no edge sentry --------- Co-authored-by: Aslam H --- .gitignore | 1 + api/bun.lockb | Bin 51428 -> 41683 bytes api/package.json | 10 +-- bun.lockb | Bin 401672 -> 488360 bytes web/.gitignore | 4 +- web/app/api/sentry-example-api/route.ts | 9 +++ web/app/global-error.tsx | 23 +++++++ web/app/sentry-example-page/page.tsx | 82 ++++++++++++++++++++++++ web/instrumentation.ts | 5 ++ web/next.config.mjs | 44 ++++++++++++- web/package.json | 1 + web/sentry.client.config.ts | 26 ++++++++ web/sentry.server.config.ts | 15 +++++ 13 files changed, 212 insertions(+), 8 deletions(-) create mode 100644 web/app/api/sentry-example-api/route.ts create mode 100644 web/app/global-error.tsx create mode 100644 web/app/sentry-example-page/page.tsx create mode 100644 web/instrumentation.ts create mode 100644 web/sentry.client.config.ts create mode 100644 web/sentry.server.config.ts diff --git a/.gitignore b/.gitignore index d909cfed..8b7e3161 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .env*.local output dist +.idea # ts node_modules diff --git a/api/bun.lockb b/api/bun.lockb index 470f8e988c1e4f660507fd6eeea401090fb6f908..6408494b86e43185254e2e6b70d6e058b790a734 100755 GIT binary patch delta 11849 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%0D^HLJ@%TQ($oau~e3aWQEfp%2%U z#8hqT^loz}GyCz(eQ(a6Dt+(gNEGq1KKQc6p1bc>4C z&j}3I@91~%VDS;!oZqe9eYsZ&*XsDLieX4S5zYto-O%ymO}JE_dhkOkF@N7ZQ521o zy$CXJQd1>BVhFM>C{)Z~vfxIyJ5WPN$`}qy#{2F|=g}}}c?xd1;C5C;84n#I^x#3L zJBWzs=$>dlsd*bm{p7M2uo38rM<+!ptN<-eVr zaJxwWxZS~+fRCc_Kl(zlgdmauW0|KKH4D?wPsL8=xCo2kKMeR<3*Rg-QR`e&CUhEl zT{O=gKY!u(FZ?KmAJWLHF1QON0s(>m;NgP2y&41fvlIj1Ie;%~>;Y^6769P8mhk}ivgS}4B=EhIfphF8U{^^2 zu;O0bLI6B-M*-k{X9NH?oDG1T2{-ikVUTnhKB&Sx!S8hBm5dI$KfkvU zOTkBqF^%)^D1e^=$yTs79ROU8Tg963uwWf92}vFvDm+ZsO3Df>hYw;{ z7XS9+gM~E!9DkgMc*s!Q!Ah|*>=`_^egJqp9I&aj*k_IaxCvhX9{_Iv7$Ne~)B}B; z@8XE10s#C0_yEHJ;s9a+@Qo&(8__7PWJnAiK@q@Y06b23yzn^SF~nnu$CMh&B*;qu z!~h8Zi2x}8oD4|dlNX?k3!IcVi~S6}Q&G=r40@_a9V5U64tQThJ;dpEav5iXQKZu*`taO&?$8U(Gd}h9 z2pn&o7taUPEw)AvR#~CYCFV*$EJ?j0Q;&-v>7mxq2qwVfQBlv2!13hqah;1A4)wxG zr+GpZJYp>X*a_65X&2|AhC{vW(rHR)2_qe4BXx9Bbo z6%<0fT9X{o4JnEQ8D8pDg-;HYUQ6;Zqx}OY{AI!#$3C6hgO`m`N{y8GY=P&7Ze-)S zpKrdNVbzq51{E9*!>+JLZ_2;(VNw5-|x3f#qChR2Bb5{mFl5~IIGR!;0^0qsae-$RMBnca6$?ZKFpwQ0+x}};^)-EUN3pBTWbh^f{-A=1s0aw) z!1!Vg6>dbuNRS+;gYJ}Cdr=`p|MOjo9C6R@yPpbHq9QCbhIpaLOQ+E1(9p@H9Qwd+ z=<(8WUn*{i3bvrr3Ve8;zC3T@pN9zVgLqSm?~c|Wwlx)TRQ>H`TQ{q-jtrVN&jWT< zg7~pJx`^1mREScDUR?~G)ILYX1ox*!rd^zY1}JqI+nS1`^6R^U@A>)S;4Xn;1GIjbr!^IVRV-iK_jYl( zVVA%O1M~_sP%&GN`#b8t8!+%>mjIkn?UqN;-A$4EQnoJ@TIKTVCi$hQd&h&DH9I&J zqIBoZ(TXb1dVdBiRZnuQjx$3?ma~1Sn5N-^i!RnSCY4cf8ysKt*#^4VF%jhxA?3#R)J+x4QP@mKAGXGC7m`gMj8xvDXHvK!6GtJE#B5DI(_VFXU=c_ z=iv{V|FHt{cN9+i=y63Da$4(Y)oQCgKFs0!{6fny>To9yYH-Hfh~#VKC~iY(Cssg>;>>4{gIhQL{kkz^J?DTXjBudN3kjqje;w(40Bu$i&p;a?OF3HNu z zbmO{dQCwJ=D6ZCa+~3*)vYmEuJ>g*Jj{OmuB9}tD(^h6k#qi-(B z$-wiD>!!}ATS6v{{m}W-EXtnhKIqfwo^&rB+P%*X@f!^6$)g8WEKZh)r*b87xlArn zC?xVMsVrShBHMav)PBMi?gy2}98m2EOC9*VN7yg3PMFbsNM*S5w;GI)b>$GP#$Fxn ziSG?-T;NtQxpFoV?yyE3yL3_e772P$IS8%W^~1h#+cxSU{%&jY45?TqmZi$%3WY3N zuAXmP(9CUGq89G{}hjvw2>3iZIGjIU9BwF=fAq7G+ delta 17908 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 ZiI`+il|Hv12RsYdl0^R?BF&hF{{p?xU6TL+ diff --git a/api/package.json b/api/package.json index 3a130cad..217c0111 100644 --- a/api/package.json +++ b/api/package.json @@ -7,14 +7,14 @@ "dev": "encore run" }, "devDependencies": { - "@types/node": "^20.5.7", - "typescript": "^5.2.2", - "vitest": "^1.5.0" + "@types/node": "^22.5.4", + "typescript": "^5.6.2", + "vitest": "^2.0.5" }, "dependencies": { - "encore.dev": "^1.35.3" + "encore.dev": "^1.41.0" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "^4.13.0" + "@rollup/rollup-linux-x64-gnu": "^4.21.2" } } diff --git a/bun.lockb b/bun.lockb index a9aec6684faaf853296f72a5a6aa58994ac9561e..eac0974b9ebd1727d59c40b55c6a0e7b815256d8 100755 GIT binary patch delta 138998 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
    s|pJ^*+YsLybV<~x2{&xY=~AZ*y?LMTUAT% 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 diff --git a/web/.gitignore b/web/.gitignore index 5083fca0..d1dc7456 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -35,4 +35,6 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts -.ronin \ No newline at end of file +# Sentry Config File +.env.sentry-build-plugin +.ronin diff --git a/web/app/api/sentry-example-api/route.ts b/web/app/api/sentry-example-api/route.ts new file mode 100644 index 00000000..ca8c0da0 --- /dev/null +++ b/web/app/api/sentry-example-api/route.ts @@ -0,0 +1,9 @@ +import { NextResponse } from "next/server" + +export const dynamic = "force-dynamic" + +// A faulty API route to test Sentry's error monitoring +export function GET() { + throw new Error("Sentry Example API Route Error") + return NextResponse.json({ data: "Testing Sentry Error..." }) +} diff --git a/web/app/global-error.tsx b/web/app/global-error.tsx new file mode 100644 index 00000000..1d4c5617 --- /dev/null +++ b/web/app/global-error.tsx @@ -0,0 +1,23 @@ +"use client" + +import * as Sentry from "@sentry/nextjs" +import NextError from "next/error" +import { useEffect } from "react" + +export default function GlobalError({ error }: { error: Error & { digest?: string } }) { + useEffect(() => { + Sentry.captureException(error) + }, [error]) + + return ( + + + {/* `NextError` is the default Next.js error page component. Its type + definition requires a `statusCode` prop. However, since the App Router + does not expose status codes for errors, we simply pass 0 to render a + generic error message. */} + + + + ) +} diff --git a/web/app/sentry-example-page/page.tsx b/web/app/sentry-example-page/page.tsx new file mode 100644 index 00000000..f8bb4ebf --- /dev/null +++ b/web/app/sentry-example-page/page.tsx @@ -0,0 +1,82 @@ +"use client" + +import Head from "next/head" +import * as Sentry from "@sentry/nextjs" + +export default function Page() { + return ( + + ) +} diff --git a/web/instrumentation.ts b/web/instrumentation.ts new file mode 100644 index 00000000..7f317a03 --- /dev/null +++ b/web/instrumentation.ts @@ -0,0 +1,5 @@ +export async function register() { + if (process.env.NEXT_RUNTIME === "nodejs") { + await import("./sentry.server.config") + } +} diff --git a/web/next.config.mjs b/web/next.config.mjs index 169ecdc1..afb4643a 100644 --- a/web/next.config.mjs +++ b/web/next.config.mjs @@ -1,3 +1,4 @@ +import { withSentryConfig } from "@sentry/nextjs" /** @type {import('next').NextConfig} */ const isTauri = process.env.TAURI_ENV_DEBUG !== undefined @@ -41,6 +42,45 @@ const webConfig = { const nextConfig = isTauri ? tauriConfig : webConfig -console.log(`Using ${isTauri ? "Tauri" : "Web"} config`) +// console.log(`Using ${isTauri ? "Tauri" : "Web"} config`) -export default nextConfig +export default withSentryConfig(nextConfig, { + // For all available options, see: + // https://github.com/getsentry/sentry-webpack-plugin#options + + org: "learn-anything", + project: "website", + sentryUrl: "https://sentry.io/", + + // Only print logs for uploading source maps in CI + silent: !process.env.CI, + + // For all available options, see: + // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ + + // Upload a larger set of source maps for prettier stack traces (increases build time) + widenClientFileUpload: true, + + // Automatically annotate React components to show their full name in breadcrumbs and session replay + reactComponentAnnotation: { + enabled: true + }, + + // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. + // This can increase your server load as well as your hosting bill. + // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- + // side errors will fail. + tunnelRoute: "/monitoring", + + // Hides source maps from generated client bundles + hideSourceMaps: true, + + // Automatically tree-shake Sentry logger statements to reduce bundle size + disableLogger: true, + + // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.) + // See the following for more information: + // https://docs.sentry.io/product/crons/ + // https://vercel.com/docs/cron-jobs + automaticVercelMonitors: true +}) diff --git a/web/package.json b/web/package.json index 5ffce2d2..ff84a657 100644 --- a/web/package.json +++ b/web/package.json @@ -95,6 +95,7 @@ "slugify": "^1.6.6", "sonner": "^1.5.0", "streaming-markdown": "^0.0.14", + "@sentry/nextjs": "^8.29.0", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", "vaul": "^0.9.2", diff --git a/web/sentry.client.config.ts b/web/sentry.client.config.ts new file mode 100644 index 00000000..83f4b56c --- /dev/null +++ b/web/sentry.client.config.ts @@ -0,0 +1,26 @@ +// This file configures the initialization of Sentry on the client. +// The config you add here will be used whenever a users loads a page in their browser. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs" + +Sentry.init({ + dsn: "https://643e210c090ea97f641b7f5fb253f16b@o301266.ingest.us.sentry.io/4507927630118912", + + // Add optional integrations for additional features + integrations: [Sentry.replayIntegration()], + + // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. + tracesSampleRate: 1, + + // Define how likely Replay events are sampled. + // This sets the sample rate to be 10%. You may want this to be 100% while + // in development and sample at a lower rate in production + replaysSessionSampleRate: 0.1, + + // Define how likely Replay events are sampled when an error occurs. + replaysOnErrorSampleRate: 1.0, + + // Setting this option to true will print useful information to the console while you're setting up Sentry. + debug: false +}) diff --git a/web/sentry.server.config.ts b/web/sentry.server.config.ts new file mode 100644 index 00000000..61484b30 --- /dev/null +++ b/web/sentry.server.config.ts @@ -0,0 +1,15 @@ +// This file configures the initialization of Sentry on the server. +// The config you add here will be used whenever the server handles a request. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs" + +Sentry.init({ + dsn: "https://643e210c090ea97f641b7f5fb253f16b@o301266.ingest.us.sentry.io/4507927630118912", + + // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. + tracesSampleRate: 1, + + // Setting this option to true will print useful information to the console while you're setting up Sentry. + debug: false +}) From 55eb5513f471261db18d6dc97500676af4cab6e3 Mon Sep 17 00:00:00 2001 From: Aslam Date: Tue, 10 Sep 2024 18:24:39 +0700 Subject: [PATCH 089/124] chore(sentry): remove local sentry and use from env (#159) * . * add sentry * prettier * up packages * no edge sentry * chore: remove local sentry and now use form env --------- Co-authored-by: Nikita --- web/.env.example | 3 +++ web/next.config.mjs | 4 +--- web/sentry.client.config.ts | 2 +- web/sentry.server.config.ts | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/web/.env.example b/web/.env.example index 8587bc0c..f06d9024 100644 --- a/web/.env.example +++ b/web/.env.example @@ -12,4 +12,7 @@ NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_JAZZ_PEER_URL="wss://" RONIN_TOKEN= + +SENTRY_DSN= +SENTRY_PROJECT= # IGNORE_BUILD_ERRORS=true \ No newline at end of file diff --git a/web/next.config.mjs b/web/next.config.mjs index afb4643a..8be71289 100644 --- a/web/next.config.mjs +++ b/web/next.config.mjs @@ -42,14 +42,12 @@ const webConfig = { const nextConfig = isTauri ? tauriConfig : webConfig -// console.log(`Using ${isTauri ? "Tauri" : "Web"} config`) - export default withSentryConfig(nextConfig, { // For all available options, see: // https://github.com/getsentry/sentry-webpack-plugin#options org: "learn-anything", - project: "website", + project: process.env.SENTRY_PROJECT, sentryUrl: "https://sentry.io/", // Only print logs for uploading source maps in CI diff --git a/web/sentry.client.config.ts b/web/sentry.client.config.ts index 83f4b56c..27056a38 100644 --- a/web/sentry.client.config.ts +++ b/web/sentry.client.config.ts @@ -5,7 +5,7 @@ import * as Sentry from "@sentry/nextjs" Sentry.init({ - dsn: "https://643e210c090ea97f641b7f5fb253f16b@o301266.ingest.us.sentry.io/4507927630118912", + dsn: process.env.SENTRY_DSN, // Add optional integrations for additional features integrations: [Sentry.replayIntegration()], diff --git a/web/sentry.server.config.ts b/web/sentry.server.config.ts index 61484b30..50176680 100644 --- a/web/sentry.server.config.ts +++ b/web/sentry.server.config.ts @@ -5,7 +5,7 @@ import * as Sentry from "@sentry/nextjs" Sentry.init({ - dsn: "https://643e210c090ea97f641b7f5fb253f16b@o301266.ingest.us.sentry.io/4507927630118912", + dsn: process.env.SENTRY_DSN, // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. tracesSampleRate: 1, From 2ff509e05ad63f73232da9130b439f746e85ddcd Mon Sep 17 00:00:00 2001 From: Aslam H Date: Tue, 10 Sep 2024 18:28:29 +0700 Subject: [PATCH 090/124] fix: capitalize first character of Delete empty page comp --- web/components/routes/page/detail/PageDetailRoute.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index 5416e349..4aa7a685 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -27,7 +27,7 @@ const emptyPage = (page: PersonalPage): boolean => { return (!page.title || page.title.trim() === "") && (!page.content || Object.keys(page.content).length === 0) } -export const deleteEmptyPage = (currentPageId: string | null) => { +export const DeleteEmptyPage = (currentPageId: string | null) => { const router = useRouter() const { me } = useAccount({ root: { @@ -60,7 +60,7 @@ export function PageDetailRoute({ pageId }: { pageId: string }) { const page = useCoState(PersonalPage, pageId as ID) const router = useRouter() const confirm = useConfirm() - deleteEmptyPage(pageId) + DeleteEmptyPage(pageId) const handleDelete = async () => { const result = await confirm({ From 7f2b594cb34a734ed743c3e2547caf28a4b7a82d Mon Sep 17 00:00:00 2001 From: Aslam H Date: Tue, 10 Sep 2024 18:48:17 +0700 Subject: [PATCH 091/124] chore: remove example sentry page --- web/app/api/sentry-example-api/route.ts | 9 --- web/app/sentry-example-page/page.tsx | 82 ------------------------- 2 files changed, 91 deletions(-) delete mode 100644 web/app/api/sentry-example-api/route.ts delete mode 100644 web/app/sentry-example-page/page.tsx diff --git a/web/app/api/sentry-example-api/route.ts b/web/app/api/sentry-example-api/route.ts deleted file mode 100644 index ca8c0da0..00000000 --- a/web/app/api/sentry-example-api/route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { NextResponse } from "next/server" - -export const dynamic = "force-dynamic" - -// A faulty API route to test Sentry's error monitoring -export function GET() { - throw new Error("Sentry Example API Route Error") - return NextResponse.json({ data: "Testing Sentry Error..." }) -} diff --git a/web/app/sentry-example-page/page.tsx b/web/app/sentry-example-page/page.tsx deleted file mode 100644 index f8bb4ebf..00000000 --- a/web/app/sentry-example-page/page.tsx +++ /dev/null @@ -1,82 +0,0 @@ -"use client" - -import Head from "next/head" -import * as Sentry from "@sentry/nextjs" - -export default function Page() { - return ( -
    - - Sentry Onboarding - - - -
    -

    - - - -

    - -

    Get started by sending us a sample error:

    - - -

    - Next, look for the error on the{" "} - Issues Page. -

    -

    - For more information, see{" "} - - https://docs.sentry.io/platforms/javascript/guides/nextjs/ - -

    -
    -
    - ) -} From b2f5de6b6d91c03c01f2d9b39ca077f4d7409b46 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 10 Sep 2024 15:33:15 +0300 Subject: [PATCH 092/124] change url for github --- web/components/custom/sidebar/partial/profile-section.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index e87e9877..97ad4a7c 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -88,7 +88,7 @@ export const ProfileSection: React.FC = () => { - +
    GitHub From b7e7f79a40aa6df083990ea9211407ad35e995ad Mon Sep 17 00:00:00 2001 From: Aslam H Date: Tue, 10 Sep 2024 20:03:25 +0700 Subject: [PATCH 093/124] fix: sentry, img validation --- web/app/actions.ts | 23 +++--- web/app/api/sentry-example-api/route.ts | 9 +++ web/app/sentry-example-page/page.tsx | 79 +++++++++++++++++++ .../components/image/image-edit-block.tsx | 8 +- web/next.config.mjs | 4 +- web/package.json | 2 +- 6 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 web/app/api/sentry-example-api/route.ts create mode 100644 web/app/sentry-example-page/page.tsx diff --git a/web/app/actions.ts b/web/app/actions.ts index b3d3d04d..7431453f 100644 --- a/web/app/actions.ts +++ b/web/app/actions.ts @@ -32,18 +32,17 @@ export const sendFeedback = authedProcedure export const storeImage = authedProcedure .input( z.object({ - file: z.custom(file => { - if (!(file instanceof File)) { - throw new Error("Not a file") - } - if (!ALLOWED_FILE_TYPES.includes(file.type)) { - throw new Error("Invalid file type. Only JPEG, PNG, GIF, and WebP images are allowed.") - } - if (file.size > MAX_FILE_SIZE) { - throw new Error("File size exceeds the maximum limit of 1 MB.") - } - return true - }) + file: z + .any() + .refine(file => file instanceof File, { + message: "Not a file" + }) + .refine(file => ALLOWED_FILE_TYPES.includes(file.type), { + message: "Invalid file type. Only JPEG, PNG, GIF, and WebP images are allowed." + }) + .refine(file => file.size <= MAX_FILE_SIZE, { + message: "File size exceeds the maximum limit of 1 MB." + }) }), { type: "formData" } ) diff --git a/web/app/api/sentry-example-api/route.ts b/web/app/api/sentry-example-api/route.ts new file mode 100644 index 00000000..f486f3d1 --- /dev/null +++ b/web/app/api/sentry-example-api/route.ts @@ -0,0 +1,9 @@ +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +// A faulty API route to test Sentry's error monitoring +export function GET() { + throw new Error("Sentry Example API Route Error"); + return NextResponse.json({ data: "Testing Sentry Error..." }); +} diff --git a/web/app/sentry-example-page/page.tsx b/web/app/sentry-example-page/page.tsx new file mode 100644 index 00000000..dd38d045 --- /dev/null +++ b/web/app/sentry-example-page/page.tsx @@ -0,0 +1,79 @@ +"use client"; + +import Head from "next/head"; +import * as Sentry from "@sentry/nextjs"; + +export default function Page() { + return ( +
    + + Sentry Onboarding + + + +
    +

    + + + +

    + +

    Get started by sending us a sample error:

    + + +

    + Next, look for the error on the{" "} + Issues Page. +

    +

    + For more information, see{" "} + + https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +

    +
    +
    + ); +} diff --git a/web/components/minimal-tiptap/components/image/image-edit-block.tsx b/web/components/minimal-tiptap/components/image/image-edit-block.tsx index f8786b8b..da4dd8be 100644 --- a/web/components/minimal-tiptap/components/image/image-edit-block.tsx +++ b/web/components/minimal-tiptap/components/image/image-edit-block.tsx @@ -41,6 +41,11 @@ const ImageEditBlock = ({ editor, className, close, ...props }: ImageEditBlockPr try { const [response, err] = await storeImage(formData) + + if (err) { + throw new Error(err.fieldErrors?.file?.join(", ")) + } + if (response?.fileModel) { editor.chain().setImage({ src: response.fileModel.content.src }).focus().run() close() @@ -48,7 +53,6 @@ const ImageEditBlock = ({ editor, className, close, ...props }: ImageEditBlockPr throw new Error("Failed to upload image") } } catch (error) { - console.error("Error uploading file:", error) setError(error instanceof Error ? error.message : "An unknown error occurred") } finally { setIsUploading(false) @@ -62,7 +66,7 @@ const ImageEditBlock = ({ editor, className, close, ...props }: ImageEditBlockPr return (
    -
    +
    diff --git a/web/next.config.mjs b/web/next.config.mjs index 8be71289..ba15307d 100644 --- a/web/next.config.mjs +++ b/web/next.config.mjs @@ -64,11 +64,11 @@ export default withSentryConfig(nextConfig, { enabled: true }, - // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. + // Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. // This can increase your server load as well as your hosting bill. // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- // side errors will fail. - tunnelRoute: "/monitoring", + // tunnelRoute: "/monitoring", // Hides source maps from generated client bundles hideSourceMaps: true, diff --git a/web/package.json b/web/package.json index ff84a657..c431e0cc 100644 --- a/web/package.json +++ b/web/package.json @@ -35,6 +35,7 @@ "@radix-ui/react-toggle": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", + "@sentry/nextjs": "^8.29.0", "@tanstack/react-virtual": "^3.10.7", "@tiptap/core": "^2.6.6", "@tiptap/extension-blockquote": "^2.6.6", @@ -95,7 +96,6 @@ "slugify": "^1.6.6", "sonner": "^1.5.0", "streaming-markdown": "^0.0.14", - "@sentry/nextjs": "^8.29.0", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", "vaul": "^0.9.2", From 0668dd5625c180d7430b9c8310c35c6d0a316fdf Mon Sep 17 00:00:00 2001 From: Aslam Date: Tue, 10 Sep 2024 22:05:28 +0700 Subject: [PATCH 094/124] feat: onboarding for existing users (#160) * feat: onboarding for existing users * fix: escape character * chore: update msg --- web/app/actions.ts | 8 ++ web/app/layout.tsx | 2 + .../custom/learn-anything-onboarding.tsx | 102 +++++++++++++++++ web/components/ui/alert-dialog.tsx | 106 ++++++++++++++++++ web/package.json | 1 + 5 files changed, 219 insertions(+) create mode 100644 web/components/custom/learn-anything-onboarding.tsx create mode 100644 web/components/ui/alert-dialog.tsx diff --git a/web/app/actions.ts b/web/app/actions.ts index 7431453f..14795825 100644 --- a/web/app/actions.ts +++ b/web/app/actions.ts @@ -1,6 +1,8 @@ "use server" import { authedProcedure } from "@/lib/utils/auth-procedure" +import { currentUser } from "@clerk/nextjs/server" +import { get } from "ronin" import { create } from "ronin" import { z } from "zod" import { ZSAError } from "zsa" @@ -68,3 +70,9 @@ export const storeImage = authedProcedure throw new ZSAError("ERROR", "Failed to store image") } }) + +export const isExistingUser = async () => { + const clerkUser = await currentUser() + const roninUser = await get.existingStripeSubscriber.with({ email: clerkUser?.emailAddresses[0].emailAddress }) + return clerkUser?.emailAddresses[0].emailAddress === roninUser?.email +} diff --git a/web/app/layout.tsx b/web/app/layout.tsx index b9dd669c..785a5550 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -10,6 +10,7 @@ import { DeepLinkProvider } from "@/lib/providers/deep-link-provider" import { GeistMono, GeistSans } from "./fonts" import { JazzAndAuth } from "@/lib/providers/jazz-provider" import { TooltipProvider } from "@/components/ui/tooltip" +import { LearnAnythingOnboarding } from "@/components/custom/learn-anything-onboarding" export const metadata: Metadata = { title: "Learn Anything", @@ -42,6 +43,7 @@ export default function RootLayout({ {children} + diff --git a/web/components/custom/learn-anything-onboarding.tsx b/web/components/custom/learn-anything-onboarding.tsx new file mode 100644 index 00000000..619d9a5a --- /dev/null +++ b/web/components/custom/learn-anything-onboarding.tsx @@ -0,0 +1,102 @@ +"use client" + +import React, { useEffect, useState } from "react" +import { atom, useAtom } from "jotai" +import { atomWithStorage } from "jotai/utils" +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle +} from "@/components/ui/alert-dialog" +import { isExistingUser } from "@/app/actions" +import { usePathname } from "next/navigation" + +const hasVisitedAtom = atomWithStorage("hasVisitedLearnAnything", false) +const isDialogOpenAtom = atom(true) + +export function LearnAnythingOnboarding() { + const pathname = usePathname() + const [hasVisited, setHasVisited] = useAtom(hasVisitedAtom) + const [isOpen, setIsOpen] = useAtom(isDialogOpenAtom) + const [isFetching, setIsFetching] = useState(true) + const [isExisting, setIsExisting] = useState(false) + + if (pathname === "/") return null + + useEffect(() => { + const loadUser = async () => { + try { + const existingUser = await isExistingUser() + setIsExisting(existingUser) + setIsOpen(true) + } catch (error) { + console.error("Error loading user:", error) + } finally { + setIsFetching(false) + } + } + + if (!hasVisited) { + loadUser() + } + }, [hasVisited, setIsOpen]) + + const handleClose = () => { + setIsOpen(false) + setHasVisited(true) + } + + if (hasVisited || isFetching) return null + + return ( + + + + +

    Welcome to Learn Anything!

    +
    +
    + + + {isExisting && ( + <> +

    Existing Customer Notice

    +

    + We noticed you are an existing Learn Anything customer. We sincerely apologize for any broken experience + you may have encountered on the old website. We've been working hard on this new version, which + addresses previous issues and offers more features. As an early customer, you're locked in at the{" "} + $3 price for our upcoming pro version. Thank you for your support! +

    + + )} +

    + Learn Anything is a learning platform that organizes knowledge in a social way. You can create pages, add + links, track learning status of any topic, and more things in the future. +

    +

    Try do these quick onboarding steps to get a feel for the product:

    +
      +
    • Create your first page
    • +
    • Add a link to a resource
    • +
    • Update your learning status on a topic
    • +
    +

    + If you have any questions, don't hesitate to reach out. Click on question mark button in the bottom right + corner and enter your message. +

    +
    + + + Close + Get Started + +
    +
    + ) +} + +export default LearnAnythingOnboarding diff --git a/web/components/ui/alert-dialog.tsx b/web/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..10a573a4 --- /dev/null +++ b/web/components/ui/alert-dialog.tsx @@ -0,0 +1,106 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
    +) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
    +) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel +} diff --git a/web/package.json b/web/package.json index c431e0cc..4aeca69c 100644 --- a/web/package.json +++ b/web/package.json @@ -17,6 +17,7 @@ "@nothing-but/utils": "^0.16.0", "@omit/react-confirm-dialog": "^1.1.5", "@omit/react-fancy-switch": "^0.1.3", + "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-context-menu": "^2.2.1", From 2a637705f29b1fcb99005ce768470b7798602346 Mon Sep 17 00:00:00 2001 From: Aslam Date: Wed, 11 Sep 2024 15:25:21 +0700 Subject: [PATCH 095/124] fix: Bug fixing & Enhancement (#161) * chore: memoize sorted pages * chore: make link size more precise * fix(link): disable enter press on create mode * fix(onboarding): move is base logic and use escape for single quote * fix(page): on delete success redirect to pages * fix(sntry): sentry client error report * chore(page): dynamic focus on title/content * chore(link): tweak badge class * chore(link): use nuqs for handling create mode * fix(link): refs * feat(palette): implement new link --- web/.env.example | 6 +- web/app/api/sentry-example-api/route.ts | 9 -- web/app/sentry-example-page/page.tsx | 79 --------------- .../custom/command-palette/command-data.ts | 2 +- .../custom/learn-anything-onboarding.tsx | 14 ++- .../custom/sidebar/partial/link-section.tsx | 98 ++++++++++--------- .../custom/sidebar/partial/page-section.tsx | 40 ++++---- web/components/routes/link/LinkRoute.tsx | 7 +- web/components/routes/link/bottom-bar.tsx | 45 +++++---- web/components/routes/link/list.tsx | 10 +- web/components/routes/link/manage.tsx | 9 +- .../routes/link/partials/link-item.tsx | 8 +- .../routes/page/detail/PageDetailRoute.tsx | 29 +++--- .../routes/page/hooks/use-page-actions.ts | 36 +++++++ web/instrumentation.ts | 4 + web/next.config.mjs | 7 +- web/package.json | 4 +- web/sentry.client.config.ts | 2 +- web/sentry.server.config.ts | 2 +- web/store/link.ts | 1 - 20 files changed, 181 insertions(+), 231 deletions(-) delete mode 100644 web/app/api/sentry-example-api/route.ts delete mode 100644 web/app/sentry-example-page/page.tsx create mode 100644 web/components/routes/page/hooks/use-page-actions.ts diff --git a/web/.env.example b/web/.env.example index f06d9024..bae601e6 100644 --- a/web/.env.example +++ b/web/.env.example @@ -13,6 +13,8 @@ NEXT_PUBLIC_JAZZ_PEER_URL="wss://" RONIN_TOKEN= -SENTRY_DSN= -SENTRY_PROJECT= +NEXT_PUBLIC_SENTRY_DSN= +NEXT_PUBLIC_SENTRY_ORG= +NEXT_PUBLIC_SENTRY_PROJECT= + # IGNORE_BUILD_ERRORS=true \ No newline at end of file diff --git a/web/app/api/sentry-example-api/route.ts b/web/app/api/sentry-example-api/route.ts deleted file mode 100644 index f486f3d1..00000000 --- a/web/app/api/sentry-example-api/route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { NextResponse } from "next/server"; - -export const dynamic = "force-dynamic"; - -// A faulty API route to test Sentry's error monitoring -export function GET() { - throw new Error("Sentry Example API Route Error"); - return NextResponse.json({ data: "Testing Sentry Error..." }); -} diff --git a/web/app/sentry-example-page/page.tsx b/web/app/sentry-example-page/page.tsx deleted file mode 100644 index dd38d045..00000000 --- a/web/app/sentry-example-page/page.tsx +++ /dev/null @@ -1,79 +0,0 @@ -"use client"; - -import Head from "next/head"; -import * as Sentry from "@sentry/nextjs"; - -export default function Page() { - return ( -
    - - Sentry Onboarding - - - -
    -

    - - - -

    - -

    Get started by sending us a sample error:

    - - -

    - Next, look for the error on the{" "} - Issues Page. -

    -

    - For more information, see{" "} - - https://docs.sentry.io/platforms/javascript/guides/nextjs/ - -

    -
    -
    - ); -} diff --git a/web/components/custom/command-palette/command-data.ts b/web/components/custom/command-palette/command-data.ts index 2a36b515..ac3f4cb1 100644 --- a/web/components/custom/command-palette/command-data.ts +++ b/web/components/custom/command-palette/command-data.ts @@ -71,7 +71,7 @@ export const createCommandGroups = ( icon: "Plus", value: "Create New Link...", label: "Create New Link...", - action: () => actions.navigateTo("/") + action: () => actions.navigateTo("/links?create=true") } ] }, diff --git a/web/components/custom/learn-anything-onboarding.tsx b/web/components/custom/learn-anything-onboarding.tsx index 619d9a5a..405f0e41 100644 --- a/web/components/custom/learn-anything-onboarding.tsx +++ b/web/components/custom/learn-anything-onboarding.tsx @@ -26,8 +26,6 @@ export function LearnAnythingOnboarding() { const [isFetching, setIsFetching] = useState(true) const [isExisting, setIsExisting] = useState(false) - if (pathname === "/") return null - useEffect(() => { const loadUser = async () => { try { @@ -41,10 +39,10 @@ export function LearnAnythingOnboarding() { } } - if (!hasVisited) { + if (!hasVisited && pathname !== "/") { loadUser() } - }, [hasVisited, setIsOpen]) + }, [hasVisited, pathname, setIsOpen]) const handleClose = () => { setIsOpen(false) @@ -68,8 +66,8 @@ export function LearnAnythingOnboarding() {

    Existing Customer Notice

    We noticed you are an existing Learn Anything customer. We sincerely apologize for any broken experience - you may have encountered on the old website. We've been working hard on this new version, which - addresses previous issues and offers more features. As an early customer, you're locked in at the{" "} + you may have encountered on the old website. We've been working hard on this new version, which + addresses previous issues and offers more features. As an early customer, you're locked in at the{" "} $3 price for our upcoming pro version. Thank you for your support!

    @@ -85,8 +83,8 @@ export function LearnAnythingOnboarding() {
  • Update your learning status on a topic
  • - If you have any questions, don't hesitate to reach out. Click on question mark button in the bottom right - corner and enter your message. + If you have any questions, don't hesitate to reach out. Click on question mark button in the bottom + right corner and enter your message.

    diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index a469aaf2..0b568943 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -1,24 +1,31 @@ import React from "react" import Link from "next/link" -import { usePathname, useRouter } from "next/navigation" +import { usePathname } from "next/navigation" import { useAccount } from "@/lib/providers/jazz-provider" import { cn } from "@/lib/utils" import { PersonalLinkLists } from "@/lib/schema/personal-link" import { useQueryState, parseAsStringLiteral } from "nuqs" import { LEARNING_STATES } from "@/lib/constants" -export const LinkSection: React.FC<{ pathname: string }> = ({ pathname }) => { +const ALL_STATES = [{ label: "All", value: "all", icon: "List", className: "text-foreground" }, ...LEARNING_STATES] +const ALL_STATES_STRING = ALL_STATES.map(ls => ls.value) + +interface LinkSectionProps { + pathname: string +} + +export const LinkSection: React.FC = ({ pathname }) => { const { me } = useAccount({ root: { personalLinks: [] } }) - const linkCount = me?.root.personalLinks?.length || 0 - const isActive = pathname === "/links" - if (!me) return null + const linkCount = me.root.personalLinks?.length || 0 + const isActive = pathname === "/links" + return (
    @@ -34,20 +41,19 @@ interface LinkSectionHeaderProps { const LinkSectionHeader: React.FC = ({ linkCount }) => { const pathname = usePathname() - const [state] = useQueryState("state", parseAsStringLiteral(LEARNING_STATES.map(ls => ls.value))) - const isLinksActive = pathname.startsWith("/links") && !state + const [state] = useQueryState("state", parseAsStringLiteral(ALL_STATES_STRING)) + const isLinksActive = pathname.startsWith("/links") && (!state || state === "all") return ( -
    +

    Links @@ -66,24 +72,29 @@ const List: React.FC = ({ personalLinks }) => { const pathname = usePathname() const [state] = useQueryState("state", parseAsStringLiteral(LEARNING_STATES.map(ls => ls.value))) - const toLearnCount = personalLinks.filter(link => link?.learningState === "wantToLearn").length - const learningCount = personalLinks.filter(link => link?.learningState === "learning").length - const learnedCount = personalLinks.filter(link => link?.learningState === "learned").length - - const isActive = (checkState: string) => { - return pathname === "/links" && state === checkState + const linkCounts = { + wantToLearn: personalLinks.filter(link => link?.learningState === "wantToLearn").length, + learning: personalLinks.filter(link => link?.learningState === "learning").length, + learned: personalLinks.filter(link => link?.learningState === "learned").length } + const isActive = (checkState: string) => pathname === "/links" && state === checkState + return (

    - - + +
    ) } @@ -95,26 +106,23 @@ interface ListItemProps { isActive: boolean } -const ListItem: React.FC = ({ label, href, count, isActive }) => { - return ( -
    -
    - -
    -

    {label}

    -
    - - - {count > 0 && ( - {count} +const ListItem: React.FC = ({ label, href, count, isActive }) => ( +
    +
    + + > +
    +

    {label}

    +
    + + {count > 0 && ( + {count} + )}
    - ) -} +
    +) diff --git a/web/components/custom/sidebar/partial/page-section.tsx b/web/components/custom/sidebar/partial/page-section.tsx index 2a0edad1..4b9daf28 100644 --- a/web/components/custom/sidebar/partial/page-section.tsx +++ b/web/components/custom/sidebar/partial/page-section.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useMemo } from "react" import { useAtom } from "jotai" import { usePathname, useRouter } from "next/navigation" import { useAccount } from "@/lib/providers/jazz-provider" @@ -9,7 +9,6 @@ import { Button } from "@/components/ui/button" import { LaIcon } from "@/components/custom/la-icon" import { toast } from "sonner" import Link from "next/link" -import { useEffect } from "react" import { DropdownMenu, DropdownMenuContent, @@ -54,14 +53,14 @@ export const PageSection: React.FC<{ pathname?: string }> = ({ pathname }) => { } }) - const [sort, setSort] = useAtom(pageSortAtom) - const [show, setShow] = useAtom(pageShowAtom) - - const pageCount = me?.root.personalPages?.length || 0 - const isActive = pathname === "/pages" + const [sort] = useAtom(pageSortAtom) + const [show] = useAtom(pageShowAtom) if (!me) return null + const pageCount = me.root.personalPages?.length || 0 + const isActive = pathname === "/pages" + return (
    @@ -142,24 +141,19 @@ interface PageListProps { show: ShowOption } -const PageList: React.FC = ({ personalPages }) => { +const PageList: React.FC = ({ personalPages, sort, show }) => { const pathname = usePathname() - const [sortCriteria] = useAtom(pageSortAtom) - const [showCount] = useAtom(pageShowAtom) - - const sortedPages = [...personalPages] - .sort((a, b) => { - switch (sortCriteria) { - case "title": + const sortedPages = useMemo(() => { + return [...personalPages] + .sort((a, b) => { + if (sort === "title") { return (a?.title ?? "").localeCompare(b?.title ?? "") - case "recent": - return (b?.updatedAt?.getTime() ?? 0) - (a?.updatedAt?.getTime() ?? 0) - default: - return 0 - } - }) - .slice(0, showCount === 0 ? personalPages.length : showCount) + } + return (b?.updatedAt?.getTime() ?? 0) - (a?.updatedAt?.getTime() ?? 0) + }) + .slice(0, show === 0 ? personalPages.length : show) + }, [personalPages, sort, show]) return (
    @@ -185,7 +179,7 @@ const PageListItem: React.FC = ({ page, isActive }) => ( { "bg-accent text-accent-foreground": isActive } )} > -
    +

    {page.title || "Untitled"}

    diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index e5bde331..62e5f8a9 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState, useCallback, useRef } from "react" import { LinkHeader } from "@/components/routes/link/header" import { LinkList } from "@/components/routes/link/list" import { LinkManage } from "@/components/routes/link/manage" -import { useQueryState } from "nuqs" +import { parseAsBoolean, useQueryState } from "nuqs" import { atom, useAtom } from "jotai" import { LinkBottomBar } from "./bottom-bar" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" @@ -14,6 +14,7 @@ export const isDeleteConfirmShownAtom = atom(false) export function LinkRoute(): React.ReactElement { const [nuqsEditId] = useQueryState("editId") const [activeItemIndex, setActiveItemIndex] = useState(null) + const [isInCreateMode] = useQueryState("create", parseAsBoolean) const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) const [isDeleteConfirmShown] = useAtom(isDeleteConfirmShownAtom) const [disableEnterKey, setDisableEnterKey] = useState(false) @@ -32,7 +33,7 @@ export function LinkRoute(): React.ReactElement { }, []) useEffect(() => { - if (isDeleteConfirmShown || isCommandPaletteOpen) { + if (isDeleteConfirmShown || isCommandPaletteOpen || isInCreateMode) { setDisableEnterKey(true) if (timeoutRef.current) { clearTimeout(timeoutRef.current) @@ -47,7 +48,7 @@ export function LinkRoute(): React.ReactElement { clearTimeout(timeoutRef.current) } } - }, [isDeleteConfirmShown, isCommandPaletteOpen, handleCommandPaletteClose]) + }, [isDeleteConfirmShown, isCommandPaletteOpen, isInCreateMode, handleCommandPaletteClose]) return (
    diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index 995c7ad0..b51e4190 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from "react" +import React, { useCallback, useEffect, useRef } from "react" import { motion, AnimatePresence } from "framer-motion" import { icons } from "lucide-react" import { Button } from "@/components/ui/button" @@ -6,8 +6,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip import { getSpecialShortcut, formatShortcut, isMacOS } from "@/lib/utils" import { LaIcon } from "@/components/custom/la-icon" import { useAtom } from "jotai" -import { linkShowCreateAtom } from "@/store/link" -import { useQueryState } from "nuqs" +import { parseAsBoolean, useQueryState } from "nuqs" import { useConfirm } from "@omit/react-confirm-dialog" import { useAccount, useCoState } from "@/lib/providers/jazz-provider" import { PersonalLink } from "@/lib/schema" @@ -48,9 +47,8 @@ ToolbarButton.displayName = "ToolbarButton" export const LinkBottomBar: React.FC = () => { const [editId, setEditId] = useQueryState("editId") + const [createMode, setCreateMode] = useQueryState("create", parseAsBoolean) const [, setGlobalLinkFormExceptionRefsAtom] = useAtom(globalLinkFormExceptionRefsAtom) - const [showCreate, setShowCreate] = useAtom(linkShowCreateAtom) - const { me } = useAccount({ root: { personalLinks: [] } }) const personalLink = useCoState(PersonalLink, editId as ID) @@ -67,6 +65,13 @@ export const LinkBottomBar: React.FC = () => { const { deleteLink } = useLinkActions() const confirm = useConfirm() + const handleCreateMode = useCallback(() => { + setEditId(null) + setTimeout(() => { + setCreateMode(prev => !prev) + }, 100) + }, [setEditId, setCreateMode]) + useEffect(() => { setGlobalLinkFormExceptionRefsAtom([ overlayRef, @@ -81,7 +86,7 @@ export const LinkBottomBar: React.FC = () => { }, [setGlobalLinkFormExceptionRefsAtom]) const handleDelete = async (e: React.MouseEvent) => { - if (!personalLink) return + if (!personalLink || !me) return const result = await confirm({ title: `Delete "${personalLink.title}"?`, @@ -106,7 +111,6 @@ export const LinkBottomBar: React.FC = () => { }) if (result) { - if (!me) return deleteLink(me, personalLink) setEditId(null) } @@ -114,24 +118,19 @@ export const LinkBottomBar: React.FC = () => { useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if (isMacOS()) { - if (event.ctrlKey && event.metaKey && event.key.toLowerCase() === "n") { - event.preventDefault() - setShowCreate(true) - } - } else { - // For Windows, we'll use Ctrl + Win + N - // Note: The Windows key is not directly detectable in most browsers - if (event.ctrlKey && event.key.toLowerCase() === "n" && (event.metaKey || event.altKey)) { - event.preventDefault() - setShowCreate(true) - } + const isCreateShortcut = isMacOS() + ? event.ctrlKey && event.metaKey && event.key.toLowerCase() === "n" + : event.ctrlKey && event.key.toLowerCase() === "n" && (event.metaKey || event.altKey) + + if (isCreateShortcut) { + event.preventDefault() + handleCreateMode() } } window.addEventListener("keydown", handleKeyDown) return () => window.removeEventListener("keydown", handleKeyDown) - }, [setShowCreate]) + }, [handleCreateMode]) const shortcutKeys = getSpecialShortcut("expandToolbar") const shortcutText = formatShortcut(shortcutKeys) @@ -172,11 +171,11 @@ export const LinkBottomBar: React.FC = () => { exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.1 }} > - {showCreate && setShowCreate(true)} />} - {!showCreate && ( + {createMode && } + {!createMode && ( setShowCreate(true)} + onClick={handleCreateMode} tooltip={`New Link (${shortcutText})`} ref={plusBtnRef} /> diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index ac68e6bc..67a0154a 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -166,13 +166,11 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex return newIndex }) - } else if (e.key === "Enter" && !disableEnterKey) { + } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null) { e.preventDefault() - if (activeItemIndex !== null) { - const activeLink = sortedLinks[activeItemIndex] - if (activeLink) { - setEditId(activeLink.id) - } + const activeLink = sortedLinks[activeItemIndex] + if (activeLink) { + setEditId(activeLink.id) } } } diff --git a/web/components/routes/link/manage.tsx b/web/components/routes/link/manage.tsx index 7c15da06..438ea906 100644 --- a/web/components/routes/link/manage.tsx +++ b/web/components/routes/link/manage.tsx @@ -1,25 +1,24 @@ "use client" import React from "react" -import { linkShowCreateAtom } from "@/store/link" -import { useAtom } from "jotai" import { useKey } from "react-use" import { LinkForm } from "./partials/form/link-form" import { motion, AnimatePresence } from "framer-motion" +import { parseAsBoolean, useQueryState } from "nuqs" interface LinkManageProps {} const LinkManage: React.FC = () => { - const [showCreate, setShowCreate] = useAtom(linkShowCreateAtom) + const [createMode, setCreateMode] = useQueryState("create", parseAsBoolean) - const handleFormClose = () => setShowCreate(false) + const handleFormClose = () => setCreateMode(false) const handleFormFail = () => {} useKey("Escape", handleFormClose) return ( - {showCreate && ( + {createMode && ( = ({ "relative cursor-default outline-none", "grid grid-cols-[auto_1fr_auto] items-center gap-x-2 py-2 max-lg:px-4 sm:px-5 sm:py-2", { - "bg-muted-foreground/10": isActive, + "bg-muted-foreground/5": isActive, "hover:bg-muted/50": !isActive } )} @@ -148,7 +148,11 @@ export const LinkItem: React.FC = ({
    - {personalLink.topic && {personalLink.topic.prettyName}} + {personalLink.topic && ( + + {personalLink.topic.prettyName} + + )}
    ) diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index 4aa7a685..1e10b180 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -18,8 +18,8 @@ import { TopicSelector } from "@/components/custom/topic-selector" import { Button } from "@/components/ui/button" import { LaIcon } from "@/components/custom/la-icon" import { useConfirm } from "@omit/react-confirm-dialog" -import { toast } from "sonner" import { useRouter } from "next/navigation" +import { usePageActions } from "../hooks/use-page-actions" const TITLE_PLACEHOLDER = "Untitled" @@ -59,7 +59,9 @@ export function PageDetailRoute({ pageId }: { pageId: string }) { const isMobile = useMedia("(max-width: 770px)") const page = useCoState(PersonalPage, pageId as ID) const router = useRouter() + const { deletePage } = usePageActions() const confirm = useConfirm() + DeleteEmptyPage(pageId) const handleDelete = async () => { @@ -73,19 +75,8 @@ export function PageDetailRoute({ pageId }: { pageId: string }) { }) if (result && me?.root.personalPages) { - try { - const index = me.root.personalPages.findIndex(item => item?.id === pageId) - if (index === -1) { - toast.error("Page not found.") - return - } - - me.root.personalPages.splice(index, 1) - toast.success("Page deleted.", { position: "bottom-right" }) - router.replace("/") - } catch (error) { - console.error("Delete operation fail", { error }) - } + deletePage(me, pageId as ID) + router.push("/pages") } } @@ -210,7 +201,7 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => { const titleEditor = useEditor({ immediatelyRender: false, - autofocus: true, + autofocus: false, extensions: [ FocusClasses, Paragraph, @@ -254,7 +245,13 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => { useEffect(() => { isTitleInitialMount.current = true isContentInitialMount.current = true - }, []) + + if (!page.title) { + titleEditor?.commands.focus() + } else { + contentEditorRef.current?.editor?.commands.focus() + } + }, [page.title, titleEditor, contentEditorRef]) return (
    diff --git a/web/components/routes/page/hooks/use-page-actions.ts b/web/components/routes/page/hooks/use-page-actions.ts new file mode 100644 index 00000000..986ea3d8 --- /dev/null +++ b/web/components/routes/page/hooks/use-page-actions.ts @@ -0,0 +1,36 @@ +import { useCallback } from "react" +import { toast } from "sonner" +import { LaAccount, PersonalPage } from "@/lib/schema" +import { ID } from "jazz-tools" + +export const usePageActions = () => { + const deletePage = useCallback((me: LaAccount, pageId: ID): void => { + if (!me.root?.personalPages) return + + const index = me.root.personalPages.findIndex(item => item?.id === pageId) + if (index === -1) { + toast.error("Page not found") + return + } + + const page = me.root.personalPages[index] + if (!page) { + toast.error("Page data is invalid") + return + } + + try { + me.root.personalPages.splice(index, 1) + + toast.success("Page deleted", { + position: "bottom-right", + description: `${page.title} has been deleted.` + }) + } catch (error) { + console.error("Failed to delete page", error) + toast.error("Failed to delete page") + } + }, []) + + return { deletePage } +} diff --git a/web/instrumentation.ts b/web/instrumentation.ts index 7f317a03..610b5214 100644 --- a/web/instrumentation.ts +++ b/web/instrumentation.ts @@ -1,5 +1,9 @@ +import * as Sentry from "@sentry/nextjs" + export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { await import("./sentry.server.config") } } + +export const onRequestError = Sentry.captureRequestError diff --git a/web/next.config.mjs b/web/next.config.mjs index ba15307d..e0d5e745 100644 --- a/web/next.config.mjs +++ b/web/next.config.mjs @@ -46,9 +46,8 @@ export default withSentryConfig(nextConfig, { // For all available options, see: // https://github.com/getsentry/sentry-webpack-plugin#options - org: "learn-anything", - project: process.env.SENTRY_PROJECT, - sentryUrl: "https://sentry.io/", + org: process.env.NEXT_PUBLIC_SENTRY_ORG, + project: process.env.NEXT_PUBLIC_SENTRY_PROJECT, // Only print logs for uploading source maps in CI silent: !process.env.CI, @@ -64,7 +63,7 @@ export default withSentryConfig(nextConfig, { enabled: true }, - // Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. + // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. // This can increase your server load as well as your hosting bill. // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- // side errors will fail. diff --git a/web/package.json b/web/package.json index 4aeca69c..278aa010 100644 --- a/web/package.json +++ b/web/package.json @@ -36,7 +36,7 @@ "@radix-ui/react-toggle": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", - "@sentry/nextjs": "^8.29.0", + "@sentry/nextjs": "^8.30.0", "@tanstack/react-virtual": "^3.10.7", "@tiptap/core": "^2.6.6", "@tiptap/extension-blockquote": "^2.6.6", @@ -105,7 +105,7 @@ "zsa-react": "^0.2.2" }, "devDependencies": { - "@ronin/learn-anything": "^0.0.0-3451915138150", + "@ronin/learn-anything": "^0.0.0-3451954511456", "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.1", "@types/jest": "^29.5.12", diff --git a/web/sentry.client.config.ts b/web/sentry.client.config.ts index 27056a38..a6e4ad98 100644 --- a/web/sentry.client.config.ts +++ b/web/sentry.client.config.ts @@ -5,7 +5,7 @@ import * as Sentry from "@sentry/nextjs" Sentry.init({ - dsn: process.env.SENTRY_DSN, + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, // Add optional integrations for additional features integrations: [Sentry.replayIntegration()], diff --git a/web/sentry.server.config.ts b/web/sentry.server.config.ts index 50176680..fe0b3c68 100644 --- a/web/sentry.server.config.ts +++ b/web/sentry.server.config.ts @@ -5,7 +5,7 @@ import * as Sentry from "@sentry/nextjs" Sentry.init({ - dsn: process.env.SENTRY_DSN, + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. tracesSampleRate: 1, diff --git a/web/store/link.ts b/web/store/link.ts index 6596857c..5ad5c59f 100644 --- a/web/store/link.ts +++ b/web/store/link.ts @@ -2,7 +2,6 @@ import { atom } from "jotai" import { atomWithStorage } from "jotai/utils" export const linkSortAtom = atomWithStorage("sort", "manual") -export const linkShowCreateAtom = atom(false) export const linkEditIdAtom = atom(null) export const linkLearningStateSelectorAtom = atom(false) export const linkOpenPopoverForIdAtom = atom(null) From 266c29cae91faa0100a2e8879aa23ac1a280e1f7 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 11 Sep 2024 11:48:06 +0300 Subject: [PATCH 096/124] Pull nodes towards dragged node (#158) Co-authored-by: Damian Tarnawski --- .../routes/public/force-graph-client-lazy.tsx | 92 ++++++++++++------- 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/web/components/routes/public/force-graph-client-lazy.tsx b/web/components/routes/public/force-graph-client-lazy.tsx index 52da5315..5d90da89 100644 --- a/web/components/routes/public/force-graph-client-lazy.tsx +++ b/web/components/routes/public/force-graph-client-lazy.tsx @@ -110,27 +110,30 @@ const TITLE_SIZE_PX = 400 const simulateGraph = ( alpha: number, - graph: fg.graph.Graph, - canvas: fg.canvas.CanvasState, + gestures: fg.canvas.CanvasGestures, vw: number, vh: number ): void => { + let c = gestures.canvas + let g = c.graph + alpha = alpha / 10 // slow things down a bit - fg.graph.simulate(graph, alpha) + fg.graph.simulate(g, alpha) /* Push nodes away from the center (the title) */ - let grid_radius = graph.options.grid_size / 2 - let origin_x = grid_radius + canvas.translate.x - let origin_y = grid_radius + canvas.translate.y + let grid_radius = g.options.grid_size / 2 + let origin_x = grid_radius + c.translate.x + let origin_y = grid_radius + c.translate.y let vmax = Math.max(vw, vh) let push_radius = - (Math.min(TITLE_SIZE_PX, vw / 2, vh / 2) / vmax) * (graph.options.grid_size / canvas.scale) + + (Math.min(TITLE_SIZE_PX, vw / 2, vh / 2) / vmax) * (g.options.grid_size / c.scale) + 80 /* additional margin for when scrolled in */ - for (let node of graph.nodes) { + for (let node of g.nodes) { + // let dist_x = node.pos.x - origin_x let dist_y = (node.pos.y - origin_y) * 2 let dist = Math.sqrt(dist_x * dist_x + dist_y * dist_y) @@ -141,6 +144,25 @@ const simulateGraph = ( node.vel.x += strength * (node.pos.x - origin_x) * 10 * alpha node.vel.y += strength * (node.pos.y - origin_y) * 10 * alpha } + + /* + When a node is being dragged + it will pull it's connections + */ + if (gestures.mode.type === fg.canvas.Mode.DraggingNode) { + // + let node = gestures.mode.node + + for (let edge of fg.graph.each_node_edge(g, node)) { + let b = edge.b === node ? edge.a : edge.b + + let dx = (b.pos.x - node.pos.x) * g.options.link_strength * edge.strength * alpha * 10 + let dy = (b.pos.y - node.pos.y) * g.options.link_strength * edge.strength * alpha * 10 + + b.vel.x -= dx / b.mass + b.vel.y -= dy / b.mass + } + } } const drawGraph = (c: fg.canvas.CanvasState, color_map: ColorMap): void => { @@ -230,32 +252,6 @@ function init( init_grid_pos: trig.ZERO }) - s.ro = new ResizeObserver(() => { - if (canvas.resizeCanvasToDisplaySize(canvas_el)) { - fg.canvas.updateTranslate(canvas_state, canvas_state.translate.x, canvas_state.translate.y) - } - }) - s.ro.observe(canvas_el) - - simulateGraph(6, s.graph, canvas_state, window.innerWidth, window.innerHeight) - - function loop(time: number) { - let is_active = gestures.mode.type === fg.canvas.Mode.DraggingNode - let iterations = Math.min(2, raf.calcIterations(s.frame_iter_limit, time)) - - for (let i = iterations; i > 0; i--) { - s.alpha = raf.updateAlpha(s.alpha, is_active || time < s.bump_end) - simulateGraph(s.alpha, s.graph, canvas_state, window.innerWidth, window.innerHeight) - } - - if (iterations > 0) { - drawGraph(canvas_state, color_map) - } - - s.raf_id = requestAnimationFrame(loop) - } - s.raf_id = requestAnimationFrame(loop) - let gestures = (s.gestures = fg.canvas.canvasGestures({ canvas: canvas_state, onGesture: e => { @@ -272,6 +268,34 @@ function init( } } })) + + s.ro = new ResizeObserver(() => { + if (canvas.resizeCanvasToDisplaySize(canvas_el)) { + fg.canvas.updateTranslate(canvas_state, canvas_state.translate.x, canvas_state.translate.y) + } + }) + s.ro.observe(canvas_el) + + // initial simulation is the most crazy + // so it's off-screen + simulateGraph(6, gestures, window.innerWidth, window.innerHeight) + + function loop(time: number) { + let is_active = gestures.mode.type === fg.canvas.Mode.DraggingNode + let iterations = Math.min(2, raf.calcIterations(s.frame_iter_limit, time)) + + for (let i = iterations; i > 0; i--) { + s.alpha = raf.updateAlpha(s.alpha, is_active || time < s.bump_end) + simulateGraph(s.alpha, gestures, window.innerWidth, window.innerHeight) + } + + if (iterations > 0) { + drawGraph(canvas_state, color_map) + } + + s.raf_id = requestAnimationFrame(loop) + } + s.raf_id = requestAnimationFrame(loop) } function updateQuery(s: State, filter_query: string) { From 1e0e139ad8fa6e5e610b1f5fcad2c9cef8d9bf50 Mon Sep 17 00:00:00 2001 From: marshennikovaolga Date: Wed, 11 Sep 2024 11:49:24 +0300 Subject: [PATCH 097/124] centered pages and links --- bun.lockb | Bin 488360 -> 488392 bytes .../routes/link/partials/link-item.tsx | 2 +- web/components/routes/page/list.tsx | 2 +- .../routes/page/partials/page-item.tsx | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bun.lockb b/bun.lockb index eac0974b9ebd1727d59c40b55c6a0e7b815256d8..263389b0ea30e2c18f8c97f78ddf45d7869532fb 100755 GIT binary patch delta 53968 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 delta 54107 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 M=MsasF3zLkfBU$JFaQ7m diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index f684a06b..1718896f 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -81,7 +81,7 @@ export const LinkItem: React.FC = ({ onBlur={() => setActiveItemIndex(null)} className={cn( "relative cursor-default outline-none", - "grid grid-cols-[auto_1fr_auto] items-center gap-x-2 py-2 max-lg:px-4 sm:px-5 sm:py-2", + "mx-auto grid w-[98%] grid-cols-[auto_1fr_auto] items-center gap-x-2 rounded-lg p-2", { "bg-muted-foreground/5": isActive, "hover:bg-muted/50": !isActive diff --git a/web/components/routes/page/list.tsx b/web/components/routes/page/list.tsx index 149cb9ce..fb525661 100644 --- a/web/components/routes/page/list.tsx +++ b/web/components/routes/page/list.tsx @@ -81,7 +81,7 @@ interface PageListItemsProps { const PageListItems: React.FC = ({ listRef, setItemRef, personalPages, activeItemIndex }) => ( diff --git a/web/components/routes/page/partials/page-item.tsx b/web/components/routes/page/partials/page-item.tsx index 6b484eed..8b8488b7 100644 --- a/web/components/routes/page/partials/page-item.tsx +++ b/web/components/routes/page/partials/page-item.tsx @@ -22,7 +22,7 @@ export const PageItem = React.forwardRef(({ pa ref={ref} tabIndex={isActive ? 0 : -1} className={cn( - "relative block cursor-default outline-none", + "relative block cursor-default rounded-lg outline-none", "h-12 items-center gap-x-2 py-2 max-lg:px-4 sm:px-6", { "bg-muted-foreground/10": isActive, From dfad7651d660d39fad01ad4999b2ef145c6362b0 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Wed, 11 Sep 2024 17:12:16 +0700 Subject: [PATCH 098/124] fix(page): should only do focus on init load --- .../routes/page/detail/PageDetailRoute.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index 1e10b180..ebeeb250 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -130,6 +130,7 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => { const contentEditorRef = useRef(null) const isTitleInitialMount = useRef(true) const isContentInitialMount = useRef(true) + const isInitialFocusApplied = useRef(false) const updatePageContent = (content: Content, model: PersonalPage) => { if (isContentInitialMount.current) { @@ -201,7 +202,6 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => { const titleEditor = useEditor({ immediatelyRender: false, - autofocus: false, extensions: [ FocusClasses, Paragraph, @@ -246,10 +246,13 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => { isTitleInitialMount.current = true isContentInitialMount.current = true - if (!page.title) { - titleEditor?.commands.focus() - } else { - contentEditorRef.current?.editor?.commands.focus() + if (!isInitialFocusApplied.current && titleEditor && contentEditorRef.current?.editor) { + isInitialFocusApplied.current = true + if (!page.title) { + titleEditor?.commands.focus() + } else { + contentEditorRef.current.editor.commands.focus() + } } }, [page.title, titleEditor, contentEditorRef]) From 78d8b7c8d106795e5cd9b886dd043cfc5c0fa97a Mon Sep 17 00:00:00 2001 From: Aslam H Date: Wed, 11 Sep 2024 17:27:19 +0700 Subject: [PATCH 099/124] fix(bottom-bar): fixed height --- web/components/routes/link/bottom-bar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index b51e4190..c5144889 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -137,7 +137,7 @@ export const LinkBottomBar: React.FC = () => { return ( From 3b68836378c87d6eb4c65ce192e334296b567f98 Mon Sep 17 00:00:00 2001 From: Kisuyo <93681660+Kisuyo@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:47:12 +0300 Subject: [PATCH 100/124] sliding model (#162) --- web/app/(pages)/layout.tsx | 4 +- web/components/routes/link/bottom-bar.tsx | 14 +++- web/components/ui/sliding-menu.tsx | 82 +++++++++++++++++++++++ web/store/sidebar.ts | 1 + 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 web/components/ui/sliding-menu.tsx diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index 4b11bfbc..f39bb728 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -3,6 +3,7 @@ import { Sidebar } from "@/components/custom/sidebar/sidebar" import { CommandPalette } from "@/components/custom/command-palette/command-palette" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" +import SlidingMenu from "@/components/ui/sliding-menu" export default function PageLayout({ children }: { children: React.ReactNode }) { const { me } = useAccountOrGuest() @@ -13,7 +14,8 @@ export default function PageLayout({ children }: { children: React.ReactNode }) {me._type !== "Anonymous" && } -
    +
    +
    {children}
    diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index c5144889..d41e5dd1 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useRef } from "react" import { motion, AnimatePresence } from "framer-motion" -import { icons } from "lucide-react" +import { icons, ZapIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { getSpecialShortcut, formatShortcut, isMacOS } from "@/lib/utils" @@ -13,6 +13,7 @@ import { PersonalLink } from "@/lib/schema" import { ID } from "jazz-tools" import { globalLinkFormExceptionRefsAtom } from "./partials/form/link-form" import { useLinkActions } from "./hooks/use-link-actions" +import { showHotkeyPanelAtom } from "@/store/sidebar" interface ToolbarButtonProps extends React.ComponentPropsWithoutRef { icon: keyof typeof icons @@ -72,6 +73,8 @@ export const LinkBottomBar: React.FC = () => { }, 100) }, [setEditId, setCreateMode]) + const [, setShowHotkeyPanel] = useAtom(showHotkeyPanelAtom) + useEffect(() => { setGlobalLinkFormExceptionRefsAtom([ overlayRef, @@ -184,6 +187,15 @@ export const LinkBottomBar: React.FC = () => { )} +
    + { + setShowHotkeyPanel(true) + }} + /> +
    ) } diff --git a/web/components/ui/sliding-menu.tsx b/web/components/ui/sliding-menu.tsx new file mode 100644 index 00000000..5378aeff --- /dev/null +++ b/web/components/ui/sliding-menu.tsx @@ -0,0 +1,82 @@ +import { XIcon } from "lucide-react" +import { useState, useEffect, useRef } from "react" +import { motion, AnimatePresence } from "framer-motion" +import { showHotkeyPanelAtom } from "@/store/sidebar" +import { useAtom } from "jotai/react" + +export default function SlidingMenu() { + const [isOpen, setIsOpen] = useAtom(showHotkeyPanelAtom) + const panelRef = useRef(null) + const [shortcuts] = useState<{ name: string; shortcut: string[] }[]>([ + // TODO: change to better keybind + // TODO: windows users don't understand these symbols, figure out better way to show keybinds + { name: "New Todo", shortcut: ["⌘", "⌃", "n"] }, + { name: "CMD Palette", shortcut: ["⌘", "k"] } + // TODO: add + // { name: "Global Search", shortcut: ["."] }, + // { name: "(/pages)", shortcut: [".", "."] } + ]) + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (panelRef.current && !panelRef.current.contains(event.target as Node)) { + setIsOpen(false) + } + } + + if (isOpen) { + document.addEventListener("mousedown", handleClickOutside) + } + + return () => { + document.removeEventListener("mousedown", handleClickOutside) + } + }, [isOpen, setIsOpen]) + + return ( + + {isOpen && ( + <> + setIsOpen(false)} + /> + +
    +
    +
    Shortcuts
    + +
    +
    + {shortcuts.map((shortcut, index) => ( +
    +
    {shortcut.name}
    +
    + {shortcut.shortcut.join(" ")} +
    +
    + ))} +
    +
    +
    + + )} +
    + ) +} diff --git a/web/store/sidebar.ts b/web/store/sidebar.ts index a3638924..79854872 100644 --- a/web/store/sidebar.ts +++ b/web/store/sidebar.ts @@ -5,3 +5,4 @@ export const toggleCollapseAtom = atom( get => get(isCollapseAtom), (get, set) => set(isCollapseAtom, !get(isCollapseAtom)) ) +export const showHotkeyPanelAtom = atom(false) From d8521a36eb8b3e712dbc7db6a7f90e9c1b06597a Mon Sep 17 00:00:00 2001 From: Kisuyo Date: Wed, 11 Sep 2024 14:13:41 +0200 Subject: [PATCH 101/124] fix: hotkey panel colors --- web/components/ui/sliding-menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/ui/sliding-menu.tsx b/web/components/ui/sliding-menu.tsx index 5378aeff..1381d753 100644 --- a/web/components/ui/sliding-menu.tsx +++ b/web/components/ui/sliding-menu.tsx @@ -67,7 +67,7 @@ export default function SlidingMenu() { {shortcuts.map((shortcut, index) => (
    {shortcut.name}
    -
    +
    {shortcut.shortcut.join(" ")}
    From fb774f381478ebef8ad729c96c92595133276389 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Wed, 11 Sep 2024 19:28:05 +0700 Subject: [PATCH 102/124] chore(layout): remove overflow --- web/app/(pages)/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index 4b11bfbc..71592f77 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -14,7 +14,7 @@ export default function PageLayout({ children }: { children: React.ReactNode }) {me._type !== "Anonymous" && }
    -
    +
    {children}
    From c5325147015e371c13b4a457418f66511365d57c Mon Sep 17 00:00:00 2001 From: Aslam H Date: Thu, 12 Sep 2024 05:22:34 +0700 Subject: [PATCH 103/124] chore: scrollable and remove link wrapper --- web/app/(pages)/layout.tsx | 2 +- web/components/routes/link/LinkRoute.tsx | 4 ++-- web/components/routes/link/list.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index f12905a6..4f86a094 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -18,7 +18,7 @@ export default function PageLayout({ children }: { children: React.ReactNode })
    -
    +
    {children}
    diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index 62e5f8a9..ce81bee5 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -51,7 +51,7 @@ export function LinkRoute(): React.ReactElement { }, [isDeleteConfirmShown, isCommandPaletteOpen, isInCreateMode, handleCommandPaletteClose]) return ( -
    + <> -
    + ) } diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 67a0154a..47f510ca 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -247,7 +247,7 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex return ( Date: Thu, 12 Sep 2024 06:30:38 +0700 Subject: [PATCH 104/124] fix(link): bottom bar --- web/components/routes/link/bottom-bar.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index d41e5dd1..ebffc4da 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -3,7 +3,7 @@ import { motion, AnimatePresence } from "framer-motion" import { icons, ZapIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" -import { getSpecialShortcut, formatShortcut, isMacOS } from "@/lib/utils" +import { getSpecialShortcut, formatShortcut, isMacOS, cn } from "@/lib/utils" import { LaIcon } from "@/components/custom/la-icon" import { useAtom } from "jotai" import { parseAsBoolean, useQueryState } from "nuqs" @@ -22,9 +22,9 @@ interface ToolbarButtonProps extends React.ComponentPropsWithoutRef( - ({ icon, onClick, tooltip, ...props }, ref) => { + ({ icon, onClick, tooltip, className, ...props }, ref) => { const button = ( - ) @@ -148,7 +148,7 @@ export const LinkBottomBar: React.FC = () => { {editId && ( { {!editId && ( Date: Fri, 13 Sep 2024 11:19:39 +0300 Subject: [PATCH 105/124] hide hotkeys on small screens --- web/components/routes/link/bottom-bar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index ebffc4da..a9e8eae3 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -187,7 +187,7 @@ export const LinkBottomBar: React.FC = () => { )} -
    +
    Date: Sat, 14 Sep 2024 14:32:59 +0300 Subject: [PATCH 106/124] discord link --- bun.lockb | Bin 488392 -> 487416 bytes web/components/custom/discordIcon.tsx | 23 ++++++++++++++++++ .../sidebar/partial/profile-section.tsx | 11 +++++++++ 3 files changed, 34 insertions(+) create mode 100644 web/components/custom/discordIcon.tsx diff --git a/bun.lockb b/bun.lockb index 263389b0ea30e2c18f8c97f78ddf45d7869532fb..69ef0f39632a76c99619b241e7556d27f099ecfb 100755 GIT binary patch delta 54054 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 diff --git a/web/components/custom/discordIcon.tsx b/web/components/custom/discordIcon.tsx new file mode 100644 index 00000000..9311a3e0 --- /dev/null +++ b/web/components/custom/discordIcon.tsx @@ -0,0 +1,23 @@ +export const DiscordIcon = () => ( + + + + + +) diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 97ad4a7c..2f768c5d 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -1,4 +1,5 @@ import { LaIcon } from "@/components/custom/la-icon" +import { DiscordIcon } from "../../discordIcon" import { useState } from "react" import { SignInButton, useAuth, useUser } from "@clerk/nextjs" import { @@ -97,6 +98,16 @@ export const ProfileSection: React.FC = () => { + + +
    + + Discord +
    + +
    + + signOut()}>
    From 4ff9868e8e394cac503e7166701b9d231d4c99f3 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 14 Sep 2024 14:38:27 +0300 Subject: [PATCH 107/124] clean text --- web/components/custom/learning-state-selector.tsx | 2 +- web/components/routes/link/partials/form/description-input.tsx | 2 +- web/components/routes/link/partials/form/link-form.tsx | 2 +- web/components/routes/link/partials/form/notes-section.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/components/custom/learning-state-selector.tsx b/web/components/custom/learning-state-selector.tsx index c1b2e4a9..5b467b86 100644 --- a/web/components/custom/learning-state-selector.tsx +++ b/web/components/custom/learning-state-selector.tsx @@ -20,7 +20,7 @@ interface LearningStateSelectorProps { export const LearningStateSelector: React.FC = ({ showSearch = true, - defaultLabel = "Select state", + defaultLabel = "State", searchPlaceholder = "Search state...", value, onChange, diff --git a/web/components/routes/link/partials/form/description-input.tsx b/web/components/routes/link/partials/form/description-input.tsx index 1ac7887d..9cc2ab7e 100644 --- a/web/components/routes/link/partials/form/description-input.tsx +++ b/web/components/routes/link/partials/form/description-input.tsx @@ -21,7 +21,7 @@ export const DescriptionInput: React.FC = () => { diff --git a/web/components/routes/link/partials/form/link-form.tsx b/web/components/routes/link/partials/form/link-form.tsx index e1f95ead..3fbce3d7 100644 --- a/web/components/routes/link/partials/form/link-form.tsx +++ b/web/components/routes/link/partials/form/link-form.tsx @@ -231,7 +231,7 @@ export const LinkForm: React.FC = ({ ( - {selectedTopic?.prettyName || "Select a topic"} + {selectedTopic?.prettyName || "Topic"} )} /> diff --git a/web/components/routes/link/partials/form/notes-section.tsx b/web/components/routes/link/partials/form/notes-section.tsx index ff3f5456..c14ff4f7 100644 --- a/web/components/routes/link/partials/form/notes-section.tsx +++ b/web/components/routes/link/partials/form/notes-section.tsx @@ -24,7 +24,7 @@ export const NotesSection: React.FC = () => { From 26d938e66c48844403e9f63ff7cf4631528316df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:28:02 +0700 Subject: [PATCH 108/124] chore(deps): bump next from 14.2.5 to 14.2.10 in /web (#170) Bumps [next](https://github.com/vercel/next.js) from 14.2.5 to 14.2.10. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v14.2.5...v14.2.10) --- updated-dependencies: - dependency-name: next dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/package.json b/web/package.json index 278aa010..1aca7cc3 100644 --- a/web/package.json +++ b/web/package.json @@ -84,7 +84,7 @@ "jotai": "^2.9.3", "lowlight": "^3.1.0", "lucide-react": "^0.429.0", - "next": "14.2.5", + "next": "14.2.10", "next-themes": "^0.3.0", "nuqs": "^1.19.1", "react": "^18.3.1", From 8871a8959c0b950cc9608c7b0427caed0756d46b Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 21:11:38 +0700 Subject: [PATCH 109/124] chore(topic): Enhancement using virtual (#164) * chore: use tanstack virtual * fix: topic learning state * chore: add skeleton loading and not found topic placeholder * fix: personal links load in list --- .../routes/topics/detail/TopicDetailRoute.tsx | 88 +++-- web/components/routes/topics/detail/list.tsx | 93 +++++ .../topics/detail/partials/link-item.tsx | 325 +++++++++--------- .../routes/topics/detail/partials/section.tsx | 94 ----- .../topics/detail/partials/topic-sections.tsx | 44 --- .../topics/detail/use-link-navigation.ts | 60 ---- web/hooks/use-topic-data.ts | 15 - 7 files changed, 324 insertions(+), 395 deletions(-) create mode 100644 web/components/routes/topics/detail/list.tsx delete mode 100644 web/components/routes/topics/detail/partials/section.tsx delete mode 100644 web/components/routes/topics/detail/partials/topic-sections.tsx delete mode 100644 web/components/routes/topics/detail/use-link-navigation.ts delete mode 100644 web/hooks/use-topic-data.ts diff --git a/web/components/routes/topics/detail/TopicDetailRoute.tsx b/web/components/routes/topics/detail/TopicDetailRoute.tsx index 1bd5f42c..e805fcc8 100644 --- a/web/components/routes/topics/detail/TopicDetailRoute.tsx +++ b/web/components/routes/topics/detail/TopicDetailRoute.tsx @@ -1,12 +1,17 @@ "use client" -import React, { useMemo, useRef } from "react" +import React, { useMemo, useState } from "react" import { TopicDetailHeader } from "./Header" -import { TopicSections } from "./partials/topic-sections" +import { useAccountOrGuest, useCoState } from "@/lib/providers/jazz-provider" +import { JAZZ_GLOBAL_GROUP_ID } from "@/lib/constants" +import { Topic } from "@/lib/schema" +import { TopicDetailList } from "./list" import { atom } from "jotai" -import { useAccount, useAccountOrGuest } from "@/lib/providers/jazz-provider" -import { useTopicData } from "@/hooks/use-topic-data" +import { Skeleton } from "@/components/ui/skeleton" +import { GraphNode } from "../../public/PublicHomeRoute" +import { LaIcon } from "@/components/custom/la-icon" +const graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) interface TopicDetailRouteProps { topicName: string } @@ -14,27 +19,70 @@ interface TopicDetailRouteProps { export const openPopoverForIdAtom = atom(null) export function TopicDetailRoute({ topicName }: TopicDetailRouteProps) { - const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) - const { topic } = useTopicData(topicName, me) - // const { activeIndex, setActiveIndex, containerRef, linkRefs } = useLinkNavigation(allLinks) - const linksRefDummy = useRef<(HTMLLIElement | null)[]>([]) - const containerRefDummy = useRef(null) + const raw_graph_data = React.use(graph_data_promise) as GraphNode[] - if (!topic || !me) { - return null + const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) + const topicID = useMemo(() => me && Topic.findUnique({ topicName }, JAZZ_GLOBAL_GROUP_ID, me), [topicName, me]) + const topic = useCoState(Topic, topicID, { latestGlobalGuide: { sections: [] } }) + const [activeIndex, setActiveIndex] = useState(-1) + + const topicExists = raw_graph_data.find(node => node.name === topicName) + + if (!topicExists) { + return + } + + const flattenedItems = topic?.latestGlobalGuide?.sections.flatMap(section => [ + { type: "section" as const, data: section }, + ...(section?.links?.map(link => ({ type: "link" as const, data: link })) || []) + ]) + + if (!topic || !me || !flattenedItems) { + return } return ( -
    + <> - {}} - linkRefs={linksRefDummy} - containerRef={containerRefDummy} - /> + + + ) +} + +function NotFoundPlaceholder() { + return ( +
    +
    + + Topic not found +
    + There is no topic with the given identifier.
    ) } + +function TopicDetailSkeleton() { + return ( + <> +
    +
    + + +
    + +
    + +
    + {[...Array(10)].map((_, index) => ( +
    + +
    + + +
    +
    + ))} +
    + + ) +} diff --git a/web/components/routes/topics/detail/list.tsx b/web/components/routes/topics/detail/list.tsx new file mode 100644 index 00000000..30a4dcd6 --- /dev/null +++ b/web/components/routes/topics/detail/list.tsx @@ -0,0 +1,93 @@ +import React, { useRef, useCallback } from "react" +import { useVirtualizer, VirtualItem } from "@tanstack/react-virtual" +import { Link as LinkSchema, Section as SectionSchema, Topic } from "@/lib/schema" +import { LinkItem } from "./partials/link-item" +import { useAccountOrGuest } from "@/lib/providers/jazz-provider" + +export type FlattenedItem = { type: "link"; data: LinkSchema | null } | { type: "section"; data: SectionSchema | null } + +interface TopicDetailListProps { + items: FlattenedItem[] + topic: Topic + activeIndex: number + setActiveIndex: (index: number) => void +} + +export function TopicDetailList({ items, topic, activeIndex, setActiveIndex }: TopicDetailListProps) { + const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) + const personalLinks = !me || me._type === "Anonymous" ? undefined : me.root.personalLinks + + const parentRef = useRef(null) + + const virtualizer = useVirtualizer({ + count: items.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 44, + overscan: 5 + }) + + const renderItem = useCallback( + (virtualRow: VirtualItem) => { + const item = items[virtualRow.index] + + if (item.type === "section") { + return ( +
    +
    +

    {item.data?.title}

    +
    +
    +
    + ) + } + + if (item.data?.id) { + return ( + + ) + } + + return null + }, + [items, topic, activeIndex, setActiveIndex, virtualizer, personalLinks] + ) + + return ( +
    +
    +
    + {virtualizer.getVirtualItems().map(renderItem)} +
    +
    +
    + ) +} diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index 0c99fd01..77964333 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -10,198 +10,199 @@ import { Button } from "@/components/ui/button" import { LearningStateSelectorContent } from "@/components/custom/learning-state-selector" import { cn, ensureUrlProtocol, generateUniqueSlug } from "@/lib/utils" -import { Link as LinkSchema, PersonalLink, Topic } from "@/lib/schema" +import { Link as LinkSchema, PersonalLink, PersonalLinkLists, Topic } from "@/lib/schema" import { openPopoverForIdAtom } from "../TopicDetailRoute" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" import { useClerk } from "@clerk/nextjs" -interface LinkItemProps { +interface LinkItemProps extends React.ComponentPropsWithoutRef<"div"> { topic: Topic link: LinkSchema isActive: boolean index: number setActiveIndex: (index: number) => void + personalLinks?: PersonalLinkLists } export const LinkItem = React.memo( - React.forwardRef(({ topic, link, isActive, index, setActiveIndex }, ref) => { - const clerk = useClerk() - const pathname = usePathname() - const router = useRouter() - const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) - const [isPopoverOpen, setIsPopoverOpen] = useState(false) + React.forwardRef( + ({ topic, link, isActive, index, setActiveIndex, className, personalLinks, ...props }, ref) => { + const clerk = useClerk() + const pathname = usePathname() + const router = useRouter() + const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom) + const [isPopoverOpen, setIsPopoverOpen] = useState(false) + const { me } = useAccountOrGuest() - const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) + const personalLink = useMemo(() => { + return personalLinks?.find(pl => pl?.link?.id === link.id) + }, [personalLinks, link.id]) - const personalLinks = useMemo(() => { - if (!me || me._type === "Anonymous") return undefined - return me?.root?.personalLinks || [] - }, [me]) + const selectedLearningState = useMemo(() => { + return LEARNING_STATES.find(ls => ls.value === personalLink?.learningState) + }, [personalLink?.learningState]) - const personalLink = useMemo(() => { - return personalLinks?.find(pl => pl?.link?.id === link.id) - }, [personalLinks, link.id]) + const handleClick = useCallback( + (e: React.MouseEvent) => { + e.preventDefault() + setActiveIndex(index) + }, + [index, setActiveIndex] + ) - const selectedLearningState = useMemo(() => { - return LEARNING_STATES.find(ls => ls.value === personalLink?.learningState) - }, [personalLink?.learningState]) - - const handleClick = useCallback( - (e: React.MouseEvent) => { - e.preventDefault() - setActiveIndex(index) - }, - [index, setActiveIndex] - ) - - const handleSelectLearningState = useCallback( - (learningState: LearningStateValue) => { - if (!personalLinks || !me || me?._type === "Anonymous") { - return clerk.redirectToSignIn({ - redirectUrl: pathname - }) - } - - const defaultToast = { - duration: 5000, - position: "bottom-right" as const, - closeButton: true, - action: { - label: "Go to list", - onClick: () => router.push("/links") + const handleSelectLearningState = useCallback( + (learningState: LearningStateValue) => { + if (!personalLinks || !me || me?._type === "Anonymous") { + return clerk.redirectToSignIn({ + redirectUrl: pathname + }) } - } - if (personalLink) { - if (personalLink.learningState === learningState) { - personalLink.learningState = undefined - toast.error("Link learning state removed", defaultToast) + const defaultToast = { + duration: 5000, + position: "bottom-right" as const, + closeButton: true, + action: { + label: "Go to list", + onClick: () => router.push("/links") + } + } + + if (personalLink) { + if (personalLink.learningState === learningState) { + personalLink.learningState = undefined + toast.error("Link learning state removed", defaultToast) + } else { + personalLink.learningState = learningState + toast.success("Link learning state updated", defaultToast) + } } else { - personalLink.learningState = learningState - toast.success("Link learning state updated", defaultToast) + const slug = generateUniqueSlug(link.title) + const newPersonalLink = PersonalLink.create( + { + url: link.url, + title: link.title, + slug, + link, + learningState, + sequence: personalLinks.length + 1, + completed: false, + topic, + createdAt: new Date(), + updatedAt: new Date() + }, + { owner: me } + ) + + personalLinks.push(newPersonalLink) + + toast.success("Link added.", { + ...defaultToast, + description: `${link.title} has been added to your personal link.` + }) } - } else { - const slug = generateUniqueSlug(link.title) - const newPersonalLink = PersonalLink.create( + + setOpenPopoverForId(null) + setIsPopoverOpen(false) + }, + [personalLink, personalLinks, me, link, router, topic, setOpenPopoverForId, clerk, pathname] + ) + + const handlePopoverOpenChange = useCallback( + (open: boolean) => { + setIsPopoverOpen(open) + setOpenPopoverForId(open ? link.id : null) + }, + [link.id, setOpenPopoverForId] + ) + + return ( +
    { - setIsPopoverOpen(open) - setOpenPopoverForId(open ? link.id : null) - }, - [link.id, setOpenPopoverForId] - ) - - return ( -
  • -
    -
    - - - - - e.preventDefault()} - > - handleSelectLearningState(value as LearningStateValue)} - /> - - - -
    -
    -

    - {link.title} -

    - -
    -
    -
  • - ) - }) + ) + } + ) ) LinkItem.displayName = "LinkItem" diff --git a/web/components/routes/topics/detail/partials/section.tsx b/web/components/routes/topics/detail/partials/section.tsx deleted file mode 100644 index 7f816432..00000000 --- a/web/components/routes/topics/detail/partials/section.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" -import { LinkItem } from "./link-item" -import { LaAccount, PersonalLinkLists, Section as SectionSchema, Topic, UserRoot } from "@/lib/schema" -import { Skeleton } from "@/components/ui/skeleton" -import { LaIcon } from "@/components/custom/la-icon" - -interface SectionProps { - topic: Topic - section: SectionSchema - activeIndex: number - startIndex: number - linkRefs: React.MutableRefObject<(HTMLLIElement | null)[]> - setActiveIndex: (index: number) => void -} - -export function Section({ topic, section, activeIndex, setActiveIndex, startIndex, linkRefs }: SectionProps) { - const [nLinksToLoad, setNLinksToLoad] = useState(10) - - const linksToLoad = useMemo(() => { - return section.links?.slice(0, nLinksToLoad) - }, [section.links, nLinksToLoad]) - - return ( -
    -
    -

    {section.title}

    -
    -
    - -
    - {linksToLoad?.map((link, index) => - link?.url ? ( - { - linkRefs.current[startIndex + index] = el - }} - /> - ) : ( - - ) - )} - {section.links?.length && section.links?.length > nLinksToLoad && ( - setNLinksToLoad(n => n + 10)} /> - )} -
    -
    - ) -} - -const LoadMoreSpinner = ({ onLoadMore }: { onLoadMore: () => void }) => { - const spinnerRef = useRef(null) - - const handleIntersection = useCallback( - (entries: IntersectionObserverEntry[]) => { - const [entry] = entries - if (entry.isIntersecting) { - onLoadMore() - } - }, - [onLoadMore] - ) - - useEffect(() => { - const observer = new IntersectionObserver(handleIntersection, { - root: null, - rootMargin: "0px", - threshold: 1.0 - }) - - const currentSpinnerRef = spinnerRef.current - - if (currentSpinnerRef) { - observer.observe(currentSpinnerRef) - } - - return () => { - if (currentSpinnerRef) { - observer.unobserve(currentSpinnerRef) - } - } - }, [handleIntersection]) - - return ( -
    - -
    - ) -} diff --git a/web/components/routes/topics/detail/partials/topic-sections.tsx b/web/components/routes/topics/detail/partials/topic-sections.tsx deleted file mode 100644 index a8b6ed5f..00000000 --- a/web/components/routes/topics/detail/partials/topic-sections.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from "react" -import { Section } from "./section" -import { LaAccount, ListOfSections, PersonalLinkLists, Topic, UserRoot } from "@/lib/schema" - -interface TopicSectionsProps { - topic: Topic - sections: (ListOfSections | null) | undefined - activeIndex: number - setActiveIndex: (index: number) => void - linkRefs: React.MutableRefObject<(HTMLLIElement | null)[]> - containerRef: React.RefObject -} - -export function TopicSections({ - topic, - sections, - activeIndex, - setActiveIndex, - linkRefs, - containerRef, -}: TopicSectionsProps) { - return ( -
    -
    -
    - {sections?.map( - (section, sectionIndex) => - section?.id && ( -
    acc + (s?.links?.length || 0), 0)} - linkRefs={linkRefs} - /> - ) - )} -
    -
    -
    - ) -} diff --git a/web/components/routes/topics/detail/use-link-navigation.ts b/web/components/routes/topics/detail/use-link-navigation.ts deleted file mode 100644 index 5e72054b..00000000 --- a/web/components/routes/topics/detail/use-link-navigation.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { useState, useRef, useCallback, useEffect } from "react" -import { Link as LinkSchema } from "@/lib/schema" -import { ensureUrlProtocol } from "@/lib/utils" - -export function useLinkNavigation(allLinks: (LinkSchema | null)[]) { - const [activeIndex, setActiveIndex] = useState(-1) - const containerRef = useRef(null) - const linkRefs = useRef<(HTMLLIElement | null)[]>(allLinks.map(() => null)) - - const scrollToLink = useCallback((index: number) => { - if (linkRefs.current[index] && containerRef.current) { - const linkElement = linkRefs.current[index] - const container = containerRef.current - - const linkRect = linkElement?.getBoundingClientRect() - const containerRect = container.getBoundingClientRect() - - if (linkRect && containerRect) { - if (linkRect.bottom > containerRect.bottom) { - container.scrollTop += linkRect.bottom - containerRect.bottom - } else if (linkRect.top < containerRect.top) { - container.scrollTop -= containerRect.top - linkRect.top - } - } - } - }, []) - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - if (e.key === "ArrowDown") { - e.preventDefault() - setActiveIndex(prevIndex => { - const newIndex = (prevIndex + 1) % allLinks.length - scrollToLink(newIndex) - return newIndex - }) - } else if (e.key === "ArrowUp") { - e.preventDefault() - setActiveIndex(prevIndex => { - const newIndex = (prevIndex - 1 + allLinks.length) % allLinks.length - scrollToLink(newIndex) - return newIndex - }) - } else if (e.key === "Enter" && activeIndex !== -1) { - const link = allLinks[activeIndex] - if (link) { - window.open(ensureUrlProtocol(link.url), "_blank") - } - } - }, - [activeIndex, allLinks, scrollToLink] - ) - - useEffect(() => { - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [handleKeyDown]) - - return { activeIndex, setActiveIndex, containerRef, linkRefs } -} diff --git a/web/hooks/use-topic-data.ts b/web/hooks/use-topic-data.ts deleted file mode 100644 index 8280762b..00000000 --- a/web/hooks/use-topic-data.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useMemo } from "react" -import { useCoState } from "@/lib/providers/jazz-provider" -import { PublicGlobalGroup } from "@/lib/schema/master/public-group" -import { Account, AnonymousJazzAgent, ID } from "jazz-tools" -import { Link, Topic } from "@/lib/schema" - -const GLOBAL_GROUP_ID = process.env.NEXT_PUBLIC_JAZZ_GLOBAL_GROUP as ID - -export function useTopicData(topicName: string, me: Account | AnonymousJazzAgent | undefined) { - const topicID = useMemo(() => me && Topic.findUnique({ topicName }, GLOBAL_GROUP_ID, me), [topicName, me]) - - const topic = useCoState(Topic, topicID, { latestGlobalGuide: { sections: [{ links: [] }] } }) - - return { topic } -} From afaef5d3c564abb8980bcb3440f7bc688dbcb0d5 Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 21:11:52 +0700 Subject: [PATCH 110/124] fix(link): Navigate between item and fix Enter keybind (#165) * feat: add item scroll to active * fix: reset enterkey and scroll to view * fix: link item displayName --- web/components/routes/link/LinkRoute.tsx | 8 +- web/components/routes/link/header.tsx | 2 +- web/components/routes/link/list.tsx | 10 +- .../routes/link/partials/link-item.tsx | 246 +++++++++--------- web/hooks/use-active-item-scroll.ts | 30 +++ 5 files changed, 167 insertions(+), 129 deletions(-) create mode 100644 web/hooks/use-active-item-scroll.ts diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index ce81bee5..bfbc424a 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -8,11 +8,12 @@ import { parseAsBoolean, useQueryState } from "nuqs" import { atom, useAtom } from "jotai" import { LinkBottomBar } from "./bottom-bar" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" +import { useKey } from "react-use" export const isDeleteConfirmShownAtom = atom(false) export function LinkRoute(): React.ReactElement { - const [nuqsEditId] = useQueryState("editId") + const [nuqsEditId, setNuqsEditId] = useQueryState("editId") const [activeItemIndex, setActiveItemIndex] = useState(null) const [isInCreateMode] = useQueryState("create", parseAsBoolean) const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) @@ -50,6 +51,11 @@ export function LinkRoute(): React.ReactElement { } }, [isDeleteConfirmShown, isCommandPaletteOpen, isInCreateMode, handleCommandPaletteClose]) + useKey("Escape", () => { + setDisableEnterKey(false) + setNuqsEditId(null) + }) + return ( <> diff --git a/web/components/routes/link/header.tsx b/web/components/routes/link/header.tsx index 3fa3d863..93d14e7c 100644 --- a/web/components/routes/link/header.tsx +++ b/web/components/routes/link/header.tsx @@ -26,7 +26,7 @@ export const LinkHeader = React.memo(() => { return ( <> - +
    diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 47f510ca..dfd10cd5 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -24,6 +24,7 @@ import { commandPaletteOpenAtom } from "@/components/custom/command-palette/comm import { useConfirm } from "@omit/react-confirm-dialog" import { useLinkActions } from "./hooks/use-link-actions" import { isDeleteConfirmShownAtom } from "./LinkRoute" +import { useActiveItemScroll } from "@/hooks/use-active-item-scroll" interface LinkListProps { activeItemIndex: number | null @@ -77,12 +78,6 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex }) ) - useKey("Escape", () => { - if (editId) { - setEditId(null) - } - }) - useKey( event => (event.metaKey || event.ctrlKey) && event.key === "Backspace", async () => { @@ -245,6 +240,8 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex setDraggingId(null) } + const setElementRef = useActiveItemScroll({ activeIndex: activeItemIndex }) + return ( = ({ activeItemIndex, setActiveItemIndex isActive={activeItemIndex === index} setActiveItemIndex={setActiveItemIndex} index={index} + ref={el => setElementRef(el, index)} /> ) )} diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index 1718896f..9a430d8b 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -15,7 +15,7 @@ import { cn, ensureUrlProtocol } from "@/lib/utils" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { linkOpenPopoverForIdAtom } from "@/store/link" -interface LinkItemProps { +interface LinkItemProps extends React.HTMLAttributes { personalLink: PersonalLink disabled?: boolean isEditing: boolean @@ -26,134 +26,138 @@ interface LinkItemProps { index: number } -export const LinkItem: React.FC = ({ - isEditing, - setEditId, - personalLink, - disabled = false, - isDragging, - isActive, - setActiveItemIndex, - index -}) => { - const [openPopoverForId, setOpenPopoverForId] = useAtom(linkOpenPopoverForIdAtom) - const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: personalLink.id, disabled }) +export const LinkItem = React.forwardRef( + ({ personalLink, disabled, isEditing, setEditId, isDragging, isActive, setActiveItemIndex, index }, ref) => { + const [openPopoverForId, setOpenPopoverForId] = useAtom(linkOpenPopoverForIdAtom) + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: personalLink.id, disabled }) - const style = useMemo( - () => ({ - transform: CSS.Transform.toString(transform), - transition, - pointerEvents: isDragging ? "none" : "auto" - }), - [transform, transition, isDragging] - ) + const style = useMemo( + () => ({ + transform: CSS.Transform.toString(transform), + transition, + pointerEvents: isDragging ? "none" : "auto" + }), + [transform, transition, isDragging] + ) - const handleSuccess = useCallback(() => setEditId(null), [setEditId]) - const handleOnClose = useCallback(() => setEditId(null), [setEditId]) - const handleRowDoubleClick = useCallback(() => setEditId(personalLink.id), [setEditId, personalLink.id]) + const handleSuccess = useCallback(() => setEditId(null), [setEditId]) + const handleOnClose = useCallback(() => setEditId(null), [setEditId]) + const handleRowDoubleClick = useCallback(() => setEditId(personalLink.id), [setEditId, personalLink.id]) - const selectedLearningState = useMemo( - () => LEARNING_STATES.find(ls => ls.value === personalLink.learningState), - [personalLink.learningState] - ) + const selectedLearningState = useMemo( + () => LEARNING_STATES.find(ls => ls.value === personalLink.learningState), + [personalLink.learningState] + ) - const handleLearningStateSelect = useCallback( - (value: string) => { - const learningState = value as LearningStateValue - personalLink.learningState = personalLink.learningState === learningState ? undefined : learningState - setOpenPopoverForId(null) - }, - [personalLink, setOpenPopoverForId] - ) + const handleLearningStateSelect = useCallback( + (value: string) => { + const learningState = value as LearningStateValue + personalLink.learningState = personalLink.learningState === learningState ? undefined : learningState + setOpenPopoverForId(null) + }, + [personalLink, setOpenPopoverForId] + ) - if (isEditing) { - return {}} /> - } + if (isEditing) { + return ( + {}} /> + ) + } - return ( -
  • setActiveItemIndex(index)} - onBlur={() => setActiveItemIndex(null)} - className={cn( - "relative cursor-default outline-none", - "mx-auto grid w-[98%] grid-cols-[auto_1fr_auto] items-center gap-x-2 rounded-lg p-2", - { - "bg-muted-foreground/5": isActive, - "hover:bg-muted/50": !isActive - } - )} - onDoubleClick={handleRowDoubleClick} - > - setOpenPopoverForId(open ? personalLink.id : null)} - > - - - - e.preventDefault()} - > - - - - -
    - {personalLink.icon && ( - {personalLink.title} + return ( +
  • { + setNodeRef(node) + if (typeof ref === "function") { + ref(node) + } else if (ref) { + ref.current = node + } + }} + style={style as React.CSSProperties} + {...attributes} + {...listeners} + tabIndex={0} + onFocus={() => setActiveItemIndex(index)} + onBlur={() => setActiveItemIndex(null)} + className={cn( + "relative cursor-default outline-none", + "grid grid-cols-[auto_1fr_auto] items-center gap-x-2 py-2 max-lg:px-4 sm:px-5 sm:py-2", + { + "bg-muted-foreground/5": isActive, + "hover:bg-muted/50": !isActive + } )} + onDoubleClick={handleRowDoubleClick} + > + setOpenPopoverForId(open ? personalLink.id : null)} + > + + + + e.preventDefault()} + > + + + +
    -

    {personalLink.title}

    - {personalLink.url && ( -
    -
    + {personalLink.icon && ( + {personalLink.title} + )} +
    +

    {personalLink.title}

    + {personalLink.url && ( +
    +
    + )} +
    +
    + +
    + {personalLink.topic && ( + + {personalLink.topic.prettyName} + )}
    -
  • + + ) + } +) -
    - {personalLink.topic && ( - - {personalLink.topic.prettyName} - - )} -
    - - ) -} +LinkItem.displayName = "LinkItem" diff --git a/web/hooks/use-active-item-scroll.ts b/web/hooks/use-active-item-scroll.ts new file mode 100644 index 00000000..81f01fee --- /dev/null +++ b/web/hooks/use-active-item-scroll.ts @@ -0,0 +1,30 @@ +import { useEffect, useRef, useCallback } from "react" + +type ElementRef = T | null +type ElementRefs = ElementRef[] + +interface ActiveItemScrollOptions { + activeIndex: number | null +} + +export function useActiveItemScroll(options: ActiveItemScrollOptions) { + const { activeIndex } = options + const elementRefs = useRef>([]) + + const scrollActiveElementIntoView = useCallback((index: number) => { + const activeElement = elementRefs.current[index] + activeElement?.scrollIntoView({ block: "nearest" }) + }, []) + + useEffect(() => { + if (activeIndex !== null) { + scrollActiveElementIntoView(activeIndex) + } + }, [activeIndex, scrollActiveElementIntoView]) + + const setElementRef = useCallback((element: ElementRef, index: number) => { + elementRefs.current[index] = element + }, []) + + return setElementRef +} From c003711905cbdfb65f32a0e5b9b769dcdd6a7e49 Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 21:12:05 +0700 Subject: [PATCH 111/124] fix(page): Add item scroll, fix display issues, refactor nav, and improve perf (#166) * feat: add item scroll to active * fix: reset enterkey and scroll to view * fix: link item displayName * refactor: remove keyboard page nav * chore: fix scrolling, perf, keys, highlight active item etc * chore: use new hook for create a page * chore: disabled auto delete page --- .../hooks/use-command-actions.ts | 19 ++-- .../custom/sidebar/partial/page-section.tsx | 15 +-- .../routes/page/detail/PageDetailRoute.tsx | 53 ++++++----- web/components/routes/page/header.tsx | 70 +++++++------- .../page/hooks/use-keyboard-navigation.ts | 69 -------------- .../routes/page/hooks/use-page-actions.ts | 11 ++- web/components/routes/page/list.tsx | 91 +++++++++++-------- .../routes/page/partials/page-item.tsx | 23 ++--- 8 files changed, 142 insertions(+), 209 deletions(-) delete mode 100644 web/components/routes/page/hooks/use-keyboard-navigation.ts diff --git a/web/components/custom/command-palette/hooks/use-command-actions.ts b/web/components/custom/command-palette/hooks/use-command-actions.ts index 453e365c..589d95d6 100644 --- a/web/components/custom/command-palette/hooks/use-command-actions.ts +++ b/web/components/custom/command-palette/hooks/use-command-actions.ts @@ -3,11 +3,13 @@ import { ensureUrlProtocol } from "@/lib/utils" import { useTheme } from "next-themes" import { toast } from "sonner" import { useRouter } from "next/navigation" -import { LaAccount, PersonalPage } from "@/lib/schema" +import { LaAccount } from "@/lib/schema" +import { usePageActions } from "@/components/routes/page/hooks/use-page-actions" export const useCommandActions = () => { const { setTheme } = useTheme() const router = useRouter() + const { newPage } = usePageActions() const changeTheme = React.useCallback( (theme: string) => { @@ -35,19 +37,10 @@ export const useCommandActions = () => { const createNewPage = React.useCallback( (me: LaAccount) => { - try { - const newPersonalPage = PersonalPage.create( - { public: false, createdAt: new Date(), updatedAt: new Date() }, - { owner: me._owner } - ) - - me.root?.personalPages?.push(newPersonalPage) - router.push(`/pages/${newPersonalPage.id}`) - } catch (error) { - toast.error("Failed to create page") - } + const page = newPage(me) + router.push(`/pages/${page.id}`) }, - [router] + [router, newPage] ) return { diff --git a/web/components/custom/sidebar/partial/page-section.tsx b/web/components/custom/sidebar/partial/page-section.tsx index 4b9daf28..c5845c58 100644 --- a/web/components/custom/sidebar/partial/page-section.tsx +++ b/web/components/custom/sidebar/partial/page-section.tsx @@ -7,7 +7,6 @@ import { atomWithStorage } from "jotai/utils" import { PersonalPage, PersonalPageLists } from "@/lib/schema/personal-page" import { Button } from "@/components/ui/button" import { LaIcon } from "@/components/custom/la-icon" -import { toast } from "sonner" import Link from "next/link" import { DropdownMenu, @@ -21,6 +20,7 @@ import { DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { icons } from "lucide-react" +import { usePageActions } from "@/components/routes/page/hooks/use-page-actions" type SortOption = "title" | "recent" type ShowOption = 5 | 10 | 15 | 20 | 0 @@ -101,20 +101,13 @@ const PageSectionHeader: React.FC = ({ pageCount, isActi const NewPageButton: React.FC = () => { const { me } = useAccount() const router = useRouter() + const { newPage } = usePageActions() if (!me) return null const handleClick = () => { - try { - const newPersonalPage = PersonalPage.create( - { public: false, createdAt: new Date(), updatedAt: new Date() }, - { owner: me._owner } - ) - me.root?.personalPages?.push(newPersonalPage) - router.push(`/pages/${newPersonalPage.id}`) - } catch (error) { - toast.error("Failed to create page") - } + const page = newPage(me) + router.push(`/pages/${page.id}`) } return ( diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index ebeeb250..0c6467ff 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -23,11 +23,11 @@ import { usePageActions } from "../hooks/use-page-actions" const TITLE_PLACEHOLDER = "Untitled" -const emptyPage = (page: PersonalPage): boolean => { +const isPageEmpty = (page: PersonalPage): boolean => { return (!page.title || page.title.trim() === "") && (!page.content || Object.keys(page.content).length === 0) } -export const DeleteEmptyPage = (currentPageId: string | null) => { +const useDeleteEmptyPage = (currentPageId: string | null) => { const router = useRouter() const { me } = useAccount({ root: { @@ -36,21 +36,17 @@ export const DeleteEmptyPage = (currentPageId: string | null) => { }) useEffect(() => { - const handleRouteChange = () => { + return () => { if (!currentPageId || !me?.root?.personalPages) return const currentPage = me.root.personalPages.find(page => page?.id === currentPageId) - if (currentPage && emptyPage(currentPage)) { + if (currentPage && isPageEmpty(currentPage)) { const index = me.root.personalPages.findIndex(page => page?.id === currentPageId) if (index !== -1) { me.root.personalPages.splice(index, 1) } } } - - return () => { - handleRouteChange() - } }, [currentPageId, me, router]) } @@ -62,9 +58,9 @@ export function PageDetailRoute({ pageId }: { pageId: string }) { const { deletePage } = usePageActions() const confirm = useConfirm() - DeleteEmptyPage(pageId) + // useDeleteEmptyPage(pageId) - const handleDelete = async () => { + const handleDelete = useCallback(async () => { const result = await confirm({ title: "Delete page", description: "Are you sure you want to delete this page?", @@ -78,7 +74,7 @@ export function PageDetailRoute({ pageId }: { pageId: string }) { deletePage(me, pageId as ID) router.push("/pages") } - } + }, [confirm, deletePage, me, pageId, router]) if (!page) return null @@ -132,29 +128,32 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => { const isContentInitialMount = useRef(true) const isInitialFocusApplied = useRef(false) - const updatePageContent = (content: Content, model: PersonalPage) => { + const updatePageContent = useCallback((content: Content, model: PersonalPage) => { if (isContentInitialMount.current) { isContentInitialMount.current = false return } model.content = content model.updatedAt = new Date() - } + }, []) - const handleUpdateTitle = (editor: Editor) => { - if (isTitleInitialMount.current) { - isTitleInitialMount.current = false - return - } + const handleUpdateTitle = useCallback( + (editor: Editor) => { + if (isTitleInitialMount.current) { + isTitleInitialMount.current = false + return + } - const newTitle = editor.getText() - if (newTitle !== page.title) { - const slug = generateUniqueSlug(page.title?.toString() || "") - page.title = newTitle - page.slug = slug - page.updatedAt = new Date() - } - } + const newTitle = editor.getText() + if (newTitle !== page.title) { + const slug = generateUniqueSlug(page.title?.toString() || "") + page.title = newTitle + page.slug = slug + page.updatedAt = new Date() + } + }, + [page] + ) const handleTitleKeyDown = useCallback((view: EditorView, event: KeyboardEvent) => { const editor = titleEditorRef.current @@ -254,7 +253,7 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => { contentEditorRef.current.editor.commands.focus() } } - }, [page.title, titleEditor, contentEditorRef]) + }, [page.title, titleEditor]) return (
    diff --git a/web/components/routes/page/header.tsx b/web/components/routes/page/header.tsx index af1c1b22..93ea9664 100644 --- a/web/components/routes/page/header.tsx +++ b/web/components/routes/page/header.tsx @@ -1,54 +1,58 @@ "use client" import * as React from "react" +import { useRouter } from "next/navigation" import { Button } from "@/components/ui/button" import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header" import { LaIcon } from "@/components/custom/la-icon" import { useAccount } from "@/lib/providers/jazz-provider" -import { useRouter } from "next/navigation" -import { PersonalPage } from "@/lib/schema" -import { toast } from "sonner" +import { usePageActions } from "./hooks/use-page-actions" -export const PageHeader = React.memo(() => { +interface PageHeaderProps {} + +export const PageHeader: React.FC = React.memo(() => { const { me } = useAccount() const router = useRouter() + const { newPage } = usePageActions() if (!me) return null - const handleClick = () => { - try { - const newPersonalPage = PersonalPage.create( - { public: false, createdAt: new Date(), updatedAt: new Date() }, - { owner: me._owner } - ) - me.root?.personalPages?.push(newPersonalPage) - router.push(`/pages/${newPersonalPage.id}`) - } catch (error) { - toast.error("Failed to create page") - } + const handleNewPageClick = () => { + const page = newPage(me) + router.push(`/pages/${page.id}`) } return ( - -
    - -
    - Pages -
    -
    - -
    - -
    -
    - -
    -
    + + +
    + ) }) PageHeader.displayName = "PageHeader" + +const HeaderTitle: React.FC = () => ( +
    + +
    + Pages +
    +
    +) + +interface NewPageButtonProps { + onClick: () => void +} + +const NewPageButton: React.FC = ({ onClick }) => ( +
    +
    + +
    +
    +) diff --git a/web/components/routes/page/hooks/use-keyboard-navigation.ts b/web/components/routes/page/hooks/use-keyboard-navigation.ts deleted file mode 100644 index 3878b653..00000000 --- a/web/components/routes/page/hooks/use-keyboard-navigation.ts +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useEffect, useRef, useCallback } from "react" -import { PersonalPage, PersonalPageLists } from "@/lib/schema" - -interface UseKeyboardNavigationProps { - personalPages?: PersonalPageLists | null - activeItemIndex: number | null - setActiveItemIndex: React.Dispatch> - isCommandPaletteOpen: boolean - disableEnterKey: boolean - onEnter?: (selectedPage: PersonalPage) => void -} - -export const useKeyboardNavigation = ({ - personalPages, - activeItemIndex, - setActiveItemIndex, - isCommandPaletteOpen, - disableEnterKey, - onEnter -}: UseKeyboardNavigationProps) => { - const listRef = useRef(null) - const itemRefs = useRef<(HTMLAnchorElement | null)[]>([]) - const itemCount = personalPages?.length || 0 - - const scrollIntoView = useCallback((index: number) => { - if (itemRefs.current[index]) { - itemRefs.current[index]?.scrollIntoView({ - block: "nearest" - }) - } - }, []) - - useEffect(() => { - if (activeItemIndex !== null) { - scrollIntoView(activeItemIndex) - } - }, [activeItemIndex, scrollIntoView]) - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - if (isCommandPaletteOpen) return - - if (e.key === "ArrowUp" || e.key === "ArrowDown") { - e.preventDefault() - setActiveItemIndex(prevIndex => { - if (prevIndex === null) return 0 - const newIndex = e.key === "ArrowUp" ? (prevIndex - 1 + itemCount) % itemCount : (prevIndex + 1) % itemCount - return newIndex - }) - } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null && personalPages) { - e.preventDefault() - const selectedPage = personalPages[activeItemIndex] - if (selectedPage) onEnter?.(selectedPage) - } - }, - [itemCount, isCommandPaletteOpen, activeItemIndex, setActiveItemIndex, disableEnterKey, personalPages, onEnter] - ) - - useEffect(() => { - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [handleKeyDown]) - - const setItemRef = useCallback((el: HTMLAnchorElement | null, index: number) => { - itemRefs.current[index] = el - }, []) - - return { listRef, setItemRef } -} diff --git a/web/components/routes/page/hooks/use-page-actions.ts b/web/components/routes/page/hooks/use-page-actions.ts index 986ea3d8..65bc7276 100644 --- a/web/components/routes/page/hooks/use-page-actions.ts +++ b/web/components/routes/page/hooks/use-page-actions.ts @@ -4,6 +4,15 @@ import { LaAccount, PersonalPage } from "@/lib/schema" import { ID } from "jazz-tools" export const usePageActions = () => { + const newPage = useCallback((me: LaAccount): PersonalPage => { + const newPersonalPage = PersonalPage.create( + { public: false, createdAt: new Date(), updatedAt: new Date() }, + { owner: me._owner } + ) + me.root?.personalPages?.push(newPersonalPage) + return newPersonalPage + }, []) + const deletePage = useCallback((me: LaAccount, pageId: ID): void => { if (!me.root?.personalPages) return @@ -32,5 +41,5 @@ export const usePageActions = () => { } }, []) - return { deletePage } + return { newPage, deletePage } } diff --git a/web/components/routes/page/list.tsx b/web/components/routes/page/list.tsx index fb525661..c226559a 100644 --- a/web/components/routes/page/list.tsx +++ b/web/components/routes/page/list.tsx @@ -1,15 +1,15 @@ -import React, { useMemo, useCallback } from "react" +import React, { useMemo, useCallback, useEffect } from "react" import { Primitive } from "@radix-ui/react-primitive" import { useAccount } from "@/lib/providers/jazz-provider" import { useAtom } from "jotai" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { PageItem } from "./partials/page-item" -import { useKeyboardNavigation } from "./hooks/use-keyboard-navigation" import { useMedia } from "react-use" import { Column } from "./partials/column" import { useColumnStyles } from "./hooks/use-column-styles" import { PersonalPage, PersonalPageLists } from "@/lib/schema" import { useRouter } from "next/navigation" +import { useActiveItemScroll } from "@/hooks/use-active-item-scroll" interface PageListProps { activeItemIndex: number | null @@ -23,6 +23,7 @@ export const PageList: React.FC = ({ activeItemIndex, setActiveIt const { me } = useAccount({ root: { personalPages: [] } }) const personalPages = useMemo(() => me?.root?.personalPages, [me?.root?.personalPages]) const router = useRouter() + const itemCount = personalPages?.length || 0 const handleEnter = useCallback( (selectedPage: PersonalPage) => { @@ -31,24 +32,35 @@ export const PageList: React.FC = ({ activeItemIndex, setActiveIt [router] ) - const { listRef, setItemRef } = useKeyboardNavigation({ - personalPages, - activeItemIndex, - setActiveItemIndex, - isCommandPaletteOpen, - disableEnterKey, - onEnter: handleEnter - }) + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (isCommandPaletteOpen) return + + if (e.key === "ArrowUp" || e.key === "ArrowDown") { + e.preventDefault() + setActiveItemIndex(prevIndex => { + if (prevIndex === null) return 0 + const newIndex = e.key === "ArrowUp" ? (prevIndex - 1 + itemCount) % itemCount : (prevIndex + 1) % itemCount + return newIndex + }) + } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null && personalPages) { + e.preventDefault() + const selectedPage = personalPages[activeItemIndex] + if (selectedPage) handleEnter?.(selectedPage) + } + }, + [itemCount, isCommandPaletteOpen, activeItemIndex, setActiveItemIndex, disableEnterKey, personalPages, handleEnter] + ) + + useEffect(() => { + window.addEventListener("keydown", handleKeyDown) + return () => window.removeEventListener("keydown", handleKeyDown) + }, [handleKeyDown]) return ( -
    +
    {!isTablet && } - +
    ) } @@ -72,29 +84,30 @@ export const ColumnHeader: React.FC = () => { } interface PageListItemsProps { - listRef: React.RefObject - setItemRef: (el: HTMLAnchorElement | null, index: number) => void personalPages?: PersonalPageLists | null activeItemIndex: number | null } -const PageListItems: React.FC = ({ listRef, setItemRef, personalPages, activeItemIndex }) => ( - - {personalPages?.map( - (page, index) => - page?.id && ( - setItemRef(el, index)} - page={page} - isActive={index === activeItemIndex} - /> - ) - )} - -) +const PageListItems: React.FC = ({ personalPages, activeItemIndex }) => { + const setElementRef = useActiveItemScroll({ activeIndex: activeItemIndex }) + + return ( + + {personalPages?.map( + (page, index) => + page?.id && ( + setElementRef(el, index)} + page={page} + isActive={index === activeItemIndex} + /> + ) + )} + + ) +} diff --git a/web/components/routes/page/partials/page-item.tsx b/web/components/routes/page/partials/page-item.tsx index 8b8488b7..65dacf66 100644 --- a/web/components/routes/page/partials/page-item.tsx +++ b/web/components/routes/page/partials/page-item.tsx @@ -21,14 +21,10 @@ export const PageItem = React.forwardRef(({ pa @@ -38,14 +34,9 @@ export const PageItem = React.forwardRef(({ pa {!isTablet && ( - <> - {/* - {page.slug} - */} - - {page.topic && {page.topic.prettyName}} - - + + {page.topic && {page.topic.prettyName}} + )} From 0df105f186278de7a123cdbb931b9413a20f407e Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 21:12:19 +0700 Subject: [PATCH 112/124] chore: add item value and cut line 1 (#167) --- web/components/custom/command-palette/command-data.ts | 1 + web/components/custom/command-palette/command-items.tsx | 6 +++--- web/components/custom/command-palette/command-palette.tsx | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/web/components/custom/command-palette/command-data.ts b/web/components/custom/command-palette/command-data.ts index ac3f4cb1..01221ec1 100644 --- a/web/components/custom/command-palette/command-data.ts +++ b/web/components/custom/command-palette/command-data.ts @@ -6,6 +6,7 @@ import { HTMLLikeElement } from "@/lib/utils" export type CommandAction = string | (() => void) export interface CommandItemType { + id?: string icon?: keyof typeof icons value: string label: HTMLLikeElement | string diff --git a/web/components/custom/command-palette/command-items.tsx b/web/components/custom/command-palette/command-items.tsx index 9555ad16..51f64d3a 100644 --- a/web/components/custom/command-palette/command-items.tsx +++ b/web/components/custom/command-palette/command-items.tsx @@ -11,14 +11,14 @@ export interface CommandItemProps extends Omit { } const HTMLLikeRenderer: React.FC<{ content: HTMLLikeElement | string }> = React.memo(({ content }) => { - return <>{renderHTMLLikeElement(content)} + return {renderHTMLLikeElement(content)} }) HTMLLikeRenderer.displayName = "HTMLLikeRenderer" export const CommandItem: React.FC = React.memo( - ({ icon, label, action, payload, shortcut, handleAction }) => ( - handleAction(action, payload)}> + ({ icon, label, action, payload, shortcut, handleAction, ...item }) => ( + handleAction(action, payload)}> {icon && } {shortcut && {shortcut}} diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index 4e2fd40c..d797e381 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -86,6 +86,7 @@ export function CommandPalette() { heading: "Personal Links", items: me?.root.personalLinks?.map(link => ({ + id: link?.id, icon: "Link" as const, value: link?.title || "Untitled", label: link?.title || "Untitled", @@ -100,6 +101,7 @@ export function CommandPalette() { heading: "Personal Pages", items: me?.root.personalPages?.map(page => ({ + id: page?.id, icon: "FileText" as const, value: page?.title || "Untitled", label: page?.title || "Untitled", @@ -184,7 +186,7 @@ export function CommandPalette() { const commandKey = React.useMemo(() => { return filteredCommands .map(group => { - const itemsKey = group.items.map(item => `${item.label}-${item.action}`).join("|") + const itemsKey = group.items.map(item => `${item.label}-${item.value}`).join("|") return `${group.heading}:${itemsKey}` }) .join("__") From 8eed3f8cc29ac07c496acbec7650cfb68b4b0a41 Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 21:17:11 +0700 Subject: [PATCH 113/124] feat(shortcut): Keyboard Navigation (#168) * chore: remove sliding menu * feat(ui): sheet * feat: shortcut component * chore: register new shortcut component to layout * fix: react attr naming * fix: set default to false for shortcut * feat(store): keydown-manager * feat(hooks): keyboard manager * chore: use util from base for la-editor * chore: use util from base for minimal-tiptap-editor * chore(utils): keyboard * chore: use new keyboard manager * fix: uniqueness of certain component * feat: global key handler * chore: implement new key handler --- web/app/(pages)/layout.tsx | 12 +- web/components/custom/Shortcut/shortcut.tsx | 155 +++++++++++++ .../command-palette/command-palette.tsx | 16 +- web/components/custom/discordIcon.tsx | 14 +- .../custom/global-keydown-handler.tsx | 63 ++++++ .../sidebar/partial/profile-section.tsx | 203 ++++++++++-------- .../la-editor/components/ui/shortcut.tsx | 4 +- .../extensions/slash-command/menu-list.tsx | 8 +- web/components/la-editor/lib/utils/index.ts | 2 - .../la-editor/lib/utils/keyboard.ts | 25 --- .../la-editor/lib/utils/platform.ts | 46 ---- .../components/shortcut-key.tsx | 48 ++--- .../components/toolbar-section.tsx | 196 ++++++++--------- web/components/minimal-tiptap/utils.ts | 89 +------- web/components/routes/link/bottom-bar.tsx | 41 ++-- .../routes/topics/detail/TopicDetailRoute.tsx | 1 + web/components/ui/sheet.tsx | 109 ++++++++++ web/components/ui/sliding-menu.tsx | 82 ------- web/hooks/use-keyboard-manager.ts | 38 ++++ web/hooks/use-keydown-listener.ts | 21 ++ web/lib/utils/keyboard.ts | 24 +-- web/store/keydown-manager.ts | 3 + web/store/sidebar.ts | 1 - 23 files changed, 686 insertions(+), 515 deletions(-) create mode 100644 web/components/custom/Shortcut/shortcut.tsx create mode 100644 web/components/custom/global-keydown-handler.tsx delete mode 100644 web/components/la-editor/lib/utils/keyboard.ts delete mode 100644 web/components/la-editor/lib/utils/platform.ts create mode 100644 web/components/ui/sheet.tsx delete mode 100644 web/components/ui/sliding-menu.tsx create mode 100644 web/hooks/use-keyboard-manager.ts create mode 100644 web/hooks/use-keydown-listener.ts create mode 100644 web/store/keydown-manager.ts diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index 4f86a094..c5928be9 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -3,8 +3,9 @@ import { Sidebar } from "@/components/custom/sidebar/sidebar" import { CommandPalette } from "@/components/custom/command-palette/command-palette" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" -import SlidingMenu from "@/components/ui/sliding-menu" import { LearnAnythingOnboarding } from "@/components/custom/learn-anything-onboarding" +import { Shortcut } from "@/components/custom/Shortcut/shortcut" +import { GlobalKeydownHandler } from "@/components/custom/global-keydown-handler" export default function PageLayout({ children }: { children: React.ReactNode }) { const { me } = useAccountOrGuest() @@ -13,11 +14,16 @@ export default function PageLayout({ children }: { children: React.ReactNode })
    + - {me._type !== "Anonymous" && } + {me._type !== "Anonymous" && ( + <> + + + + )}
    -
    {children}
    diff --git a/web/components/custom/Shortcut/shortcut.tsx b/web/components/custom/Shortcut/shortcut.tsx new file mode 100644 index 00000000..e4bd5fc6 --- /dev/null +++ b/web/components/custom/Shortcut/shortcut.tsx @@ -0,0 +1,155 @@ +"use client" + +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { atom, useAtom } from "jotai" +import { Sheet, SheetPortal, SheetOverlay, SheetTitle, sheetVariants, SheetDescription } from "@/components/ui/sheet" +import { LaIcon } from "../la-icon" +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { useKeyboardManager } from "@/hooks/use-keyboard-manager" + +export const showShortcutAtom = atom(false) + +type ShortcutItem = { + label: string + keys: string[] + then?: string[] +} + +type ShortcutSection = { + title: string + shortcuts: ShortcutItem[] +} + +const SHORTCUTS: ShortcutSection[] = [ + { + title: "General", + shortcuts: [ + { label: "Open command menu", keys: ["⌘", "k"] }, + { label: "Log out", keys: ["⌥", "⇧", "q"] } + ] + }, + { + title: "Navigation", + shortcuts: [ + { label: "Go to link", keys: ["G"], then: ["L"] }, + { label: "Go to page", keys: ["G"], then: ["P"] }, + { label: "Go to topic", keys: ["G"], then: ["T"] } + ] + } +] + +const ShortcutKey: React.FC<{ keyChar: string }> = ({ keyChar }) => ( + +) + +const ShortcutItem: React.FC = ({ label, keys, then }) => ( +
    +
    + {label} +
    +
    + + + {keys.map((key, index) => ( + + ))} + {then && ( + <> + then + {then.map((key, index) => ( + + ))} + + )} + + +
    +
    +) + +const ShortcutSection: React.FC = ({ title, shortcuts }) => ( +
    +

    {title}

    +
    + {shortcuts.map((shortcut, index) => ( + + ))} +
    +
    +) + +export function Shortcut() { + const [showShortcut, setShowShortcut] = useAtom(showShortcutAtom) + const [searchQuery, setSearchQuery] = React.useState("") + + const { disableKeydown } = useKeyboardManager("shortcutSection") + + React.useEffect(() => { + disableKeydown(showShortcut) + }, [showShortcut, disableKeydown]) + + const filteredShortcuts = React.useMemo(() => { + if (!searchQuery) return SHORTCUTS + + return SHORTCUTS.map(section => ({ + ...section, + shortcuts: section.shortcuts.filter(shortcut => shortcut.label.toLowerCase().includes(searchQuery.toLowerCase())) + })).filter(section => section.shortcuts.length > 0) + }, [searchQuery]) + + return ( + + + + +
    + Keyboard Shortcuts + Quickly navigate around the app + +
    + + + + Close + +
    + +
    + + + setSearchQuery(e.target.value)} + /> + +
    + +
    +
    +
    + {filteredShortcuts.map((section, index) => ( + + ))} +
    +
    +
    +
    +
    +
    + ) +} diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index d797e381..39aad7c9 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -9,6 +9,7 @@ import { searchSafeRegExp } from "@/lib/utils" import { GraphNode } from "@/components/routes/public/PublicHomeRoute" import { useCommandActions } from "./hooks/use-command-actions" import { atom, useAtom } from "jotai" +import { useKeydownListener } from "@/hooks/use-keydown-listener" const graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) @@ -29,17 +30,17 @@ export function CommandPalette() { const raw_graph_data = React.use(graph_data_promise) as GraphNode[] - React.useEffect(() => { - const down = (e: KeyboardEvent) => { + const handleKeydown = React.useCallback( + (e: KeyboardEvent) => { if (e.key === "k" && (e.metaKey || e.ctrlKey)) { e.preventDefault() setOpen(prev => !prev) } - } + }, + [setOpen] + ) - document.addEventListener("keydown", down) - return () => document.removeEventListener("keydown", down) - }, [setOpen]) + useKeydownListener(handleKeydown) const bounce = React.useCallback(() => { if (dialogRef.current) { @@ -118,11 +119,9 @@ export function CommandPalette() { if (activePage === "home") { if (!inputValue) { - // Only show items from the home object when there's no search input return commandGroups.home } - // When there's a search input, search across all categories const allGroups = [...Object.values(commandGroups).flat(), personalLinks, personalPages, topics] return allGroups @@ -133,7 +132,6 @@ export function CommandPalette() { .filter(group => group.items.length > 0) } - // Handle other active pages (searchLinks, searchPages, etc.) switch (activePage) { case "searchLinks": return [...commandGroups.searchLinks, { items: filterItems(personalLinks.items, searchRegex) }] diff --git a/web/components/custom/discordIcon.tsx b/web/components/custom/discordIcon.tsx index 9311a3e0..9d172b4b 100644 --- a/web/components/custom/discordIcon.tsx +++ b/web/components/custom/discordIcon.tsx @@ -3,21 +3,21 @@ export const DiscordIcon = () => ( ) diff --git a/web/components/custom/global-keydown-handler.tsx b/web/components/custom/global-keydown-handler.tsx new file mode 100644 index 00000000..527162cd --- /dev/null +++ b/web/components/custom/global-keydown-handler.tsx @@ -0,0 +1,63 @@ +"use client" + +import { useState, useEffect, useCallback } from "react" +import { useKeydownListener } from "@/hooks/use-keydown-listener" +import { useAuth } from "@clerk/nextjs" +import { useRouter } from "next/navigation" + +type Sequence = { + [key: string]: string +} + +const SEQUENCES: Sequence = { + GL: "/links", + GP: "/pages", + GT: "/topics" +} + +const MAX_SEQUENCE_TIME = 1000 + +export function GlobalKeydownHandler() { + const [sequence, setSequence] = useState([]) + const { signOut } = useAuth() + const router = useRouter() + + const resetSequence = useCallback(() => { + setSequence([]) + }, []) + + const checkSequence = useCallback(() => { + const sequenceStr = sequence.join("") + const route = SEQUENCES[sequenceStr] + + if (route) { + console.log(`Navigating to ${route}...`) + router.push(route) + resetSequence() + } + }, [sequence, router, resetSequence]) + + useKeydownListener((e: KeyboardEvent) => { + // Check for logout shortcut + if (e.altKey && e.shiftKey && e.code === "KeyQ") { + signOut() + return + } + + // Key sequence handling + const key = e.key.toUpperCase() + setSequence(prev => [...prev, key]) + }) + + useEffect(() => { + checkSequence() + + const timeoutId = setTimeout(() => { + resetSequence() + }, MAX_SEQUENCE_TIME) + + return () => clearTimeout(timeoutId) + }, [sequence, checkSequence, resetSequence]) + + return null +} diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 2f768c5d..2396c7ac 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -1,7 +1,14 @@ -import { LaIcon } from "@/components/custom/la-icon" -import { DiscordIcon } from "../../discordIcon" -import { useState } from "react" +"use client" + +import { useEffect, useState } from "react" import { SignInButton, useAuth, useUser } from "@clerk/nextjs" +import { useAtom } from "jotai" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { icons } from "lucide-react" + +import { LaIcon } from "@/components/custom/la-icon" +import { DiscordIcon } from "@/components/custom/discordIcon" import { DropdownMenu, DropdownMenuContent, @@ -10,17 +17,25 @@ import { DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { Avatar, AvatarImage } from "@/components/ui/avatar" -import Link from "next/link" -import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" -import { usePathname } from "next/navigation" +import { cn } from "@/lib/utils" import { Feedback } from "./feedback" +import { showShortcutAtom } from "@/components/custom/Shortcut/shortcut" +import { ShortcutKey } from "@/components/minimal-tiptap/components/shortcut-key" +import { useKeyboardManager } from "@/hooks/use-keyboard-manager" export const ProfileSection: React.FC = () => { const { user, isSignedIn } = useUser() const { signOut } = useAuth() const [menuOpen, setMenuOpen] = useState(false) const pathname = usePathname() + const [, setShowShortcut] = useAtom(showShortcutAtom) + + const { disableKeydown } = useKeyboardManager("profileSection") + + useEffect(() => { + disableKeydown(menuOpen) + }, [menuOpen, disableKeydown]) if (!isSignedIn) { return ( @@ -38,88 +53,104 @@ export const ProfileSection: React.FC = () => { return (
    -
    - - - - - - - -
    - - My profile -
    - -
    - - - -
    - - Onboarding -
    - -
    - - - - -
    - - Docs -
    - -
    - - - - -
    - - GitHub -
    - -
    - - - - -
    - - Discord -
    - -
    - - - signOut()}> -
    - - Log out -
    -
    -
    -
    -
    - +
    ) } + +interface ProfileDropdownProps { + user: any + menuOpen: boolean + setMenuOpen: (open: boolean) => void + signOut: () => void + setShowShortcut: (show: boolean) => void +} + +const ProfileDropdown: React.FC = ({ user, menuOpen, setMenuOpen, signOut, setShowShortcut }) => ( +
    + + + + + + + + +
    +) + +interface DropdownMenuItemsProps { + signOut: () => void + setShowShortcut: (show: boolean) => void +} + +const DropdownMenuItems: React.FC = ({ signOut, setShowShortcut }) => ( + <> + + setShowShortcut(true)}> + + Shortcut + + + + + + + + +
    + + Log out +
    + +
    +
    +
    + +) + +interface MenuLinkProps { + href: string + icon: keyof typeof icons | React.FC + text: string + iconClass?: string +} + +const MenuLink: React.FC = ({ href, icon, text, iconClass = "" }) => { + const IconComponent = typeof icon === "string" ? icons[icon] : icon + return ( + + +
    + + {text} +
    + +
    + ) +} + +export default ProfileSection diff --git a/web/components/la-editor/components/ui/shortcut.tsx b/web/components/la-editor/components/ui/shortcut.tsx index 978a2b4a..cdb2f976 100644 --- a/web/components/la-editor/components/ui/shortcut.tsx +++ b/web/components/la-editor/components/ui/shortcut.tsx @@ -1,6 +1,6 @@ import * as React from "react" import { cn } from "@/lib/utils" -import { getShortcutKey } from "../../lib/utils" +import { getShortcutKey } from "@/lib/utils" export interface ShortcutKeyWrapperProps extends React.HTMLAttributes { ariaLabel: string @@ -32,7 +32,7 @@ const ShortcutKey = React.forwardRef(({ class {...props} ref={ref} > - {getShortcutKey(shortcut)} + {getShortcutKey(shortcut).symbol} ) }) diff --git a/web/components/la-editor/extensions/slash-command/menu-list.tsx b/web/components/la-editor/extensions/slash-command/menu-list.tsx index 10377d18..ebf92265 100644 --- a/web/components/la-editor/extensions/slash-command/menu-list.tsx +++ b/web/components/la-editor/extensions/slash-command/menu-list.tsx @@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button" import { Separator } from "@/components/ui/separator" import { Command, MenuListProps } from "./types" -import { getShortcutKeys } from "../../lib/utils" +import { getShortcutKeys } from "@/lib/utils" import { Icon } from "../../components/ui/icon" import { PopoverWrapper } from "../../components/ui/popover-wrapper" import { Shortcut } from "../../components/ui/shortcut" @@ -136,7 +136,11 @@ export const MenuList = React.forwardRef((props: MenuListProps, ref) => { {command.label}
    - + shortcut.readable) + .join(" + ")} + > {command.shortcuts.map(shortcut => ( ))} diff --git a/web/components/la-editor/lib/utils/index.ts b/web/components/la-editor/lib/utils/index.ts index 885f4fa4..60123d5b 100644 --- a/web/components/la-editor/lib/utils/index.ts +++ b/web/components/la-editor/lib/utils/index.ts @@ -8,7 +8,5 @@ export function getOutput(editor: Editor, output: LAEditorProps["output"]) { return "" } -export * from "./keyboard" -export * from "./platform" export * from "./isCustomNodeSelected" export * from "./isTextSelected" diff --git a/web/components/la-editor/lib/utils/keyboard.ts b/web/components/la-editor/lib/utils/keyboard.ts deleted file mode 100644 index 09739fc2..00000000 --- a/web/components/la-editor/lib/utils/keyboard.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { isMacOS } from "./platform" - -export const getShortcutKey = (key: string) => { - const lowercaseKey = key.toLowerCase() - const macOS = isMacOS() - - switch (lowercaseKey) { - case "mod": - return macOS ? "⌘" : "Ctrl" - case "alt": - return macOS ? "⌥" : "Alt" - case "shift": - return macOS ? "⇧" : "Shift" - default: - return key - } -} - -export const getShortcutKeys = (keys: string | string[], separator: string = "") => { - const keyArray = Array.isArray(keys) ? keys : keys.split(/\s+/) - const shortcutKeys = keyArray.map(getShortcutKey) - return shortcutKeys.join(separator) -} - -export default { getShortcutKey, getShortcutKeys } diff --git a/web/components/la-editor/lib/utils/platform.ts b/web/components/la-editor/lib/utils/platform.ts deleted file mode 100644 index 2dffa98a..00000000 --- a/web/components/la-editor/lib/utils/platform.ts +++ /dev/null @@ -1,46 +0,0 @@ -export interface NavigatorWithUserAgentData extends Navigator { - userAgentData?: { - brands: { brand: string; version: string }[] - mobile: boolean - platform: string - getHighEntropyValues: (hints: string[]) => Promise<{ - platform: string - platformVersion: string - uaFullVersion: string - }> - } -} - -let isMac: boolean | undefined - -const getPlatform = () => { - const nav = navigator as NavigatorWithUserAgentData - if (nav.userAgentData) { - if (nav.userAgentData.platform) { - return nav.userAgentData.platform - } - - nav.userAgentData - .getHighEntropyValues(["platform"]) - .then(highEntropyValues => { - if (highEntropyValues.platform) { - return highEntropyValues.platform - } - }) - .catch(() => { - return navigator.platform || "" - }) - } - - return navigator.platform || "" -} - -export const isMacOS = () => { - if (isMac === undefined) { - isMac = getPlatform().toLowerCase().includes("mac") - } - - return isMac -} - -export default isMacOS diff --git a/web/components/minimal-tiptap/components/shortcut-key.tsx b/web/components/minimal-tiptap/components/shortcut-key.tsx index 2691528d..e81bdd88 100644 --- a/web/components/minimal-tiptap/components/shortcut-key.tsx +++ b/web/components/minimal-tiptap/components/shortcut-key.tsx @@ -1,33 +1,33 @@ -import * as React from 'react' -import { cn } from '@/lib/utils' -import { getShortcutKey } from '../utils' +import * as React from "react" +import { cn } from "@/lib/utils" +import { getShortcutKey } from "@/lib/utils" export interface ShortcutKeyProps extends React.HTMLAttributes { - keys: string[] + keys: string[] } export const ShortcutKey = React.forwardRef(({ className, keys, ...props }, ref) => { - const modifiedKeys = keys.map(key => getShortcutKey(key)) - const ariaLabel = modifiedKeys.map(shortcut => shortcut.readable).join(' + ') + const modifiedKeys = keys.map(key => getShortcutKey(key)) + const ariaLabel = modifiedKeys.map(shortcut => shortcut.readable).join(" + ") - return ( - - {modifiedKeys.map(shortcut => ( - + {modifiedKeys.map(shortcut => ( + - {shortcut.symbol} - - ))} - - ) + className + )} + {...props} + ref={ref} + > + {shortcut.symbol} + + ))} + + ) }) -ShortcutKey.displayName = 'ShortcutKey' +ShortcutKey.displayName = "ShortcutKey" diff --git a/web/components/minimal-tiptap/components/toolbar-section.tsx b/web/components/minimal-tiptap/components/toolbar-section.tsx index e296fd17..9aacc01b 100644 --- a/web/components/minimal-tiptap/components/toolbar-section.tsx +++ b/web/components/minimal-tiptap/components/toolbar-section.tsx @@ -1,112 +1,112 @@ -import * as React from 'react' -import type { Editor } from '@tiptap/react' -import { cn } from '@/lib/utils' -import { CaretDownIcon } from '@radix-ui/react-icons' -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' -import { ToolbarButton } from './toolbar-button' -import { ShortcutKey } from './shortcut-key' -import { getShortcutKey } from '../utils' -import type { FormatAction } from '../types' -import type { VariantProps } from 'class-variance-authority' -import type { toggleVariants } from '@/components/ui/toggle' +import * as React from "react" +import type { Editor } from "@tiptap/react" +import { cn } from "@/lib/utils" +import { CaretDownIcon } from "@radix-ui/react-icons" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" +import { ToolbarButton } from "./toolbar-button" +import { ShortcutKey } from "./shortcut-key" +import { getShortcutKey } from "@/lib/utils" +import type { FormatAction } from "../types" +import type { VariantProps } from "class-variance-authority" +import type { toggleVariants } from "@/components/ui/toggle" interface ToolbarSectionProps extends VariantProps { - editor: Editor - actions: FormatAction[] - activeActions?: string[] - mainActionCount?: number - dropdownIcon?: React.ReactNode - dropdownTooltip?: string - dropdownClassName?: string + editor: Editor + actions: FormatAction[] + activeActions?: string[] + mainActionCount?: number + dropdownIcon?: React.ReactNode + dropdownTooltip?: string + dropdownClassName?: string } export const ToolbarSection: React.FC = ({ - editor, - actions, - activeActions = actions.map(action => action.value), - mainActionCount = 0, - dropdownIcon, - dropdownTooltip = 'More options', - dropdownClassName = 'w-12', - size, - variant + editor, + actions, + activeActions = actions.map(action => action.value), + mainActionCount = 0, + dropdownIcon, + dropdownTooltip = "More options", + dropdownClassName = "w-12", + size, + variant }) => { - const { mainActions, dropdownActions } = React.useMemo(() => { - const sortedActions = actions - .filter(action => activeActions.includes(action.value)) - .sort((a, b) => activeActions.indexOf(a.value) - activeActions.indexOf(b.value)) + const { mainActions, dropdownActions } = React.useMemo(() => { + const sortedActions = actions + .filter(action => activeActions.includes(action.value)) + .sort((a, b) => activeActions.indexOf(a.value) - activeActions.indexOf(b.value)) - return { - mainActions: sortedActions.slice(0, mainActionCount), - dropdownActions: sortedActions.slice(mainActionCount) - } - }, [actions, activeActions, mainActionCount]) + return { + mainActions: sortedActions.slice(0, mainActionCount), + dropdownActions: sortedActions.slice(mainActionCount) + } + }, [actions, activeActions, mainActionCount]) - const renderToolbarButton = React.useCallback( - (action: FormatAction) => ( - action.action(editor)} - disabled={!action.canExecute(editor)} - isActive={action.isActive(editor)} - tooltip={`${action.label} ${action.shortcuts.map(s => getShortcutKey(s).symbol).join(' ')}`} - aria-label={action.label} - size={size} - variant={variant} - > - {action.icon} - - ), - [editor, size, variant] - ) + const renderToolbarButton = React.useCallback( + (action: FormatAction) => ( + action.action(editor)} + disabled={!action.canExecute(editor)} + isActive={action.isActive(editor)} + tooltip={`${action.label} ${action.shortcuts.map(s => getShortcutKey(s).symbol).join(" ")}`} + aria-label={action.label} + size={size} + variant={variant} + > + {action.icon} + + ), + [editor, size, variant] + ) - const renderDropdownMenuItem = React.useCallback( - (action: FormatAction) => ( - action.action(editor)} - disabled={!action.canExecute(editor)} - className={cn('flex flex-row items-center justify-between gap-4', { - 'bg-accent': action.isActive(editor) - })} - aria-label={action.label} - > - {action.label} - - - ), - [editor] - ) + const renderDropdownMenuItem = React.useCallback( + (action: FormatAction) => ( + action.action(editor)} + disabled={!action.canExecute(editor)} + className={cn("flex flex-row items-center justify-between gap-4", { + "bg-accent": action.isActive(editor) + })} + aria-label={action.label} + > + {action.label} + + + ), + [editor] + ) - const isDropdownActive = React.useMemo( - () => dropdownActions.some(action => action.isActive(editor)), - [dropdownActions, editor] - ) + const isDropdownActive = React.useMemo( + () => dropdownActions.some(action => action.isActive(editor)), + [dropdownActions, editor] + ) - return ( - <> - {mainActions.map(renderToolbarButton)} - {dropdownActions.length > 0 && ( - - - - {dropdownIcon || } - - - - {dropdownActions.map(renderDropdownMenuItem)} - - - )} - - ) + return ( + <> + {mainActions.map(renderToolbarButton)} + {dropdownActions.length > 0 && ( + + + + {dropdownIcon || } + + + + {dropdownActions.map(renderDropdownMenuItem)} + + + )} + + ) } export default ToolbarSection diff --git a/web/components/minimal-tiptap/utils.ts b/web/components/minimal-tiptap/utils.ts index d8772d84..d749a3cc 100644 --- a/web/components/minimal-tiptap/utils.ts +++ b/web/components/minimal-tiptap/utils.ts @@ -1,81 +1,14 @@ -import type { Editor } from '@tiptap/core' -import type { MinimalTiptapProps } from './minimal-tiptap' +import type { Editor } from "@tiptap/core" +import type { MinimalTiptapProps } from "./minimal-tiptap" -let isMac: boolean | undefined +export function getOutput(editor: Editor, format: MinimalTiptapProps["output"]) { + if (format === "json") { + return editor.getJSON() + } -interface Navigator { - userAgentData?: { - brands: { brand: string; version: string }[] - mobile: boolean - platform: string - getHighEntropyValues: (hints: string[]) => Promise<{ - platform: string - platformVersion: string - uaFullVersion: string - }> - } -} - -function getPlatform(): string { - const nav = navigator as Navigator - - if (nav.userAgentData) { - if (nav.userAgentData.platform) { - return nav.userAgentData.platform - } - - nav.userAgentData.getHighEntropyValues(['platform']).then(highEntropyValues => { - if (highEntropyValues.platform) { - return highEntropyValues.platform - } - }) - } - - if (typeof navigator.platform === 'string') { - return navigator.platform - } - - return '' -} - -export function isMacOS() { - if (isMac === undefined) { - isMac = getPlatform().toLowerCase().includes('mac') - } - - return isMac -} - -interface ShortcutKeyResult { - symbol: string - readable: string -} - -export function getShortcutKey(key: string): ShortcutKeyResult { - const lowercaseKey = key.toLowerCase() - if (lowercaseKey === 'mod') { - return isMacOS() ? { symbol: '⌘', readable: 'Command' } : { symbol: 'Ctrl', readable: 'Control' } - } else if (lowercaseKey === 'alt') { - return isMacOS() ? { symbol: '⌥', readable: 'Option' } : { symbol: 'Alt', readable: 'Alt' } - } else if (lowercaseKey === 'shift') { - return isMacOS() ? { symbol: '⇧', readable: 'Shift' } : { symbol: 'Shift', readable: 'Shift' } - } else { - return { symbol: key, readable: key } - } -} - -export function getShortcutKeys(keys: string[]): ShortcutKeyResult[] { - return keys.map(key => getShortcutKey(key)) -} - -export function getOutput(editor: Editor, format: MinimalTiptapProps['output']) { - if (format === 'json') { - return editor.getJSON() - } - - if (format === 'html') { - return editor.getText() ? editor.getHTML() : '' - } - - return editor.getText() + if (format === "html") { + return editor.getText() ? editor.getHTML() : "" + } + + return editor.getText() } diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index a9e8eae3..df2165ab 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -1,9 +1,11 @@ +"use client" + import React, { useCallback, useEffect, useRef } from "react" import { motion, AnimatePresence } from "framer-motion" -import { icons, ZapIcon } from "lucide-react" +import type { icons } from "lucide-react" import { Button } from "@/components/ui/button" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" -import { getSpecialShortcut, formatShortcut, isMacOS, cn } from "@/lib/utils" +import { cn, getShortcutKeys } from "@/lib/utils" import { LaIcon } from "@/components/custom/la-icon" import { useAtom } from "jotai" import { parseAsBoolean, useQueryState } from "nuqs" @@ -13,7 +15,7 @@ import { PersonalLink } from "@/lib/schema" import { ID } from "jazz-tools" import { globalLinkFormExceptionRefsAtom } from "./partials/form/link-form" import { useLinkActions } from "./hooks/use-link-actions" -import { showHotkeyPanelAtom } from "@/store/sidebar" +import { useKeydownListener } from "@/hooks/use-keydown-listener" interface ToolbarButtonProps extends React.ComponentPropsWithoutRef { icon: keyof typeof icons @@ -73,8 +75,6 @@ export const LinkBottomBar: React.FC = () => { }, 100) }, [setEditId, setCreateMode]) - const [, setShowHotkeyPanel] = useAtom(showHotkeyPanelAtom) - useEffect(() => { setGlobalLinkFormExceptionRefsAtom([ overlayRef, @@ -119,24 +119,21 @@ export const LinkBottomBar: React.FC = () => { } } - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - const isCreateShortcut = isMacOS() - ? event.ctrlKey && event.metaKey && event.key.toLowerCase() === "n" - : event.ctrlKey && event.key.toLowerCase() === "n" && (event.metaKey || event.altKey) + const handleKeydown = useCallback( + (event: KeyboardEvent) => { + const isCreateShortcut = event.key === "c" if (isCreateShortcut) { event.preventDefault() handleCreateMode() } - } + }, + [handleCreateMode] + ) - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [handleCreateMode]) + useKeydownListener(handleKeydown) - const shortcutKeys = getSpecialShortcut("expandToolbar") - const shortcutText = formatShortcut(shortcutKeys) + const shortcutText = getShortcutKeys(["c"]) return ( { s.symbol).join("")})`} ref={plusBtnRef} /> )} - {/* */} )} -
    - { - setShowHotkeyPanel(true) - }} - /> -
    ) } diff --git a/web/components/routes/topics/detail/TopicDetailRoute.tsx b/web/components/routes/topics/detail/TopicDetailRoute.tsx index e805fcc8..310ae004 100644 --- a/web/components/routes/topics/detail/TopicDetailRoute.tsx +++ b/web/components/routes/topics/detail/TopicDetailRoute.tsx @@ -22,6 +22,7 @@ export function TopicDetailRoute({ topicName }: TopicDetailRouteProps) { const raw_graph_data = React.use(graph_data_promise) as GraphNode[] const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) + const topicID = useMemo(() => me && Topic.findUnique({ topicName }, JAZZ_GLOBAL_GROUP_ID, me), [topicName, me]) const topic = useCoState(Topic, topicID, { latestGlobalGuide: { sections: [] } }) const [activeIndex, setActiveIndex] = useState(-1) diff --git a/web/components/ui/sheet.tsx b/web/components/ui/sheet.tsx new file mode 100644 index 00000000..3b5c5dc7 --- /dev/null +++ b/web/components/ui/sheet.tsx @@ -0,0 +1,109 @@ +"use client" + +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { Cross2Icon } from "@radix-ui/react-icons" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = SheetPrimitive.Portal + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +export const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm" + } + }, + defaultVariants: { + side: "right" + } + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef, SheetContentProps>( + ({ side = "right", className, children, ...props }, ref) => ( + + + + + + Close + + {children} + + + ) +) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
    +) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
    +) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription +} diff --git a/web/components/ui/sliding-menu.tsx b/web/components/ui/sliding-menu.tsx deleted file mode 100644 index 1381d753..00000000 --- a/web/components/ui/sliding-menu.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { XIcon } from "lucide-react" -import { useState, useEffect, useRef } from "react" -import { motion, AnimatePresence } from "framer-motion" -import { showHotkeyPanelAtom } from "@/store/sidebar" -import { useAtom } from "jotai/react" - -export default function SlidingMenu() { - const [isOpen, setIsOpen] = useAtom(showHotkeyPanelAtom) - const panelRef = useRef(null) - const [shortcuts] = useState<{ name: string; shortcut: string[] }[]>([ - // TODO: change to better keybind - // TODO: windows users don't understand these symbols, figure out better way to show keybinds - { name: "New Todo", shortcut: ["⌘", "⌃", "n"] }, - { name: "CMD Palette", shortcut: ["⌘", "k"] } - // TODO: add - // { name: "Global Search", shortcut: ["."] }, - // { name: "(/pages)", shortcut: [".", "."] } - ]) - - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (panelRef.current && !panelRef.current.contains(event.target as Node)) { - setIsOpen(false) - } - } - - if (isOpen) { - document.addEventListener("mousedown", handleClickOutside) - } - - return () => { - document.removeEventListener("mousedown", handleClickOutside) - } - }, [isOpen, setIsOpen]) - - return ( - - {isOpen && ( - <> - setIsOpen(false)} - /> - -
    -
    -
    Shortcuts
    - -
    -
    - {shortcuts.map((shortcut, index) => ( -
    -
    {shortcut.name}
    -
    - {shortcut.shortcut.join(" ")} -
    -
    - ))} -
    -
    -
    - - )} -
    - ) -} diff --git a/web/hooks/use-keyboard-manager.ts b/web/hooks/use-keyboard-manager.ts new file mode 100644 index 00000000..bd8c34de --- /dev/null +++ b/web/hooks/use-keyboard-manager.ts @@ -0,0 +1,38 @@ +import { useAtom } from "jotai" +import { useEffect, useCallback } from "react" +import { keyboardDisableSourcesAtom } from "@/store/keydown-manager" + +export function useKeyboardManager(sourceId: string) { + const [disableSources, setDisableSources] = useAtom(keyboardDisableSourcesAtom) + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (disableSources.size > 0) { + event.preventDefault() + } + } + + window.addEventListener("keydown", handleKeyDown) + return () => window.removeEventListener("keydown", handleKeyDown) + }, [disableSources]) + + const disableKeydown = useCallback( + (disable: boolean) => { + console.log(`${sourceId} disable:`, disable) + setDisableSources(prev => { + const next = new Set(prev) + if (disable) { + next.add(sourceId) + } else { + next.delete(sourceId) + } + return next + }) + }, + [setDisableSources, sourceId] + ) + + const isKeyboardDisabled = disableSources.size > 0 + + return { disableKeydown, isKeyboardDisabled } +} diff --git a/web/hooks/use-keydown-listener.ts b/web/hooks/use-keydown-listener.ts new file mode 100644 index 00000000..888f140e --- /dev/null +++ b/web/hooks/use-keydown-listener.ts @@ -0,0 +1,21 @@ +import { useAtomValue } from "jotai" +import { useEffect, useCallback } from "react" +import { keyboardDisableSourcesAtom } from "@/store/keydown-manager" + +export function useKeydownListener(callback: (event: KeyboardEvent) => void) { + const disableSources = useAtomValue(keyboardDisableSourcesAtom) + + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + if (disableSources.size === 0) { + callback(event) + } + }, + [disableSources, callback] + ) + + useEffect(() => { + window.addEventListener("keydown", handleKeyDown) + return () => window.removeEventListener("keydown", handleKeyDown) + }, [handleKeyDown]) +} diff --git a/web/lib/utils/keyboard.ts b/web/lib/utils/keyboard.ts index faaaf7ce..50255db7 100644 --- a/web/lib/utils/keyboard.ts +++ b/web/lib/utils/keyboard.ts @@ -55,11 +55,7 @@ export function getShortcutKey(key: string): ShortcutKeyResult { } else if (lowercaseKey === "alt") { return isMacOS() ? { symbol: "⌥", readable: "Option" } : { symbol: "Alt", readable: "Alt" } } else if (lowercaseKey === "shift") { - return { symbol: "⇧", readable: "Shift" } - } else if (lowercaseKey === "control") { - return { symbol: "⌃", readable: "Control" } - } else if (lowercaseKey === "windows" && !isMacOS()) { - return { symbol: "Win", readable: "Windows" } + return isMacOS() ? { symbol: "⇧", readable: "Shift" } : { symbol: "Shift", readable: "Shift" } } else { return { symbol: key.toUpperCase(), readable: key } } @@ -68,21 +64,3 @@ export function getShortcutKey(key: string): ShortcutKeyResult { export function getShortcutKeys(keys: string[]): ShortcutKeyResult[] { return keys.map(key => getShortcutKey(key)) } - -export function getSpecialShortcut(shortcutName: string): ShortcutKeyResult[] { - if (shortcutName === "expandToolbar") { - return isMacOS() - ? [getShortcutKey("control"), getShortcutKey("mod"), getShortcutKey("n")] - : [getShortcutKey("mod"), getShortcutKey("windows"), getShortcutKey("n")] - } - - return [] -} - -export function formatShortcut(shortcutKeys: ShortcutKeyResult[]): string { - return shortcutKeys.map(key => key.symbol).join("") -} - -export function formatReadableShortcut(shortcutKeys: ShortcutKeyResult[]): string { - return shortcutKeys.map(key => key.readable).join(" + ") -} diff --git a/web/store/keydown-manager.ts b/web/store/keydown-manager.ts new file mode 100644 index 00000000..d6617dc1 --- /dev/null +++ b/web/store/keydown-manager.ts @@ -0,0 +1,3 @@ +import { atom } from "jotai" + +export const keyboardDisableSourcesAtom = atom>(new Set()) diff --git a/web/store/sidebar.ts b/web/store/sidebar.ts index 79854872..a3638924 100644 --- a/web/store/sidebar.ts +++ b/web/store/sidebar.ts @@ -5,4 +5,3 @@ export const toggleCollapseAtom = atom( get => get(isCollapseAtom), (get, set) => set(isCollapseAtom, !get(isCollapseAtom)) ) -export const showHotkeyPanelAtom = atom(false) From b648c8cd99688482352ba0aca73446cd253e8b0d Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 21:22:25 +0700 Subject: [PATCH 114/124] feat(metadata): Viewport (#169) * chore: remove sliding menu * feat(ui): sheet * feat: shortcut component * chore: register new shortcut component to layout * fix: react attr naming * fix: set default to false for shortcut * feat: viewport --- web/app/(pages)/layout.tsx | 20 +++++++++---------- web/app/layout.tsx | 9 ++++++++- .../command-palette/command-palette.tsx | 12 ++++++++++- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index c5928be9..3b897096 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -1,27 +1,25 @@ -"use client" - +import type { Viewport } from "next" import { Sidebar } from "@/components/custom/sidebar/sidebar" import { CommandPalette } from "@/components/custom/command-palette/command-palette" -import { useAccountOrGuest } from "@/lib/providers/jazz-provider" import { LearnAnythingOnboarding } from "@/components/custom/learn-anything-onboarding" import { Shortcut } from "@/components/custom/Shortcut/shortcut" import { GlobalKeydownHandler } from "@/components/custom/global-keydown-handler" -export default function PageLayout({ children }: { children: React.ReactNode }) { - const { me } = useAccountOrGuest() +export const viewport: Viewport = { + width: "device-width, shrink-to-fit=no", + maximumScale: 1, + userScalable: false +} +export default function PageLayout({ children }: { children: React.ReactNode }) { return (
    - {me._type !== "Anonymous" && ( - <> - - - - )} + +
    diff --git a/web/app/layout.tsx b/web/app/layout.tsx index c9fd5e6c..871c2b75 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -1,4 +1,4 @@ -import type { Metadata } from "next" +import type { Metadata, Viewport } from "next" import { cn } from "@/lib/utils" import { ThemeProvider } from "@/lib/providers/theme-provider" import "./globals.css" @@ -11,6 +11,13 @@ import { GeistMono, GeistSans } from "./fonts" import { JazzAndAuth } from "@/lib/providers/jazz-provider" import { TooltipProvider } from "@/components/ui/tooltip" +export const viewport: Viewport = { + width: "device-width", + height: "device-height", + initialScale: 1, + viewportFit: "cover" +} + export const metadata: Metadata = { title: "Learn Anything", description: "Organize world's knowledge, explore connections and curate learning paths" diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index 39aad7c9..11feb4dd 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -1,10 +1,12 @@ +"use client" + import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" import { Command } from "cmdk" import { Dialog, DialogPortal, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog" import { CommandGroup } from "./command-items" import { CommandAction, CommandItemType, createCommandGroups } from "./command-data" -import { useAccount } from "@/lib/providers/jazz-provider" +import { useAccount, useAccountOrGuest } from "@/lib/providers/jazz-provider" import { searchSafeRegExp } from "@/lib/utils" import { GraphNode } from "@/components/routes/public/PublicHomeRoute" import { useCommandActions } from "./hooks/use-command-actions" @@ -19,6 +21,14 @@ const filterItems = (items: CommandItemType[], searchRegex: RegExp) => export const commandPaletteOpenAtom = atom(false) export function CommandPalette() { + const { me } = useAccountOrGuest() + + if (me._type === "Anonymous") return null + + return +} + +export function RealCommandPalette() { const { me } = useAccount({ root: { personalLinks: [], personalPages: [] } }) const dialogRef = React.useRef(null) const [inputValue, setInputValue] = React.useState("") From 333dcd26ef65ad0d19ccd9e8e63d85541a1deb51 Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 21:22:37 +0700 Subject: [PATCH 115/124] chore(deps): bump multiple dependencies in root and web (#171) --- package.json | 6 ++-- web/package.json | 86 ++++++++++++++++++++++++------------------------ 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index eb489b5d..3d94d0f8 100644 --- a/package.json +++ b/package.json @@ -14,14 +14,14 @@ "web" ], "dependencies": { - "@clerk/themes": "^2.1.27", - "@tauri-apps/cli": "^2.0.0-rc.12", + "@clerk/themes": "^2.1.30", + "@tauri-apps/cli": "^2.0.0-rc.16", "@tauri-apps/plugin-fs": "^2.0.0-rc.2", "jazz-nodejs": "0.7.35-guest-auth.5", "react-icons": "^5.3.0" }, "devDependencies": { - "bun-types": "^1.1.27" + "bun-types": "^1.1.28" }, "prettier": { "plugins": [ diff --git a/web/package.json b/web/package.json index 1aca7cc3..67037938 100644 --- a/web/package.json +++ b/web/package.json @@ -9,11 +9,11 @@ "test": "jest" }, "dependencies": { - "@clerk/nextjs": "^5.4.1", + "@clerk/nextjs": "^5.6.0", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@hookform/resolvers": "^3.9.0", - "@nothing-but/force-graph": "^0.9.4", + "@nothing-but/force-graph": "^0.9.5", "@nothing-but/utils": "^0.16.0", "@omit/react-confirm-dialog": "^1.1.5", "@omit/react-fancy-switch": "^0.1.3", @@ -37,45 +37,45 @@ "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@sentry/nextjs": "^8.30.0", - "@tanstack/react-virtual": "^3.10.7", - "@tiptap/core": "^2.6.6", - "@tiptap/extension-blockquote": "^2.6.6", - "@tiptap/extension-bold": "^2.6.6", - "@tiptap/extension-bullet-list": "^2.6.6", - "@tiptap/extension-code": "^2.6.6", - "@tiptap/extension-code-block-lowlight": "^2.6.6", - "@tiptap/extension-color": "^2.6.6", - "@tiptap/extension-document": "^2.6.6", - "@tiptap/extension-dropcursor": "^2.6.6", - "@tiptap/extension-focus": "^2.6.6", - "@tiptap/extension-gapcursor": "^2.6.6", - "@tiptap/extension-hard-break": "^2.6.6", - "@tiptap/extension-heading": "^2.6.6", - "@tiptap/extension-history": "^2.6.6", - "@tiptap/extension-horizontal-rule": "^2.6.6", - "@tiptap/extension-image": "^2.6.6", - "@tiptap/extension-italic": "^2.6.6", - "@tiptap/extension-link": "^2.6.6", - "@tiptap/extension-list-item": "^2.6.6", - "@tiptap/extension-ordered-list": "^2.6.6", - "@tiptap/extension-paragraph": "^2.6.6", - "@tiptap/extension-placeholder": "^2.6.6", - "@tiptap/extension-strike": "^2.6.6", - "@tiptap/extension-task-item": "^2.6.6", - "@tiptap/extension-task-list": "^2.6.6", - "@tiptap/extension-text": "^2.6.6", - "@tiptap/extension-typography": "^2.6.6", - "@tiptap/pm": "^2.6.6", - "@tiptap/react": "^2.6.6", - "@tiptap/starter-kit": "^2.6.6", - "@tiptap/suggestion": "^2.6.6", + "@tanstack/react-virtual": "^3.10.8", + "@tiptap/core": "^2.7.2", + "@tiptap/extension-blockquote": "^2.7.2", + "@tiptap/extension-bold": "^2.7.2", + "@tiptap/extension-bullet-list": "^2.7.2", + "@tiptap/extension-code": "^2.7.2", + "@tiptap/extension-code-block-lowlight": "^2.7.2", + "@tiptap/extension-color": "^2.7.2", + "@tiptap/extension-document": "^2.7.2", + "@tiptap/extension-dropcursor": "^2.7.2", + "@tiptap/extension-focus": "^2.7.2", + "@tiptap/extension-gapcursor": "^2.7.2", + "@tiptap/extension-hard-break": "^2.7.2", + "@tiptap/extension-heading": "^2.7.2", + "@tiptap/extension-history": "^2.7.2", + "@tiptap/extension-horizontal-rule": "^2.7.2", + "@tiptap/extension-image": "^2.7.2", + "@tiptap/extension-italic": "^2.7.2", + "@tiptap/extension-link": "^2.7.2", + "@tiptap/extension-list-item": "^2.7.2", + "@tiptap/extension-ordered-list": "^2.7.2", + "@tiptap/extension-paragraph": "^2.7.2", + "@tiptap/extension-placeholder": "^2.7.2", + "@tiptap/extension-strike": "^2.7.2", + "@tiptap/extension-task-item": "^2.7.2", + "@tiptap/extension-task-list": "^2.7.2", + "@tiptap/extension-text": "^2.7.2", + "@tiptap/extension-typography": "^2.7.2", + "@tiptap/pm": "^2.7.2", + "@tiptap/react": "^2.7.2", + "@tiptap/starter-kit": "^2.7.2", + "@tiptap/suggestion": "^2.7.2", "axios": "^1.7.7", "cheerio": "1.0.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", - "framer-motion": "^11.5.4", + "framer-motion": "^11.5.5", "geist": "^1.3.1", "jazz-browser-auth-clerk": "0.7.35-guest-auth.5", "jazz-react": "0.7.35-guest-auth.5", @@ -99,27 +99,27 @@ "streaming-markdown": "^0.0.14", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", - "vaul": "^0.9.2", + "vaul": "^0.9.4", "zod": "^3.23.8", "zsa": "^0.6.0", "zsa-react": "^0.2.2" }, "devDependencies": { - "@ronin/learn-anything": "^0.0.0-3451954511456", + "@ronin/learn-anything": "^0.0.0-3452357373461", "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.1", - "@types/jest": "^29.5.12", - "@types/node": "^22.5.4", - "@types/react": "^18.3.5", + "@types/jest": "^29.5.13", + "@types/node": "^22.5.5", + "@types/react": "^18.3.7", "@types/react-dom": "^18.3.0", "dotenv": "^16.4.5", - "eslint": "^8.57.0", + "eslint": "^8.57.1", "eslint-config-next": "14.2.5", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "postcss": "^8.4.45", + "postcss": "^8.4.47", "prettier-plugin-tailwindcss": "^0.6.6", - "tailwindcss": "^3.4.10", + "tailwindcss": "^3.4.12", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", "typescript": "^5.6.2" From 1a6c2ab42070376e5e0064fb09f02e040c80646b Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 21:28:48 +0700 Subject: [PATCH 116/124] feat(topic): Topic List Route (#172) * feat: add item scroll to active * fix: reset enterkey and scroll to view * fix: link item displayName * refactor: remove keyboard page nav * chore: fix scrolling, perf, keys, highlight active item etc * chore: use new hook for create a page * chore: disabled auto delete page * wip * chore: add learning selector * chore: learning selector update --- web/app/(pages)/topics/page.tsx | 5 + .../page/partials => custom}/column.tsx | 0 web/components/routes/page/list.tsx | 2 +- .../routes/page/partials/page-item.tsx | 2 +- web/components/routes/topics/TopicRoute.tsx | 35 ++++ web/components/routes/topics/header.tsx | 31 ++++ .../routes/topics/hooks/use-column-styles.ts | 14 ++ web/components/routes/topics/list.tsx | 157 +++++++++++++++++ .../routes/topics/partials/topic-item.tsx | 158 ++++++++++++++++++ 9 files changed, 402 insertions(+), 2 deletions(-) create mode 100644 web/app/(pages)/topics/page.tsx rename web/components/{routes/page/partials => custom}/column.tsx (100%) create mode 100644 web/components/routes/topics/TopicRoute.tsx create mode 100644 web/components/routes/topics/header.tsx create mode 100644 web/components/routes/topics/hooks/use-column-styles.ts create mode 100644 web/components/routes/topics/list.tsx create mode 100644 web/components/routes/topics/partials/topic-item.tsx diff --git a/web/app/(pages)/topics/page.tsx b/web/app/(pages)/topics/page.tsx new file mode 100644 index 00000000..6251415e --- /dev/null +++ b/web/app/(pages)/topics/page.tsx @@ -0,0 +1,5 @@ +import { TopicRoute } from "@/components/routes/topics/TopicRoute" + +export default function Page() { + return +} diff --git a/web/components/routes/page/partials/column.tsx b/web/components/custom/column.tsx similarity index 100% rename from web/components/routes/page/partials/column.tsx rename to web/components/custom/column.tsx diff --git a/web/components/routes/page/list.tsx b/web/components/routes/page/list.tsx index c226559a..62be86d2 100644 --- a/web/components/routes/page/list.tsx +++ b/web/components/routes/page/list.tsx @@ -5,11 +5,11 @@ import { useAtom } from "jotai" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { PageItem } from "./partials/page-item" import { useMedia } from "react-use" -import { Column } from "./partials/column" import { useColumnStyles } from "./hooks/use-column-styles" import { PersonalPage, PersonalPageLists } from "@/lib/schema" import { useRouter } from "next/navigation" import { useActiveItemScroll } from "@/hooks/use-active-item-scroll" +import { Column } from "@/components/custom/column" interface PageListProps { activeItemIndex: number | null diff --git a/web/components/routes/page/partials/page-item.tsx b/web/components/routes/page/partials/page-item.tsx index 65dacf66..002a01e1 100644 --- a/web/components/routes/page/partials/page-item.tsx +++ b/web/components/routes/page/partials/page-item.tsx @@ -3,10 +3,10 @@ import Link from "next/link" import { cn } from "@/lib/utils" import { PersonalPage } from "@/lib/schema" import { Badge } from "@/components/ui/badge" -import { Column } from "./column" import { useMedia } from "react-use" import { useColumnStyles } from "../hooks/use-column-styles" import { format } from "date-fns" +import { Column } from "@/components/custom/column" interface PageItemProps { page: PersonalPage diff --git a/web/components/routes/topics/TopicRoute.tsx b/web/components/routes/topics/TopicRoute.tsx new file mode 100644 index 00000000..b0ce6356 --- /dev/null +++ b/web/components/routes/topics/TopicRoute.tsx @@ -0,0 +1,35 @@ +"use client" + +import { useCallback, useEffect, useState } from "react" +import { TopicHeader } from "./header" +import { TopicList } from "./list" +import { useAtom } from "jotai" +import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" + +export function TopicRoute() { + const [activeItemIndex, setActiveItemIndex] = useState(null) + const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) + const [disableEnterKey, setDisableEnterKey] = useState(false) + + const handleCommandPaletteClose = useCallback(() => { + setDisableEnterKey(true) + setTimeout(() => setDisableEnterKey(false), 100) + }, []) + + useEffect(() => { + if (!isCommandPaletteOpen) { + handleCommandPaletteClose() + } + }, [isCommandPaletteOpen, handleCommandPaletteClose]) + + return ( +
    + + +
    + ) +} diff --git a/web/components/routes/topics/header.tsx b/web/components/routes/topics/header.tsx new file mode 100644 index 00000000..9b949313 --- /dev/null +++ b/web/components/routes/topics/header.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header" +import { useAccount } from "@/lib/providers/jazz-provider" + +interface TopicHeaderProps {} + +export const TopicHeader: React.FC = React.memo(() => { + const { me } = useAccount() + + if (!me) return null + + return ( + + +
    + + ) +}) + +TopicHeader.displayName = "TopicHeader" + +const HeaderTitle: React.FC = () => ( +
    + +
    + Topics +
    +
    +) diff --git a/web/components/routes/topics/hooks/use-column-styles.ts b/web/components/routes/topics/hooks/use-column-styles.ts new file mode 100644 index 00000000..7cecc98b --- /dev/null +++ b/web/components/routes/topics/hooks/use-column-styles.ts @@ -0,0 +1,14 @@ +import { useMedia } from "react-use" + +export const useColumnStyles = () => { + const isTablet = useMedia("(max-width: 640px)") + + return { + title: { + "--width": "69px", + "--min-width": "200px", + "--max-width": isTablet ? "none" : "auto" + }, + topic: { "--width": "65px", "--min-width": "120px", "--max-width": "120px" } + } +} diff --git a/web/components/routes/topics/list.tsx b/web/components/routes/topics/list.tsx new file mode 100644 index 00000000..dc96124b --- /dev/null +++ b/web/components/routes/topics/list.tsx @@ -0,0 +1,157 @@ +import React, { useCallback, useEffect, useMemo } from "react" +import { Primitive } from "@radix-ui/react-primitive" +import { useAccount } from "@/lib/providers/jazz-provider" +import { atom, useAtom } from "jotai" +import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" +import { TopicItem } from "./partials/topic-item" +import { useMedia } from "react-use" +import { useRouter } from "next/navigation" +import { useActiveItemScroll } from "@/hooks/use-active-item-scroll" +import { Column } from "@/components/custom/column" +import { useColumnStyles } from "./hooks/use-column-styles" +import { LaAccount, ListOfTopics, Topic, UserRoot } from "@/lib/schema" +import { LearningStateValue } from "@/lib/constants" + +interface TopicListProps { + activeItemIndex: number | null + setActiveItemIndex: React.Dispatch> + disableEnterKey: boolean +} + +interface MainTopicListProps extends TopicListProps { + me: { + root: { + topicsWantToLearn: ListOfTopics + topicsLearning: ListOfTopics + topicsLearned: ListOfTopics + } & UserRoot + } & LaAccount +} + +export interface PersonalTopic { + topic: Topic | null + learningState: LearningStateValue +} + +export const topicOpenPopoverForIdAtom = atom(null) + +export const TopicList: React.FC = ({ activeItemIndex, setActiveItemIndex, disableEnterKey }) => { + const { me } = useAccount({ root: { topicsWantToLearn: [], topicsLearning: [], topicsLearned: [] } }) + + if (!me) return null + + return ( + + ) +} + +export const MainTopicList: React.FC = ({ + me, + activeItemIndex, + setActiveItemIndex, + disableEnterKey +}) => { + const isTablet = useMedia("(max-width: 640px)") + const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) + const router = useRouter() + + const personalTopics = useMemo( + () => [ + ...me.root.topicsWantToLearn.map(topic => ({ topic, learningState: "wantToLearn" as const })), + ...me.root.topicsLearning.map(topic => ({ topic, learningState: "learning" as const })), + ...me.root.topicsLearned.map(topic => ({ topic, learningState: "learned" as const })) + ], + [me.root.topicsWantToLearn, me.root.topicsLearning, me.root.topicsLearned] + ) + + const itemCount = personalTopics.length + + const handleEnter = useCallback( + (selectedTopic: Topic) => { + router.push(`/${selectedTopic.name}`) + }, + [router] + ) + + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (isCommandPaletteOpen) return + + if (e.key === "ArrowUp" || e.key === "ArrowDown") { + e.preventDefault() + setActiveItemIndex(prevIndex => { + if (prevIndex === null) return 0 + const newIndex = e.key === "ArrowUp" ? (prevIndex - 1 + itemCount) % itemCount : (prevIndex + 1) % itemCount + return newIndex + }) + } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null && personalTopics) { + e.preventDefault() + const selectedTopic = personalTopics[activeItemIndex] + if (selectedTopic?.topic) handleEnter?.(selectedTopic.topic) + } + }, + [itemCount, isCommandPaletteOpen, activeItemIndex, setActiveItemIndex, disableEnterKey, personalTopics, handleEnter] + ) + + useEffect(() => { + window.addEventListener("keydown", handleKeyDown) + return () => window.removeEventListener("keydown", handleKeyDown) + }, [handleKeyDown]) + + return ( +
    + {!isTablet && } + +
    + ) +} + +export const ColumnHeader: React.FC = () => { + const columnStyles = useColumnStyles() + + return ( +
    + + Name + + + State + +
    + ) +} + +interface TopicListItemsProps { + personalTopics: PersonalTopic[] | null + activeItemIndex: number | null +} + +const TopicListItems: React.FC = ({ personalTopics, activeItemIndex }) => { + const setElementRef = useActiveItemScroll({ activeIndex: activeItemIndex }) + + return ( + + {personalTopics?.map( + (pt, index) => + pt.topic?.id && ( + setElementRef(el, index)} + topic={pt.topic} + learningState={pt.learningState} + isActive={index === activeItemIndex} + /> + ) + )} + + ) +} diff --git a/web/components/routes/topics/partials/topic-item.tsx b/web/components/routes/topics/partials/topic-item.tsx new file mode 100644 index 00000000..ffef13d8 --- /dev/null +++ b/web/components/routes/topics/partials/topic-item.tsx @@ -0,0 +1,158 @@ +import React, { useCallback, useMemo } from "react" +import Link from "next/link" +import { cn } from "@/lib/utils" +import { useColumnStyles } from "../hooks/use-column-styles" +import { ListOfTopics, Topic } from "@/lib/schema" +import { Column } from "@/components/custom/column" +import { Button } from "@/components/ui/button" +import { LaIcon } from "@/components/custom/la-icon" +import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover" +import { LearningStateSelectorContent } from "@/components/custom/learning-state-selector" +import { useAtom } from "jotai" +import { topicOpenPopoverForIdAtom } from "../list" +import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" +import { useAccount } from "@/lib/providers/jazz-provider" + +interface TopicItemProps { + topic: Topic + learningState: LearningStateValue + isActive: boolean +} + +export const TopicItem = React.forwardRef(({ topic, learningState, isActive }, ref) => { + const columnStyles = useColumnStyles() + const [openPopoverForId, setOpenPopoverForId] = useAtom(topicOpenPopoverForIdAtom) + const { me } = useAccount({ root: { topicsWantToLearn: [], topicsLearning: [], topicsLearned: [] } }) + + let p: { + index: number + topic?: Topic | null + learningState: LearningStateValue + } | null = null + + const wantToLearnIndex = me?.root.topicsWantToLearn.findIndex(t => t?.id === topic.id) ?? -1 + if (wantToLearnIndex !== -1) { + p = { + index: wantToLearnIndex, + topic: me?.root.topicsWantToLearn[wantToLearnIndex], + learningState: "wantToLearn" + } + } + + const learningIndex = me?.root.topicsLearning.findIndex(t => t?.id === topic.id) ?? -1 + if (learningIndex !== -1) { + p = { + index: learningIndex, + topic: me?.root.topicsLearning[learningIndex], + learningState: "learning" + } + } + + const learnedIndex = me?.root.topicsLearned.findIndex(t => t?.id === topic.id) ?? -1 + if (learnedIndex !== -1) { + p = { + index: learnedIndex, + topic: me?.root.topicsLearned[learnedIndex], + learningState: "learned" + } + } + + const selectedLearningState = useMemo(() => LEARNING_STATES.find(ls => ls.value === learningState), [learningState]) + + const handleLearningStateSelect = useCallback( + (value: string) => { + const newLearningState = value as LearningStateValue + + const topicLists: Record = { + wantToLearn: me?.root.topicsWantToLearn, + learning: me?.root.topicsLearning, + learned: me?.root.topicsLearned + } + + const removeFromList = (state: LearningStateValue, index: number) => { + topicLists[state]?.splice(index, 1) + } + + if (p) { + if (newLearningState === p.learningState) { + removeFromList(p.learningState, p.index) + return + } + removeFromList(p.learningState, p.index) + } + + topicLists[newLearningState]?.push(topic) + + setOpenPopoverForId(null) + }, + [setOpenPopoverForId, me?.root.topicsWantToLearn, me?.root.topicsLearning, me?.root.topicsLearned, p, topic] + ) + + const handlePopoverTriggerClick = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() + + setOpenPopoverForId(openPopoverForId === topic.id ? null : topic.id) + } + + return ( +
    + + + {topic.prettyName} + + + + setOpenPopoverForId(open ? topic.id : null)} + > + + + + e.stopPropagation()} + onCloseAutoFocus={e => e.preventDefault()} + > + + + + + +
    + ) +}) + +TopicItem.displayName = "TopicItem" From bf5ae100ab4048247edf6d0fbe519c8308e15c7c Mon Sep 17 00:00:00 2001 From: Aslam Date: Thu, 19 Sep 2024 22:15:58 +0700 Subject: [PATCH 117/124] fix(key): Allow Esc and Any other input event (#173) * fix(key): Allow Esc and input handler * chore: set search autoFocus on shortcut component * fix: allow enter, arrow and disable list if keyboard --- web/components/custom/Shortcut/shortcut.tsx | 1 + web/components/routes/link/list.tsx | 83 ++++++++++----------- web/hooks/use-keyboard-manager.ts | 24 ++++-- 3 files changed, 58 insertions(+), 50 deletions(-) diff --git a/web/components/custom/Shortcut/shortcut.tsx b/web/components/custom/Shortcut/shortcut.tsx index e4bd5fc6..a8918077 100644 --- a/web/components/custom/Shortcut/shortcut.tsx +++ b/web/components/custom/Shortcut/shortcut.tsx @@ -131,6 +131,7 @@ export function Shortcut() {
    = ({ activeItemIndex, setActiveItemIndex }) }, []) - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (isCommandPalettePpen || !me?.root?.personalLinks || sortedLinks.length === 0 || editId !== null) return + const { isKeyboardDisabled } = useKeyboardManager("XComponent") - if (e.key === "ArrowUp" || e.key === "ArrowDown") { - e.preventDefault() - setActiveItemIndex(prevIndex => { - if (prevIndex === null) return 0 - const newIndex = - e.key === "ArrowUp" ? Math.max(0, prevIndex - 1) : Math.min(sortedLinks.length - 1, prevIndex + 1) + useKeydownListener((e: KeyboardEvent) => { + if ( + isKeyboardDisabled || + isCommandPalettePpen || + !me?.root?.personalLinks || + sortedLinks.length === 0 || + editId !== null + ) + return - if (e.metaKey && sort === "manual") { - const linksArray = [...me.root.personalLinks] - const newLinks = arrayMove(linksArray, prevIndex, newIndex) + if (e.key === "ArrowUp" || e.key === "ArrowDown") { + e.preventDefault() + setActiveItemIndex(prevIndex => { + if (prevIndex === null) return 0 + const newIndex = + e.key === "ArrowUp" ? Math.max(0, prevIndex - 1) : Math.min(sortedLinks.length - 1, prevIndex + 1) - while (me.root.personalLinks.length > 0) { - me.root.personalLinks.pop() - } + if (e.metaKey && sort === "manual") { + const linksArray = [...me.root.personalLinks] + const newLinks = arrayMove(linksArray, prevIndex, newIndex) - newLinks.forEach(link => { - if (link) { - me.root.personalLinks.push(link) - } - }) - - updateSequences(me.root.personalLinks) + while (me.root.personalLinks.length > 0) { + me.root.personalLinks.pop() } - return newIndex - }) - } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null) { - e.preventDefault() - const activeLink = sortedLinks[activeItemIndex] - if (activeLink) { - setEditId(activeLink.id) + newLinks.forEach(link => { + if (link) { + me.root.personalLinks.push(link) + } + }) + + updateSequences(me.root.personalLinks) } + + return newIndex + }) + } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null) { + e.preventDefault() + const activeLink = sortedLinks[activeItemIndex] + if (activeLink) { + setEditId(activeLink.id) } } - - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [ - me?.root?.personalLinks, - sortedLinks, - editId, - sort, - updateSequences, - isCommandPalettePpen, - activeItemIndex, - setEditId, - setActiveItemIndex, - disableEnterKey - ]) + }) const handleDragStart = useCallback( (event: DragStartEvent) => { diff --git a/web/hooks/use-keyboard-manager.ts b/web/hooks/use-keyboard-manager.ts index bd8c34de..f73d0994 100644 --- a/web/hooks/use-keyboard-manager.ts +++ b/web/hooks/use-keyboard-manager.ts @@ -2,19 +2,31 @@ import { useAtom } from "jotai" import { useEffect, useCallback } from "react" import { keyboardDisableSourcesAtom } from "@/store/keydown-manager" +const allowedKeys = ["Escape", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Enter"] + export function useKeyboardManager(sourceId: string) { const [disableSources, setDisableSources] = useAtom(keyboardDisableSourcesAtom) useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if (disableSources.size > 0) { - event.preventDefault() + if (disableSources.has(sourceId)) { + if (allowedKeys.includes(event.key)) { + if (event.key === "Escape") { + setDisableSources(prev => { + const next = new Set(prev) + next.delete(sourceId) + return next + }) + } + } else { + event.stopPropagation() + } } } - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [disableSources]) + window.addEventListener("keydown", handleKeyDown, true) + return () => window.removeEventListener("keydown", handleKeyDown, true) + }, [disableSources, sourceId, setDisableSources]) const disableKeydown = useCallback( (disable: boolean) => { @@ -32,7 +44,7 @@ export function useKeyboardManager(sourceId: string) { [setDisableSources, sourceId] ) - const isKeyboardDisabled = disableSources.size > 0 + const isKeyboardDisabled = disableSources.has(sourceId) return { disableKeydown, isKeyboardDisabled } } From 21084cd3f383f77923ac297c189a579d8f737d65 Mon Sep 17 00:00:00 2001 From: Aslam Date: Sat, 21 Sep 2024 19:37:29 +0700 Subject: [PATCH 118/124] fix(link): Keybind, scroll behaviour, restrict drag to vertical (#176) * chore: expose scrollActiveElementIntoView * feat(utils): editable element * fix: memoize exceptionRefs, use animation frame and check editable element * fix: improve btn on mobile * chore(drps): bump framer motion version * fix(link): big fix * chore: remove comment code * feat: touch device --- web/app/custom.css | 11 + web/app/globals.css | 1 + web/components/routes/link/LinkRoute.tsx | 47 +-- web/components/routes/link/bottom-bar.tsx | 42 +-- web/components/routes/link/header.tsx | 4 +- .../routes/link/hooks/use-link-actions.ts | 12 +- web/components/routes/link/list.tsx | 271 ++++++++++-------- .../routes/link/partials/link-item.tsx | 153 +++++----- web/components/routes/page/list.tsx | 2 +- web/components/routes/topics/list.tsx | 2 +- web/hooks/use-active-item-scroll.ts | 5 +- web/hooks/use-touch-sensor.ts | 20 ++ web/lib/utils/index.ts | 18 ++ web/package.json | 251 ++++++++-------- 14 files changed, 453 insertions(+), 386 deletions(-) create mode 100644 web/app/custom.css create mode 100644 web/hooks/use-touch-sensor.ts diff --git a/web/app/custom.css b/web/app/custom.css new file mode 100644 index 00000000..3d0b3660 --- /dev/null +++ b/web/app/custom.css @@ -0,0 +1,11 @@ +:root { + --link-background-muted: hsl(0, 0%, 97.3%); + --link-border-after: hsl(0, 0%, 91%); + --link-shadow: hsl(240, 5.6%, 82.5%); +} + +.dark { + --link-background-muted: hsl(220, 6.7%, 8.8%); + --link-border-after: hsl(230, 10%, 11.8%); + --link-shadow: hsl(234.9, 27.1%, 25.3%); +} diff --git a/web/app/globals.css b/web/app/globals.css index 8bdc5be5..0b181a0d 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -73,3 +73,4 @@ } @import "./command-palette.css"; +@import "./custom.css"; diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index bfbc424a..e6cb4cef 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -1,13 +1,12 @@ "use client" -import React, { useEffect, useState, useCallback, useRef } from "react" +import React, { useState } from "react" import { LinkHeader } from "@/components/routes/link/header" import { LinkList } from "@/components/routes/link/list" import { LinkManage } from "@/components/routes/link/manage" -import { parseAsBoolean, useQueryState } from "nuqs" -import { atom, useAtom } from "jotai" +import { useQueryState } from "nuqs" +import { atom } from "jotai" import { LinkBottomBar } from "./bottom-bar" -import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { useKey } from "react-use" export const isDeleteConfirmShownAtom = atom(false) @@ -15,44 +14,9 @@ export const isDeleteConfirmShownAtom = atom(false) export function LinkRoute(): React.ReactElement { const [nuqsEditId, setNuqsEditId] = useQueryState("editId") const [activeItemIndex, setActiveItemIndex] = useState(null) - const [isInCreateMode] = useQueryState("create", parseAsBoolean) - const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) - const [isDeleteConfirmShown] = useAtom(isDeleteConfirmShownAtom) - const [disableEnterKey, setDisableEnterKey] = useState(false) - const timeoutRef = useRef(null) - - const handleCommandPaletteClose = useCallback(() => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current) - } - - setDisableEnterKey(true) - timeoutRef.current = setTimeout(() => { - setDisableEnterKey(false) - timeoutRef.current = null - }, 100) - }, []) - - useEffect(() => { - if (isDeleteConfirmShown || isCommandPaletteOpen || isInCreateMode) { - setDisableEnterKey(true) - if (timeoutRef.current) { - clearTimeout(timeoutRef.current) - timeoutRef.current = null - } - } else if (!isCommandPaletteOpen) { - handleCommandPaletteClose() - } - - return () => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current) - } - } - }, [isDeleteConfirmShown, isCommandPaletteOpen, isInCreateMode, handleCommandPaletteClose]) + const [keyboardActiveIndex, setKeyboardActiveIndex] = useState(null) useKey("Escape", () => { - setDisableEnterKey(false) setNuqsEditId(null) }) @@ -64,7 +28,8 @@ export function LinkRoute(): React.ReactElement { key={nuqsEditId} activeItemIndex={activeItemIndex} setActiveItemIndex={setActiveItemIndex} - disableEnterKey={disableEnterKey} + keyboardActiveIndex={keyboardActiveIndex} + setKeyboardActiveIndex={setKeyboardActiveIndex} /> diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index df2165ab..2dbf44c4 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -1,11 +1,11 @@ "use client" -import React, { useCallback, useEffect, useRef } from "react" +import React, { useCallback, useEffect, useMemo, useRef } from "react" import { motion, AnimatePresence } from "framer-motion" import type { icons } from "lucide-react" import { Button } from "@/components/ui/button" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" -import { cn, getShortcutKeys } from "@/lib/utils" +import { cn, getShortcutKeys, isEditableElement } from "@/lib/utils" import { LaIcon } from "@/components/custom/la-icon" import { useAtom } from "jotai" import { parseAsBoolean, useQueryState } from "nuqs" @@ -70,13 +70,13 @@ export const LinkBottomBar: React.FC = () => { const handleCreateMode = useCallback(() => { setEditId(null) - setTimeout(() => { + requestAnimationFrame(() => { setCreateMode(prev => !prev) - }, 100) + }) }, [setEditId, setCreateMode]) - useEffect(() => { - setGlobalLinkFormExceptionRefsAtom([ + const exceptionRefs = useMemo( + () => [ overlayRef, contentRef, deleteBtnRef, @@ -85,8 +85,13 @@ export const LinkBottomBar: React.FC = () => { confirmBtnRef, plusBtnRef, plusMoreBtnRef - ]) - }, [setGlobalLinkFormExceptionRefsAtom]) + ], + [] + ) + + useEffect(() => { + setGlobalLinkFormExceptionRefsAtom(exceptionRefs) + }, [setGlobalLinkFormExceptionRefsAtom, exceptionRefs]) const handleDelete = async (e: React.MouseEvent) => { if (!personalLink || !me) return @@ -122,8 +127,9 @@ export const LinkBottomBar: React.FC = () => { const handleKeydown = useCallback( (event: KeyboardEvent) => { const isCreateShortcut = event.key === "c" + const target = event.target as HTMLElement - if (isCreateShortcut) { + if (isCreateShortcut && !isEditableElement(target)) { event.preventDefault() handleCreateMode() } @@ -136,29 +142,26 @@ export const LinkBottomBar: React.FC = () => { const shortcutText = getShortcutKeys(["c"]) return ( - +
    {editId && ( - setEditId(null)} /> + setEditId(null)} aria-label="Go back" /> - + )} @@ -171,19 +174,20 @@ export const LinkBottomBar: React.FC = () => { exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.1 }} > - {createMode && } + {createMode && } {!createMode && ( s.symbol).join("")})`} ref={plusBtnRef} + aria-label="New link" /> )} )} - +
    ) } diff --git a/web/components/routes/link/header.tsx b/web/components/routes/link/header.tsx index 93d14e7c..9215fc27 100644 --- a/web/components/routes/link/header.tsx +++ b/web/components/routes/link/header.tsx @@ -42,7 +42,7 @@ export const LinkHeader = React.memo(() => { {isTablet && ( -
    +
    )} @@ -115,7 +115,7 @@ const FilterAndSort = React.memo(() => {
    - diff --git a/web/components/routes/link/hooks/use-link-actions.ts b/web/components/routes/link/hooks/use-link-actions.ts index 98fb4428..02d576db 100644 --- a/web/components/routes/link/hooks/use-link-actions.ts +++ b/web/components/routes/link/hooks/use-link-actions.ts @@ -9,18 +9,20 @@ export const useLinkActions = () => { try { const index = me.root.personalLinks.findIndex(item => item?.id === link.id) if (index === -1) { - console.error("Delete operation fail", { index, link }) - return + throw new Error(`Link with id ${link.id} not found`) } + me.root.personalLinks.splice(index, 1) + toast.success("Link deleted.", { position: "bottom-right", description: `${link.title} has been deleted.` }) - - me.root.personalLinks.splice(index, 1) } catch (error) { - toast.error("Failed to delete link") + console.error("Failed to delete link:", error) + toast.error("Failed to delete link", { + description: error instanceof Error ? error.message : "An unknown error occurred" + }) } }, []) diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 4a4f1f49..46342985 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo } from "react" +import React, { useCallback, useMemo } from "react" import { DndContext, closestCenter, @@ -8,17 +8,20 @@ import { useSensors, DragEndEvent, DragStartEvent, - UniqueIdentifier + UniqueIdentifier, + MeasuringStrategy, + TouchSensor } from "@dnd-kit/core" -import { Primitive } from "@radix-ui/react-primitive" import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from "@dnd-kit/sortable" +import type { MeasuringConfiguration } from "@dnd-kit/core" +import { restrictToVerticalAxis } from "@dnd-kit/modifiers" import { useAccount } from "@/lib/providers/jazz-provider" import { PersonalLinkLists } from "@/lib/schema/personal-link" import { useAtom } from "jotai" import { linkSortAtom } from "@/store/link" import { useKey } from "react-use" import { LinkItem } from "./partials/link-item" -import { useQueryState } from "nuqs" +import { parseAsBoolean, useQueryState } from "nuqs" import { learningStateAtom } from "./header" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { useConfirm } from "@omit/react-confirm-dialog" @@ -27,30 +30,43 @@ import { isDeleteConfirmShownAtom } from "./LinkRoute" import { useActiveItemScroll } from "@/hooks/use-active-item-scroll" import { useKeyboardManager } from "@/hooks/use-keyboard-manager" import { useKeydownListener } from "@/hooks/use-keydown-listener" +import { useTouchSensor } from "@/hooks/use-touch-sensor" interface LinkListProps { activeItemIndex: number | null setActiveItemIndex: React.Dispatch> - disableEnterKey: boolean + keyboardActiveIndex: number | null + setKeyboardActiveIndex: React.Dispatch> } -const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex, disableEnterKey }) => { - const [isCommandPalettePpen] = useAtom(commandPaletteOpenAtom) +const measuring: MeasuringConfiguration = { + droppable: { + strategy: MeasuringStrategy.Always + } +} + +const LinkList: React.FC = ({ + activeItemIndex, + setActiveItemIndex, + keyboardActiveIndex, + setKeyboardActiveIndex +}) => { + const isTouchDevice = useTouchSensor() + const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) const [, setIsDeleteConfirmShown] = useAtom(isDeleteConfirmShownAtom) const [editId, setEditId] = useQueryState("editId") + const [createMode] = useQueryState("create", parseAsBoolean) const [activeLearningState] = useAtom(learningStateAtom) const [draggingId, setDraggingId] = React.useState(null) + const [sort] = useAtom(linkSortAtom) const { deleteLink } = useLinkActions() const confirm = useConfirm() + const { me } = useAccount({ root: { personalLinks: [] } }) + const { isKeyboardDisabled } = useKeyboardManager("XComponent") - const { me } = useAccount({ - root: { personalLinks: [] } - }) const personalLinks = useMemo(() => me?.root?.personalLinks || [], [me?.root?.personalLinks]) - const [sort] = useAtom(linkSortAtom) - const filteredLinks = useMemo( () => personalLinks.filter(link => { @@ -70,9 +86,9 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex ) const sensors = useSensors( - useSensor(PointerSensor, { + useSensor(isTouchDevice ? TouchSensor : PointerSensor, { activationConstraint: { - distance: 8 + distance: 5 } }), useSensor(KeyboardSensor, { @@ -80,51 +96,6 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex }) ) - useKey( - event => (event.metaKey || event.ctrlKey) && event.key === "Backspace", - async () => { - if (activeItemIndex !== null) { - setIsDeleteConfirmShown(true) - const activeLink = sortedLinks[activeItemIndex] - if (activeLink) { - const result = await confirm({ - title: `Delete "${activeLink.title}"?`, - description: "This action cannot be undone.", - alertDialogTitle: { - className: "text-base" - }, - cancelButton: { - variant: "outline" - }, - confirmButton: { - variant: "destructive" - } - }) - - if (result) { - if (!me) return - deleteLink(me, activeLink) - - setIsDeleteConfirmShown(false) - } else { - setIsDeleteConfirmShown(false) - } - } - } - }, - { event: "keydown" } - ) - - // on mounted, if editId is set, set activeItemIndex to the index of the item with the editId - useEffect(() => { - if (editId) { - const index = sortedLinks.findIndex(link => link?.id === editId) - if (index !== -1) { - setActiveItemIndex(index) - } - } - }, [editId, sortedLinks, setActiveItemIndex]) - const updateSequences = useCallback((links: PersonalLinkLists) => { links.forEach((link, index) => { if (link) { @@ -133,62 +104,105 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex }) }, []) - const { isKeyboardDisabled } = useKeyboardManager("XComponent") + const handleDeleteLink = useCallback(async () => { + if (activeItemIndex === null) return + setIsDeleteConfirmShown(true) + const activeLink = sortedLinks[activeItemIndex] + if (!activeLink || !me) return + + const result = await confirm({ + title: `Delete "${activeLink.title}"?`, + description: "This action cannot be undone.", + alertDialogTitle: { className: "text-base" }, + cancelButton: { variant: "outline" }, + confirmButton: { variant: "destructive" } + }) + + if (result) { + deleteLink(me, activeLink) + } + setIsDeleteConfirmShown(false) + }, [activeItemIndex, sortedLinks, me, confirm, deleteLink, setIsDeleteConfirmShown]) + + useKey(event => (event.metaKey || event.ctrlKey) && event.key === "Backspace", handleDeleteLink, { event: "keydown" }) useKeydownListener((e: KeyboardEvent) => { if ( isKeyboardDisabled || - isCommandPalettePpen || + isCommandPaletteOpen || !me?.root?.personalLinks || sortedLinks.length === 0 || - editId !== null + editId !== null || + e.defaultPrevented ) return - if (e.key === "ArrowUp" || e.key === "ArrowDown") { - e.preventDefault() - setActiveItemIndex(prevIndex => { - if (prevIndex === null) return 0 - const newIndex = - e.key === "ArrowUp" ? Math.max(0, prevIndex - 1) : Math.min(sortedLinks.length - 1, prevIndex + 1) + switch (e.key) { + case "ArrowUp": + case "ArrowDown": + e.preventDefault() + setActiveItemIndex(prevIndex => { + if (prevIndex === null) return 0 - if (e.metaKey && sort === "manual") { - const linksArray = [...me.root.personalLinks] - const newLinks = arrayMove(linksArray, prevIndex, newIndex) + const newIndex = + e.key === "ArrowUp" ? Math.max(0, prevIndex - 1) : Math.min(sortedLinks.length - 1, prevIndex + 1) - while (me.root.personalLinks.length > 0) { - me.root.personalLinks.pop() + if (e.metaKey && sort === "manual") { + const linksArray = [...me.root.personalLinks] + const newLinks = arrayMove(linksArray, prevIndex, newIndex) + + while (me.root.personalLinks.length > 0) { + me.root.personalLinks.pop() + } + + newLinks.forEach(link => { + if (link) { + me.root.personalLinks.push(link) + } + }) + + updateSequences(me.root.personalLinks) } - newLinks.forEach(link => { - if (link) { - me.root.personalLinks.push(link) - } - }) + setKeyboardActiveIndex(newIndex) - updateSequences(me.root.personalLinks) - } - - return newIndex - }) - } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null) { - e.preventDefault() - const activeLink = sortedLinks[activeItemIndex] - if (activeLink) { - setEditId(activeLink.id) - } + return newIndex + }) + break + case "Home": + e.preventDefault() + setActiveItemIndex(0) + break + case "End": + e.preventDefault() + setActiveItemIndex(sortedLinks.length - 1) + break } }) const handleDragStart = useCallback( (event: DragStartEvent) => { if (sort !== "manual") return + if (!me) return + const { active } = event + const activeIndex = me?.root.personalLinks.findIndex(item => item?.id === active.id) + + if (activeIndex === -1) { + console.error("Drag operation fail", { activeIndex, activeId: active.id }) + return + } + + setActiveItemIndex(activeIndex) setDraggingId(active.id) }, - [sort] + [sort, me, setActiveItemIndex] ) + const handleDragCancel = useCallback(() => { + setDraggingId(null) + }, []) + const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event @@ -226,51 +240,64 @@ const LinkList: React.FC = ({ activeItemIndex, setActiveItemIndex }) updateSequences(me.root.personalLinks) - setActiveItemIndex(newIndex) } catch (error) { console.error("Error during link reordering:", error) } } + setActiveItemIndex(null) setDraggingId(null) } - const setElementRef = useActiveItemScroll({ activeIndex: activeItemIndex }) + const { setElementRef } = useActiveItemScroll({ activeIndex: keyboardActiveIndex }) return ( - - +
    item?.id || "") || []} strategy={verticalListSortingStrategy}> -
      - {sortedLinks.map( - (linkItem, index) => - linkItem && ( - setElementRef(el, index)} - /> - ) - )} -
    +
    +
    +
    + {sortedLinks.map( + (linkItem, index) => + linkItem && ( + { + if (editId !== null || draggingId !== null || createMode) { + return undefined + } + + setKeyboardActiveIndex(null) + setActiveItemIndex(index) + }} + index={index} + onItemSelected={link => setEditId(link.id)} + data-keyboard-active={keyboardActiveIndex === index} + ref={el => setElementRef(el, index)} + /> + ) + )} +
    +
    +
    - - +
    +
    ) } diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index 9a430d8b..1b5c4816 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -15,34 +15,35 @@ import { cn, ensureUrlProtocol } from "@/lib/utils" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { linkOpenPopoverForIdAtom } from "@/store/link" -interface LinkItemProps extends React.HTMLAttributes { +interface LinkItemProps extends React.HTMLAttributes { personalLink: PersonalLink disabled?: boolean - isEditing: boolean + editId: string | null setEditId: (id: string | null) => void - isDragging: boolean isActive: boolean setActiveItemIndex: (index: number | null) => void index: number + onItemSelected?: (personalLink: PersonalLink) => void } -export const LinkItem = React.forwardRef( - ({ personalLink, disabled, isEditing, setEditId, isDragging, isActive, setActiveItemIndex, index }, ref) => { +export const LinkItem = React.forwardRef( + ( + { personalLink, disabled, editId, setEditId, isActive, setActiveItemIndex, index, onItemSelected, ...props }, + ref + ) => { const [openPopoverForId, setOpenPopoverForId] = useAtom(linkOpenPopoverForIdAtom) const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: personalLink.id, disabled }) const style = useMemo( () => ({ transform: CSS.Transform.toString(transform), - transition, - pointerEvents: isDragging ? "none" : "auto" + transition }), - [transform, transition, isDragging] + [transform, transition] ) const handleSuccess = useCallback(() => setEditId(null), [setEditId]) const handleOnClose = useCallback(() => setEditId(null), [setEditId]) - const handleRowDoubleClick = useCallback(() => setEditId(personalLink.id), [setEditId, personalLink.id]) const selectedLearningState = useMemo( () => LEARNING_STATES.find(ls => ls.value === personalLink.learningState), @@ -58,14 +59,14 @@ export const LinkItem = React.forwardRef( [personalLink, setOpenPopoverForId] ) - if (isEditing) { + if (editId === personalLink.id) { return ( {}} /> ) } return ( -
  • { setNodeRef(node) if (typeof ref === "function") { @@ -75,61 +76,73 @@ export const LinkItem = React.forwardRef( } }} style={style as React.CSSProperties} + {...props} {...attributes} {...listeners} tabIndex={0} - onFocus={() => setActiveItemIndex(index)} - onBlur={() => setActiveItemIndex(null)} - className={cn( - "relative cursor-default outline-none", - "grid grid-cols-[auto_1fr_auto] items-center gap-x-2 py-2 max-lg:px-4 sm:px-5 sm:py-2", - { - "bg-muted-foreground/5": isActive, - "hover:bg-muted/50": !isActive + onDoubleClick={() => onItemSelected?.(personalLink)} + aria-disabled={disabled} + aria-selected={isActive} + data-disabled={disabled} + data-active={isActive} + className="w-full overflow-visible border-b-[0.5px] border-transparent outline-none data-[active='true']:bg-[var(--link-background-muted)] data-[keyboard-active='true']:focus-visible:shadow-[var(--link-shadow)_0px_0px_0px_1px_inset]" + onKeyDown={e => { + if (e.key === "Enter") { + e.preventDefault() + onItemSelected?.(personalLink) } - )} - onDoubleClick={handleRowDoubleClick} + }} > - setOpenPopoverForId(open ? personalLink.id : null)} - > - - - - e.preventDefault()} - > - - - - -
    - {personalLink.icon && ( - {personalLink.title} +
    -

    {personalLink.title}

    + > + setOpenPopoverForId(open ? personalLink.id : null)} + > + + + + + + + + +
    +
    + {personalLink.icon && ( + {personalLink.title} + )} +

    {personalLink.title}

    +
    {personalLink.url && (
    )}
    + +
    + +
    + {personalLink.topic && ( + + {personalLink.topic.prettyName} + + )} +
    -
    - {personalLink.topic && ( - - {personalLink.topic.prettyName} - - )} -
    -
  • +
    +
    ) } ) diff --git a/web/components/routes/page/list.tsx b/web/components/routes/page/list.tsx index 62be86d2..4afc59f5 100644 --- a/web/components/routes/page/list.tsx +++ b/web/components/routes/page/list.tsx @@ -89,7 +89,7 @@ interface PageListItemsProps { } const PageListItems: React.FC = ({ personalPages, activeItemIndex }) => { - const setElementRef = useActiveItemScroll({ activeIndex: activeItemIndex }) + const { setElementRef } = useActiveItemScroll({ activeIndex: activeItemIndex }) return ( = ({ personalTopics, activeItemIndex }) => { - const setElementRef = useActiveItemScroll({ activeIndex: activeItemIndex }) + const { setElementRef } = useActiveItemScroll({ activeIndex: activeItemIndex }) return ( (options: ActiveItemSc const scrollActiveElementIntoView = useCallback((index: number) => { const activeElement = elementRefs.current[index] - activeElement?.scrollIntoView({ block: "nearest" }) + activeElement?.focus() + // activeElement?.scrollIntoView({ block: "nearest" }) }, []) useEffect(() => { @@ -26,5 +27,5 @@ export function useActiveItemScroll(options: ActiveItemSc elementRefs.current[index] = element }, []) - return setElementRef + return { setElementRef, scrollActiveElementIntoView } } diff --git a/web/hooks/use-touch-sensor.ts b/web/hooks/use-touch-sensor.ts new file mode 100644 index 00000000..437aff41 --- /dev/null +++ b/web/hooks/use-touch-sensor.ts @@ -0,0 +1,20 @@ +import { useState, useEffect } from "react" + +export function useTouchSensor() { + const [isTouchDevice, setIsTouchDevice] = useState(false) + + useEffect(() => { + const detectTouch = () => { + setIsTouchDevice("ontouchstart" in window || navigator.maxTouchPoints > 0) + } + + detectTouch() + window.addEventListener("touchstart", detectTouch, false) + + return () => { + window.removeEventListener("touchstart", detectTouch) + } + }, []) + + return isTouchDevice +} diff --git a/web/lib/utils/index.ts b/web/lib/utils/index.ts index 7e4dfbe3..9be23460 100644 --- a/web/lib/utils/index.ts +++ b/web/lib/utils/index.ts @@ -34,6 +34,24 @@ export function shuffleArray(array: T[]): T[] { return shuffled } +export const isEditableElement = (element: HTMLElement): boolean => { + if (element.isContentEditable) { + return true + } + + const tagName = element.tagName.toLowerCase() + const editableTags = ["input", "textarea", "select", "option"] + + if (editableTags.includes(tagName)) { + return true + } + + const role = element.getAttribute("role") + const editableRoles = ["textbox", "combobox", "listbox"] + + return role ? editableRoles.includes(role) : false +} + export * from "./urls" export * from "./slug" export * from "./keyboard" diff --git a/web/package.json b/web/package.json index 67037938..619a7b73 100644 --- a/web/package.json +++ b/web/package.json @@ -1,127 +1,128 @@ { - "name": "web", - "version": "0.1.0", - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "test": "jest" - }, - "dependencies": { - "@clerk/nextjs": "^5.6.0", - "@dnd-kit/core": "^6.1.0", - "@dnd-kit/sortable": "^8.0.0", - "@hookform/resolvers": "^3.9.0", - "@nothing-but/force-graph": "^0.9.5", - "@nothing-but/utils": "^0.16.0", - "@omit/react-confirm-dialog": "^1.1.5", - "@omit/react-fancy-switch": "^0.1.3", - "@radix-ui/react-alert-dialog": "^1.1.1", - "@radix-ui/react-avatar": "^1.1.0", - "@radix-ui/react-checkbox": "^1.1.1", - "@radix-ui/react-context-menu": "^2.2.1", - "@radix-ui/react-dialog": "^1.1.1", - "@radix-ui/react-dismissable-layer": "^1.1.0", - "@radix-ui/react-dropdown-menu": "^2.1.1", - "@radix-ui/react-focus-scope": "^1.1.0", - "@radix-ui/react-icons": "^1.3.0", - "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-popover": "^1.1.1", - "@radix-ui/react-scroll-area": "^1.1.0", - "@radix-ui/react-select": "^2.1.1", - "@radix-ui/react-separator": "^1.1.0", - "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-switch": "^1.1.0", - "@radix-ui/react-toggle": "^1.1.0", - "@radix-ui/react-toggle-group": "^1.1.0", - "@radix-ui/react-tooltip": "^1.1.2", - "@sentry/nextjs": "^8.30.0", - "@tanstack/react-virtual": "^3.10.8", - "@tiptap/core": "^2.7.2", - "@tiptap/extension-blockquote": "^2.7.2", - "@tiptap/extension-bold": "^2.7.2", - "@tiptap/extension-bullet-list": "^2.7.2", - "@tiptap/extension-code": "^2.7.2", - "@tiptap/extension-code-block-lowlight": "^2.7.2", - "@tiptap/extension-color": "^2.7.2", - "@tiptap/extension-document": "^2.7.2", - "@tiptap/extension-dropcursor": "^2.7.2", - "@tiptap/extension-focus": "^2.7.2", - "@tiptap/extension-gapcursor": "^2.7.2", - "@tiptap/extension-hard-break": "^2.7.2", - "@tiptap/extension-heading": "^2.7.2", - "@tiptap/extension-history": "^2.7.2", - "@tiptap/extension-horizontal-rule": "^2.7.2", - "@tiptap/extension-image": "^2.7.2", - "@tiptap/extension-italic": "^2.7.2", - "@tiptap/extension-link": "^2.7.2", - "@tiptap/extension-list-item": "^2.7.2", - "@tiptap/extension-ordered-list": "^2.7.2", - "@tiptap/extension-paragraph": "^2.7.2", - "@tiptap/extension-placeholder": "^2.7.2", - "@tiptap/extension-strike": "^2.7.2", - "@tiptap/extension-task-item": "^2.7.2", - "@tiptap/extension-task-list": "^2.7.2", - "@tiptap/extension-text": "^2.7.2", - "@tiptap/extension-typography": "^2.7.2", - "@tiptap/pm": "^2.7.2", - "@tiptap/react": "^2.7.2", - "@tiptap/starter-kit": "^2.7.2", - "@tiptap/suggestion": "^2.7.2", - "axios": "^1.7.7", - "cheerio": "1.0.0", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.1", - "cmdk": "^1.0.0", - "date-fns": "^3.6.0", - "framer-motion": "^11.5.5", - "geist": "^1.3.1", - "jazz-browser-auth-clerk": "0.7.35-guest-auth.5", - "jazz-react": "0.7.35-guest-auth.5", - "jazz-react-auth-clerk": "0.7.35-guest-auth.5", - "jazz-tools": "0.7.35-guest-auth.5", - "jotai": "^2.9.3", - "lowlight": "^3.1.0", - "lucide-react": "^0.429.0", - "next": "14.2.10", - "next-themes": "^0.3.0", - "nuqs": "^1.19.1", - "react": "^18.3.1", - "react-day-picker": "^8.10.1", - "react-dom": "^18.3.1", - "react-hook-form": "^7.53.0", - "react-textarea-autosize": "^8.5.3", - "react-use": "^17.5.1", - "ronin": "^4.3.1", - "slugify": "^1.6.6", - "sonner": "^1.5.0", - "streaming-markdown": "^0.0.14", - "tailwind-merge": "^2.5.2", - "tailwindcss-animate": "^1.0.7", - "vaul": "^0.9.4", - "zod": "^3.23.8", - "zsa": "^0.6.0", - "zsa-react": "^0.2.2" - }, - "devDependencies": { - "@ronin/learn-anything": "^0.0.0-3452357373461", - "@testing-library/jest-dom": "^6.5.0", - "@testing-library/react": "^16.0.1", - "@types/jest": "^29.5.13", - "@types/node": "^22.5.5", - "@types/react": "^18.3.7", - "@types/react-dom": "^18.3.0", - "dotenv": "^16.4.5", - "eslint": "^8.57.1", - "eslint-config-next": "14.2.5", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "postcss": "^8.4.47", - "prettier-plugin-tailwindcss": "^0.6.6", - "tailwindcss": "^3.4.12", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", - "typescript": "^5.6.2" - } + "name": "web", + "version": "0.1.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "jest" + }, + "dependencies": { + "@clerk/nextjs": "^5.6.0", + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/modifiers": "^7.0.0", + "@dnd-kit/sortable": "^8.0.0", + "@hookform/resolvers": "^3.9.0", + "@nothing-but/force-graph": "^0.9.5", + "@nothing-but/utils": "^0.16.0", + "@omit/react-confirm-dialog": "^1.1.5", + "@omit/react-fancy-switch": "^0.1.3", + "@radix-ui/react-alert-dialog": "^1.1.1", + "@radix-ui/react-avatar": "^1.1.0", + "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-context-menu": "^2.2.1", + "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-dismissable-layer": "^1.1.0", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-focus-scope": "^1.1.0", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-scroll-area": "^1.1.0", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-toggle-group": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.2", + "@sentry/nextjs": "^8.30.0", + "@tanstack/react-virtual": "^3.10.8", + "@tiptap/core": "^2.7.2", + "@tiptap/extension-blockquote": "^2.7.2", + "@tiptap/extension-bold": "^2.7.2", + "@tiptap/extension-bullet-list": "^2.7.2", + "@tiptap/extension-code": "^2.7.2", + "@tiptap/extension-code-block-lowlight": "^2.7.2", + "@tiptap/extension-color": "^2.7.2", + "@tiptap/extension-document": "^2.7.2", + "@tiptap/extension-dropcursor": "^2.7.2", + "@tiptap/extension-focus": "^2.7.2", + "@tiptap/extension-gapcursor": "^2.7.2", + "@tiptap/extension-hard-break": "^2.7.2", + "@tiptap/extension-heading": "^2.7.2", + "@tiptap/extension-history": "^2.7.2", + "@tiptap/extension-horizontal-rule": "^2.7.2", + "@tiptap/extension-image": "^2.7.2", + "@tiptap/extension-italic": "^2.7.2", + "@tiptap/extension-link": "^2.7.2", + "@tiptap/extension-list-item": "^2.7.2", + "@tiptap/extension-ordered-list": "^2.7.2", + "@tiptap/extension-paragraph": "^2.7.2", + "@tiptap/extension-placeholder": "^2.7.2", + "@tiptap/extension-strike": "^2.7.2", + "@tiptap/extension-task-item": "^2.7.2", + "@tiptap/extension-task-list": "^2.7.2", + "@tiptap/extension-text": "^2.7.2", + "@tiptap/extension-typography": "^2.7.2", + "@tiptap/pm": "^2.7.2", + "@tiptap/react": "^2.7.2", + "@tiptap/starter-kit": "^2.7.2", + "@tiptap/suggestion": "^2.7.2", + "axios": "^1.7.7", + "cheerio": "1.0.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.0", + "date-fns": "^3.6.0", + "framer-motion": "^11.5.6", + "geist": "^1.3.1", + "jazz-browser-auth-clerk": "0.7.35-guest-auth.5", + "jazz-react": "0.7.35-guest-auth.5", + "jazz-react-auth-clerk": "0.7.35-guest-auth.5", + "jazz-tools": "0.7.35-guest-auth.5", + "jotai": "^2.10.0", + "lowlight": "^3.1.0", + "lucide-react": "^0.429.0", + "next": "14.2.10", + "next-themes": "^0.3.0", + "nuqs": "^1.19.1", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.53.0", + "react-textarea-autosize": "^8.5.3", + "react-use": "^17.5.1", + "ronin": "^4.3.1", + "slugify": "^1.6.6", + "sonner": "^1.5.0", + "streaming-markdown": "^0.0.14", + "tailwind-merge": "^2.5.2", + "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.4", + "zod": "^3.23.8", + "zsa": "^0.6.0", + "zsa-react": "^0.2.3" + }, + "devDependencies": { + "@ronin/learn-anything": "0.0.0-3452357373461", + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^16.0.1", + "@types/jest": "^29.5.13", + "@types/node": "^22.5.5", + "@types/react": "^18.3.8", + "@types/react-dom": "^18.3.0", + "dotenv": "^16.4.5", + "eslint": "^8.57.1", + "eslint-config-next": "14.2.5", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "postcss": "^8.4.47", + "prettier-plugin-tailwindcss": "^0.6.6", + "tailwindcss": "^3.4.12", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "^5.6.2" + } } From 867478d55cf0ac30512430591671ffabee67cae0 Mon Sep 17 00:00:00 2001 From: Aslam Date: Mon, 23 Sep 2024 23:16:02 +0700 Subject: [PATCH 119/124] fix: Link, Pages, Topic, Hook and Others (#178) * chore: remove useKeyDownListener * chore: remove react-use, update jazz version and add query string * chore: update jazz version * chore: use simple mac or win utils code * feat(util): add isTextInput * feat(hooks): all needed hooks * fix: link bunch stuff * fix: page bunch stuff * chore: bunch update for custom component * chore: use throttle from internal hook * chore: topic bunch stuff * chore: update layout * fix: truncate content header of topic detail --- package.json | 5 +- web/app/(pages)/layout.tsx | 5 +- web/components/custom/Shortcut/shortcut.tsx | 8 ++ .../command-palette/command-palette.tsx | 13 -- web/components/custom/content-header.tsx | 10 +- .../custom/global-keyboard-handler.tsx | 130 +++++++++++++++++ .../custom/global-keydown-handler.tsx | 63 -------- .../custom/learning-state-selector.tsx | 32 +++-- .../custom/sidebar/partial/page-section.tsx | 5 +- .../sidebar/partial/profile-section.tsx | 6 +- web/components/custom/sidebar/sidebar.tsx | 10 +- web/components/custom/topic-selector.tsx | 7 +- web/components/la-editor/la-editor.tsx | 40 +----- web/components/routes/link/LinkRoute.tsx | 20 +-- web/components/routes/link/bottom-bar.tsx | 42 ++---- web/components/routes/link/header.tsx | 6 +- web/components/routes/link/list.tsx | 135 +++++++----------- web/components/routes/link/manage.tsx | 6 +- .../routes/link/partials/form/link-form.tsx | 40 +++--- .../routes/link/partials/link-item.tsx | 41 +++--- .../routes/page/detail/PageDetailRoute.tsx | 2 +- .../routes/page/hooks/use-column-styles.ts | 2 +- web/components/routes/page/list.tsx | 2 +- .../routes/page/partials/page-item.tsx | 2 +- web/components/routes/public/Autocomplete.tsx | 41 +++--- .../routes/topics/detail/Header.tsx | 13 +- .../topics/detail/partials/link-item.tsx | 7 +- .../routes/topics/hooks/use-column-styles.ts | 2 +- web/components/routes/topics/list.tsx | 2 +- .../routes/topics/partials/topic-item.tsx | 1 - web/hooks/use-active-item-scroll.ts | 4 +- web/hooks/use-event-listener.ts | 33 +++++ web/hooks/use-is-mounted.ts | 19 +++ web/hooks/use-key-down.ts | 79 ++++++++++ web/hooks/use-keyboard-manager.ts | 13 +- web/hooks/use-keydown-listener.ts | 21 --- web/hooks/use-media.ts | 23 +++ web/hooks/use-on-click-outside.ts | 28 ++++ web/hooks/use-throttle.ts | 34 +++++ web/hooks/use-touch-sensor.ts | 9 +- web/lib/utils/index.ts | 24 ++-- web/lib/utils/keyboard.ts | 87 ++++++----- web/package.json | 10 +- 43 files changed, 616 insertions(+), 466 deletions(-) create mode 100644 web/components/custom/global-keyboard-handler.tsx delete mode 100644 web/components/custom/global-keydown-handler.tsx create mode 100644 web/hooks/use-event-listener.ts create mode 100644 web/hooks/use-is-mounted.ts create mode 100644 web/hooks/use-key-down.ts delete mode 100644 web/hooks/use-keydown-listener.ts create mode 100644 web/hooks/use-media.ts create mode 100644 web/hooks/use-on-click-outside.ts create mode 100644 web/hooks/use-throttle.ts diff --git a/package.json b/package.json index 3d94d0f8..42acf914 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,10 @@ "@clerk/themes": "^2.1.30", "@tauri-apps/cli": "^2.0.0-rc.16", "@tauri-apps/plugin-fs": "^2.0.0-rc.2", - "jazz-nodejs": "0.7.35-guest-auth.5", - "react-icons": "^5.3.0" + "jazz-nodejs": "0.8.0" }, "devDependencies": { - "bun-types": "^1.1.28" + "bun-types": "^1.1.29" }, "prettier": { "plugins": [ diff --git a/web/app/(pages)/layout.tsx b/web/app/(pages)/layout.tsx index 3b897096..6f3decf8 100644 --- a/web/app/(pages)/layout.tsx +++ b/web/app/(pages)/layout.tsx @@ -3,7 +3,7 @@ import { Sidebar } from "@/components/custom/sidebar/sidebar" import { CommandPalette } from "@/components/custom/command-palette/command-palette" import { LearnAnythingOnboarding } from "@/components/custom/learn-anything-onboarding" import { Shortcut } from "@/components/custom/Shortcut/shortcut" -import { GlobalKeydownHandler } from "@/components/custom/global-keydown-handler" +import { GlobalKeyboardHandler } from "@/components/custom/global-keyboard-handler" export const viewport: Viewport = { width: "device-width, shrink-to-fit=no", @@ -16,8 +16,7 @@ export default function PageLayout({ children }: { children: React.ReactNode })
    - - + diff --git a/web/components/custom/Shortcut/shortcut.tsx b/web/components/custom/Shortcut/shortcut.tsx index a8918077..070d446d 100644 --- a/web/components/custom/Shortcut/shortcut.tsx +++ b/web/components/custom/Shortcut/shortcut.tsx @@ -38,6 +38,14 @@ const SHORTCUTS: ShortcutSection[] = [ { label: "Go to page", keys: ["G"], then: ["P"] }, { label: "Go to topic", keys: ["G"], then: ["T"] } ] + }, + { + title: "Links", + shortcuts: [{ label: "Create new link", keys: ["c"] }] + }, + { + title: "Pages", + shortcuts: [{ label: "Create new page", keys: ["p"] }] } ] diff --git a/web/components/custom/command-palette/command-palette.tsx b/web/components/custom/command-palette/command-palette.tsx index 11feb4dd..db76b28b 100644 --- a/web/components/custom/command-palette/command-palette.tsx +++ b/web/components/custom/command-palette/command-palette.tsx @@ -11,7 +11,6 @@ import { searchSafeRegExp } from "@/lib/utils" import { GraphNode } from "@/components/routes/public/PublicHomeRoute" import { useCommandActions } from "./hooks/use-command-actions" import { atom, useAtom } from "jotai" -import { useKeydownListener } from "@/hooks/use-keydown-listener" const graph_data_promise = import("@/components/routes/public/graph-data.json").then(a => a.default) @@ -40,18 +39,6 @@ export function RealCommandPalette() { const raw_graph_data = React.use(graph_data_promise) as GraphNode[] - const handleKeydown = React.useCallback( - (e: KeyboardEvent) => { - if (e.key === "k" && (e.metaKey || e.ctrlKey)) { - e.preventDefault() - setOpen(prev => !prev) - } - }, - [setOpen] - ) - - useKeydownListener(handleKeydown) - const bounce = React.useCallback(() => { if (dialogRef.current) { dialogRef.current.style.transform = "scale(0.99) translateX(-50%)" diff --git a/web/components/custom/content-header.tsx b/web/components/custom/content-header.tsx index 500e0254..550f3459 100644 --- a/web/components/custom/content-header.tsx +++ b/web/components/custom/content-header.tsx @@ -1,12 +1,12 @@ "use client" -import React from "react" +import * as React from "react" import { Button } from "../ui/button" -import { PanelLeftIcon } from "lucide-react" import { useAtom } from "jotai" import { isCollapseAtom, toggleCollapseAtom } from "@/store/sidebar" -import { useMedia } from "react-use" +import { useMedia } from "@/hooks/use-media" import { cn } from "@/lib/utils" +import { LaIcon } from "./la-icon" type ContentHeaderProps = Omit, "title"> @@ -15,7 +15,7 @@ export const ContentHeader = React.forwardRef { className="text-primary/60" onClick={handleClick} > - +
    ) diff --git a/web/components/custom/global-keyboard-handler.tsx b/web/components/custom/global-keyboard-handler.tsx new file mode 100644 index 00000000..4c7aa6ba --- /dev/null +++ b/web/components/custom/global-keyboard-handler.tsx @@ -0,0 +1,130 @@ +"use client" + +import { useState, useEffect, useCallback } from "react" +import { useKeyDown, KeyFilter, Options } from "@/hooks/use-key-down" +import { useAccountOrGuest } from "@/lib/providers/jazz-provider" +import { useRouter } from "next/navigation" +import queryString from "query-string" +import { usePageActions } from "../routes/page/hooks/use-page-actions" +import { useAuth } from "@clerk/nextjs" +import { isModKey } from "@/lib/utils" +import { useAtom } from "jotai" +import { commandPaletteOpenAtom } from "./command-palette/command-palette" + +type RegisterKeyDownProps = { + trigger: KeyFilter + handler: (event: KeyboardEvent) => void + options?: Options +} + +function RegisterKeyDown({ trigger, handler, options }: RegisterKeyDownProps) { + useKeyDown(trigger, handler, options) + return null +} + +type Sequence = { + [key: string]: string +} + +const SEQUENCES: Sequence = { + GL: "/links", + GP: "/pages", + GT: "/topics" +} + +const MAX_SEQUENCE_TIME = 1000 + +export function GlobalKeyboardHandler() { + const [openCommandPalette, setOpenCommandPalette] = useAtom(commandPaletteOpenAtom) + const [sequence, setSequence] = useState([]) + const { signOut } = useAuth() + const router = useRouter() + const { me } = useAccountOrGuest() + const { newPage } = usePageActions() + + const resetSequence = useCallback(() => { + setSequence([]) + }, []) + + const checkSequence = useCallback(() => { + const sequenceStr = sequence.join("") + const route = SEQUENCES[sequenceStr] + + if (route) { + console.log(`Navigating to ${route}...`) + router.push(route) + resetSequence() + } + }, [sequence, router, resetSequence]) + + const goToNewLink = useCallback( + (event: KeyboardEvent) => { + if (event.metaKey || event.altKey) { + return + } + + router.push(`/links?${queryString.stringify({ create: true })}`) + }, + [router] + ) + + const goToNewPage = useCallback( + (event: KeyboardEvent) => { + if (event.metaKey || event.altKey) { + return + } + + if (!me || me._type === "Anonymous") { + return + } + + const page = newPage(me) + + router.push(`/pages/${page.id}`) + }, + [me, newPage, router] + ) + + useKeyDown( + e => e.altKey && e.shiftKey && e.code === "KeyQ", + () => { + signOut() + } + ) + + useKeyDown( + () => true, + e => { + const key = e.key.toUpperCase() + setSequence(prev => [...prev, key]) + } + ) + + useKeyDown( + e => isModKey(e) && e.code === "KeyK", + e => { + e.preventDefault() + setOpenCommandPalette(prev => !prev) + } + ) + + useEffect(() => { + checkSequence() + + const timeoutId = setTimeout(() => { + resetSequence() + }, MAX_SEQUENCE_TIME) + + return () => clearTimeout(timeoutId) + }, [sequence, checkSequence, resetSequence]) + + return ( + me && + me._type !== "Anonymous" && ( + <> + + + + ) + ) +} diff --git a/web/components/custom/global-keydown-handler.tsx b/web/components/custom/global-keydown-handler.tsx deleted file mode 100644 index 527162cd..00000000 --- a/web/components/custom/global-keydown-handler.tsx +++ /dev/null @@ -1,63 +0,0 @@ -"use client" - -import { useState, useEffect, useCallback } from "react" -import { useKeydownListener } from "@/hooks/use-keydown-listener" -import { useAuth } from "@clerk/nextjs" -import { useRouter } from "next/navigation" - -type Sequence = { - [key: string]: string -} - -const SEQUENCES: Sequence = { - GL: "/links", - GP: "/pages", - GT: "/topics" -} - -const MAX_SEQUENCE_TIME = 1000 - -export function GlobalKeydownHandler() { - const [sequence, setSequence] = useState([]) - const { signOut } = useAuth() - const router = useRouter() - - const resetSequence = useCallback(() => { - setSequence([]) - }, []) - - const checkSequence = useCallback(() => { - const sequenceStr = sequence.join("") - const route = SEQUENCES[sequenceStr] - - if (route) { - console.log(`Navigating to ${route}...`) - router.push(route) - resetSequence() - } - }, [sequence, router, resetSequence]) - - useKeydownListener((e: KeyboardEvent) => { - // Check for logout shortcut - if (e.altKey && e.shiftKey && e.code === "KeyQ") { - signOut() - return - } - - // Key sequence handling - const key = e.key.toUpperCase() - setSequence(prev => [...prev, key]) - }) - - useEffect(() => { - checkSequence() - - const timeoutId = setTimeout(() => { - resetSequence() - }, MAX_SEQUENCE_TIME) - - return () => clearTimeout(timeoutId) - }, [sequence, checkSequence, resetSequence]) - - return null -} diff --git a/web/components/custom/learning-state-selector.tsx b/web/components/custom/learning-state-selector.tsx index 5b467b86..6f00af5c 100644 --- a/web/components/custom/learning-state-selector.tsx +++ b/web/components/custom/learning-state-selector.tsx @@ -8,6 +8,7 @@ import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { linkLearningStateSelectorAtom } from "@/store/link" import { Command, CommandInput, CommandList, CommandItem, CommandGroup } from "@/components/ui/command" import { ScrollArea } from "@/components/ui/scroll-area" +import type { icons } from "lucide-react" interface LearningStateSelectorProps { showSearch?: boolean @@ -16,6 +17,7 @@ interface LearningStateSelectorProps { value?: string onChange: (value: LearningStateValue) => void className?: string + defaultIcon?: keyof typeof icons } export const LearningStateSelector: React.FC = ({ @@ -24,7 +26,8 @@ export const LearningStateSelector: React.FC = ({ searchPlaceholder = "Search state...", value, onChange, - className + className, + defaultIcon }) => { const [isLearningStateSelectorOpen, setIsLearningStateSelectorOpen] = useAtom(linkLearningStateSelectorAtom) const selectedLearningState = useMemo(() => LEARNING_STATES.find(ls => ls.value === value), [value]) @@ -44,21 +47,24 @@ export const LearningStateSelector: React.FC = ({ variant="secondary" className={cn("gap-x-2 text-sm", className)} > - {selectedLearningState?.icon && ( - - )} - - {selectedLearningState?.label || defaultLabel} - + {selectedLearningState?.icon || + (defaultIcon && ( + + ))} + + {selectedLearningState?.label || + (defaultLabel && ( + + {selectedLearningState?.label || defaultLabel} + + ))} - e.preventDefault()} - > + = ({ pageCount, isActi isActive ? "bg-accent text-accent-foreground" : "hover:bg-accent hover:text-accent-foreground" )} > - +

    Pages {pageCount > 0 && {pageCount}} diff --git a/web/components/custom/sidebar/partial/profile-section.tsx b/web/components/custom/sidebar/partial/profile-section.tsx index 2396c7ac..09c73624 100644 --- a/web/components/custom/sidebar/partial/profile-section.tsx +++ b/web/components/custom/sidebar/partial/profile-section.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useState } from "react" +import * as React from "react" import { SignInButton, useAuth, useUser } from "@clerk/nextjs" import { useAtom } from "jotai" import Link from "next/link" @@ -27,13 +27,13 @@ import { useKeyboardManager } from "@/hooks/use-keyboard-manager" export const ProfileSection: React.FC = () => { const { user, isSignedIn } = useUser() const { signOut } = useAuth() - const [menuOpen, setMenuOpen] = useState(false) + const [menuOpen, setMenuOpen] = React.useState(false) const pathname = usePathname() const [, setShowShortcut] = useAtom(showShortcutAtom) const { disableKeydown } = useKeyboardManager("profileSection") - useEffect(() => { + React.useEffect(() => { disableKeydown(menuOpen) }, [menuOpen, disableKeydown]) diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index 3679128a..64a4f9ae 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -3,9 +3,8 @@ import * as React from "react" import Link from "next/link" import { usePathname } from "next/navigation" -import { useMedia } from "react-use" +import { useMedia } from "@/hooks/use-media" import { useAtom } from "jotai" -import { SearchIcon } from "lucide-react" import { Logo } from "@/components/custom/logo" import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" @@ -15,6 +14,7 @@ import { PageSection } from "./partial/page-section" import { TopicSection } from "./partial/topic-section" import { ProfileSection } from "./partial/profile-section" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" +import { LaIcon } from "../la-icon" interface SidebarContextType { isCollapsed: boolean @@ -98,7 +98,7 @@ const LogoAndSearch: React.FC = React.memo(() => { type="button" className="text-primary/60 flex w-20 items-center justify-start py-4 pl-2" > - + )} @@ -119,11 +119,11 @@ const SidebarContent: React.FC = React.memo(() => {

    -
    +
    {me._type === "Account" && } - {me._type === "Account" && } {me._type === "Account" && } + {me._type === "Account" && }
    diff --git a/web/components/custom/topic-selector.tsx b/web/components/custom/topic-selector.tsx index 8a1f6691..2807b1fc 100644 --- a/web/components/custom/topic-selector.tsx +++ b/web/components/custom/topic-selector.tsx @@ -79,12 +79,7 @@ export const TopicSelector = forwardRef( - e.preventDefault()} - > + {group?.root.topics && ( , "value"> { output?: "html" | "json" | "text" @@ -25,10 +25,6 @@ export interface LAEditorRef { editor: Editor | null } -interface CustomEditor extends Editor { - previousBlockCount?: number -} - export const LAEditor = React.forwardRef( ( { @@ -46,32 +42,13 @@ export const LAEditor = React.forwardRef( }, ref ) => { - const [content, setContent] = React.useState(value) - const throttledContent = useThrottleFn(defaultContent => defaultContent, throttleDelay, [content]) - const [lastThrottledContent, setLastThrottledContent] = React.useState(throttledContent) + const throttledSetValue = useThrottle((value: Content) => onUpdate?.(value), throttleDelay) const handleUpdate = React.useCallback( (editor: Editor) => { - const newContent = getOutput(editor, output) - setContent(newContent) - - const customEditor = editor as CustomEditor - const json = customEditor.getJSON() - - if (json.content && Array.isArray(json.content)) { - const currentBlockCount = json.content.length - - if ( - typeof customEditor.previousBlockCount === "number" && - currentBlockCount > customEditor.previousBlockCount - ) { - onNewBlock?.(newContent) - } - - customEditor.previousBlockCount = currentBlockCount - } + throttledSetValue(getOutput(editor, output)) }, - [output, onNewBlock] + [output, throttledSetValue] ) const editor = useEditor({ @@ -96,13 +73,6 @@ export const LAEditor = React.forwardRef( } }) - React.useEffect(() => { - if (lastThrottledContent !== throttledContent) { - setLastThrottledContent(throttledContent) - onUpdate?.(throttledContent!) - } - }, [throttledContent, lastThrottledContent, onUpdate]) - React.useImperativeHandle( ref, () => ({ diff --git a/web/components/routes/link/LinkRoute.tsx b/web/components/routes/link/LinkRoute.tsx index e6cb4cef..8b31f071 100644 --- a/web/components/routes/link/LinkRoute.tsx +++ b/web/components/routes/link/LinkRoute.tsx @@ -1,36 +1,20 @@ "use client" -import React, { useState } from "react" +import * as React from "react" import { LinkHeader } from "@/components/routes/link/header" import { LinkList } from "@/components/routes/link/list" import { LinkManage } from "@/components/routes/link/manage" -import { useQueryState } from "nuqs" import { atom } from "jotai" import { LinkBottomBar } from "./bottom-bar" -import { useKey } from "react-use" export const isDeleteConfirmShownAtom = atom(false) export function LinkRoute(): React.ReactElement { - const [nuqsEditId, setNuqsEditId] = useQueryState("editId") - const [activeItemIndex, setActiveItemIndex] = useState(null) - const [keyboardActiveIndex, setKeyboardActiveIndex] = useState(null) - - useKey("Escape", () => { - setNuqsEditId(null) - }) - return ( <> - + ) diff --git a/web/components/routes/link/bottom-bar.tsx b/web/components/routes/link/bottom-bar.tsx index 2dbf44c4..aa4a812b 100644 --- a/web/components/routes/link/bottom-bar.tsx +++ b/web/components/routes/link/bottom-bar.tsx @@ -1,11 +1,11 @@ "use client" -import React, { useCallback, useEffect, useMemo, useRef } from "react" +import * as React from "react" import { motion, AnimatePresence } from "framer-motion" import type { icons } from "lucide-react" import { Button } from "@/components/ui/button" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" -import { cn, getShortcutKeys, isEditableElement } from "@/lib/utils" +import { cn, getShortcutKeys } from "@/lib/utils" import { LaIcon } from "@/components/custom/la-icon" import { useAtom } from "jotai" import { parseAsBoolean, useQueryState } from "nuqs" @@ -15,7 +15,6 @@ import { PersonalLink } from "@/lib/schema" import { ID } from "jazz-tools" import { globalLinkFormExceptionRefsAtom } from "./partials/form/link-form" import { useLinkActions } from "./hooks/use-link-actions" -import { useKeydownListener } from "@/hooks/use-keydown-listener" interface ToolbarButtonProps extends React.ComponentPropsWithoutRef { icon: keyof typeof icons @@ -55,27 +54,27 @@ export const LinkBottomBar: React.FC = () => { const { me } = useAccount({ root: { personalLinks: [] } }) const personalLink = useCoState(PersonalLink, editId as ID) - const cancelBtnRef = useRef(null) - const confirmBtnRef = useRef(null) - const overlayRef = useRef(null) - const contentRef = useRef(null) + const cancelBtnRef = React.useRef(null) + const confirmBtnRef = React.useRef(null) + const overlayRef = React.useRef(null) + const contentRef = React.useRef(null) - const deleteBtnRef = useRef(null) - const editMoreBtnRef = useRef(null) - const plusBtnRef = useRef(null) - const plusMoreBtnRef = useRef(null) + const deleteBtnRef = React.useRef(null) + const editMoreBtnRef = React.useRef(null) + const plusBtnRef = React.useRef(null) + const plusMoreBtnRef = React.useRef(null) const { deleteLink } = useLinkActions() const confirm = useConfirm() - const handleCreateMode = useCallback(() => { + const handleCreateMode = React.useCallback(() => { setEditId(null) requestAnimationFrame(() => { setCreateMode(prev => !prev) }) }, [setEditId, setCreateMode]) - const exceptionRefs = useMemo( + const exceptionRefs = React.useMemo( () => [ overlayRef, contentRef, @@ -89,7 +88,7 @@ export const LinkBottomBar: React.FC = () => { [] ) - useEffect(() => { + React.useEffect(() => { setGlobalLinkFormExceptionRefsAtom(exceptionRefs) }, [setGlobalLinkFormExceptionRefsAtom, exceptionRefs]) @@ -124,21 +123,6 @@ export const LinkBottomBar: React.FC = () => { } } - const handleKeydown = useCallback( - (event: KeyboardEvent) => { - const isCreateShortcut = event.key === "c" - const target = event.target as HTMLElement - - if (isCreateShortcut && !isEditableElement(target)) { - event.preventDefault() - handleCreateMode() - } - }, - [handleCreateMode] - ) - - useKeydownListener(handleKeydown) - const shortcutText = getShortcutKeys(["c"]) return ( diff --git a/web/components/routes/link/header.tsx b/web/components/routes/link/header.tsx index 9215fc27..bc6ecbe1 100644 --- a/web/components/routes/link/header.tsx +++ b/web/components/routes/link/header.tsx @@ -1,10 +1,9 @@ "use client" import * as React from "react" -import { ListFilterIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header" -import { useMedia } from "react-use" +import { useMedia } from "@/hooks/use-media" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" @@ -15,6 +14,7 @@ import { LEARNING_STATES } from "@/lib/constants" import { useQueryState, parseAsStringLiteral } from "nuqs" import { FancySwitch } from "@omit/react-fancy-switch" import { cn } from "@/lib/utils" +import { LaIcon } from "@/components/custom/la-icon" const ALL_STATES = [{ label: "All", value: "all", icon: "List", className: "text-foreground" }, ...LEARNING_STATES] const ALL_STATES_STRING = ALL_STATES.map(ls => ls.value) @@ -116,7 +116,7 @@ const FilterAndSort = React.memo(() => { diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 46342985..9710c83b 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from "react" +import * as React from "react" import { DndContext, closestCenter, @@ -19,25 +19,18 @@ import { useAccount } from "@/lib/providers/jazz-provider" import { PersonalLinkLists } from "@/lib/schema/personal-link" import { useAtom } from "jotai" import { linkSortAtom } from "@/store/link" -import { useKey } from "react-use" import { LinkItem } from "./partials/link-item" import { parseAsBoolean, useQueryState } from "nuqs" import { learningStateAtom } from "./header" -import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { useConfirm } from "@omit/react-confirm-dialog" import { useLinkActions } from "./hooks/use-link-actions" import { isDeleteConfirmShownAtom } from "./LinkRoute" import { useActiveItemScroll } from "@/hooks/use-active-item-scroll" -import { useKeyboardManager } from "@/hooks/use-keyboard-manager" -import { useKeydownListener } from "@/hooks/use-keydown-listener" import { useTouchSensor } from "@/hooks/use-touch-sensor" +import { useKeyDown } from "@/hooks/use-key-down" +import { isModKey } from "@/lib/utils" -interface LinkListProps { - activeItemIndex: number | null - setActiveItemIndex: React.Dispatch> - keyboardActiveIndex: number | null - setKeyboardActiveIndex: React.Dispatch> -} +interface LinkListProps {} const measuring: MeasuringConfiguration = { droppable: { @@ -45,14 +38,11 @@ const measuring: MeasuringConfiguration = { } } -const LinkList: React.FC = ({ - activeItemIndex, - setActiveItemIndex, - keyboardActiveIndex, - setKeyboardActiveIndex -}) => { +const LinkList: React.FC = () => { const isTouchDevice = useTouchSensor() - const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) + const lastActiveIndexRef = React.useRef(null) + const [activeItemIndex, setActiveItemIndex] = React.useState(null) + const [keyboardActiveIndex, setKeyboardActiveIndex] = React.useState(null) const [, setIsDeleteConfirmShown] = useAtom(isDeleteConfirmShownAtom) const [editId, setEditId] = useQueryState("editId") const [createMode] = useQueryState("create", parseAsBoolean) @@ -63,11 +53,10 @@ const LinkList: React.FC = ({ const { deleteLink } = useLinkActions() const confirm = useConfirm() const { me } = useAccount({ root: { personalLinks: [] } }) - const { isKeyboardDisabled } = useKeyboardManager("XComponent") - const personalLinks = useMemo(() => me?.root?.personalLinks || [], [me?.root?.personalLinks]) + const personalLinks = React.useMemo(() => me?.root?.personalLinks || [], [me?.root?.personalLinks]) - const filteredLinks = useMemo( + const filteredLinks = React.useMemo( () => personalLinks.filter(link => { if (activeLearningState === "all") return true @@ -77,7 +66,7 @@ const LinkList: React.FC = ({ [personalLinks, activeLearningState] ) - const sortedLinks = useMemo( + const sortedLinks = React.useMemo( () => sort === "title" ? [...filteredLinks].sort((a, b) => (a?.title || "").localeCompare(b?.title || "")) @@ -85,9 +74,21 @@ const LinkList: React.FC = ({ [filteredLinks, sort] ) + React.useEffect(() => { + if (editId !== null) { + const index = sortedLinks.findIndex(link => link?.id === editId) + if (index !== -1) { + lastActiveIndexRef.current = index + setActiveItemIndex(index) + setKeyboardActiveIndex(index) + } + } + }, [editId, setActiveItemIndex, setKeyboardActiveIndex, sortedLinks]) + const sensors = useSensors( useSensor(isTouchDevice ? TouchSensor : PointerSensor, { activationConstraint: { + ...(isTouchDevice ? { delay: 100, tolerance: 5 } : {}), distance: 5 } }), @@ -96,7 +97,7 @@ const LinkList: React.FC = ({ }) ) - const updateSequences = useCallback((links: PersonalLinkLists) => { + const updateSequences = React.useCallback((links: PersonalLinkLists) => { links.forEach((link, index) => { if (link) { link.sequence = index @@ -104,7 +105,7 @@ const LinkList: React.FC = ({ }) }, []) - const handleDeleteLink = useCallback(async () => { + const handleDeleteLink = React.useCallback(async () => { if (activeItemIndex === null) return setIsDeleteConfirmShown(true) const activeLink = sortedLinks[activeItemIndex] @@ -124,63 +125,31 @@ const LinkList: React.FC = ({ setIsDeleteConfirmShown(false) }, [activeItemIndex, sortedLinks, me, confirm, deleteLink, setIsDeleteConfirmShown]) - useKey(event => (event.metaKey || event.ctrlKey) && event.key === "Backspace", handleDeleteLink, { event: "keydown" }) + useKeyDown(e => isModKey(e) && e.key === "Backspace", handleDeleteLink) - useKeydownListener((e: KeyboardEvent) => { - if ( - isKeyboardDisabled || - isCommandPaletteOpen || - !me?.root?.personalLinks || - sortedLinks.length === 0 || - editId !== null || - e.defaultPrevented - ) - return + const next = () => Math.min((activeItemIndex ?? 0) + 1, sortedLinks.length - 1) - switch (e.key) { - case "ArrowUp": + const prev = () => Math.max((activeItemIndex ?? 0) - 1, 0) + + const handleKeyDown = (ev: KeyboardEvent) => { + switch (ev.key) { case "ArrowDown": - e.preventDefault() - setActiveItemIndex(prevIndex => { - if (prevIndex === null) return 0 - - const newIndex = - e.key === "ArrowUp" ? Math.max(0, prevIndex - 1) : Math.min(sortedLinks.length - 1, prevIndex + 1) - - if (e.metaKey && sort === "manual") { - const linksArray = [...me.root.personalLinks] - const newLinks = arrayMove(linksArray, prevIndex, newIndex) - - while (me.root.personalLinks.length > 0) { - me.root.personalLinks.pop() - } - - newLinks.forEach(link => { - if (link) { - me.root.personalLinks.push(link) - } - }) - - updateSequences(me.root.personalLinks) - } - - setKeyboardActiveIndex(newIndex) - - return newIndex - }) - break - case "Home": - e.preventDefault() - setActiveItemIndex(0) - break - case "End": - e.preventDefault() - setActiveItemIndex(sortedLinks.length - 1) + ev.preventDefault() + ev.stopPropagation() + setActiveItemIndex(next()) + setKeyboardActiveIndex(next()) break + case "ArrowUp": + ev.preventDefault() + ev.stopPropagation() + setActiveItemIndex(prev()) + setKeyboardActiveIndex(prev()) } - }) + } - const handleDragStart = useCallback( + useKeyDown(() => true, handleKeyDown) + + const handleDragStart = React.useCallback( (event: DragStartEvent) => { if (sort !== "manual") return if (!me) return @@ -199,7 +168,7 @@ const LinkList: React.FC = ({ [sort, me, setActiveItemIndex] ) - const handleDragCancel = useCallback(() => { + const handleDragCancel = React.useCallback(() => { setDraggingId(null) }, []) @@ -249,7 +218,9 @@ const LinkList: React.FC = ({ setDraggingId(null) } - const { setElementRef } = useActiveItemScroll({ activeIndex: keyboardActiveIndex }) + const { setElementRef } = useActiveItemScroll({ + activeIndex: keyboardActiveIndex + }) return ( = ({ measuring={measuring} modifiers={[restrictToVerticalAxis]} > -
    +
    item?.id || "") || []} strategy={verticalListSortingStrategy}>
    @@ -274,9 +245,7 @@ const LinkList: React.FC = ({ isActive={activeItemIndex === index} personalLink={linkItem} editId={editId} - setEditId={setEditId} disabled={sort !== "manual" || editId !== null} - setActiveItemIndex={setActiveItemIndex} onPointerMove={() => { if (editId !== null || draggingId !== null || createMode) { return undefined @@ -285,6 +254,12 @@ const LinkList: React.FC = ({ setKeyboardActiveIndex(null) setActiveItemIndex(index) }} + onFormClose={() => { + setEditId(null) + setActiveItemIndex(lastActiveIndexRef.current) + setKeyboardActiveIndex(lastActiveIndexRef.current) + console.log(keyboardActiveIndex) + }} index={index} onItemSelected={link => setEditId(link.id)} data-keyboard-active={keyboardActiveIndex === index} diff --git a/web/components/routes/link/manage.tsx b/web/components/routes/link/manage.tsx index 438ea906..4884ef1c 100644 --- a/web/components/routes/link/manage.tsx +++ b/web/components/routes/link/manage.tsx @@ -1,7 +1,6 @@ "use client" import React from "react" -import { useKey } from "react-use" import { LinkForm } from "./partials/form/link-form" import { motion, AnimatePresence } from "framer-motion" import { parseAsBoolean, useQueryState } from "nuqs" @@ -12,9 +11,6 @@ const LinkManage: React.FC = () => { const [createMode, setCreateMode] = useQueryState("create", parseAsBoolean) const handleFormClose = () => setCreateMode(false) - const handleFormFail = () => {} - - useKey("Escape", handleFormClose) return ( @@ -25,7 +21,7 @@ const LinkManage: React.FC = () => { exit={{ height: 0, opacity: 0 }} transition={{ duration: 0.1 }} > - + )} diff --git a/web/components/routes/link/partials/form/link-form.tsx b/web/components/routes/link/partials/form/link-form.tsx index 3fbce3d7..a74b610e 100644 --- a/web/components/routes/link/partials/form/link-form.tsx +++ b/web/components/routes/link/partials/form/link-form.tsx @@ -19,6 +19,7 @@ import { FormField, FormItem, FormLabel } from "@/components/ui/form" import { LearningStateSelector } from "@/components/custom/learning-state-selector" import { TopicSelector, topicSelectorAtom } from "@/components/custom/topic-selector" import { JAZZ_GLOBAL_GROUP_ID } from "@/lib/constants" +import { useOnClickOutside } from "@/hooks/use-on-click-outside" export const globalLinkFormExceptionRefsAtom = atom[]>([]) @@ -78,26 +79,16 @@ export const LinkForm: React.FC = ({ [exceptionsRefs, globalExceptionRefs] ) - React.useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - const isClickInsideForm = formRef.current && formRef.current.contains(event.target as Node) - - const isClickInsideExceptions = allExceptionRefs.some((ref, index) => { - const isInside = ref.current && ref.current.contains(event.target as Node) - return isInside - }) - - if (!isClickInsideForm && !istopicSelectorOpen && !islearningStateSelectorOpen && !isClickInsideExceptions) { - onClose?.() - } + useOnClickOutside(formRef, event => { + if ( + !istopicSelectorOpen && + !islearningStateSelectorOpen && + !allExceptionRefs.some(ref => ref.current?.contains(event.target as Node)) + ) { + console.log("clicking outside") + onClose?.() } - - document.addEventListener("mousedown", handleClickOutside) - - return () => { - document.removeEventListener("mousedown", handleClickOutside) - } - }, [islearningStateSelectorOpen, istopicSelectorOpen, allExceptionRefs, onClose]) + }) React.useEffect(() => { if (selectedLink) { @@ -193,7 +184,15 @@ export const LinkForm: React.FC = ({ const canSubmit = form.formState.isValid && !form.formState.isSubmitting return ( -
    +
    { + if (e.key === "Escape") { + handleCancel() + } + }} + >
    @@ -213,7 +212,6 @@ export const LinkForm: React.FC = ({ { - // toggle, if already selected set undefined form.setValue("learningState", field.value === value ? undefined : value) }} showSearch={false} diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index 1b5c4816..8340aac4 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from "react" +import * as React from "react" import Image from "next/image" import Link from "next/link" import { useAtom } from "jotai" @@ -19,22 +19,18 @@ interface LinkItemProps extends React.HTMLAttributes { personalLink: PersonalLink disabled?: boolean editId: string | null - setEditId: (id: string | null) => void isActive: boolean - setActiveItemIndex: (index: number | null) => void index: number onItemSelected?: (personalLink: PersonalLink) => void + onFormClose?: () => void } export const LinkItem = React.forwardRef( - ( - { personalLink, disabled, editId, setEditId, isActive, setActiveItemIndex, index, onItemSelected, ...props }, - ref - ) => { + ({ personalLink, disabled, editId, isActive, index, onItemSelected, onFormClose, ...props }, ref) => { const [openPopoverForId, setOpenPopoverForId] = useAtom(linkOpenPopoverForIdAtom) const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: personalLink.id, disabled }) - const style = useMemo( + const style = React.useMemo( () => ({ transform: CSS.Transform.toString(transform), transition @@ -42,15 +38,12 @@ export const LinkItem = React.forwardRef( [transform, transition] ) - const handleSuccess = useCallback(() => setEditId(null), [setEditId]) - const handleOnClose = useCallback(() => setEditId(null), [setEditId]) - - const selectedLearningState = useMemo( + const selectedLearningState = React.useMemo( () => LEARNING_STATES.find(ls => ls.value === personalLink.learningState), [personalLink.learningState] ) - const handleLearningStateSelect = useCallback( + const handleLearningStateSelect = React.useCallback( (value: string) => { const learningState = value as LearningStateValue personalLink.learningState = personalLink.learningState === learningState ? undefined : learningState @@ -59,10 +52,19 @@ export const LinkItem = React.forwardRef( [personalLink, setOpenPopoverForId] ) + const handleKeyDown = React.useCallback( + (ev: React.KeyboardEvent) => { + if (ev.key === "Enter") { + ev.preventDefault() + ev.stopPropagation() + onItemSelected?.(personalLink) + } + }, + [personalLink, onItemSelected] + ) + if (editId === personalLink.id) { - return ( - {}} /> - ) + return {}} /> } return ( @@ -86,12 +88,7 @@ export const LinkItem = React.forwardRef( data-disabled={disabled} data-active={isActive} className="w-full overflow-visible border-b-[0.5px] border-transparent outline-none data-[active='true']:bg-[var(--link-background-muted)] data-[keyboard-active='true']:focus-visible:shadow-[var(--link-shadow)_0px_0px_0px_1px_inset]" - onKeyDown={e => { - if (e.key === "Enter") { - e.preventDefault() - onItemSelected?.(personalLink) - } - }} + onKeyDown={handleKeyDown} >
    { const isTablet = useMedia("(max-width: 640px)") diff --git a/web/components/routes/page/list.tsx b/web/components/routes/page/list.tsx index 4afc59f5..cd085729 100644 --- a/web/components/routes/page/list.tsx +++ b/web/components/routes/page/list.tsx @@ -4,7 +4,7 @@ import { useAccount } from "@/lib/providers/jazz-provider" import { useAtom } from "jotai" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { PageItem } from "./partials/page-item" -import { useMedia } from "react-use" +import { useMedia } from "@/hooks/use-media" import { useColumnStyles } from "./hooks/use-column-styles" import { PersonalPage, PersonalPageLists } from "@/lib/schema" import { useRouter } from "next/navigation" diff --git a/web/components/routes/page/partials/page-item.tsx b/web/components/routes/page/partials/page-item.tsx index 002a01e1..5a0d1af1 100644 --- a/web/components/routes/page/partials/page-item.tsx +++ b/web/components/routes/page/partials/page-item.tsx @@ -3,7 +3,7 @@ import Link from "next/link" import { cn } from "@/lib/utils" import { PersonalPage } from "@/lib/schema" import { Badge } from "@/components/ui/badge" -import { useMedia } from "react-use" +import { useMedia } from "@/hooks/use-media" import { useColumnStyles } from "../hooks/use-column-styles" import { format } from "date-fns" import { Column } from "@/components/custom/column" diff --git a/web/components/routes/public/Autocomplete.tsx b/web/components/routes/public/Autocomplete.tsx index 6cb80f60..25e49199 100644 --- a/web/components/routes/public/Autocomplete.tsx +++ b/web/components/routes/public/Autocomplete.tsx @@ -1,9 +1,9 @@ -import React, { useState, useRef, useCallback, useMemo, useEffect } from "react" +import * as React from "react" import { Command, CommandGroup, CommandItem, CommandList } from "@/components/ui/command" import { Command as CommandPrimitive } from "cmdk" import { motion, AnimatePresence } from "framer-motion" import { cn, searchSafeRegExp, shuffleArray } from "@/lib/utils" -import { useMountedState } from "react-use" +import { useIsMounted } from "@/hooks/use-is-mounted" interface GraphNode { name: string @@ -18,16 +18,16 @@ interface AutocompleteProps { } export function Autocomplete({ topics = [], onSelect, onInputChange }: AutocompleteProps): JSX.Element { - const inputRef = useRef(null) - const [open, setOpen] = useState(false) - const isMounted = useMountedState() - const [inputValue, setInputValue] = useState("") - const [hasInteracted, setHasInteracted] = useState(false) - const [showDropdown, setShowDropdown] = useState(false) + const inputRef = React.useRef(null) + const [, setOpen] = React.useState(false) + const isMounted = useIsMounted() + const [inputValue, setInputValue] = React.useState("") + const [hasInteracted, setHasInteracted] = React.useState(false) + const [showDropdown, setShowDropdown] = React.useState(false) - const initialShuffledTopics = useMemo(() => shuffleArray(topics).slice(0, 5), [topics]) + const initialShuffledTopics = React.useMemo(() => shuffleArray(topics).slice(0, 5), [topics]) - const filteredTopics = useMemo(() => { + const filteredTopics = React.useMemo(() => { if (!inputValue) { return initialShuffledTopics } @@ -44,7 +44,7 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl .slice(0, 10) }, [inputValue, topics, initialShuffledTopics]) - const handleSelect = useCallback( + const handleSelect = React.useCallback( (topic: GraphNode) => { setOpen(false) onSelect(topic.name) @@ -52,7 +52,7 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl [onSelect] ) - const handleInputChange = useCallback( + const handleInputChange = React.useCallback( (value: string) => { setInputValue(value) setShowDropdown(true) @@ -62,34 +62,27 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl [onInputChange] ) - const handleFocus = useCallback(() => { + const handleFocus = React.useCallback(() => { setHasInteracted(true) }, []) - const handleClick = useCallback(() => { + const handleClick = React.useCallback(() => { setShowDropdown(true) setHasInteracted(true) }, []) - const commandKey = useMemo(() => { + const commandKey = React.useMemo(() => { return filteredTopics .map(topic => `${topic.name}:${topic.prettyName}:${topic.connectedTopics.join(",")}`) .join("__") }, [filteredTopics]) - useEffect(() => { + React.useEffect(() => { if (inputRef.current && isMounted() && hasInteracted) { inputRef.current.focus() } }, [commandKey, isMounted, hasInteracted]) - const animationProps = { - initial: { opacity: 0, y: -10 }, - animate: { opacity: 1, y: 0 }, - exit: { opacity: 0, y: -10 }, - transition: { duration: 0.1 } - } - return (
    diff --git a/web/components/routes/topics/detail/Header.tsx b/web/components/routes/topics/detail/Header.tsx index 0987e9a0..1349271b 100644 --- a/web/components/routes/topics/detail/Header.tsx +++ b/web/components/routes/topics/detail/Header.tsx @@ -8,6 +8,7 @@ import { useAccountOrGuest } from "@/lib/providers/jazz-provider" import { LearningStateValue } from "@/lib/constants" import { useClerk } from "@clerk/nextjs" import { usePathname } from "next/navigation" +import { useMedia } from "@/hooks/use-media" interface TopicDetailHeaderProps { topic: Topic @@ -16,6 +17,7 @@ interface TopicDetailHeaderProps { export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic }: TopicDetailHeaderProps) { const clerk = useClerk() const pathname = usePathname() + const isMobile = useMedia("(max-width: 770px)") const { me } = useAccountOrGuest({ root: { topicsWantToLearn: [], @@ -90,20 +92,19 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic } return ( -
    +
    -
    - {topic.prettyName} +
    +

    {topic.prettyName}

    -
    - ) diff --git a/web/components/routes/topics/detail/partials/link-item.tsx b/web/components/routes/topics/detail/partials/link-item.tsx index 77964333..b232b425 100644 --- a/web/components/routes/topics/detail/partials/link-item.tsx +++ b/web/components/routes/topics/detail/partials/link-item.tsx @@ -151,12 +151,7 @@ export const LinkItem = React.memo( )} - e.preventDefault()} - > + { const isTablet = useMedia("(max-width: 640px)") diff --git a/web/components/routes/topics/list.tsx b/web/components/routes/topics/list.tsx index 09f6c488..4d951498 100644 --- a/web/components/routes/topics/list.tsx +++ b/web/components/routes/topics/list.tsx @@ -4,7 +4,7 @@ import { useAccount } from "@/lib/providers/jazz-provider" import { atom, useAtom } from "jotai" import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { TopicItem } from "./partials/topic-item" -import { useMedia } from "react-use" +import { useMedia } from "@/hooks/use-media" import { useRouter } from "next/navigation" import { useActiveItemScroll } from "@/hooks/use-active-item-scroll" import { Column } from "@/components/custom/column" diff --git a/web/components/routes/topics/partials/topic-item.tsx b/web/components/routes/topics/partials/topic-item.tsx index ffef13d8..05aec78d 100644 --- a/web/components/routes/topics/partials/topic-item.tsx +++ b/web/components/routes/topics/partials/topic-item.tsx @@ -139,7 +139,6 @@ export const TopicItem = React.forwardRef(({ top side="bottom" align="end" onClick={e => e.stopPropagation()} - onCloseAutoFocus={e => e.preventDefault()} > (options: ActiveItemSc const { activeIndex } = options const elementRefs = useRef>([]) - const scrollActiveElementIntoView = useCallback((index: number) => { + const scrollActiveElementIntoView = (index: number) => { const activeElement = elementRefs.current[index] activeElement?.focus() // activeElement?.scrollIntoView({ block: "nearest" }) - }, []) + } useEffect(() => { if (activeIndex !== null) { diff --git a/web/hooks/use-event-listener.ts b/web/hooks/use-event-listener.ts new file mode 100644 index 00000000..6bb50a22 --- /dev/null +++ b/web/hooks/use-event-listener.ts @@ -0,0 +1,33 @@ +import * as React from "react" + +type EventMap = WindowEventMap & HTMLElementEventMap & VisualViewportEventMap + +export function useEventListener< + K extends keyof EventMap, + T extends Window | HTMLElement | VisualViewport | null = Window +>( + eventName: K, + handler: (event: EventMap[K]) => void, + element: T = window as unknown as T, // Cast to `unknown` first, then `T` + options: AddEventListenerOptions = {} +) { + const savedHandler = React.useRef<(event: EventMap[K]) => void>() + const { capture, passive, once } = options + + React.useEffect(() => { + savedHandler.current = handler + }, [handler]) + + React.useEffect(() => { + const isSupported = element && element.addEventListener + if (!isSupported) return + + const eventListener = (event: EventMap[K]) => savedHandler.current?.(event) + + const opts = { capture, passive, once } + element.addEventListener(eventName, eventListener as EventListener, opts) + return () => { + element.removeEventListener(eventName, eventListener as EventListener, opts) + } + }, [eventName, element, capture, passive, once]) +} diff --git a/web/hooks/use-is-mounted.ts b/web/hooks/use-is-mounted.ts new file mode 100644 index 00000000..334b1a17 --- /dev/null +++ b/web/hooks/use-is-mounted.ts @@ -0,0 +1,19 @@ +import * as React from "react" + +/** + * Hook to check if component is still mounted + * + * @returns {boolean} true if the component is mounted, false otherwise + */ +export function useIsMounted() { + const isMounted = React.useRef(false) + + React.useEffect(() => { + isMounted.current = true + return () => { + isMounted.current = false + } + }, []) + + return React.useCallback(() => isMounted.current, []) +} diff --git a/web/hooks/use-key-down.ts b/web/hooks/use-key-down.ts new file mode 100644 index 00000000..ac42fed6 --- /dev/null +++ b/web/hooks/use-key-down.ts @@ -0,0 +1,79 @@ +import { isModKey, isTextInput } from "@/lib/utils" +import * as React from "react" + +type Callback = (event: KeyboardEvent) => void + +export type KeyFilter = ((event: KeyboardEvent) => boolean) | string + +export type Options = { + allowInInput?: boolean +} + +type RegisteredCallback = { + callback: Callback + options?: Options +} + +// Registered keyboard event callbacks +let callbacks: RegisteredCallback[] = [] + +// Track if IME input suggestions are open so we can ignore keydown shortcuts +// in this case, they should never be triggered from mobile keyboards. +let imeOpen = false + +// Based on implementation in react-use +// https://github.com/streamich/react-use/blob/master/src/useKey.ts#L15-L22 +const createKeyPredicate = (keyFilter: KeyFilter) => + typeof keyFilter === "function" + ? keyFilter + : typeof keyFilter === "string" + ? (event: KeyboardEvent) => event.key === keyFilter + : keyFilter + ? (_event: KeyboardEvent) => true + : (_event: KeyboardEvent) => false + +export function useKeyDown(key: KeyFilter, fn: Callback, options?: Options): void { + const predicate = createKeyPredicate(key) + + React.useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (predicate(event)) { + fn(event) + } + } + + callbacks.push({ + callback: handler, + options + }) + + return () => { + callbacks = callbacks.filter(cb => cb.callback !== handler) + } + }, [fn, predicate, options]) +} + +window.addEventListener("keydown", event => { + if (imeOpen) { + return + } + + // reverse so that the last registered callbacks get executed first + for (const registered of callbacks.reverse()) { + if (event.defaultPrevented === true) { + break + } + + if (!isTextInput(event.target as HTMLElement) || registered.options?.allowInInput || isModKey(event)) { + registered.callback(event) + } + } +}) + +window.addEventListener("compositionstart", () => { + imeOpen = true +}) + +window.addEventListener("compositionend", () => { + imeOpen = false +}) diff --git a/web/hooks/use-keyboard-manager.ts b/web/hooks/use-keyboard-manager.ts index f73d0994..076f483d 100644 --- a/web/hooks/use-keyboard-manager.ts +++ b/web/hooks/use-keyboard-manager.ts @@ -2,7 +2,18 @@ import { useAtom } from "jotai" import { useEffect, useCallback } from "react" import { keyboardDisableSourcesAtom } from "@/store/keydown-manager" -const allowedKeys = ["Escape", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Enter"] +const allowedKeys = [ + "Escape", + "ArrowUp", + "ArrowDown", + "ArrowLeft", + "ArrowRight", + "Enter", + "Tab", + "Backspace", + "Home", + "End" +] export function useKeyboardManager(sourceId: string) { const [disableSources, setDisableSources] = useAtom(keyboardDisableSourcesAtom) diff --git a/web/hooks/use-keydown-listener.ts b/web/hooks/use-keydown-listener.ts deleted file mode 100644 index 888f140e..00000000 --- a/web/hooks/use-keydown-listener.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useAtomValue } from "jotai" -import { useEffect, useCallback } from "react" -import { keyboardDisableSourcesAtom } from "@/store/keydown-manager" - -export function useKeydownListener(callback: (event: KeyboardEvent) => void) { - const disableSources = useAtomValue(keyboardDisableSourcesAtom) - - const handleKeyDown = useCallback( - (event: KeyboardEvent) => { - if (disableSources.size === 0) { - callback(event) - } - }, - [disableSources, callback] - ) - - useEffect(() => { - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [handleKeyDown]) -} diff --git a/web/hooks/use-media.ts b/web/hooks/use-media.ts new file mode 100644 index 00000000..6f68e6bf --- /dev/null +++ b/web/hooks/use-media.ts @@ -0,0 +1,23 @@ +import { useState, useEffect } from "react" + +export function useMedia(query: string): boolean { + const [matches, setMatches] = useState(false) + + useEffect(() => { + if (window.matchMedia) { + const media = window.matchMedia(query) + if (media.matches !== matches) { + setMatches(media.matches) + } + const listener = () => { + setMatches(media.matches) + } + media.addListener(listener) + return () => media.removeListener(listener) + } + + return undefined + }, [matches, query]) + + return matches +} diff --git a/web/hooks/use-on-click-outside.ts b/web/hooks/use-on-click-outside.ts new file mode 100644 index 00000000..faed4679 --- /dev/null +++ b/web/hooks/use-on-click-outside.ts @@ -0,0 +1,28 @@ +import * as React from "react" +import { useEventListener } from "./use-event-listener" + +/** + * Hook to detect clicks outside of a specified element. + * + * @param ref The React ref to the element. + * @param callback The handler to call when a click outside the element is detected. + */ +export function useOnClickOutside( + ref: React.RefObject, + callback?: (event: MouseEvent | TouchEvent) => void, + options: AddEventListenerOptions = {} +) { + const listener = React.useCallback( + (event: MouseEvent | TouchEvent) => { + // Do nothing if clicking ref's element or descendent elements + if (!ref.current || ref.current.contains(event.target as Node)) { + return + } + callback?.(event) + }, + [ref, callback] + ) + + useEventListener("mousedown", listener, window, options) + useEventListener("touchstart", listener, window, options) +} diff --git a/web/hooks/use-throttle.ts b/web/hooks/use-throttle.ts new file mode 100644 index 00000000..7b58a4da --- /dev/null +++ b/web/hooks/use-throttle.ts @@ -0,0 +1,34 @@ +import { useRef, useCallback } from "react" + +export function useThrottle void>( + callback: T, + delay: number +): (...args: Parameters) => void { + const lastRan = useRef(Date.now()) + const timeoutRef = useRef(null) + + return useCallback( + (...args: Parameters) => { + const handler = () => { + if (Date.now() - lastRan.current >= delay) { + callback(...args) + lastRan.current = Date.now() + } else { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + timeoutRef.current = setTimeout( + () => { + callback(...args) + lastRan.current = Date.now() + }, + delay - (Date.now() - lastRan.current) + ) + } + } + + handler() + }, + [callback, delay] + ) +} diff --git a/web/hooks/use-touch-sensor.ts b/web/hooks/use-touch-sensor.ts index 437aff41..c0fa78ba 100644 --- a/web/hooks/use-touch-sensor.ts +++ b/web/hooks/use-touch-sensor.ts @@ -1,11 +1,18 @@ import { useState, useEffect } from "react" +const SSR = typeof window === "undefined" + export function useTouchSensor() { const [isTouchDevice, setIsTouchDevice] = useState(false) useEffect(() => { const detectTouch = () => { - setIsTouchDevice("ontouchstart" in window || navigator.maxTouchPoints > 0) + setIsTouchDevice( + !SSR && + (window.matchMedia?.("(hover: none) and (pointer: coarse)")?.matches || + "ontouchstart" in window || + navigator.maxTouchPoints > 0) + ) } detectTouch() diff --git a/web/lib/utils/index.ts b/web/lib/utils/index.ts index 9be23460..43f9bd26 100644 --- a/web/lib/utils/index.ts +++ b/web/lib/utils/index.ts @@ -34,22 +34,16 @@ export function shuffleArray(array: T[]): T[] { return shuffled } -export const isEditableElement = (element: HTMLElement): boolean => { - if (element.isContentEditable) { - return true - } +const inputs = ["input", "select", "button", "textarea"] // detect if node is a text input element - const tagName = element.tagName.toLowerCase() - const editableTags = ["input", "textarea", "select", "option"] - - if (editableTags.includes(tagName)) { - return true - } - - const role = element.getAttribute("role") - const editableRoles = ["textbox", "combobox", "listbox"] - - return role ? editableRoles.includes(role) : false +export function isTextInput(element: Element): boolean { + return !!( + element && + element.tagName && + (inputs.indexOf(element.tagName.toLowerCase()) !== -1 || + element.attributes.getNamedItem("role")?.value === "textbox" || + element.attributes.getNamedItem("contenteditable")?.value === "true") + ) } export * from "./urls" diff --git a/web/lib/utils/keyboard.ts b/web/lib/utils/keyboard.ts index 50255db7..27e8c208 100644 --- a/web/lib/utils/keyboard.ts +++ b/web/lib/utils/keyboard.ts @@ -1,47 +1,4 @@ -let isMac: boolean | undefined - -interface Navigator { - userAgentData?: { - brands: { brand: string; version: string }[] - mobile: boolean - platform: string - getHighEntropyValues: (hints: string[]) => Promise<{ - platform: string - platformVersion: string - uaFullVersion: string - }> - } -} - -function getPlatform(): string { - const nav = navigator as Navigator - - if (nav.userAgentData) { - if (nav.userAgentData.platform) { - return nav.userAgentData.platform - } - - nav.userAgentData.getHighEntropyValues(["platform"]).then(highEntropyValues => { - if (highEntropyValues.platform) { - return highEntropyValues.platform - } - }) - } - - if (typeof navigator.platform === "string") { - return navigator.platform - } - - return "" -} - -export function isMacOS() { - if (isMac === undefined) { - isMac = getPlatform().toLowerCase().includes("mac") - } - - return isMac -} +const SSR = typeof window === "undefined" interface ShortcutKeyResult { symbol: string @@ -51,11 +8,11 @@ interface ShortcutKeyResult { export function getShortcutKey(key: string): ShortcutKeyResult { const lowercaseKey = key.toLowerCase() if (lowercaseKey === "mod") { - return isMacOS() ? { symbol: "⌘", readable: "Command" } : { symbol: "Ctrl", readable: "Control" } + return isMac() ? { symbol: "⌘", readable: "Command" } : { symbol: "Ctrl", readable: "Control" } } else if (lowercaseKey === "alt") { - return isMacOS() ? { symbol: "⌥", readable: "Option" } : { symbol: "Alt", readable: "Alt" } + return isMac() ? { symbol: "⌥", readable: "Option" } : { symbol: "Alt", readable: "Alt" } } else if (lowercaseKey === "shift") { - return isMacOS() ? { symbol: "⇧", readable: "Shift" } : { symbol: "Shift", readable: "Shift" } + return isMac() ? { symbol: "⇧", readable: "Shift" } : { symbol: "Shift", readable: "Shift" } } else { return { symbol: key.toUpperCase(), readable: key } } @@ -64,3 +21,39 @@ export function getShortcutKey(key: string): ShortcutKeyResult { export function getShortcutKeys(keys: string[]): ShortcutKeyResult[] { return keys.map(key => getShortcutKey(key)) } + +export function isModKey(event: KeyboardEvent | MouseEvent | React.KeyboardEvent) { + return isMac() ? event.metaKey : event.ctrlKey +} + +export function isMac(): boolean { + if (SSR) { + return false + } + return window.navigator.platform === "MacIntel" +} + +export function isWindows(): boolean { + if (SSR) { + return false + } + return window.navigator.platform === "Win32" +} + +let supportsPassive = false + +try { + const opts = Object.defineProperty({}, "passive", { + get() { + supportsPassive = true + } + }) + // @ts-expect-error ts-migrate(2769) testPassive is not a real event + window.addEventListener("testPassive", null, opts) + // @ts-expect-error ts-migrate(2769) testPassive is not a real event + window.removeEventListener("testPassive", null, opts) +} catch (e) { + // No-op +} + +export const supportsPassiveListener = supportsPassive diff --git a/web/package.json b/web/package.json index 619a7b73..0135be5a 100644 --- a/web/package.json +++ b/web/package.json @@ -78,22 +78,22 @@ "date-fns": "^3.6.0", "framer-motion": "^11.5.6", "geist": "^1.3.1", - "jazz-browser-auth-clerk": "0.7.35-guest-auth.5", - "jazz-react": "0.7.35-guest-auth.5", - "jazz-react-auth-clerk": "0.7.35-guest-auth.5", - "jazz-tools": "0.7.35-guest-auth.5", + "jazz-browser-auth-clerk": "0.8.0", + "jazz-react": "0.8.0", + "jazz-react-auth-clerk": "0.8.0", + "jazz-tools": "0.8.0", "jotai": "^2.10.0", "lowlight": "^3.1.0", "lucide-react": "^0.429.0", "next": "14.2.10", "next-themes": "^0.3.0", "nuqs": "^1.19.1", + "query-string": "^9.1.0", "react": "^18.3.1", "react-day-picker": "^8.10.1", "react-dom": "^18.3.1", "react-hook-form": "^7.53.0", "react-textarea-autosize": "^8.5.3", - "react-use": "^17.5.1", "ronin": "^4.3.1", "slugify": "^1.6.6", "sonner": "^1.5.0", From cffe65ec5f67843dbcb6956709346a4fb7d84317 Mon Sep 17 00:00:00 2001 From: Aslam Date: Tue, 24 Sep 2024 18:55:40 +0700 Subject: [PATCH 120/124] fix(page): improve keybind (#180) --- web/components/routes/link/list.tsx | 1 - .../routes/link/partials/link-item.tsx | 5 +- web/components/routes/page/PageRoute.tsx | 24 +--- web/components/routes/page/list.tsx | 122 +++++++----------- .../routes/page/partials/page-item.tsx | 31 ++++- 5 files changed, 78 insertions(+), 105 deletions(-) diff --git a/web/components/routes/link/list.tsx b/web/components/routes/link/list.tsx index 9710c83b..d76dfaf2 100644 --- a/web/components/routes/link/list.tsx +++ b/web/components/routes/link/list.tsx @@ -258,7 +258,6 @@ const LinkList: React.FC = () => { setEditId(null) setActiveItemIndex(lastActiveIndexRef.current) setKeyboardActiveIndex(lastActiveIndexRef.current) - console.log(keyboardActiveIndex) }} index={index} onItemSelected={link => setEditId(link.id)} diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index 8340aac4..9eab3b6f 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -87,7 +87,10 @@ export const LinkItem = React.forwardRef( aria-selected={isActive} data-disabled={disabled} data-active={isActive} - className="w-full overflow-visible border-b-[0.5px] border-transparent outline-none data-[active='true']:bg-[var(--link-background-muted)] data-[keyboard-active='true']:focus-visible:shadow-[var(--link-shadow)_0px_0px_0px_1px_inset]" + className={cn( + "w-full overflow-visible border-b-[0.5px] border-transparent outline-none", + "data-[active='true']:bg-[var(--link-background-muted)] data-[keyboard-active='true']:focus-visible:shadow-[var(--link-shadow)_0px_0px_0px_1px_inset]" + )} onKeyDown={handleKeyDown} >
    (null) - const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) - const [disableEnterKey, setDisableEnterKey] = useState(false) - - const handleCommandPaletteClose = useCallback(() => { - setDisableEnterKey(true) - setTimeout(() => setDisableEnterKey(false), 100) - }, []) - - useEffect(() => { - if (!isCommandPaletteOpen) { - handleCommandPaletteClose() - } - }, [isCommandPaletteOpen, handleCommandPaletteClose]) - return (
    - +
    ) } diff --git a/web/components/routes/page/list.tsx b/web/components/routes/page/list.tsx index cd085729..de35cded 100644 --- a/web/components/routes/page/list.tsx +++ b/web/components/routes/page/list.tsx @@ -1,66 +1,71 @@ -import React, { useMemo, useCallback, useEffect } from "react" +import * as React from "react" import { Primitive } from "@radix-ui/react-primitive" import { useAccount } from "@/lib/providers/jazz-provider" -import { useAtom } from "jotai" -import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" import { PageItem } from "./partials/page-item" import { useMedia } from "@/hooks/use-media" import { useColumnStyles } from "./hooks/use-column-styles" -import { PersonalPage, PersonalPageLists } from "@/lib/schema" -import { useRouter } from "next/navigation" import { useActiveItemScroll } from "@/hooks/use-active-item-scroll" import { Column } from "@/components/custom/column" +import { useKeyDown } from "@/hooks/use-key-down" -interface PageListProps { - activeItemIndex: number | null - setActiveItemIndex: React.Dispatch> - disableEnterKey: boolean -} +interface PageListProps {} -export const PageList: React.FC = ({ activeItemIndex, setActiveItemIndex, disableEnterKey }) => { +export const PageList: React.FC = () => { const isTablet = useMedia("(max-width: 640px)") - const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) const { me } = useAccount({ root: { personalPages: [] } }) - const personalPages = useMemo(() => me?.root?.personalPages, [me?.root?.personalPages]) - const router = useRouter() - const itemCount = personalPages?.length || 0 + const [activeItemIndex, setActiveItemIndex] = React.useState(null) + const [keyboardActiveIndex, setKeyboardActiveIndex] = React.useState(null) + const personalPages = React.useMemo(() => me?.root?.personalPages, [me?.root?.personalPages]) - const handleEnter = useCallback( - (selectedPage: PersonalPage) => { - router.push(`/pages/${selectedPage.id}`) - }, - [router] - ) + const next = () => Math.min((activeItemIndex ?? 0) + 1, (personalPages?.length ?? 0) - 1) - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - if (isCommandPaletteOpen) return + const prev = () => Math.max((activeItemIndex ?? 0) - 1, 0) - if (e.key === "ArrowUp" || e.key === "ArrowDown") { - e.preventDefault() - setActiveItemIndex(prevIndex => { - if (prevIndex === null) return 0 - const newIndex = e.key === "ArrowUp" ? (prevIndex - 1 + itemCount) % itemCount : (prevIndex + 1) % itemCount - return newIndex - }) - } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null && personalPages) { - e.preventDefault() - const selectedPage = personalPages[activeItemIndex] - if (selectedPage) handleEnter?.(selectedPage) - } - }, - [itemCount, isCommandPaletteOpen, activeItemIndex, setActiveItemIndex, disableEnterKey, personalPages, handleEnter] - ) + const handleKeyDown = (ev: KeyboardEvent) => { + switch (ev.key) { + case "ArrowDown": + ev.preventDefault() + ev.stopPropagation() + setActiveItemIndex(next()) + setKeyboardActiveIndex(next()) + break + case "ArrowUp": + ev.preventDefault() + ev.stopPropagation() + setActiveItemIndex(prev()) + setKeyboardActiveIndex(prev()) + } + } - useEffect(() => { - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [handleKeyDown]) + useKeyDown(() => true, handleKeyDown) + + const { setElementRef } = useActiveItemScroll({ activeIndex: keyboardActiveIndex }) return (
    {!isTablet && } - + + {personalPages?.map( + (page, index) => + page?.id && ( + setElementRef(el, index)} + page={page} + isActive={index === activeItemIndex} + onPointerMove={() => { + setKeyboardActiveIndex(null) + setActiveItemIndex(index) + }} + data-keyboard-active={keyboardActiveIndex === index} + /> + ) + )} +
    ) } @@ -82,32 +87,3 @@ export const ColumnHeader: React.FC = () => {
    ) } - -interface PageListItemsProps { - personalPages?: PersonalPageLists | null - activeItemIndex: number | null -} - -const PageListItems: React.FC = ({ personalPages, activeItemIndex }) => { - const { setElementRef } = useActiveItemScroll({ activeIndex: activeItemIndex }) - - return ( - - {personalPages?.map( - (page, index) => - page?.id && ( - setElementRef(el, index)} - page={page} - isActive={index === activeItemIndex} - /> - ) - )} - - ) -} diff --git a/web/components/routes/page/partials/page-item.tsx b/web/components/routes/page/partials/page-item.tsx index 5a0d1af1..ebf5df18 100644 --- a/web/components/routes/page/partials/page-item.tsx +++ b/web/components/routes/page/partials/page-item.tsx @@ -7,26 +7,43 @@ import { useMedia } from "@/hooks/use-media" import { useColumnStyles } from "../hooks/use-column-styles" import { format } from "date-fns" import { Column } from "@/components/custom/column" +import { useRouter } from "next/navigation" -interface PageItemProps { +interface PageItemProps extends React.HTMLAttributes { page: PersonalPage isActive: boolean } -export const PageItem = React.forwardRef(({ page, isActive }, ref) => { +export const PageItem = React.forwardRef(({ page, isActive, ...props }, ref) => { const isTablet = useMedia("(max-width: 640px)") const columnStyles = useColumnStyles() + const router = useRouter() + + const handleKeyDown = React.useCallback( + (ev: React.KeyboardEvent) => { + if (ev.key === "Enter") { + ev.preventDefault() + ev.stopPropagation() + router.push(`/pages/${page.id}`) + } + }, + [router, page.id] + ) return (
    From 58ce33fed5873f2f7ddedb5bcd42646a633e8441 Mon Sep 17 00:00:00 2001 From: Aslam Date: Tue, 24 Sep 2024 18:55:52 +0700 Subject: [PATCH 121/124] fix(topic): Topic list keybind (#181) * fix(page): improve keybind * fix(topic): improve keybind * fix: learning state selector --- .../custom/learning-state-selector.tsx | 23 +- .../custom/sidebar/partial/topic-section.tsx | 20 +- web/components/routes/topics/TopicRoute.tsx | 24 +- web/components/routes/topics/list.tsx | 136 ++++------ .../routes/topics/partials/topic-item.tsx | 249 ++++++++++-------- 5 files changed, 204 insertions(+), 248 deletions(-) diff --git a/web/components/custom/learning-state-selector.tsx b/web/components/custom/learning-state-selector.tsx index 6f00af5c..984f8456 100644 --- a/web/components/custom/learning-state-selector.tsx +++ b/web/components/custom/learning-state-selector.tsx @@ -8,7 +8,7 @@ import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { linkLearningStateSelectorAtom } from "@/store/link" import { Command, CommandInput, CommandList, CommandItem, CommandGroup } from "@/components/ui/command" import { ScrollArea } from "@/components/ui/scroll-area" -import type { icons } from "lucide-react" +import { icons } from "lucide-react" interface LearningStateSelectorProps { showSearch?: boolean @@ -37,6 +37,9 @@ export const LearningStateSelector: React.FC = ({ setIsLearningStateSelectorOpen(false) } + const iconName = selectedLearningState?.icon || defaultIcon + const labelText = selectedLearningState?.label || defaultLabel + return ( @@ -47,20 +50,8 @@ export const LearningStateSelector: React.FC = ({ variant="secondary" className={cn("gap-x-2 text-sm", className)} > - {selectedLearningState?.icon || - (defaultIcon && ( - - ))} - - {selectedLearningState?.label || - (defaultLabel && ( - - {selectedLearningState?.label || defaultLabel} - - ))} + {iconName && } + {labelText && {labelText}} @@ -97,7 +88,7 @@ export const LearningStateSelectorContent: React.FC {LEARNING_STATES.map(ls => ( - + {ls.icon && } {ls.label} = ({ pathname }) => { if (!me) return null return ( -
    +
    = ({ topicCount, isA isActive ? "bg-accent text-accent-foreground" : "hover:bg-accent hover:text-accent-foreground" )} > - +
    ) @@ -78,7 +74,7 @@ const List: React.FC = ({ topicsWantToLearn, topicsLearning, topicsLe count={topicsWantToLearn.length} label="To Learn" value="wantToLearn" - href="/me/wantToLearn" + href="#" isActive={pathname === "/me/wantToLearn"} /> = ({ topicsWantToLearn, topicsLearning, topicsLe label="Learning" value="learning" count={topicsLearning.length} - href="/me/learning" + href="#" isActive={pathname === "/me/learning"} /> = ({ topicsWantToLearn, topicsLearning, topicsLe label="Learned" value="learned" count={topicsLearned.length} - href="/me/learned" + href="#" isActive={pathname === "/me/learned"} />
    @@ -118,7 +114,7 @@ const ListItem: React.FC = ({ label, value, href, count, isActive
    (null) - const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) - const [disableEnterKey, setDisableEnterKey] = useState(false) - - const handleCommandPaletteClose = useCallback(() => { - setDisableEnterKey(true) - setTimeout(() => setDisableEnterKey(false), 100) - }, []) - - useEffect(() => { - if (!isCommandPaletteOpen) { - handleCommandPaletteClose() - } - }, [isCommandPaletteOpen, handleCommandPaletteClose]) - return (
    - +
    ) } diff --git a/web/components/routes/topics/list.tsx b/web/components/routes/topics/list.tsx index 4d951498..8b16f767 100644 --- a/web/components/routes/topics/list.tsx +++ b/web/components/routes/topics/list.tsx @@ -1,8 +1,7 @@ -import React, { useCallback, useEffect, useMemo } from "react" +import * as React from "react" import { Primitive } from "@radix-ui/react-primitive" import { useAccount } from "@/lib/providers/jazz-provider" -import { atom, useAtom } from "jotai" -import { commandPaletteOpenAtom } from "@/components/custom/command-palette/command-palette" +import { atom } from "jotai" import { TopicItem } from "./partials/topic-item" import { useMedia } from "@/hooks/use-media" import { useRouter } from "next/navigation" @@ -11,12 +10,9 @@ import { Column } from "@/components/custom/column" import { useColumnStyles } from "./hooks/use-column-styles" import { LaAccount, ListOfTopics, Topic, UserRoot } from "@/lib/schema" import { LearningStateValue } from "@/lib/constants" +import { useKeyDown } from "@/hooks/use-key-down" -interface TopicListProps { - activeItemIndex: number | null - setActiveItemIndex: React.Dispatch> - disableEnterKey: boolean -} +interface TopicListProps {} interface MainTopicListProps extends TopicListProps { me: { @@ -35,32 +31,21 @@ export interface PersonalTopic { export const topicOpenPopoverForIdAtom = atom(null) -export const TopicList: React.FC = ({ activeItemIndex, setActiveItemIndex, disableEnterKey }) => { +export const TopicList: React.FC = () => { const { me } = useAccount({ root: { topicsWantToLearn: [], topicsLearning: [], topicsLearned: [] } }) if (!me) return null - return ( - - ) + return } -export const MainTopicList: React.FC = ({ - me, - activeItemIndex, - setActiveItemIndex, - disableEnterKey -}) => { +export const MainTopicList: React.FC = ({ me }) => { const isTablet = useMedia("(max-width: 640px)") - const [isCommandPaletteOpen] = useAtom(commandPaletteOpenAtom) + const [activeItemIndex, setActiveItemIndex] = React.useState(null) + const [keyboardActiveIndex, setKeyboardActiveIndex] = React.useState(null) const router = useRouter() - const personalTopics = useMemo( + const personalTopics = React.useMemo( () => [ ...me.root.topicsWantToLearn.map(topic => ({ topic, learningState: "wantToLearn" as const })), ...me.root.topicsLearning.map(topic => ({ topic, learningState: "learning" as const })), @@ -69,44 +54,63 @@ export const MainTopicList: React.FC = ({ [me.root.topicsWantToLearn, me.root.topicsLearning, me.root.topicsLearned] ) - const itemCount = personalTopics.length - - const handleEnter = useCallback( + const handleEnter = React.useCallback( (selectedTopic: Topic) => { router.push(`/${selectedTopic.name}`) }, [router] ) - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - if (isCommandPaletteOpen) return + const next = () => Math.min((activeItemIndex ?? 0) + 1, (personalTopics?.length ?? 0) - 1) - if (e.key === "ArrowUp" || e.key === "ArrowDown") { - e.preventDefault() - setActiveItemIndex(prevIndex => { - if (prevIndex === null) return 0 - const newIndex = e.key === "ArrowUp" ? (prevIndex - 1 + itemCount) % itemCount : (prevIndex + 1) % itemCount - return newIndex - }) - } else if (e.key === "Enter" && !disableEnterKey && activeItemIndex !== null && personalTopics) { - e.preventDefault() - const selectedTopic = personalTopics[activeItemIndex] - if (selectedTopic?.topic) handleEnter?.(selectedTopic.topic) - } - }, - [itemCount, isCommandPaletteOpen, activeItemIndex, setActiveItemIndex, disableEnterKey, personalTopics, handleEnter] - ) + const prev = () => Math.max((activeItemIndex ?? 0) - 1, 0) - useEffect(() => { - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [handleKeyDown]) + const handleKeyDown = (ev: KeyboardEvent) => { + switch (ev.key) { + case "ArrowDown": + ev.preventDefault() + ev.stopPropagation() + setActiveItemIndex(next()) + setKeyboardActiveIndex(next()) + break + case "ArrowUp": + ev.preventDefault() + ev.stopPropagation() + setActiveItemIndex(prev()) + setKeyboardActiveIndex(prev()) + } + } + + useKeyDown(() => true, handleKeyDown) + + const { setElementRef } = useActiveItemScroll({ activeIndex: keyboardActiveIndex }) return (
    {!isTablet && } - + + {personalTopics?.map( + (pt, index) => + pt.topic?.id && ( + setElementRef(el, index)} + topic={pt.topic} + learningState={pt.learningState} + isActive={index === activeItemIndex} + onPointerMove={() => { + setKeyboardActiveIndex(null) + setActiveItemIndex(index) + }} + data-keyboard-active={keyboardActiveIndex === index} + /> + ) + )} +
    ) } @@ -125,33 +129,3 @@ export const ColumnHeader: React.FC = () => {
    ) } - -interface TopicListItemsProps { - personalTopics: PersonalTopic[] | null - activeItemIndex: number | null -} - -const TopicListItems: React.FC = ({ personalTopics, activeItemIndex }) => { - const { setElementRef } = useActiveItemScroll({ activeIndex: activeItemIndex }) - - return ( - - {personalTopics?.map( - (pt, index) => - pt.topic?.id && ( - setElementRef(el, index)} - topic={pt.topic} - learningState={pt.learningState} - isActive={index === activeItemIndex} - /> - ) - )} - - ) -} diff --git a/web/components/routes/topics/partials/topic-item.tsx b/web/components/routes/topics/partials/topic-item.tsx index 05aec78d..57b18308 100644 --- a/web/components/routes/topics/partials/topic-item.tsx +++ b/web/components/routes/topics/partials/topic-item.tsx @@ -12,146 +12,163 @@ import { useAtom } from "jotai" import { topicOpenPopoverForIdAtom } from "../list" import { LEARNING_STATES, LearningStateValue } from "@/lib/constants" import { useAccount } from "@/lib/providers/jazz-provider" +import { useRouter } from "next/navigation" -interface TopicItemProps { +interface TopicItemProps extends React.HTMLAttributes { topic: Topic learningState: LearningStateValue isActive: boolean } -export const TopicItem = React.forwardRef(({ topic, learningState, isActive }, ref) => { - const columnStyles = useColumnStyles() - const [openPopoverForId, setOpenPopoverForId] = useAtom(topicOpenPopoverForIdAtom) - const { me } = useAccount({ root: { topicsWantToLearn: [], topicsLearning: [], topicsLearned: [] } }) +export const TopicItem = React.forwardRef( + ({ topic, learningState, isActive, ...props }, ref) => { + const columnStyles = useColumnStyles() + const [openPopoverForId, setOpenPopoverForId] = useAtom(topicOpenPopoverForIdAtom) + const router = useRouter() + const { me } = useAccount({ root: { topicsWantToLearn: [], topicsLearning: [], topicsLearned: [] } }) - let p: { - index: number - topic?: Topic | null - learningState: LearningStateValue - } | null = null + let p: { + index: number + topic?: Topic | null + learningState: LearningStateValue + } | null = null - const wantToLearnIndex = me?.root.topicsWantToLearn.findIndex(t => t?.id === topic.id) ?? -1 - if (wantToLearnIndex !== -1) { - p = { - index: wantToLearnIndex, - topic: me?.root.topicsWantToLearn[wantToLearnIndex], - learningState: "wantToLearn" - } - } - - const learningIndex = me?.root.topicsLearning.findIndex(t => t?.id === topic.id) ?? -1 - if (learningIndex !== -1) { - p = { - index: learningIndex, - topic: me?.root.topicsLearning[learningIndex], - learningState: "learning" - } - } - - const learnedIndex = me?.root.topicsLearned.findIndex(t => t?.id === topic.id) ?? -1 - if (learnedIndex !== -1) { - p = { - index: learnedIndex, - topic: me?.root.topicsLearned[learnedIndex], - learningState: "learned" - } - } - - const selectedLearningState = useMemo(() => LEARNING_STATES.find(ls => ls.value === learningState), [learningState]) - - const handleLearningStateSelect = useCallback( - (value: string) => { - const newLearningState = value as LearningStateValue - - const topicLists: Record = { - wantToLearn: me?.root.topicsWantToLearn, - learning: me?.root.topicsLearning, - learned: me?.root.topicsLearned + const wantToLearnIndex = me?.root.topicsWantToLearn.findIndex(t => t?.id === topic.id) ?? -1 + if (wantToLearnIndex !== -1) { + p = { + index: wantToLearnIndex, + topic: me?.root.topicsWantToLearn[wantToLearnIndex], + learningState: "wantToLearn" } + } - const removeFromList = (state: LearningStateValue, index: number) => { - topicLists[state]?.splice(index, 1) + const learningIndex = me?.root.topicsLearning.findIndex(t => t?.id === topic.id) ?? -1 + if (learningIndex !== -1) { + p = { + index: learningIndex, + topic: me?.root.topicsLearning[learningIndex], + learningState: "learning" } + } - if (p) { - if (newLearningState === p.learningState) { - removeFromList(p.learningState, p.index) - return + const learnedIndex = me?.root.topicsLearned.findIndex(t => t?.id === topic.id) ?? -1 + if (learnedIndex !== -1) { + p = { + index: learnedIndex, + topic: me?.root.topicsLearned[learnedIndex], + learningState: "learned" + } + } + + const selectedLearningState = useMemo(() => LEARNING_STATES.find(ls => ls.value === learningState), [learningState]) + + const handleLearningStateSelect = useCallback( + (value: string) => { + const newLearningState = value as LearningStateValue + + const topicLists: Record = { + wantToLearn: me?.root.topicsWantToLearn, + learning: me?.root.topicsLearning, + learned: me?.root.topicsLearned } - removeFromList(p.learningState, p.index) - } - topicLists[newLearningState]?.push(topic) + const removeFromList = (state: LearningStateValue, index: number) => { + topicLists[state]?.splice(index, 1) + } - setOpenPopoverForId(null) - }, - [setOpenPopoverForId, me?.root.topicsWantToLearn, me?.root.topicsLearning, me?.root.topicsLearned, p, topic] - ) + if (p) { + if (newLearningState === p.learningState) { + removeFromList(p.learningState, p.index) + return + } + removeFromList(p.learningState, p.index) + } - const handlePopoverTriggerClick = (e: React.MouseEvent) => { - e.preventDefault() - e.stopPropagation() + topicLists[newLearningState]?.push(topic) - setOpenPopoverForId(openPopoverForId === topic.id ? null : topic.id) - } + setOpenPopoverForId(null) + }, + [setOpenPopoverForId, me?.root.topicsWantToLearn, me?.root.topicsLearning, me?.root.topicsLearned, p, topic] + ) - return ( -
    + const handlePopoverTriggerClick = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() + + setOpenPopoverForId(openPopoverForId === topic.id ? null : topic.id) + } + + const handleKeyDown = React.useCallback( + (ev: React.KeyboardEvent) => { + if (ev.key === "Enter") { + ev.preventDefault() + ev.stopPropagation() + router.push(`/${topic.name}`) + } + }, + [router, topic.id] + ) + + return ( - - {topic.prettyName} - +
    + + {topic.prettyName} + - - setOpenPopoverForId(open ? topic.id : null)} - > - - - - e.stopPropagation()} + + setOpenPopoverForId(open ? topic.id : null)} > - - - - + + + + e.stopPropagation()} + > + + + + +
    -
    - ) -}) + ) + } +) TopicItem.displayName = "TopicItem" From 223a4524ab6caed26b61a787bf7d3406a276dba6 Mon Sep 17 00:00:00 2001 From: Aslam H Date: Wed, 25 Sep 2024 18:48:23 +0700 Subject: [PATCH 122/124] chore: height on mobile --- .../custom/sidebar/partial/link-section.tsx | 6 ++-- .../custom/sidebar/partial/page-section.tsx | 6 ++-- .../custom/sidebar/partial/topic-section.tsx | 6 ++-- web/components/routes/link/header.tsx | 2 +- .../routes/link/partials/link-item.tsx | 2 +- .../routes/page/detail/PageDetailRoute.tsx | 29 ------------------- 6 files changed, 11 insertions(+), 40 deletions(-) diff --git a/web/components/custom/sidebar/partial/link-section.tsx b/web/components/custom/sidebar/partial/link-section.tsx index 0b568943..44e94eaf 100644 --- a/web/components/custom/sidebar/partial/link-section.tsx +++ b/web/components/custom/sidebar/partial/link-section.tsx @@ -47,7 +47,7 @@ const LinkSectionHeader: React.FC = ({ linkCount }) => { return (
    @@ -55,7 +55,7 @@ const LinkSectionHeader: React.FC = ({ linkCount }) => { href="/links" className="flex flex-1 items-center justify-start rounded-md px-2 py-1 focus-visible:outline-none focus-visible:ring-0" > -

    +

    Links {linkCount > 0 && {linkCount}}

    @@ -112,7 +112,7 @@ const ListItem: React.FC = ({ label, href, count, isActive }) => diff --git a/web/components/custom/sidebar/partial/page-section.tsx b/web/components/custom/sidebar/partial/page-section.tsx index de2304b0..652aaf31 100644 --- a/web/components/custom/sidebar/partial/page-section.tsx +++ b/web/components/custom/sidebar/partial/page-section.tsx @@ -77,12 +77,12 @@ interface PageSectionHeaderProps { const PageSectionHeader: React.FC = ({ pageCount, isActive }) => (
    -

    +

    Pages {pageCount > 0 && {pageCount}}

    @@ -165,7 +165,7 @@ const PageListItem: React.FC = ({ page, isActive }) => ( diff --git a/web/components/custom/sidebar/partial/topic-section.tsx b/web/components/custom/sidebar/partial/topic-section.tsx index 62b01d70..d814dff5 100644 --- a/web/components/custom/sidebar/partial/topic-section.tsx +++ b/web/components/custom/sidebar/partial/topic-section.tsx @@ -45,12 +45,12 @@ interface TopicSectionHeaderProps { const TopicSectionHeader: React.FC = ({ topicCount, isActive }) => (
    -

    +

    Topics {topicCount > 0 && {topicCount}}

    @@ -116,7 +116,7 @@ const ListItem: React.FC = ({ label, value, href, count, isActive { {isTablet && ( -
    +
    )} diff --git a/web/components/routes/link/partials/link-item.tsx b/web/components/routes/link/partials/link-item.tsx index 9eab3b6f..ec99e691 100644 --- a/web/components/routes/link/partials/link-item.tsx +++ b/web/components/routes/link/partials/link-item.tsx @@ -152,7 +152,7 @@ export const LinkItem = React.forwardRef( prefetch={false} target="_blank" onClick={e => e.stopPropagation()} - className="hover:text-primary truncate text-xs" + className="hover:text-primary mr-1 truncate text-xs" > {personalLink.url} diff --git a/web/components/routes/page/detail/PageDetailRoute.tsx b/web/components/routes/page/detail/PageDetailRoute.tsx index d819e7d8..7a7bc32e 100644 --- a/web/components/routes/page/detail/PageDetailRoute.tsx +++ b/web/components/routes/page/detail/PageDetailRoute.tsx @@ -23,33 +23,6 @@ import { usePageActions } from "../hooks/use-page-actions" const TITLE_PLACEHOLDER = "Untitled" -const isPageEmpty = (page: PersonalPage): boolean => { - return (!page.title || page.title.trim() === "") && (!page.content || Object.keys(page.content).length === 0) -} - -const useDeleteEmptyPage = (currentPageId: string | null) => { - const router = useRouter() - const { me } = useAccount({ - root: { - personalPages: [] - } - }) - - useEffect(() => { - return () => { - if (!currentPageId || !me?.root?.personalPages) return - - const currentPage = me.root.personalPages.find(page => page?.id === currentPageId) - if (currentPage && isPageEmpty(currentPage)) { - const index = me.root.personalPages.findIndex(page => page?.id === currentPageId) - if (index !== -1) { - me.root.personalPages.splice(index, 1) - } - } - } - }, [currentPageId, me, router]) -} - export function PageDetailRoute({ pageId }: { pageId: string }) { const { me } = useAccount({ root: { personalLinks: [] } }) const isMobile = useMedia("(max-width: 770px)") @@ -58,8 +31,6 @@ export function PageDetailRoute({ pageId }: { pageId: string }) { const { deletePage } = usePageActions() const confirm = useConfirm() - // useDeleteEmptyPage(pageId) - const handleDelete = useCallback(async () => { const result = await confirm({ title: "Delete page", From 34d69be960dc0cdd5c66b12e2bbf312813e824a6 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 27 Sep 2024 20:55:03 +0300 Subject: [PATCH 123/124] Tasks (#175) * tasks * task input * fixed jazz things * create new task ui * feat: simple feature flag --------- Co-authored-by: marshennikovaolga Co-authored-by: Aslam H --- bun.lockb | Bin 487416 -> 498464 bytes web/app/(pages)/tasks/page.tsx | 15 ++ web/app/actions.ts | 13 + .../custom/sidebar/partial/task-section.tsx | 149 +++++++++++ web/components/custom/sidebar/sidebar.tsx | 28 +- web/components/routes/task/TaskForm.tsx | 114 ++++++++ web/components/routes/task/TaskItem.tsx | 26 ++ web/components/routes/task/TaskList.tsx | 23 ++ web/components/routes/task/TaskRoute.tsx | 33 +++ web/lib/schema/index.ts | 7 +- web/lib/schema/tasks.ts | 12 + web/middleware.ts | 3 +- web/package.json | 252 +++++++++--------- 13 files changed, 533 insertions(+), 142 deletions(-) create mode 100644 web/app/(pages)/tasks/page.tsx create mode 100644 web/components/custom/sidebar/partial/task-section.tsx create mode 100644 web/components/routes/task/TaskForm.tsx create mode 100644 web/components/routes/task/TaskItem.tsx create mode 100644 web/components/routes/task/TaskList.tsx create mode 100644 web/components/routes/task/TaskRoute.tsx create mode 100644 web/lib/schema/tasks.ts diff --git a/bun.lockb b/bun.lockb index 69ef0f39632a76c99619b241e7556d27f099ecfb..dc8827a85c0f586201454dcedc088ce0855b8284 100755 GIT binary patch delta 84325 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 delta 78888 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 nG!XKUA888LMZ-G7#DCHb)KQo8*d>z)4x+J0$1 diff --git a/web/app/(pages)/tasks/page.tsx b/web/app/(pages)/tasks/page.tsx new file mode 100644 index 00000000..cabad9b4 --- /dev/null +++ b/web/app/(pages)/tasks/page.tsx @@ -0,0 +1,15 @@ +import { TaskRoute } from "@/components/routes/task/TaskRoute" +import { currentUser } from "@clerk/nextjs/server" +import { notFound } from "next/navigation" +import { get } from "ronin" + +export default async function TaskPage() { + const user = await currentUser() + const flag = await get.featureFlag.with.name("TASK") + + if (!user?.emailAddresses.some(email => flag?.emails.includes(email.emailAddress))) { + notFound() + } + + return +} diff --git a/web/app/actions.ts b/web/app/actions.ts index 14795825..42f598f6 100644 --- a/web/app/actions.ts +++ b/web/app/actions.ts @@ -10,6 +10,19 @@ import { ZSAError } from "zsa" const MAX_FILE_SIZE = 1 * 1024 * 1024 const ALLOWED_FILE_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"] +export const getFeatureFlag = authedProcedure + .input( + z.object({ + name: z.string() + }) + ) + .handler(async ({ input }) => { + const { name } = input + const flag = await get.featureFlag.with.name(name) + + return { flag } + }) + export const sendFeedback = authedProcedure .input( z.object({ diff --git a/web/components/custom/sidebar/partial/task-section.tsx b/web/components/custom/sidebar/partial/task-section.tsx new file mode 100644 index 00000000..203100f7 --- /dev/null +++ b/web/components/custom/sidebar/partial/task-section.tsx @@ -0,0 +1,149 @@ +import Link from "next/link" +import { usePathname } from "next/navigation" +import { cn } from "@/lib/utils" +import { ListOfTasks } from "@/lib/schema/tasks" +import { LaIcon } from "../../la-icon" +import { useEffect, useState } from "react" +import { useAuth, useUser } from "@clerk/nextjs" +import { getFeatureFlag } from "@/app/actions" + +export const TaskSection: React.FC<{ pathname: string }> = ({ pathname }) => { + const me = { root: { tasks: [{ id: "1", title: "Test Task" }] } } + + const taskCount = me?.root.tasks?.length || 0 + const isActive = pathname === "/tasks" + + const [isFetching, setIsFetching] = useState(false) + const [isFeatureActive, setIsFeatureActive] = useState(false) + const { isLoaded, isSignedIn } = useAuth() + const { user } = useUser() + + useEffect(() => { + async function checkFeatureFlag() { + setIsFetching(true) + + if (isLoaded && isSignedIn) { + const [data, err] = await getFeatureFlag({ name: "TASK" }) + + if (err) { + console.error(err) + setIsFetching(false) + return + } + + if (user?.emailAddresses.some(email => data.flag?.emails.includes(email.emailAddress))) { + setIsFeatureActive(true) + } + setIsFetching(false) + } + } + + checkFeatureFlag() + }, [isLoaded, isSignedIn, user]) + + if (!isLoaded || !isSignedIn) { + return
    Loading...
    + } + + if (!me) return null + + if (!isFeatureActive) { + return null + } + + return ( +
    + + {isFetching ? ( +
    Fetching tasks...
    + ) : ( + + )} +
    + ) +} + +interface TaskSectionHeaderProps { + taskCount: number + isActive: boolean +} + +const TaskSectionHeader: React.FC = ({ taskCount, isActive }) => ( +
    + +

    + Tasks + {taskCount > 0 && {taskCount}} +

    + +
    + //
    + // + //
    +) + +interface ListProps { + tasks: ListOfTasks +} + +const List: React.FC = ({ tasks }) => { + const pathname = usePathname() + + return ( +
    + +
    + ) +} + +interface ListItemProps { + label: string + href: string + count: number + isActive: boolean +} + +const ListItem: React.FC = ({ label, href, count, isActive }) => ( +
    +
    + +
    + +

    {label}

    +
    + + {count > 0 && ( + {count} + )} +
    +
    +) diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index 64a4f9ae..b1f70df1 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -13,6 +13,7 @@ import { LinkSection } from "./partial/link-section" import { PageSection } from "./partial/page-section" import { TopicSection } from "./partial/topic-section" import { ProfileSection } from "./partial/profile-section" +import { TaskSection } from "./partial/task-section" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" import { LaIcon } from "../la-icon" @@ -114,21 +115,20 @@ const SidebarContent: React.FC = React.memo(() => { const pathname = usePathname() return ( - <> - ) }) diff --git a/web/components/routes/task/TaskForm.tsx b/web/components/routes/task/TaskForm.tsx new file mode 100644 index 00000000..990170a7 --- /dev/null +++ b/web/components/routes/task/TaskForm.tsx @@ -0,0 +1,114 @@ +"use client" +import { useState, useEffect, useRef } from "react" +import { motion, AnimatePresence } from "framer-motion" +import { ListOfTasks, Task } from "@/lib/schema/tasks" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import { useAccount } from "@/lib/providers/jazz-provider" +import { LaIcon } from "@/components/custom/la-icon" +import { Checkbox } from "@/components/ui/checkbox" +import { format } from "date-fns" + +interface TaskFormProps {} + +export const TaskForm: React.FC = ({}) => { + const [title, setTitle] = useState("") + const [inputVisible, setInputVisible] = useState(false) + const { me } = useAccount({ root: {} }) + const inputRef = useRef(null) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + if (title.trim()) { + if (me?.root?.tasks === undefined) { + if (!me) return + me.root.tasks = ListOfTasks.create([], { owner: me }) + } + + const newTask = Task.create( + { + title, + description: "", + status: "todo", + createdAt: new Date() + // updatedAt: new Date() + }, + { owner: me._owner } + ) + me.root.tasks?.push(newTask) + resetForm() + } + } + + const resetForm = () => { + setTitle("") + setInputVisible(false) + } + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Escape") { + resetForm() + } else if (e.key === "Backspace" && title.trim() === "") { + resetForm() + } + } + + useEffect(() => { + if (inputVisible && inputRef.current) { + inputRef.current.focus() + } + }, [inputVisible]) + + const formattedDate = format(new Date(), "EEE, MMMM do, yyyy") + + return ( +
    + + {!inputVisible ? ( + + + + ) : ( + +
    + {}} /> + setTitle(e.target.value)} + onKeyDown={handleKeyDown} + // placeholder="Task title" + /> +
    +
    + {formattedDate} +
    +
    + )} +
    +
    + ) +} diff --git a/web/components/routes/task/TaskItem.tsx b/web/components/routes/task/TaskItem.tsx new file mode 100644 index 00000000..e4fd058c --- /dev/null +++ b/web/components/routes/task/TaskItem.tsx @@ -0,0 +1,26 @@ +import { Task } from "@/lib/schema/tasks" +import { Checkbox } from "@/components/ui/checkbox" +import { format } from "date-fns" + +interface TaskItemProps { + task: Task + onUpdateTask: (taskId: string, updates: Partial) => void +} + +export const TaskItem: React.FC = ({ task, onUpdateTask }) => { + const statusChange = (checked: boolean) => { + onUpdateTask(task.id, { status: checked ? "done" : "todo" }) + } + + const formattedDate = format(new Date(task.createdAt), "EEE, MMMM do, yyyy") + + return ( +
  • +
    + +

    {task.title}

    +
    + {formattedDate} +
  • + ) +} diff --git a/web/components/routes/task/TaskList.tsx b/web/components/routes/task/TaskList.tsx new file mode 100644 index 00000000..8478dae3 --- /dev/null +++ b/web/components/routes/task/TaskList.tsx @@ -0,0 +1,23 @@ +import React from "react" +import { ListOfTasks, Task } from "@/lib/schema/tasks" +import { TaskItem } from "./TaskItem" + +interface TaskListProps { + tasks?: ListOfTasks + onUpdateTask: (taskId: string, updates: Partial) => void +} + +export const TaskList: React.FC = ({ tasks, onUpdateTask }) => { + return ( +
      + {tasks?.map( + task => + task?.id && ( +
    • + +
    • + ) + )} +
    + ) +} diff --git a/web/components/routes/task/TaskRoute.tsx b/web/components/routes/task/TaskRoute.tsx new file mode 100644 index 00000000..a2efe5ec --- /dev/null +++ b/web/components/routes/task/TaskRoute.tsx @@ -0,0 +1,33 @@ +"use client" + +import { useAccount } from "@/lib/providers/jazz-provider" +import { Task } from "@/lib/schema/tasks" +import { TaskList } from "./TaskList" +import { TaskForm } from "./TaskForm" +import { LaIcon } from "@/components/custom/la-icon" + +export const TaskRoute: React.FC = () => { + const { me } = useAccount({ root: { tasks: [] } }) + const tasks = me?.root.tasks + console.log(tasks, "tasks here") + + const updateTask = (taskId: string, updates: Partial) => { + if (me?.root?.tasks) { + const taskIndex = me.root.tasks.findIndex(task => task?.id === taskId) + if (taskIndex !== -1) { + Object.assign(me.root.tasks[taskIndex]!, updates) + } + } + } + + return ( +
    +
    + +

    Current Tasks

    +
    + + +
    + ) +} diff --git a/web/lib/schema/index.ts b/web/lib/schema/index.ts index 12ebd60e..8b15d634 100644 --- a/web/lib/schema/index.ts +++ b/web/lib/schema/index.ts @@ -12,6 +12,7 @@ import { CoMap, co, Account, Profile } from "jazz-tools" import { PersonalPageLists } from "./personal-page" import { PersonalLinkLists } from "./personal-link" import { ListOfTopics } from "./master/topic" +import { ListOfTasks } from "./tasks" declare module "jazz-tools" { interface Profile { @@ -32,6 +33,8 @@ export class UserRoot extends CoMap { topicsWantToLearn = co.ref(ListOfTopics) topicsLearning = co.ref(ListOfTopics) topicsLearned = co.ref(ListOfTopics) + + tasks = co.ref(ListOfTasks) } export class LaAccount extends Account { @@ -59,7 +62,9 @@ export class LaAccount extends Account { topicsWantToLearn: ListOfTopics.create([], { owner: this }), topicsLearning: ListOfTopics.create([], { owner: this }), - topicsLearned: ListOfTopics.create([], { owner: this }) + topicsLearned: ListOfTopics.create([], { owner: this }), + + tasks: ListOfTasks.create([], { owner: this }) }, { owner: this } ) diff --git a/web/lib/schema/tasks.ts b/web/lib/schema/tasks.ts new file mode 100644 index 00000000..f96b8df9 --- /dev/null +++ b/web/lib/schema/tasks.ts @@ -0,0 +1,12 @@ +import { co, CoList, CoMap, Encoders } from "jazz-tools" + +export class Task extends CoMap { + title = co.string + description = co.optional.string + status = co.literal("todo", "in_progress", "done") + createdAt = co.encoded(Encoders.Date) + // updatedAt = co.encoded(Encoders.Date) + completedAt = co.optional.encoded(Encoders.Date) +} + +export class ListOfTasks extends CoList.Of(co.ref(Task)) {} diff --git a/web/middleware.ts b/web/middleware.ts index 9081e846..32be843a 100644 --- a/web/middleware.ts +++ b/web/middleware.ts @@ -10,7 +10,8 @@ const ROUTE_PATTERNS = { "/search(.*)", "/settings(.*)", "/tauri(.*)", - "/onboarding(.*)" + "/onboarding(.*)", + "/tasks(.*)" ] } diff --git a/web/package.json b/web/package.json index 0135be5a..ae4a70da 100644 --- a/web/package.json +++ b/web/package.json @@ -1,128 +1,128 @@ { - "name": "web", - "version": "0.1.0", - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "test": "jest" - }, - "dependencies": { - "@clerk/nextjs": "^5.6.0", - "@dnd-kit/core": "^6.1.0", - "@dnd-kit/modifiers": "^7.0.0", - "@dnd-kit/sortable": "^8.0.0", - "@hookform/resolvers": "^3.9.0", - "@nothing-but/force-graph": "^0.9.5", - "@nothing-but/utils": "^0.16.0", - "@omit/react-confirm-dialog": "^1.1.5", - "@omit/react-fancy-switch": "^0.1.3", - "@radix-ui/react-alert-dialog": "^1.1.1", - "@radix-ui/react-avatar": "^1.1.0", - "@radix-ui/react-checkbox": "^1.1.1", - "@radix-ui/react-context-menu": "^2.2.1", - "@radix-ui/react-dialog": "^1.1.1", - "@radix-ui/react-dismissable-layer": "^1.1.0", - "@radix-ui/react-dropdown-menu": "^2.1.1", - "@radix-ui/react-focus-scope": "^1.1.0", - "@radix-ui/react-icons": "^1.3.0", - "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-popover": "^1.1.1", - "@radix-ui/react-scroll-area": "^1.1.0", - "@radix-ui/react-select": "^2.1.1", - "@radix-ui/react-separator": "^1.1.0", - "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-switch": "^1.1.0", - "@radix-ui/react-toggle": "^1.1.0", - "@radix-ui/react-toggle-group": "^1.1.0", - "@radix-ui/react-tooltip": "^1.1.2", - "@sentry/nextjs": "^8.30.0", - "@tanstack/react-virtual": "^3.10.8", - "@tiptap/core": "^2.7.2", - "@tiptap/extension-blockquote": "^2.7.2", - "@tiptap/extension-bold": "^2.7.2", - "@tiptap/extension-bullet-list": "^2.7.2", - "@tiptap/extension-code": "^2.7.2", - "@tiptap/extension-code-block-lowlight": "^2.7.2", - "@tiptap/extension-color": "^2.7.2", - "@tiptap/extension-document": "^2.7.2", - "@tiptap/extension-dropcursor": "^2.7.2", - "@tiptap/extension-focus": "^2.7.2", - "@tiptap/extension-gapcursor": "^2.7.2", - "@tiptap/extension-hard-break": "^2.7.2", - "@tiptap/extension-heading": "^2.7.2", - "@tiptap/extension-history": "^2.7.2", - "@tiptap/extension-horizontal-rule": "^2.7.2", - "@tiptap/extension-image": "^2.7.2", - "@tiptap/extension-italic": "^2.7.2", - "@tiptap/extension-link": "^2.7.2", - "@tiptap/extension-list-item": "^2.7.2", - "@tiptap/extension-ordered-list": "^2.7.2", - "@tiptap/extension-paragraph": "^2.7.2", - "@tiptap/extension-placeholder": "^2.7.2", - "@tiptap/extension-strike": "^2.7.2", - "@tiptap/extension-task-item": "^2.7.2", - "@tiptap/extension-task-list": "^2.7.2", - "@tiptap/extension-text": "^2.7.2", - "@tiptap/extension-typography": "^2.7.2", - "@tiptap/pm": "^2.7.2", - "@tiptap/react": "^2.7.2", - "@tiptap/starter-kit": "^2.7.2", - "@tiptap/suggestion": "^2.7.2", - "axios": "^1.7.7", - "cheerio": "1.0.0", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.1", - "cmdk": "^1.0.0", - "date-fns": "^3.6.0", - "framer-motion": "^11.5.6", - "geist": "^1.3.1", - "jazz-browser-auth-clerk": "0.8.0", - "jazz-react": "0.8.0", - "jazz-react-auth-clerk": "0.8.0", - "jazz-tools": "0.8.0", - "jotai": "^2.10.0", - "lowlight": "^3.1.0", - "lucide-react": "^0.429.0", - "next": "14.2.10", - "next-themes": "^0.3.0", - "nuqs": "^1.19.1", - "query-string": "^9.1.0", - "react": "^18.3.1", - "react-day-picker": "^8.10.1", - "react-dom": "^18.3.1", - "react-hook-form": "^7.53.0", - "react-textarea-autosize": "^8.5.3", - "ronin": "^4.3.1", - "slugify": "^1.6.6", - "sonner": "^1.5.0", - "streaming-markdown": "^0.0.14", - "tailwind-merge": "^2.5.2", - "tailwindcss-animate": "^1.0.7", - "vaul": "^0.9.4", - "zod": "^3.23.8", - "zsa": "^0.6.0", - "zsa-react": "^0.2.3" - }, - "devDependencies": { - "@ronin/learn-anything": "0.0.0-3452357373461", - "@testing-library/jest-dom": "^6.5.0", - "@testing-library/react": "^16.0.1", - "@types/jest": "^29.5.13", - "@types/node": "^22.5.5", - "@types/react": "^18.3.8", - "@types/react-dom": "^18.3.0", - "dotenv": "^16.4.5", - "eslint": "^8.57.1", - "eslint-config-next": "14.2.5", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "postcss": "^8.4.47", - "prettier-plugin-tailwindcss": "^0.6.6", - "tailwindcss": "^3.4.12", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", - "typescript": "^5.6.2" - } + "name": "web", + "version": "0.1.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "jest" + }, + "dependencies": { + "@clerk/nextjs": "^5.6.0", + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/modifiers": "^7.0.0", + "@dnd-kit/sortable": "^8.0.0", + "@hookform/resolvers": "^3.9.0", + "@nothing-but/force-graph": "^0.9.5", + "@nothing-but/utils": "^0.16.0", + "@omit/react-confirm-dialog": "^1.1.5", + "@omit/react-fancy-switch": "^0.1.3", + "@radix-ui/react-alert-dialog": "^1.1.1", + "@radix-ui/react-avatar": "^1.1.0", + "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-context-menu": "^2.2.1", + "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-dismissable-layer": "^1.1.0", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-focus-scope": "^1.1.0", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-scroll-area": "^1.1.0", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-toggle-group": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.2", + "@sentry/nextjs": "^8.31.0", + "@tanstack/react-virtual": "^3.10.8", + "@tiptap/core": "^2.7.2", + "@tiptap/extension-blockquote": "^2.7.2", + "@tiptap/extension-bold": "^2.7.2", + "@tiptap/extension-bullet-list": "^2.7.2", + "@tiptap/extension-code": "^2.7.2", + "@tiptap/extension-code-block-lowlight": "^2.7.2", + "@tiptap/extension-color": "^2.7.2", + "@tiptap/extension-document": "^2.7.2", + "@tiptap/extension-dropcursor": "^2.7.2", + "@tiptap/extension-focus": "^2.7.2", + "@tiptap/extension-gapcursor": "^2.7.2", + "@tiptap/extension-hard-break": "^2.7.2", + "@tiptap/extension-heading": "^2.7.2", + "@tiptap/extension-history": "^2.7.2", + "@tiptap/extension-horizontal-rule": "^2.7.2", + "@tiptap/extension-image": "^2.7.2", + "@tiptap/extension-italic": "^2.7.2", + "@tiptap/extension-link": "^2.7.2", + "@tiptap/extension-list-item": "^2.7.2", + "@tiptap/extension-ordered-list": "^2.7.2", + "@tiptap/extension-paragraph": "^2.7.2", + "@tiptap/extension-placeholder": "^2.7.2", + "@tiptap/extension-strike": "^2.7.2", + "@tiptap/extension-task-item": "^2.7.2", + "@tiptap/extension-task-list": "^2.7.2", + "@tiptap/extension-text": "^2.7.2", + "@tiptap/extension-typography": "^2.7.2", + "@tiptap/pm": "^2.7.2", + "@tiptap/react": "^2.7.2", + "@tiptap/starter-kit": "^2.7.2", + "@tiptap/suggestion": "^2.7.2", + "axios": "^1.7.7", + "cheerio": "1.0.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.0", + "date-fns": "^3.6.0", + "framer-motion": "^11.5.6", + "geist": "^1.3.1", + "jazz-browser-auth-clerk": "0.8.0", + "jazz-react": "0.8.0", + "jazz-react-auth-clerk": "0.8.0", + "jazz-tools": "0.8.0", + "jotai": "^2.10.0", + "lowlight": "^3.1.0", + "lucide-react": "^0.429.0", + "next": "14.2.10", + "next-themes": "^0.3.0", + "nuqs": "^1.19.2", + "query-string": "^9.1.0", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.53.0", + "react-textarea-autosize": "^8.5.3", + "ronin": "^4.3.1", + "slugify": "^1.6.6", + "sonner": "^1.5.0", + "streaming-markdown": "^0.0.14", + "tailwind-merge": "^2.5.2", + "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.4", + "zod": "^3.23.8", + "zsa": "^0.6.0", + "zsa-react": "^0.2.3" + }, + "devDependencies": { + "@ronin/learn-anything": "^0.0.0-3453250405724", + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^16.0.1", + "@types/jest": "^29.5.13", + "@types/node": "^22.6.1", + "@types/react": "^18.3.8", + "@types/react-dom": "^18.3.0", + "dotenv": "^16.4.5", + "eslint": "^8.57.1", + "eslint-config-next": "14.2.5", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "postcss": "^8.4.47", + "prettier-plugin-tailwindcss": "^0.6.6", + "tailwindcss": "^3.4.13", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "typescript": "^5.6.2" + } } From 3e7c8cf38ab78709ef726610ce741dee1a9c5416 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 28 Sep 2024 15:47:10 +0300 Subject: [PATCH 124/124] Q & A + journal (#174) * tasks * task input * community route * added thread and list for community QA * answers thread * journal sidebar section * journal calendar * fix: stuff * fix: stuff * fix: stuff * chore: disable comunitty toggle * fix: typo import header --------- Co-authored-by: marshennikovaolga Co-authored-by: Aslam H --- bun.lockb | Bin 498464 -> 479720 bytes package.json | 67 ++++--- q&a | 0 .../(pages)/community/[topicName]/page.tsx | 5 + web/app/(pages)/journal/page.tsx | 15 ++ web/app/globals.css | 9 + .../custom/GuideCommunityToggle.tsx | 50 ++++++ web/components/custom/QuestionList.tsx | 65 +++++++ web/components/custom/QuestionThread.tsx | 167 ++++++++++++++++++ .../sidebar/partial/journal-section.tsx | 113 ++++++++++++ web/components/custom/sidebar/sidebar.tsx | 2 + .../routes/community/CommunityTopicRoute.tsx | 74 ++++++++ .../routes/journal/JournalRoute.tsx | 114 ++++++++++++ web/components/routes/task/TaskForm.tsx | 4 +- .../routes/topics/detail/TopicDetailRoute.tsx | 2 +- .../topics/detail/{Header.tsx => header.tsx} | 3 + web/components/routes/topics/list.tsx | 8 - .../routes/topics/partials/topic-item.tsx | 2 +- web/lib/schema/index.ts | 5 +- web/lib/schema/journal.ts | 11 ++ web/lib/schema/tasks.ts | 3 +- web/lib/utils/index.ts | 8 + web/middleware.ts | 3 +- web/package.json | 79 +++++---- 24 files changed, 720 insertions(+), 89 deletions(-) create mode 100644 q&a create mode 100644 web/app/(pages)/community/[topicName]/page.tsx create mode 100644 web/app/(pages)/journal/page.tsx create mode 100644 web/components/custom/GuideCommunityToggle.tsx create mode 100644 web/components/custom/QuestionList.tsx create mode 100644 web/components/custom/QuestionThread.tsx create mode 100644 web/components/custom/sidebar/partial/journal-section.tsx create mode 100644 web/components/routes/community/CommunityTopicRoute.tsx create mode 100644 web/components/routes/journal/JournalRoute.tsx rename web/components/routes/topics/detail/{Header.tsx => header.tsx} (96%) create mode 100644 web/lib/schema/journal.ts diff --git a/bun.lockb b/bun.lockb index dc8827a85c0f586201454dcedc088ce0855b8284..2f631d9676d902df391a6d37fc93cb3738151f8f 100755 GIT binary patch delta 122358 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 delta 133195 zcmeFaXLuCH)-BvK(!ewzq7gY~fj}aSKw`)sauzu#p@4EA!-!xE7%L;)t6 z3M2AxS8KY8-3%nGlWQ#u#3Z5i*Z6Y(Rv zq9hGNGAl4H*gr4<>DTyV1Mg%|6qRzB?_^YzEZ~9B;qlQ?it-Aa`QAI01KN$gMEPHt0Zhjqn)Er7*?hPuF7WThK6yMXXmR3TwdLcC2_ zdIka*mDCGJ1{`CX)+YCN8!M-Kn?CF4vfpKC|?2h!Z3QS9rE!n zsTO`fl+*%+N&riN)8Je-Bv2>mD9nWJr025YBybiCD5)s;m$Vjgj`dX_4fqYU$s=Jc z4cSpz$lc0_j$kN);{z2X8FH3Oh+XJb%9WvO1`apWZ zQ(_5;xg9N=7K7;R3S#7FQWm~CAA|#riU5capqoTtwh!w!;$U***`UuVfr}_y% zsu?aZE;v3qqJOY5r?LpasX*!(4P?1c|M=(uQHpY|ifG46=EsEf9fZKJDWxHx{V`Hu zFpv)FCNV7DJtQJJKsoOz6t{((4Roz46#7X12RIE5i1QB&#>DDgO_W;zq#;j%%&#iT ziw@e9q~Z`TBQiKHBp6B@4Zo-^dfeAPJ|PyS#sw$F)9B4L#0U+nDPnd%khAr+EFTye z-7hT2Kdx_laR1<_gm^4VK|x`0F_SIAkoI08#{HuPMf%$k0z=~y1LOSTF$-%+PlUz! zC!h$B;iMB0MQQJ|>O@_!eAm=uAhp6B_*iVqL~Ze)VB_t-T6iGAuWZ!9NmZmhgr6yzDQ7kACQi7tiO%G zX;3vF8w`>Sb^ww$kXRMSdLEBbOIS!P+%Ga zG$b(8KQ1UZLHQjOIl@(d>=EP<{ZO$qID1y8nU>pJ*7mf6m=a?<3J3fS&i2j#Sv{ze z=tox|t0{g$9v$be+wmhf*MOx`J`>315Folazjqe-j&YrceCm<)Y)PXap#5#Tii(bq zkB$mX2#X9>BEurX0^R#!7{l}iQ(T3iL%ItL2hyRPfZRq}0I9DU5DR!xL0|zOS4Vd& z)z~&8!r~QWb58~+7Lo3~M8lhbthicYOCUW^8_1DdjS9?H`-t+LfO)`YLe7R&IE3YU z0qKz>$Y}uDbWey24tD5`42p6O@=x&31;bPu3ViG)8h8w(f@?tb=nD1}4#h!mD%uPr zp9K$aDpbS*K!aj|g@J{D%>Neo)Ds6JM`OWoV0=_!Ec&e|QNaTfi0iSE*b1V+OeBal zAYIj0Vrw89t|_q?kPWLq=uUbbB6@lc$k00}v1^3j2f#Vj+krH+P^94N!By~>=y)um z@$pJPVthdKAjBr7DiuYBCFmB0pcN{}Xz_o;V21(SUEzN=7!f@nA}k~{;VqU9@}S`U z5z#S<5)>Sp=#Sn!hMb1nfdiQz9-ZLNaBKntIre3M^i0V_kv|ql&kdE_2l@0sNN`v@ zdzK$uU;k~9LH#%uj>F5}4>gX{*5405|L)A>d=LR0QYj_yFXYILSYLP*h;Qu!J$F zKm#7;6`t4+)l#x;fnabp=nE_YtSY%H zkmZlV08XDvb}{XC0BOh%Kn7hxSWL{I%Hi?e--}on0Xg+7M?KabAUn__FZ@qS%0a*a zJ75_b9y?nE&m{1C;Av=>IA7wAbA%#?ryQ~3h=C+I_Crv<4C+0Fds+Y44?_Pfa0Y#Z zdA0UkaMLWkOo?0y>@FvgSa8{kMIu)ZUoMTJb>t7QgM{yKx6{4 z=L^<}5T5{KFeG`Uh=%(^K+AW6Q$YvtQo!m!hS=qWqT%=JMGvAjC`x(AA41MKuxX=+ zta(5-I96hFAVaV(uq5yV%2R(d(8f^AyIE1n0aYN!?87GE(z9E{81Dkouw==nNj?n7 zhC-#>4@l3%#6^eTPA|SCI9G$n?E*IdnV%3>y(VM9dkZEb*R^kd5(R>S0}?}!5s7%< z^;8-X9~{}AV;&M5#g$Vjxl<@|1=8S=y9Dn8WDgqy*|E&X=g{$9$3G%od9_3Ep+L4f zYmZHgspF*TSV$c~=a?1$?iS-d6w#FvfjNQU;|44_rw_G1URFAzr+~# zi0D8ZzJNHyCMf$4i~OMIgy5+D@E~uFamD>2YOGRjY$5K(;mLEaSg~JhzW7$n`ods z1ci{13rLIKp%of(N8(yw5%3a^MR4^1rvZ(CG`up9hUAv=H&_fPzYZ)2JPw5Fq)k%p z;EQY!&{b1`sGIaPkgnYPR8%lN6N(2)dCuoT{v2|a)9>hj@d-ihoZ#^T;t+C*VnjIx z+pjMKIvzHZekoVl=mhsb9ARy|L*?V7Jg?+h?H}VF9T2VrM&r3uFiw7Gh|zrm$gy=i zjm8tl@L()h`VTbN>5Yhm2SApu@mBDdej)C7o~1YztiLT(TGM>FY;RtRI@R9`uNDDv z6kY-8y_`UHcJT+{Bxmq!;GQ3a^Bk*p3$&dD@_mXyALb+=*CHPv>(@6Ll;XftXM;Ym z2AT}iZ%f(^K^0_72XbbG11ka>0y*P~NO_u*LGb|p3Frx&4detG0;J+jK-RAeWIYvF z2KW#SGrSH1$x8x@0kZ(}a}9cBFzE5&CoOgCH;z^H7v!@Cdw?7TOLl`kytie&`KZV6 zDVIa&DGcN&IQFyn2>_u$=J27N!f z24s8PfgF*Sc~BnM44m;?2gs3d>>`e5e7*AuJ&x_>Y+kK>fwHy(XpUyK$S2GTgxQSJ zKftNZapv0$^Vn=)7?#Ms(aJ?xK0rVDDN5G@Lj3?dU*fsoG&sxSNmT@TsRR#<2@Xt9 zloaTv-r#}3f&Lh1rCcF{KHUle>Cfyy+V2cx}yV}bPB(;^(W zPky-$&S@O!-w$_uVR0wG+0k7<%-p0S#n=u8It2pucrlO)=N1@1 z9#bJN1vw23=5vbRxIy_O-%(ODl&6$I-}Rz_Y;Y%#0Wb{t^vo)j!(x{-2MHGVrL^oR zI1OoDMl_IBD!u{E9{dVqkH<(w3i7GAU^x*9j$^nPoUVRbR_NQK zcsYBW&!cE8rT6)u{7u+s1KAzmY6+VPbRoF?i1Ptwe-_jf19S&S|Aoinc?R^Fy$t%=^VA{+L_W;0CEb94 zBjPx9p9H5Pb^{p!?Q09}c)YR!az?^3Ao~^TEie>Fe`WF!`LDnk!ZGm??5;9fmY)pd zfQr+8=}*V}G05Nu4g*qAa6BKw#48CFZEz8*%?-xrlN4Voa^N2}5R2qVAP;=&C3Xa| z<8~k&bi0v3--CyP7XS|dG5{I@*}rN{M6+>Wk#YWg6$M{W^ryX7Ag7(U+r?B0vMJSN zh46TH{rjsqO-08YTUb$F(ebd5sOUIsiH^Go$DSG-kFkP$Y%|fnvd~8d6$H|ejvLIJ z;PgW#iPjc06pI2TxgI&yAz*YHU}%(|P@If{oCNi{>N|GQCY0wK%-l@`{Y)SmSc&p_L*R^o0YG*jO6I>q zeYUq4^%-*;{R}pJtR_Oh;OQ?hNET=gNCWVFV_a}Z@WA(Im<@%+ z`^Uxk4^r-c(_p2Kkaq{tBfmjTj~xXvM)vyI#F$Q(6}|@2@Of%P3{k$c|a zaIqC~bikIh2no7yTZ}MdHINp67b_~xmwY0S9+?s+JoE_2dR^lMe+HyW{Sw5`w3LV# ziO08N_y*NI(mzH~uAv-<;t){31Ko@SgJ-a8AOc9&9q2C&eEa96ADzl^`=So z?Pb0`xMWxH6FZA9&#`NMgWqb*E0a{I;@80e+T_wP+M!y-r6`jqi4|Mm6ZwmthBisCosvWk=@C+Y7fjsCkslZ`(iHP1eJz&7^;TVf$R2TOZ1e3?H^h`JZp>e@n7u%elI_23 zlw;kpgo!7|2Tj=I7k2rFhZ~lczi!A`yKi#5R<2k0p4{Hgd$*i=L>ueoQG9Wy#mV0v z=@!^`%#_qZ&vztFsnTs?*&plFYTb0qvwTIfx7V(_&D4@A&eV!ms%NWr<;Q;erd2Ym zG`Upjbtz}w7nM8<7o6|jbVk;K$FwyCpOyN>w{PgVa@9&4ExRLaf7ZgsKVIx^-`aC_ zV~<-WU2kRzd71R)ZK?Q9OK6zc(ASD?&9xH z9aej;P3`?)NaK^;I=uAmx+pd_B72SeIc-Z_Ka^~|YwhVt*^*D5tnEA}^!A?6ui{?R zFJI83S=^>Qjpv-4-KBWB>xIj?T=BW5)F}7j%4pMqS&8eT9g2&Ft}0UDtozF*<#HUl z-?aIg9m{K&HjX>+Q-MQM)GJ@FTX`htb%`mBfB!zhXVsd72VebJC1A$jG>@w99`-tZ zt#Enc(z{xpO2xIwmGWzcDs9nDR%&Zrv;65d?PsMN-jL*e(x7#z;$<#W(rI41`PFY} zldCM>(AL9j(E4}|Ebg+Y_L*MATG!d|Fn6(@r3)y#M@*mgYTIyg=+o5wqpEdw)h>7z z*X~v;@1b1|-R9Ee(l7UCPBSjbTsicc7NxV8stoM2EI4gSql<64z8kx2>V@O6dF#H4 zskkk3+E{I7)fg>jwfve#wfyA|y{?{hn)~VfJ(HXd~HMi>N%Zl+b^b>FAe_T;EHbT#^t)TGIrY83y~cI#y)eZ zy&=UXe8$xon_4U~TlT*^ociM0pA#0G*=uWkBl_C*UY*AHXAj@BVbs3!jUJ4+Go*CW z%#m&ZwTqov-6~<+;~EFAtZKTk(cN-c4=wtk*uZ&n=MQ|hWy*?kW%|wj>E)8wgMRC} zI$M@tYvIshhL_rMuj1vyet(oy`r&rv?dj8v>kL`9ZMU=G=j(}mFAVO~H}jOe$2o_%9DynnzSTyD08dzx6%h z$N7HPxTN&Zh4oAu%#FP;q)p0e^_t|@lA6>jzqMo1khM9NzpB$ZW>Ar@_xiY+x)y0& zWYpn;dCTM)QL4jH$K=s=HyNw7ZQ4a0i*uP3OPERX>}XMc2CD{^Tk~vTF`5ioczdhb z%b+N=A#~O}$5_fQ25Sk1l1gTL@W@ZUW<#1j9h zur|4A{__4P+~6N*)=nNTU~tlseXYhuPFkw3RUPi6;Dr&K(=^YP7UMQ2ExegkeGZ`o zLW4cU)nGAIa>h3ST4Z}~V~Des+RSQP=&Xe|w;HcGYsrA}CM^{ZZPMIYSk>)#SW$;{ zwI>}dhF99X7Ilr*OqyFuYx#kh6s5;Mu@YS}re@NTM_G+!Gi%{3t;T+twPa{G2~X68 z26Xo?S6pD_>J;ReyEN|EfQJw06k`sv=GLlUwwjPX*!WGhs_Tcfquewe=g$Dmy;Z({ z&3b^WKA+EOiQTQ&=d+TL75e$C+}L#gO5S~?!^o~ zW~8dJnwAn~$yFXF6CByFv-8;lo)Dr&IqESE$ga7ywHkM1*TMlGvTMn0t!hKFajNv&nw|2wT7dVx$MMZlOYB4s*r=_;DsFEPFjH#w}&F)E-vjv~rqTPpi?nycXWmY8qW$QQB*1&AiofNHy21 z!r^N13Zmam_LOP{FGB?_yqDFq212Wr=Id>IP(e%WWi?iGBbdV7Ff3YH2XA!^QjBA) zI$bPks$^!(Gs0r5Qc-j3V^sqxity2mQm2A3w4Aw2sk^}{={7v+U~zc>#^ukfciZ!p z9ww>>3N+B0@N8x=#ew-~Xjg9l*74KSJ^hOCUI2w1@aCCj9@umB9JXp*x% zC9lED45lv`8hoV|!7@MwKX$d4`hqpEyMKlGS5*rSv8rhh&?_*Yg+(og4N^qO6POwd z#;TZ!P_!0|=DHMH<>tLZ)RYHE?ayp7drYN_E?bu{`^4~@XfaK~vdF&R_ZS&RiN zT5>QF)WNVkE0EL z!)d+1*c=wCu9#@vT5_~itpulb6crExNnpNUP&UrX3_@Lq02o^s`!rc*`;(R!S1_@r zGpc_4#GvF37@dP|fz?JO+5nYcL&1cPV3Bd7Rdb888eQvZ;c-^A9aazKW!6$sz04r! zOOmRk^s}hHf(exf9Anvf`Z5xZ@n8`rEjG@g?g3*viWZ9ja>5K^p4tAy2i8iaM~IpY z#TZJN7=>zk$xJ$%1jb>;0@lu=J_hpx6Sg;LC_I3L0zDZm8N35ep8!K)^qD#fVT{>= zL5sy!)EP{-Mfc+rnWvwj)MJtfTU1PLwysB{+8GSl=#jnzZh(1Fh;uj~k zuvIOAIB2D}{-n9Z6a`kDE3k2)ua=7Jdk`=H(0znmKr^vsq5Cjy5t!)4lin6}EMiW$ zhTZrROgI=b(crD!8&+5C-$IxVYsOpD?O^PvzMiSCWuB@#Z+uHf9B}zM3C3NZEg?&gb$(ZrM@+#_4%Q!HO6Lbc88QN;$ zBdn%e?a(F7y@$84MLR8ZgjJmnf#{XzcrU)d&6$t*Kn%FH7cnO+Y!AlR5mw9q<6!AG zAI3xNweV3034bH`!fB~q*dC&0l?yNL5i7A>~DMI8gimJvJX{eCc6 z5(D#9SK&%ng}h#1)GpXUFxJDO)7_$82jdWk3G3QT_)yr`8jSXfL&H!oo~!^3yCKD8M4SX>fWb`kh8iA%K_v&@*JASRiIz2YUvG6k zQXC$%in;d+tN|E8bC8$0m*|K%bnF46a!k737MJ&uDY(#Fb8(8P)mu@pHekjFBt;U52HB$v;u3w zT~yVO5?;l*PE7-2FhXUd#Z)6mQQBycKY6R8k>bjLF4i|#j0b|W)bUoeMzF&lSr{WZAV?EIJQE z6$R0mBv|9HjBE}V=7r3A@)y=5Tv0F`W#0FHVGqEtB*?rv{m{j9HUSJPsmRN79*pB^ z;Be!5dHAr_r)e49g{eg&M4%$DhT_qO|ZyR#T~H1cnwl z$=fs}J#`u>KV4QK#*wli)lrw7PEVDKcLcYfEaG5#JcJS))sjNd#o7;TCgVE z>P!!i^5e~t+F)S%ffal41h8<*O)rq@rI+Y9D1Bz)l(-QL%L1Q7b%w^lSnl-l-H_5X zn${tODCy;Gavq}JB%_t~NcCYY%`@JM;+A@)4@fm-@G;h=p%&8&FdPX`@d8pF zsEA;|G%ybnD;S~|)1VKS$YWP0gVhvGfHfN~mO*_(F@85(OP*_0_d`f;;P8v+`N}4u zPB33E1}4^Y>|;q_97VB9-UgF%5%oMUK#hQ($OtWYzE#aPGF@#;izyfkn@vk^buLn(an9zOV72we>5syr zgwgt78#|8D+!k8Z)ezD?2vOk)7%K=zG#ahBEwUO%jMl;zVKR;uc3|-gv8dU;POk@x z+k(*_II*?2xQqqEfm2`1a&YUi_o-`8&9a8ko>CfHSKcwgp^~osM%K|%c4L!4ee zGTmZqF;)v-f}_@0(Kn;rvpen}z!?892lpCf$BE5XoO4!zap=SW;T>3OFg?%Ie7qh{ z&Ad%BL@Lx zKg6wRC76%qKE_+UlU@glys6lC`ZQ|qZHh;#z2@G{TRlLTZkK0Ii>bh5_8UupF?cc` ze5sou;F!q)cm&EYleFCw6#qo$g3_ZhHx1P96KyzEJQ#dCo;r#LBi6&ULXRt*)a2BcL{MNHnMV2$)UiBzP# zAQw}-cEqSw5eY421aPpB*y~SGi&sKHtVzF*1eDtmE1mik`TOh2? z9!o@^ipOT(f{AIE^4!ZTiGHu7=3bh9w~1;opV(lFaotkQZKqXrSth51KBdFJI0Zzj zTfyj2Jko#-X<+P$QUB(t%yKP!msRbzTr3ZT^jnKfV2w}&H(prB-+D@oE*n<~6Yz)z26Y1CrlVI;r+|qtWXU5kPruPsGp}~+A-vCT z1=bApU^J9)B{$v?%MQq zwFqKr@>(tYC#!lJQpP2=wLW;NwN4A)Vl_Qm$LNXNf^$rY*#0q9LM^6&U`=%S5v14? zRKr8!%s776cLRI_{MkSPR;GORow+4+rwPIZku%%LRm}u z)?1y1RJz=FeV68T!fI@?TMIv7HT|#~lYym-S9fcvkh|{D+)i3eetY!A>V&r`8L0-E zJ5mpjs;Wi)?5!5v>xd2xZWI`2IQoJk-+D0mMPKyPR4|4d9&q4!VflTU+pktNe4isy zpJ1A9C!-0bmq_6-j8udDY|4GGw=rQqp8%NFL(oTCeazd`>;S`=_AEdO-&G>@7O5cS zs^9%ACa{>?hrw#44##7%BaV7FKB?hgO<7XknA8+dPCQ8; z{FRnFD(=H%ZXeJLg>QiRp)SU*n?B)k!V&vQ<5F((xsJ>@A4amodgj z(MlZHCs@>KzY2HbEDdLbfpMwFt-sz^YNN=D7!7AA-ps04sk<3_E5t?3o4D z93}MyUi|~CDi}hdhsET2nNtT})Tv#N;%pRQG)FRhO;E3cF*MO{&C75_Oa8-Z47j4D z0#07h+-_M-)vxM%<}Ey#zp5oea0P-;E%Ju9vGFy{?Y7l);2L%(j)Sq}buIa}RZY5% z->HC7{XvWA7Fb_x^=)sp#SL*MtPhiEC7I@a%iEOorYvEMy{Wm~wHj~S)WYvtO?7^! z-2JY%anbMeno0e`AsF_DmVD1@+71Ek)DZ#&Zi(f)pceawS2mCaGyqpojHr0p_yW>; zqOXXHnzuz6_yJD@2ZC{O!AJ34W)Oxhc6rRue0RiRDW1;F1nVIRBJUa4rvX@YH(d$x zZh*;68?4H`bhg9G48k}y`>x7SuR?4<}2SdAxxOTf4n2zCXm zW;!eKN|ZF(QyO60z?g?60$%zKtU8#!tEv0IxX^%MdKg{{15pyEQ}5TB8{W?v0HFm% za6^LwZ^1X>`EwD?^Octwgp*4@&8jcKs7v4SP4(Y$q2^9F0x8jc>{c&xI)M*OX<+!q z8HFmoOW)ey+dwc@f!}a6_#UjPmWJ0zkl@%9(U zP`V36bG*z3gFb_Gt)}5%4ejn`JP0!ylu#7XrN&@4^=KY?VKOaaBTaV7_L{X~=MQ zZ3V-95H|7ANO4vRefPn*Qs6lT?)E0;5=^XsnQ|NC6L-%ESg626v|>gW=jXPkW-(Yz zze9@CDI%g+9)mm%a1jUrBn&x03yDU7lfHR$`6&>jfJ;f8hu=~ggq{o+0^ zGk%-X3k-h7U07YPM%wCj-o{bI?8&a^`gurbC#=RvBwumlgW)}lL@zT4J0yl^85kay z!m!_wVhG>~44TVR!ZF8arXLu)hIqmnKN*bP5*yWCuuolZDk)|vHpCSclMh&Zdm3J- zbVjPN-8~cLC4MiI2NirZ4_!7e9zyhPnGS;?U_!jr%%wj?1nyzL(w(V(1NLeDp9T}r z%>i=3FN=P1uOFBruJC#o*e6T>1QRC{KDze7FOBkiCLA^rj5>Aqsy~6zMiDtx@N1?W z9LzYfoISY+9DV}Q^msBAE)NUsk(tq3r09C#s}!(Lnrc+YxF#im$pOY1avbba$ue%~ z)}fF6z&`bADVT_TzKN0sR!LZn*LVt46alKQqNYw@xO>Ed@0mz(Qel)Zy63>seP*m# z$(~#SJ=#^tpznD29z4xr%d0VI25djwz{9U zx)&*qF7{aX&&gBN6>nnH0Ap-pe&d^&U@(q}v)!{P&M#n9z%W7wEvB1bo?03fwLDen zExu0IqN?4k99sSw5@AmYe7hNp{lk300qqGGd*r0>O;Y*ht&>MJgI3YMjwz%X=A1pU z1?B-#Sm`_an33qESLY+-uWE|<3?m{eYD=(A$P)p#8jKBnrLQY*z__hp8O15DzQv&5 z*C7uFi}GGL8|dryWTe`o6!c*Yy9p+P8?1aSgZ`vO1b2dD;;XscV4M_UUCC0L`vA{R zfk-h##HKhOjQWK3+hAN!#0wMUy&WrLESxk;|_0ocqKS8kB_K<4HEWr0^@K% zH#Uav!777cDaYaYAedNhSu%4S5w_UK5JZi^XcNAuMczm-4zX^NX+86_$T)AcpjCJe z+A(!Hf^lyV8~=EjhdNj#_k&S`I0ife;|hy<_-4OSU3+R3gVm*1U9pK6G0Z4;qN4qJ zF+8P%lz}0(+XG;-Ci?SU=7|Gwm3m_O={Z#p*N>QxHBu-XJh8F2X~r!RR5x9?t4_!MKQs9jR5MPow{9VPBVu9( z82isJDZI=epH3jgmVzN{5t>#ongTWoPtU=yzBTuDS%VbMAb%b3-&tBeJ{4(|z6@Yn z?*xWtg3W!*NQCQs!NTg&rnMYZj5SsWc`w+#jo2b_YPxGNu4-d<^MQwMLrO#7YbY$< z7V0(kaBou=r10ny+x~c@ID17bJO|^<7BjO^J4ZKClJM%DW5103)5{qIFRXX)Hq~j5 zGmEZi8B#6N>na@_dNEf`eqj0w?oQJ>7_|Gob=2dKwAD)^LQF+F!n1nJw?+!iz%!=V zNO41i6Tk6t2Em&VcthT+lRdSb!D>9$+3r>!XJ?Nt+yU5?{zx%Mu-M?O_%&VZsmy-> zDc+xe!fss+j^`|J#wIZF_D11um|WCgT7Xn-ZS^c4XFY*Rro7#`2C{ljq~w7CF|-U! zA7cBHW_U6WhC59dQLKl<3%qUY2gZX7HZ;7#wGIrwuz*sTdWy51!JbkPwZZVsGYnXc zR1f>=TsS!N5@$j59!J_9z3kyl(3v%m;#D!+C_F=ookc!oSf$<>Y%}!-tEq2_tB~rb zm+~BJG3DxmQd(q?j~NN!e4c%FfiVQIZ{f*?!QY8|RBcgSqMk0l;m(b`rq_|Xw z1*K>JCY8QC^+HN)2)Io!Ed~>h3IAjXy?HppR5g$q`6f#gQXO?i>_v))T!Y=SoB^-f z1qnsy6{b-H80WA!4y*%XeCds=@4&bs>XB+{9n2x*nmHdS{ociN7b!f*^Yu10>B|`9 zL-!w$5<1{jmsBuDm=QAxk7wjFJ+BbC&Fjy_OyQt-px@J5J%p58*zs^d3DpO)o3|+p zDgAJ7+Kd!lZNLdDa~STJb@Q+S7{kKs$!#zlS3^oq!|2goW)M0Dt7%h#9Dj@Ox|7L^~NaCKe3?q1!FaF6qpazNmu?HDV~4DYkC-F5c=!r1ZtW?&*YaRboUO;;|QY^`~I$m42Eqwu-gmk1JS>%VO=RKvjtoPQorf z5WguEXAk#-bP=RtbxA=lO!1Dm)@K=*$m3e#oWKa+EbD_5muL|&bJ7`i`wL*)*+gEp z#0<<-53GYdEejrkXh@1Fdk-m2f8A)cTL1LLDa2wL0frl}7d~brM2nnQRR%cL2tByN zK*elG>FZ?%5i9Kzj8Ue6avJDonI2#~EF&_eTh!@boCP?nx4Y zvhTs@4e@Ss#Bgy>C%(>K3&x7M^sgQM1VdEeMgB@Qdw6g3!e*0Bu&30)2_B61gK!BR zQhWew28QDdwsZd^F|hgusICEHn}WRpqbo7xLcGi)9E%`!N@M>K_HchJf~z2HudSZ% zt$sjC6y~eUwMUAZ4gEZ38VhE%d)9QylXD1?F3`E@tm~5#CEgywS?nuYW<|UeX#jHT zC#n*&0C^Fe@yU*l5ucp+uxx&OSiS&0y#5KYd_ldmjy7FEAqbdJSYi>0MI{yk@*>iJ z5+t~Y+lW{UVZM zb<+{&eEq7)_G9o_iVyG4R^Y>n$O_5$P`+B?Iw0jK_~gcCpXB?2Z1`tdNdA4RK1fOr5>P& zL<%VDB+*Y6ATrrm^8bV^(nXfXK~lenG@!fWMDm`J_rzcN(Hp?gO~>96`^W;HLyj{J zGWtbi62}%@F-{vg$3a5Bh-?tY1YM2;fqtbU{m(@uL1qw{#MY)Gb}#)RQW5qV{dI1A z+`uy;hX{+EUT>7d(Li3GLk4P{@v;Ds4NQ=nNQK`4DVi+hL?);3hrMU0p;#CaiYCR= zBu-~GE+W<1B`31oxj?F)4`kK?{*XxdV#zb2!F~+Q={-)C*+dp!E%|>!mRpB%G-tgm z|Gz=j+vtc`B=lSRT|nyEjUQZle*vRAQk5U!KB5y*PmB;N^yKAW-! zi9E+ zWHJChXjq7pXGF>)AgAIenNMUg7C)#r4oJ`RmpIr-&VMBIg2RBkh*V?)(lcL6`R9;| z$H;sl>x~7np$R}{eTN?`H(BBoiBp~6|F4j-R0@_!TrP2i#AJypC9VRpp*0fMNqLII z^-{i3;wCBIEcsT+x3Pz`Y`estB<=umtNB^-gFs$HCV!FgBT`Oe^0<_rka!YELw}Wc z28e&kdH%p8}lO-+#Y0(vlSAh(Y$3PA;|6UR=BKaF2Men7Y$oqio7y#)Aj`L@E-SPFvca~JXG9ik zFXcpM`+;~vIeq)?21EKSaJwkqI3syDKtu zWW{@uXGD7H0p!e2lleq8{0PWkelGK0$b2GQ{vJq?0o~W*&x8aOnk8lhGWhZUc@bG5 zpX5ZAF9>9V{10%cxRlJ#h%8rD%8AtDCb^DWIhbHiDguiGTL4+HHIRxs$$UR4?+T=% zoam>WW)U>4hB-;5Xo&4 zM*&%WjO61bpD6hhApR*cOlY49XMwSyITC*WQo#Zs^Os0m0mMIL4SulUwG!6>>8Z_< zZ;`ka$SWgK-ww!eKcpNpq5mxND}L}{>2+CwNCSSCcpJ#9`;tEd(%{DwN~A$gzFE)@uFQ_^HXA{9T9_(bAUAPsp2RGOS2QKpuuJ!^=1K5 zPgYtdQ9r(ClLfNN0y!k+l$Z<1i%1*tNlv7~{1OXDEGYAd%r7K4k%klj@_Bh}a?U>= zS%Ap1WLqGkvkwaZ17tptJV;_+iJ=n1B}Pb$k{BZ~PGW+@{y_cYHc$!%OB^b3xWpuh zBY}*CF+h&xI3TajAtVd)6 z8zui7vi???{|VuL_T(q2U0VDnLceJ;%$tTyDkWKwr zfV3m4#B8z}k#ZLxN3Il*c9oU+8Id=5_$FV!h)mYQ50-Bz~fNXG&EJ&n+y^<5j_eoBqK|f3RK`GCO)OT3QiJWvNBqt)@rkp~8ihq?E{{&h7 zw5)dqNLOA1@@d2qAcyKXkk{vs^6;B74>rNX4CGzMsU- z61xED`fieU2l67)5PvBT0P>isgd#zOkw97)BQaiLe;^wkEcq}X8{mJ5K=~*jFCzIj z62FylBIVynPGrX>XOYK*DKdk|jHy62Fbzn9>_94*E#-5ie7=+~mV6nI^_NS&QsNqk zDH1mVxmNzf|5btw?Sz00>;+Q6K8gE*G~l4*hkz`1RPtkzp90d8=Otc|`Bx=g1M~L@w2~ zuVjWRkc#rj0z~$-5Rf-e6@XM!8OSZA29SGDeIUy<2C{*sKwh6i>i0!Hv6(=d(p**` zGNT2M^SCFF3VTcR2XaP+16i&gkXJ^eJOXmwQ1SokVf_(68affkp`0vH(-Gr80}1wQ zHju7e04xhUs29MI56Fv1g-3yG=$PaufUI{G$RWG}WJA}1ygrAle}nm)|4h)OcY$mm z703ejS>XQ%vJUm}Lp?N#NbeD4sOYFarsDXrVcwgt@PFZLBE6x1@G=qm|9|6cB3+oSl3OG{h3*lt=T&-;lfemzRgWygXDHfxz(a0P06qBIEDN%R^sY9{Td~kn-i_p)W5F zVKv0{PhKkGO8VvHp)W5FDPLY5`ttJ7mzRgWygc;f<)JSx4}E!g=*!DPe2M7G%R@Xy z>n{_1d3gvg70K^fzPvp2<>jF-FAsfrdFac_LtkDV`hWWJP)qE_|FxHg>|0kD66$47 zEd69b_co<-G_N-=-@)#^SLB=+Qzv_imIG(ao3y2N%-ZNZrj|d4D3|k{xRIsg@w0t% z4vCrWmDP3M!(xFElNY}kY>!MfdmQY+5u8nmWs@yUM<>3o0W<|D?Zk=4qLdVBKa!HxYs)R{JIYx;1{DplEPP>NP(I}N0OZ>= zj>}p3(0W=}?r*-G-0xlQPP?XWNLUnV$-mEcbDiJHv^^Vlxl8f!r6z6Z(!D@)pDwZW zwU@ttQ#$Oz)q_i&VkY(IF>*`2mnZGRR>2PwSHcgf{o+da!E3d_rDdxeYvQ(+{CRbm zw!6mr?-?7?F{FS2>`@~!(CY7K6j zxE}L9zSKtXw`YXCS?%LiLG4|rwfQ{llId{2N~b$Ed{J<~@S{b(b!qVHwgRGYTuYV_K7 zErV(eyqnAZ{Tc{fQScsuR($u*aXH6i$v11~$iZMA+upN_x2^E_Jj#-#h+ zr|Y!1b4@vX#H&}_GM_bvce(Sgzc{?d7xEmJ^PUSAM?JM99t}t~jk(t`L@C($)H%gy=kg)B8m<|-yn{6;(9rhR59*6Fx52T+*Zbc6W%k{9KiX>64PT=xc2<(0 zk$*1CaXDMH)P((y%M{;Tapba^-Zvg?T+qCU_2J9~XFvRKr@nj73@kl`a*&C7*OKf$KR(Nk@UXLu~LJq_D1dB)D}AOAQosNLg7S-Kjh zT(5d`{Pf;8E;(IkWl`j{+iO(UrrBJF7fp%zalq`}!!uu~-Fj^ECk1jH2zj#e*lBmw&ue4t8Q&R> z*{bwA7Bk%K!oH=_#k9D1M#a9ToMMWt>T)mQU6F#Xn|CbYw`;-OD$^5x@$Z-C=UZtb z0xI6}J6CAidH1>3`c6GkH(^ENc3Od2yG=)v*XQcq)h)R~2E8RR>iw;DSC{23whhVm zlV?>bJaa)|*Y%q&WVg>4aq0GjaqW!v%kBJn`PZBJmR)l0yIMO`B!NjE;!z21H6;VQ0;esQY5dW3VE zRmYwb8`gKg>(!-ps;{ojT~ydsrg5qJrFRcFR4s8uuEpCX`kH=7(iMxbE0s}k-(NO8 z+gD@ZH{YDUQ=*>h_F<0?%=0KPXkGohm#S>|X5;NMKNdSw?&iqRubxeq_nU2D%d@>J zIBUP%y!865b7bedcjtLXy)?MALodHQcg}Gs#ivr(;cxOZ%ethrf9K0jlgd9GH{igW zF0oCY7M)h^gP>8ZbDo%o~G7iPm z+xT_a18IM}-rdRRb>4t$*)L72*kH-{vhCg*OT29uvu%Ijr-e=z+1o6SVS~qtS2OG9 zJ9NXZhs(_C(f8`_oDp4X>Mt<`?Z-A@=kxjX6 z?=?)wR;l{v=M_Kf-n>67ut3#(cZZyBKE-u>Xo9`d>3L)KpV}~@)yNMkbo0bcQZA!; zM@k*bH)j6v+wYIXFW7uC=yB}M+V!thT38_^tCpo~vkjI7F^f7bUwODdaN&-7&a}(v zTF!L3%>7~Cm9JDNu1Q3eys@3^!!{f8*^kaLwlr2y>;-2V{p?;_ux9ou^UcxzzCZg` zT9+_w_w;^K>K)u=ZhEv$-&LKm^_;uMZBam>s?X-V-19y$XWz1yi+WXW{(M*KtA1_I zkDpbl&JW(}#ovMw23NG7`jJ~7)Y@xo!rJU^AF&C7nOo70Tm1(I?mqkOSJyKOCwVD# zd*rNsqRm%%-A>hut$4KQ?kio~?rgRf|GCioaZStKHV?bDWOwOO8fxY@enmJa*h%qs73^3{~G#bakGt6$&96Bd`d%um4$R4Tdx1(U)KEoy<6h7 zZ>^CP?{ynJqrru~*Ir#qagHv0&YW{ifeKqQx1D=o=;p`gGjyefz1cRX?eY^`wt5@d zvDjZzVBP^i&+XVZYh{druBj7-2DB)6bMNxlnT`CP%t<~U>$N(5;m0|7U55o;Zk7D= z)oX3muS{-xWcr}~y<*oc9qH%gH>pWqkA){)j;{?1*>7LH1N;6R?;Ab8ZC9f})ciKT z8-K{;w}0-|PSt7zEUq zCwb3zI-9|MZ+q9DU~cG6m^*O~?5|_Lw-18+yCA5u7lOL>`uia`M8VvB5Y)Gi+Y7QmRw=&+f?L$l*#3l7E>N)JCkUF_)g2Iw-3!67oe(s$=h_K@*FFdmDQICY zu^WQB6nwoKf>!nldmxy(-wNeMz!JNMtQ*hsqjRicndjl!}DxyHG@;kSvk+cS`Sb-*-OG|2f^S@jJhB=FH4- z=iV7V&j5x#qKrc_Yw;rO+36Xp>PD5Sgr(VCo4*iAv|Fp+u|L(jt$A+w()f$Dtgze1 zN889c%Lpyuuyl1$kNIn;#{}iR1}Lke+=w>5p*%)uhD_c7*w(`uRc~Mob3|VU!1)%y zsSdybm1C&Gz+DeuiR|hDycz%+F<2qeTL6)F0Df-)tWiCNZVciLFr?0r52?Lu1FD~5 zu3A+Z>YeI%toHkg{O@y`M(_Q0#Q%_K=sCc0Dd42unEAUI7q#zqY(y=>7?VPBl9{Ws zTV*Q=3I)RY$gdIhkpYNHX@^ALK}E7nP*K!700-2AVI06;dy@6?$$iH6>$u&W%)!#!m zBsW2SyOHPHM;+m4)4g#=Ue1*q#r^mDUk=d@Qa#+0|DZ*htv96n?&EvnVO(-oXR@AZ z?RRa|`Buq6quRPQLVRDdad=bo22C{e9{O9m8M@aUB{f4uIW17pDh3av@gBgq6(IjT zfG1kQK-UIf-U8r_a$5k(Ffg?OTtX(T0JiM_RT%sbeH#F02Y^!>fIlk7P=|rL9Uu_d zwF7u{0yJU>LZl7=kuCtg4uD`(kD(ibcqhO$Yz6owl}t_MK94+3lN8rdI56!0ah_2AdNl%;{kyD zJ^+N404NiYUOyB`$YcP1Ts8L0I8@PLmdY0 z4*==N?gN0=FhC=QyNEOdATk2rHw2J{>M?X<5Fdu&kWD@uE>L}=AJblY$6V-GcuB$L z-6j3_)e@eUSACZ|_B?X?f!wY6Rdu>cKFMLE$4nJ_`E?kT?o3h2bHR`vjmq29W#-pa@N1 zSj3<`3h)RejRNF+23W=L1Zj)`7>@(wj{%gTB@A>E0Op?o%2DoTfHDkB;{cV&WE{Zu z3qTcyDnvg4z&Q!vGy(7om1C&G!2JcF2HAZ9@cIhSh~Xt7O#+Ba!67*{?%-$i*5&L2 z-xvI>rfd%=j_%ma{MeW6&BQh1)%UK)0=Yv|QWVX(MFd>7s=%p<&No-a2&ri$M? z%szJ7muz2sh3awGZU~FSzd}84koQ-B&}o1H4E0EK3PAQ7K-3gK1M0yrjzM7>pb>>l z10;S2n8MJE(vqI;j=VHXWovES9k9ka%Z5O{~@bLe4V6sLgv_iU7tJZq!uJu z`08lnQ(36;cU3TGSW`8=oSGV3KK!ucTM9o}2lS!bSqN)84`EdkEO^#_zHR@Ql&0P| z64xb@>PF*UQCS!p|GlR4S%hsH3Rl=W9=SU@{rJ#f0 z?rAHdC&fNAS!O=J-@;Ap77=jniQ%jO?XpAg=SjQ!)t;K+c0SGXl>c2jVz`g-@0V)+ z&i_6lI~-PI5yF1!)?GD7K4;D5-ua3%Y5efgajDo}BbI56LL6OeJLyg2fB#;18Xfu4 zz`5SaBhrragL`gD^0~{7JW^ql_ksZVI2lE5KOt;4ghd@cp*^1w-vU7B57C3m|6| zAnO;vJet8^{2Rbv1z-U!!TS`5ZVkYE6<`VFt^$-{VEPU43z__;G)k}~z?Q(TR}uXh ztiVYL;Isy?hRQM20VHq}GzpZH3HI>UffoT@H^OToCE}t45Fx^E_))^Pqek_V1n3P6 z;sgL%5X5cT<(2$PK9x0wlS5T{VzYe@e*CN$d4~Juk7ZmKP-lHf+kbOcU+ z4@q|8z6zmp3Fc9xGeOflh(&;QpXBguVVd+GSxlDvt{HaGnPP4AJ0@7Z(L6eFaUlEV z<{=TMX^NmCTEhHT2Oy!^7n?g&%&LsgHm zm8s3@EUEhsx6*_=zshOSDH{8bcD&-O$I!-yZ>zlu#P<$(HSe*qKd@1u^KfyBlE{K9 zS$Z7g>FuU*$mlwHAnIZ=V^X}EP|4^(j{U|>htnOnL~UE#%%p@f)w#s&*+^x?t-7*B z6%m9tcC}kmg@dF^pK%!8-OPItt>W}9Zjv(ZV!pM=(se;S;bxmEG3P{Kf2e`2I=lLA zy4vm{bxn4YSyQ$hX*(+A_n^eD-&qD6o|*Ag-JZLauKz;7f-F4}d3wL@y|LZRtu7fL z)_7EC?==G-XZr^8N=Mc7B$aQ)mcw<5A1?lP#U+IF>cf-Q^%H0Fy)N-JIqmfAo3Q>Y z-fB^89f9&0AiazilqgN@#M#u1TGx6{Ut%_UA#}-TWAX4#$u_k^le->h@@N$(7PX7* zJN_Vfe0))?B|go$aTEOTG!;B};EBd3u7=F4SYPnx<=N=VvdJU(jM5={L{w zn7?LP85);cE^Il)KtJyL)`eAxTUVMM^e}7nWlc$$H>Qq{KQ+aokL%RQ}#17#zB?gbJHQz4|o)+9IXlG!* zyP;PxYLV9e`!~~|Jxq-`5mEI@ts#5$+%^pTx*se^xJDLuJ9%KK{bvo6nYLG7)>=## zf1RA?ZIE_G#Pm8Hm5)Rq{x=bRnu&l|ZlxCRNTl&;O> zkj3TscaLqnLsQ+RpLSS0o70rb5q9vHcT%5BtxoO9EbA7Q?}x5jT~RMg7!e3|2$_@f z%ig%tkTOPBN9vmr&wl?qx6<3#uI5V#k_F}^PxzRQ!6dcmyG8p?b$0SYthGr?Z=SUN zXXkNrpd+=+zq(P}TSVGTMPxAY{r%>~#kSSk0fkmaTk@G*NGI<`ZIs!G2j#ym<|7ZB zz{;C3Wm9Q!YANKgMD4eoPc%mNvpT0G9Ha|P@@w~fJeZvKO+mv$$;hShrl&&KHQja< zcLI;|zT-5Pj=T|Bz;}{|PXPTr@$sW2T7nU!0Mer)82RrYujVGZ$k<(dmE_v09l3+G zF8{smP?nl}YVEU0m}O6j`R&u1Qv9B63`U~w2Lk;=5^s8!6D8cKj&bLrV-23i4!w>b zOCR^qzlF*-dRzHm;NWBD#q{pDAy@y=;N4b4x>%3@T!h@ZE>8(ENm_C3n(`e{=?h_d z5_K`}8^_g8eRoTFsy_RfqSA%VcGS2L2C(s-XBFZJ23&K4dlMo?r>^G`uM1k~w3t%Z z-uzI0z~u{H{YKZ*dqZNsCpk18+t>f?1Yz>@oJHQ0 z4~2NDe9398a=As=j{_ny_ied?2l~9>c6ahEtOv#e4yvHi#o~qj)j^{cgL}8 z7HJdnqs}Hn!Dg#Er5X9dpK@+=Y|!M%IH}s1QanZ0d!k5E56)<^%y34#c)8G1v+$Dx z_aow~%XJ&F<4(}(_A*VU?i%D2w0OQ_KgaI$4)N2Duf9}XZ+h-I5#Rk#lklHtTrN|$ zqRn%G4)UYQF7j4yUpU(Rv~ER-pzRqMfeyMxUTM~JT{$$s46WYw%hbVtnD81vPbza-0^iZ}*rFnN=mcro7d7r}}HweBH`|rSw%Ktay#gVcC zKGU;6tGly&_cMD7rIp5s@A@%Epp3nJ{6gX5W3r4C58h`D6%3aRQZF5R8^p}sp&u0A z%oJHh9e=qYILV<_#QC`Dk8C`w|LsZt9&3SzBNdI%iA8Z8&ALPP&+0iQ6@TuxdrPNH zlQ;QANAvecAZhCse^SA~!U##t)AyF%4L!r+ptmlUC7kn@re~z(ne;Qok-}yeiZTsn z;qGd09QVx5XeE!>*wP6_Qa&m1P(B;4srv51q?t|OV-s#Zl6qx`sqdzeiLdIFt+^NW z*DPO>Vn}PhQQ|?Co+SB3u`OqJPkM;<&`aGh$s=)g308Dzv!{1ovo78dbn?2HlWKUjJe`TLry|N{gPv+x<5K-@ zuzMo6@w=7f4+(MGi)Org9w)p|UE0JEOh1-=(Td@XyZkOq>GqL!Eu+H$uXsp;2{+g; z7X zY1B}KRGy%ai@aU55V-i%j_cLrk&s_XU%GDzYcyZtYrL^-@5B4@0V3Bwr@sHt%#t-f zn^-0LC+^>iUvR++UyAszXReA!7B zpBmwRf8r{W$DMII6J1+mYLvMD=}mswD;DFdeg4|UnxF4$t0#;0UpELX&pjgYo67Ck zYGY@JDg#5V&r2WHg*?utpEe8C$s7rPr#OGFe&K6^{}!sX{*Kzz@Smo0>UL+k*48Qx z##|0tv++zhH@SGP_l#mt{NkCU6c_dzHH;?Oy0?q2-Rxwc(Xjciwms)m^UK^#et52q zGsah`4wDD2a%(y$#>4c@UQ9l3N?Yf2W1-%S@WX+UUn0L3KCtUcvlhMY#Gk^w)VFr-hN?_7j+piY;xT&}#!7udnp@zuTl>6_Q+a}p^ zV`U7S-Yt*M8XUXBVc{DUw(U+WW03FW5M!<$>I=@hezM&>e7e@HFp;ccO{B~MVaFjX z%3^_fw9yQP#BBfun*nrD`ep!iZU7HP06kR02s4=zh_nF;edLCVlc*jS28fRd3Pa?L z3nSEm3u7d@5egF&2n8zRg+TMTNoHsXN4DhyFy8`Tj&ipEaPk8%u>x2i6IOsa3{@B` z5j`7#mjIlr`0ujs44&R19ea#(U|!uczK?}eYE>Ci!d?+2PS9Tr^~sJhkiJ$@bbl$t zm-$U#{X=J~`~56=GqJ`FBb?45f1if)xBIQg2Zxf~d-I*)dVc9NJ`XvQv7J5e+!jVt+QNp?P%nasa_vp(nS|RmpZO-0!Qgt!?L)q zANUXZ=(N>6+*3k2^J1zuY~y$Ji~W71c-igKA=_f^8?YRBYl#|hdMi&yPkxEtm2<7H z-SuMs7%|%9;p)`B2Uk9Qj`w~3K9Pr)vnQf}p}#f4V`oBj+!7^sYT((X&!*B#kA*hx z3>?SL6#ADQKBoU$s4ni}4l1K9-_!HtM$$!%h|KSqQ!6INriN(vXet7pm`A$I*Axw; z+#zMj@-k3Yq_$o)^GN0>FtEAPALX5ZMjD z%?A*S?Dzn>F*IVhhDiJXp?d)Q_yNLDJqB4x0C53;2;?mQFpgmWLlhF-0gxyK5VZr~ z2I|3}z86425FiGH2?8u)n8FZ;F=s6_#8BRx?7TNyY+)L8lST-m~MH&?yDcSy}hF#AnAWv+a!L4bxu>}a3Xmu8D= zb-rJ(X1Tm57BK&gj2|#;tjccD8Wyg8S-qT=$4a)jlgS79**K<(@Hu6=@P-5YkA$xU z%q+x-?QUs!pDMO=Wy~;?QPo&*D+jM1Ns_;@BJ^(gQ(BXy9?5BrFy|w=W*!$vMYM=X z7WP549lD6Yc}oWEhCBING*2fKSsSY~*=HA%SRVG>q0~9p(V28EM?!f(r`-I;O7#5# z2^sVLUOm6@K1M$=kEK~lNPivo4Z9gBiQp< zpttat#X@H5dLE5I|f4;6Cz}02s$G zfFU1=?gmIy0EpTR@DTN2P*(&{*aLIqBJu|6%O-pH^;in=J8$r9i%R!BbFbo9Il*|M zH;*zj{#4|ll|7e#6z^baj#utDFrc+~D(8{Za9YZc4HJj?p}~`z$Iz4_v~OM#Dtd&J zrJ$mmLr_tc6u=WSgTYt{z+f*xDN5f9K&K2qy$_%q>Fon3!|(({C8CrDuvG!DmWE`j z$di3=nsz~@S#rA$uk*L^JuNR^6){EXnu|Ge^;IuB>T(RZOqpjy&K38s+SZ$^^{3}( z9@7o&OOxQqKUqnY@Xmi8F&&1n;(BkGbcw$|&`G(5)GL{xwl%21RYw00@;f&Bp zkp~~OG7{w3?B=&gFt?m%Ha#$>;VqGQXgBl8%u3hTJp8`kza!T(^6U%`zMgXS+|8Xg z<8dKhdGN`}_r_8sGZ$WHJJmI_OKwlTo5u6$XZ{Q+KzGToxpKou>$WR--K0XL{##o{ zKOR4I#u8QG^d9m)zu+=!emwSKz-MO92l(NIH{9{%^gg{tmdJ7=%J{#8LSWyKFQ*Y8oOe{im? zQ#L5t^=!}k5*61)^FV#6XYWX#)y`9hTNTptS^if1k?9(b(J)^{=ruZ3 zamovk8{#u-MM~wOtP4IdH74(Nm3v07`;}2!{4}Cex>ym;t=^t>wqnPt;Gu70fn@2u zB2UlM^BSu$MUL2xqG~hYY>Qpq5j6=Xgp*aD71 zmLJiwsGoSfz{*>^{os{Sp>wDar-u?}6&`F8zLjiq_R&KDMx)PZ%qYD#RVgXn{aGe$ z1=FSw7VYZsT5ij29oCFN{B%(l;O(H+Zox;iRGx@a>$zM%QD$Ext#?p+dM6mvy`tV`+o zmnxB;8l?B}nu(M+Q^%a;;krsklaE6;c|tBzPENil(@fd%^F*h>1Hq>GF{i~vwo#kSTTiywmNyw{7E11sSCF{L<^7KY1T@6z;mIk!e?&jsauqk0$Q!nFJ+wpFB z`@ZVq4}HGP<{4j?>5U~F;Bscw))i7-JP>`Y_f&VOkRGS{_`t%nAQC?c=>=SWGCI%v8Du1d6%7j^4j7NS^61{T)Nq(rMAn8X=p08!?~Bk{GhD zihRT99#4LhZy-<4On~NX%dm_4*8Q7(V^rJTEWSC{(QJ3OMQBRjvcp7rqi9fZ*>pxd zsr&B6x+3a^(sJ$>Cu6P+AMrV_Wcew4s;>YI;PfcvCwWrUw=Otdj#*v``K|Ut)v@1) zT2v>$8QC6KM+k3((xnSJ7<5uXOktKoBlhn;3p;i zTc{Wv?j60Yee`hs^xC~@ji-}6U!&+t@*nN9Ax69WUJ7a1>~>uIalaNR=xjHITHTgc zk<;hs43sTvS5{Oe<2Sr~VT+>FA-z@R^S|%e2~N|Y6LSJ)*-C_)dt@8T87TC-nfRwu z2zt|Ze7~z6QD~a^{dG?DQxbKw+PlZG2=G-Hl#Ze6@RP=E-;++=A|N?V(Q#YOEvw9Wd0;`DUMl!-I6nzDd=+AJ2w5| z-)8&ll?X^d1JYwrG7eQ%h_6<#R8#p`xpYD{wk>3qR(ZI{p5Rj|eP>I>ivtgrq@E2H z2J;+PEt3=9 zvD5@GdTrMqOp~S?s_Cg{3ng9D@X+%vI!EX4dCSLP(u5?fV-ynp->VV?bvcC#d ze%;rLQuCFqug`OJt-gNnpy3N`uc$8j2C{tNnz$l_HP(c)UcMrn^}5I#pnv6K%<)OV z7#@=`(}?Gsx<$7HvM&WmjZLNWY3{#Jc-i8>(~>+b#`@aB0-dDzX7Bne^^@DxwhW9G zP_&L&-RMWFn$W#;)}l}NdM$j8N1L#(JoUJA=;RbvV6hNk-sq-INqw6^`t|8&y7%&a zZn`MIX5D;YPt4O$QvtuU{rWyxXQKER$ge#2kY_iU@Vqy#^vYO16~~N}y{Qf{UHSkM z@%&UCvKE@04Zc#CnswPchKZ!eU~@E$_tOWvnK`4|e!gatG?RQ=S9C7jM)_Kh-nDFn z^Nw%*tYWU(O(Y%4|H3dcC++&oM3PRg@|Tddc6>h-gL8tsctfbZu6yXopTi6@c~gU* z_BT{%W$o-X_}xj?n|(ieNI;sTUzknz#1RY@MuV<&#>En=Z*QLjUfrMxF%Uc`|b$`8l;0)YNLvwB;$x%O2&e z2`1?TcceXBIlFjZrRk`oF!g~G{>1_B+wXk}mmo{;Ga?;^Aswm*$1lIbaQwp0+x@pt z&0RkFZH9%{VP=b6m0Nh=r~2ffg$6}~6c z(M7N297j~E3v>u4{zKv?;P~rj0w2(rq(pB`-~$@tCkRa1>5gO6cXpq7vM)4dYNM^= z?ShgA;@)g*W9Ro|b}|3@W~91lyOpqZfiD%aj^Fmj+$o7YvcfH-pp~0DZwvom3pq;G zfGJAkV+wH-^`Qj=rf{VHhFWx?N$Mv7q|E?k5Z@7iMGRT`0CULB7$C<0z~CgnJfhSD zFg66R)&y8UMHuLe0NAtumXM_uKpBQt7=9rZZ2((i01s_|RaAq4(*!_J2Vf1k=>XJW z=l~#45)i*Gr0Qh~60D0qwL(BUkAsMqL8m-(%YS)(Z|3F;74(uU{;Vy|)jIBPRhe&c zaa%DiMy^i1jY!RVI;GjJMEZQM$NtTM;jA|`ERB4Wdv8Bni$L3x$U23ZfO>J3FQU?@;L$}69I9-1|w`Fpc4jA zZX%#OD3Q7aL|`(62rL9-Y6!B3WfeM>u!VrWoB+u=4U!DCXtS-~eG(5TD(8LzYl4+P z2>XVv-pX3bb3-17=QYz+wiGzE#Q!et?4&Ucy0sWKeP)N6xsuV{`sbY)_qM%@pj}K* z*{H93EjSHnTM{_6S=yCsH)Qs_qUs{1cFR#+J7_P*`0Bt_4c7Jw>91jJdbvHNk!sOO zyxs45dAqipzfzXtnle+T#BVbptJ#x!?eCMU|GwqPK|p7WAYZyOknbxa$oJpFmH*_M zQ21YUEB#Q)1I-AtQK^ZJYR3BS$x>RIOQoCZ=-2}HDHVP3a$bH;O4B)1qVa^tBorS# zzcXaGqVqrnlO3bY6v{sX!~e-K=$Wkq)C#?dY^|UsX;lzz0=l9K!g&^C6blalNvVO< zVM$N};e)M)#mgE*^(cq{0mU8#5wQW8$07*ZQ5~ckOO`r_FagbC2|Wj5paCKZ+g1Za z7JtBs8t1bMw(2pEaS*NFYAm&<8_Xn|Xe+8;w8yLHIXroD((SE9!|3my0+gBOpDGm( zzj35fh=~2PU;Jptt<^9S%3_gc5?>77dk_jD@Pz!|;SRn{VFrUf(GDsa#XBE-58^)n zb$d8x21%8d6M~F$&hy+(PTPJtJ&P%S_gcl1VmmK}r{^Dyz&qX5vfD=z=X*5|u4YSl z{&-h(kF%*r$elwXgYmvb{u>xeP}QG{Mff(d{}!qm`}9)B)4?xFwd}^zE}nSD)Ok$C zDdolt`+m(TyTT7|`{EsWMc!%p+jzSf-4CkYeO);1w(q`CdiLVABXv1UOtXI)@b|iz z6nS9!C6|y(GY2*0NM|L=jyZ?I zK4N$G=9=xXCOT;~CWP;IPX38YL5J^&zxUr$sd3g+$KNi5>+UGrIH&w`u4ske@^#AR z{AatNtrmkc*<*7QovsRt&#Lr3^c2tLJ$D@Xno!z)BYwclvAIR-+`FjcdVDpK5^lGT z1MeeG_-G~jsL3JjY_S8Ic zXKW$kuAL;4TNXz@zTy3}MO)*@pTJaq0!#ltf!7w!YHV#<-ds{uM=YPAjyppgS#-d- zDTnTKnA~8L+gQq1;=9H+DyHnQ*&ADyC^^{2nOXL<9GTO)YSy8!H3-M`hkK3UjQ5iV zzB02>WPr!GD!?#{QpDXzf!$(9-&>vL9V)C(-572K$E2P0x^iDSnTb;{Ws~;oqC^)h!wFxI2d5M;%|;`M{=Anjt)3(^#mctoYRri?Tk8 zd)!B;1n9bKOBx!@k}3wIV&oZ0(s-mhQ^T5md$vW~O&r`t$a-RiRvlsIHL!p&EQiu9 zU<{Wz;l1ZHfIQMW4PfgG@C1VbqO=6ybOErogyYB|@?7Ls6{rVE9p4@FgWY3U>5&*=bJR+b}T$CJ)oEjyJRBIo*WvO#}&|3bBHOMS-8LxCH+T_kx9K-~i%;T(V- z8pg1QLDd#OAH~=LjL-}QIxheNdjJ!ZZVym~f!YDU4Cy%l z*m?sz!C;Ok9RWCf0IVGWEKm`KIt*-10G7zo3Bcf?WX|Pz#1}4AO1@PAJd~AkiOS6oU(rJP)8A0LR7_ zZdy~Tp65XYDT?$eL2*Bfih?I8PWEa=H{QFn_m_%+8Ye5ohdzZH%{h~)oVR!+-{nbL z%gSbYjo0%xhdlBi|ImpW`Pi$ubF$kbtEql^&dJL{qIIfPy-YEECgpI7w@4)FY87Xa z2;ZI+zk}4*SmtAXyvvmy;Xet0^#Ie*jyP5COSX*mQseQ1_ zx#PxGKh*cOY4O#Xjo7c*RLZs>rr**U+5|oBN*%Z4s+^Z0FSI=SVkFeM_MLwF`z-z& z;@{x=_mq0rV=1WUQeTyy`QAdwz?t=W_tMz>*maMb+VfU}(UsQ-AFtE~{F2dW`DtNI za!~ljR=7hi^%CdlZ-cLQ2_{O_{FIRjAHRo^17Y8YZ5KGzEFaY;X{NS|yRNjT`jMO1 z@E)a`DUUn!XG6|fu%^lH82T-FL{vCj=AwpZ5dXf+m(uWr3$FnULPUT*zTo~ZJrAUO z0XB;971&By7hqTRB=63>lgDx&(9Vm$$U9DVKHCJY8Wy(iV0ozZVEG1pMA!MK7ekNj z_KVdYvi?rP7j5MjzBCtGRdO@)m$ksa${J&gHCnv_z1i?%=lKP`kT$`S8&l*G@9_PY zd2;iaB0XPW9~(G#1LUxyZu;wiBdFm{2_;MK5_x(Gd9RMLa(>~+dacE} z(SH2flRIOdLK*XVGMW!Y1?{q;v`LpTl|Hvfia*#~rU4gp=nAU&p+6`TMb+zY{<|MCl0|n==?TwzVhhlK!X&LmdV-FMvQ~ z=>^~w0`Lk$5MuEL5V;26;SCUsYA|$T5cGiobB(;spJeYzQ|4Scrg{8g%g>CbBZ9X~ zW&0j+=AH6Aa9FWy=|RgCqr7K5`(xz2{WrcZ{9)D>&y?9-Rc!CrQ-R9GCqmTs|}Z~o@nClU(S@;t#(k< zcje4yn-V?ii%lZ;|4wlJHaQIOU4nFF!yw(@OOS2^dAd5G`v@Dq>U(S7xVA~}&J)qi zM)kox3ATkdwHHN$v}@`2J(UfgaN5dI|4?)6)`!4*`0sEF=5RAdkU09Q~00Lm~>2Liwq)Ib2+8vsu* zBqPcz0G!bP)>i;hQ4xkZ3~WIF>Buq&z$*se6^6TrFhDkP z3kC>{1L(kzi}*qSWa9yXLjdlh77XJUq^|+wqrhtbi3tFs7#^aqPyqFt08^m=MJOf= zU=hQ781&*J@?KoBANiTgyzO1}=j)ccHpri5ESC{`HM>*j;IkIx2h)L@E^kq!bN9Y_ zu}n;JyK= zJR?tKghzJIZBII%sjU`5T(73Z_}&$a%gztJWLMpHSFD^aw#!hgf%Jst;)J}-|QiyTR6 zT{PzKVo6lxaJ9Po>dcBG*RO~$y0V`aMHFKDRq5nO9~tWfyU7lVHRRb@)cPJ!{MB*Q zdTrC4cAo%7v%S0Dbt-S8uBP~|YhD%io^XLn!gK8zSH8bNYfdD}=+tXltqv%;BW-ee zj#=gPvKJ+CzC+;9syo=BJ3H2;eeQ7l3xko0PnG5}k3SAGILxzl2|F~iR^;#Awz1%r zBs`c>;Du*>aJ+jI{d?w`;IWLy1t)~;#(T-~dx`9BKzE8HLw9yWL;t=)H5j@v_+fa1 zNYMbHDFEUz0QJZ_20%6yU;sk{5{(5I#}E|@(1?04B&Got#x00#s79kiqb0OPv=s~EbF#!UdaOo05G06l04Lm38h1OVS2Ljbl} z08EJh@V&1@0M2^=RT$uVUr7LU7@U$|@C=i09_a#J*;4l7!K@xG@;haJ+7sPd@91e{ zMoCWDx?DdsL9MwXvB2FaJ(vE?WsRpEUa9B1KNZusI~7P=zq!!G=u(B8vI!)ATMD<4 z9YXDbgTqn^W$hwAU*2mFm>zLjRA9C$WtUZosJ?wKd&u{Z#bM#Q!LdWXvgzHuZ+WYf zYT3U1kN(K1&1Bo=BYAS)pPtL7__)Av%ysy8ptRa$$cSg}CrNG|3dGNHz~DN=N~iM8g=yF{q{jOre-m zfW-R%^BBG%eUL>gt5~)W(Xj_0Ib|UE z4?x(6Xc>!fIf!{a2nP}6<%7^wfG`z+kch~%0Hh2{6_%|;#PATrwi3kYAqY1SRbb(K z3c_6o!b3#%g&=iU8nN&}{}h3ERe|^wK^F?3`XcB;k!k?(VgNzpT@28TVE}_L5`6>^ z`V1iI5r8P_!65q_K;bdKE)@0{U>w5~25}_!1R${nAo&TvZZv^G{RMz_34kO@Dgju; zu!>o>9H);7092886+kzJ0Srfx zXf;6STY#u)01ecGLAC)v;TeD?3VQ}Hj$sOeHj;Y|koXQD`8j|tn!upm2%ud9pofxb z02VQtg+V23| zP|`bqMGUJL+>u5jKu!-pej|VfTEbx53t-*^;E8gZ0OS^>NU0sLA4f>1pMkq-dkZ2-Z@yA7Zl z!vKbBNVFXwbO<1-9Uu(#V2~XKQ0M@NKw%vK;~1teL?Jo&2q{V&0Z8ryxPc}xsDA{| z?gEHGNnHSo7*;XFA&qW;oKFDx-2e$_34`${fO!u9Lb*KvbYlQay#Ptbq!*wJLluVG zh`tZN_A`J}A3!oH$G|xbz}*j!itPFU>M%57NJpdr0IvxEzX5=|s2+pJ7Xa}=I89~2 zV~hSRRNK4O0=#}Mzun~0!Bv0wzld4Wfe)fO4pp-4AFf^HoUM9CxvxiF?%{+QJ4cCY zxH28%+Z|cU_qg)Ln8uwQ>VxOttNd`9QfP!LLfLQtZiLTqhfYFGQy|*8K&En zB)n|c{IWpK`nis{r%GWs1#bp_*sFl6K1N~2&#p3Ne~ULaHqqQ?c8=|JxE=Eos+Dox z3O|N8d7;-S@MV6eX#iRPvwYDH(4_INP*KzefPB=0A#n;oVF&N6jMM!QK zU=c&|Fu)@;fg$G`fc6LgOwUIEjK2e{Vkku#9|7oQ0P;Tqz`Pnm83yxD0F@~B6M*e3 z0MjTy6*3tG;G6@f!te~yj{($Sa2f-sLFE{{egJTP27tNaX8@6TfJO|j5NRBs8-w3C zz#CMLA@nDJ_yhpV9VY-}7vN$q6);jy(!}iz8$vP(*?Yw@`o4BIXkJX>gJ*OZpp2&WMiWZlkqU5gtFp!yIvbGGHtg{n0`~^ zMD+Xe{YT8P1}pEWkH^U7f-oJPg0RNFAnew;Lytov^Y=)yoV5`DK`&()W#K|s%PD(s z+sdg-zFg%7*6VS*ZJja$Ji;jMc3o#1{rpTYqgeMt=eff)*5{soB?}9a>S+i|w*u+s zPeb}Jsm4%-!TcKlOsc;D*scOFeFuO^^>+Zy-vCt@U{XB;P=~>324EPKWAIu7;GPA5 z+aSymj1okMFi!C6QAC=9*WK{iZw_96M)mL-g;D~D{{Wak-ai0j2>=5aCXwhoz&M7e zd4MU@gCUU!pzssm8w&dgpiTuag<%HCEdVTHNM3*;Fh@QF&NP<>o$WO%qu4OwuJ}Pl z{kF=+Edt41#61SbS7ld2nUsC+yb?KNL#f)<|2g~K$2X4GZ~S(=%E+owaIL-Yp#K<3 zrY6FTZx*4Vd6cvW6&ce&MXMMVkj4@K9W9Ybi~7-vmWHcuWY0)lr}jUtZ$vq9UH<2H z3B9FCeOa3;MdGd-IZnEMJ5dM3=atSTr zuw@VynJ+^Bi#s8-$h$y~Pr`0YrQagq{ldtbxcffec__q)OOHgefdQ zLKIPxz(keM3$GJ5!U_tM0Gp^#I3M1_{Ilx>D}%&B3WtyCzF8pL)B2ontmHx)9a z0pVl?slviTg&1f->aaM`g787-VDVxD;idx-fX<-<5n%^u#3BftLl4r8#gCqdr?B<( zL>Rmr0OAY)qR5*8K$a6=0K+aM$_OxyA&L<|9Q9yGBmpRF0N9PfHUOw|0Zd_#L~=|3 zix`rb0QRB@3^`i?v^N4sqoj=h#@hf^F~}f|O#pP<0Qs8$HZ5G5USh{iUq1<-vO&M3V&dEUXki3r5n<9*W-Y zYX50F`-b-i#{WCQax6+F%$^}FE`oX&$CF!g2FY24{Eq%QWFuD@mE*8pyigDK7O3Yi zvfBb6!UxcZK^2i$0lG2xu>u@L^%z3=0mRven%Wxp!r%HrHFEa($t8zVH=_pSRa?!3 z4+_wv6e#8Fc=F17;pg?p>!?)I(#6d-U?uX^tJ+&VR(YU z3{h?auoVTc-Ui)fPTp;IhIQyO)Ov63l;eEc)nl*tKudnsuIhc#g@u;-LWBFh zMbk7@F}|E*40`WEX~|8;Ntw9uzc0pd>1#rtdlA!4IOwr)Lq!(Ik{c?j+X)rD!eEJ5 zwgY(W0`S-lV1;Thh=>6Q@&H&PHy(g)3>_HGAwFJ!P;r1@UI07PfazvM##070 z%0cW>2dbD7UPRtKv!98j@W^+{l_?o#H@*gKZra*QH_^$G2V-G zNuF=_h+RwAo_ljaK(;tH$Y3?>NMhg~6PoH{q>uB(Ms}JXr5!Uw$5eNB_8VRGh^;j~ zX?yv?&%a+c{d=k5DtX*f^0EyHo8-Socpf-?G(96f*6qWC*z>ZNK1@kP#h0IDW6e?e zK>7ZF#eWMXT$ef$A8Q_X86j8_<7uQh>E^Z)EF({%@ATDB14=!Snq&O6DC=3_zXc!j> zNKOKZn`iU_`*qU~hN94j*Avk0QlG#Bl0vbqWd={PVUyKSx@=L^?v304h)+O7YI=Q&7NN zwEwii-u>^>WkXLwQvZL;JgELu>TK?FIbaY#98T71B$Fa?}5Mgu&ij+}qO0kz$dh^tvOliT3~br4IN2 zDDK7~C^w@TFJd#FHEu5~xo}4aJO*%VdrJrF|7*Av`hPR&_9UME)1LPpkUG_V%D=EWOMpR&V2~RA+3f!dLrKAe6M>uX?k7@jfbWjuVKolrpUo&kSrSd$Ll6JA zzXk6iK{WIce|J@_B&1|Xb#VQL2*20)<~W6#obX z#nyGh@s-zY+q&Ur6&zVN?scPq*J}T6$gq7KX#q9XSL9hYcvKX{v328JH)th=*1GYn z8*C+tkagoN zSvP#siaYBjx^67+`rl2BcdpxJFe&&C_wKI0=8q2z6uaR+yojx@2;&zI$3Khsy5Xl~ zwXUNC818!54Jn$|O>%ub4lr-rJ+MfvuZKTk=Cf|n>qY{*grB*D7uoe+#&^mOSYJ_Y z-SA!RiFmufa$w!?4eF`ZO&$z)E55fp^|~pn8@?~R%zC^->%YqbCcAD*{_FUEXgd%1 zsEW34C(DK|gc1m_^eRFK0YdLZdhfjpN^c@90g9MbKm#$a%Se7J?21I+6pV%z&VkXv$CqzE*G-$R#wf* z7&t){tgO0~~wwZca88_;nJ66`)$}~a!p~->2K2}x{|98l|^{>^j3Q!5IX}007 zpOsa{f78nPTUixkx2$XcGF4JlxMO95tX(x^1#CeETf6GWp0%=<-K4LCYd}FO)Y4e8 z=ixakdj%Q)1=WN?RyNW`SPNNUE7NFFW@~k zxKofR;ieE`15dSf&5-?IhuAbLYmRIm=x@4}wZQ+GSK|AhRV%%gaKOrDU>74{EBM?B z-?o8UbG??^mWJd^WXen%sIFTXf3vJzTl{zN>ugzqW@fA3q_3;0tb)3{$^Wj*kRTDuRB$=egsTG=uydlA_t{QCRQ%3i|%mhCei zA=3wmf_lNLR=83^u6x7Bw%u1*Ss!GN8M7MCtF5dr{_Dsp;;yl>e)u2Sq4=?t^+)y? zStZ=HRyF{C%xr78&I$)2oNHz4t&H_r(0nV~fJ_A)3`?y{rBtSez%VQO#M-@#thSYH zwsu32)$$cMCTNQlzJltHVoM_Ht;qp8;)$DwcBoGBakh&vK>}764`Yt+i7K^ zkflJT;k?T$qyE(d&R<_^DUPIO~#tIKu;W&i)KU-<2eQssr@sGlG19{1@~Nq(r99 z^MkdUgFiW$j>WxhWpnZ0vi;+RmA#8h@u&%JTG>4O6Y%TrmhM=p&H2#G3U6E40%Va^ zb_Z7_eh-S`S6lyNWef2;Ka$o|toRmz-riCZ{$gc|@oQwMxqh{>nD@ER(30>sD_nwK zAH&k$?^gB!e*K;o%Xj%*$>wf`Sh_96a{k}&vG?8d8zhJ4H9skT-_b!7T;AydBAz&ChQ zLy1CYe+fSZO%jW7-PUj|e&t#v3bC?v_|+Gb`@~kZ9{*1^GfAv$12Sb=_DQX5=xkq3 z=4#^SFL}Aa#iGpp-tOlVdr;zymHW6q49>X#|9hZTRUVb4_n|06KoJw%&z&*mS-b_| zIVc2$AvYuk4ZD<}|C!$JWSm2jM+f2#a7bybnv@16T^n zQqXQ6;#mP7!Ae*Kt6>d%3~ON>tcMM-5jMdm@F{GDEwB}~!FJdIJ7E{>hCQ$s_JQ_v z+QSWp5!$zn#4`$FKzp{)pnaP5W!i@&2JOAH*V5KXTPbawv{lLtc_1%D!!wWyv}Fo~ zG@z|g7-WEq5DuBZ16fUQe;VNzn*CS!4er5xcmRLELnub~DFGaa1eFARC#wvU1%2hQ zJXC;+a2Wd|piS17a16A$(q?Kod3WNhk-u((@iAVqk%H<3-mD1{Wj(H-tc9NCL?r1w6q1w?us@F`lz<0%}4ns10?X zF4Tki&;S}jBWMgwpeZzi=FkFKLMzw`+h9BFgbGj*Dw%HwxUyuzG=!Y6ZK|hJmUQWL~7y%<;6vRL*jD|7rDvX7upnHfWpW68AKz7KXpX}wt zlN<6tUdRXep#VG!1>reZN2P1CxB)i7C$Jf`S=5)1wm~Qfq=B@M4$?yyWPprtk)9t) zg{OgQ_B+zTn24rPZ01FoQ5;- zJq@Vctah*3t!j7rBV;G@IY2v8?L@Wl)J9VqOKl{zanwdp`$Fvli$h5$1?hCh3d55D zv?0_6uo{WgfajqmXalGXUtOpV4WJ=3g2vDUnnH7E0WF~ww1zg&7QP|gD!9$ba4%}N zH}r)8+A$2o(-FErJR!{^A1BbXljiCu>m-z||hY>ImMu9e!`f2A`W`T2X0lo)q zEcemzK7#}BIUHk_I1Z=btX8bkU^=`Ft6>d%4C`P$6oMiU2}L0aia~KG0VSanl!h`; z7Ro_+r~nnA5>$pNP!+WCtPL?*3Tc^>4$?yyXlMBf^FS{c2?L-#bcTaa8LB{4h=O8J z97;f8hyd;VvQgSMaR)$e=mk_%P(9q5z;BGJ2I=>}`iZT64XYonmV{D}3bZjy1KJFx zgY*yvZU}+Izz^?}4|Qjb*+=#3wT=Ct4=iOSp>SA?KP?5iPDj_)N*l(Rpgm$H(ElEN z2k8I%sjuZ$!e%P&o3I-B8qnTRE8?x#Z-cKGAYa32I0I*s(~-~NnMWcEAUouMT#y^` zKwiiP`Jn)0f@v@wUWYee68ubJci|U!iR%n7g18^TBX|Nq$gbkvfJS8Od~!PSAM|eR zR3CxrIarb4H=vE_9MEP|o6iqG8_ozQ0{1D^Al$*AO=k?e2IHU~^oQ!8&1W4b4b=#v zjb}ZmAHzQl;Bz<%+I(uWnHoYNJ%m99$OzhWE&^>dwXw_$8DS`y9smQOH*|*{&=WdA zXJ|qAb~MjmXbvAi%tZYI`b=43jFLii{MQ+R+Gw7nQcl5PxIxeNKvu{OIpGt~Mso_p zf;O0gpd)mG{O~MjS+8ZhmhD=mYnOFiM{lp_s;d7`C)u0>{g&K7BYETx+fi|MrcxHs;F4{i@o@Ag+rVIX{^!MN=$U>># z$JP13B&vBQwWH6ouTxFYiBiG?{0|`m{)`X~nIQ{!ASdL4JWvP1q;XFDa^q0+%_*aOFqABWFDyWn{+9~QuSun@HC)#1}BY(56k+sw~&vBb3=B}_F6mV zTW}lhz+LzSeuWn)`Ag6e+xFDJ7~ROUUA|2Zy#rmLJ7{Y>5p-0llTn?7z6n!d8t8Ph zGjxF-@FKhfy`T@Yfp*XyIzSU>3ff(lf-<08b+H)!sZOV>3@Jfd=;Lq-^vSM=@EAOF z#5%Oy66gu_pg3*Y4Z1^5Xo##4G={2NSA$uEeFx^kJ>;KJ3l}K;*KitQ&hXD+I8H`R zz~^ufzQgbmtb}Z2tOF5fxBCLM(*t_Ki|`V3#lAaqg6a?rPp~gZ21-L2Si$wz$hArB zMVONK*Q)(PNg#^S?S|zTmZg0%LvaG^#&`#8g>;N%JzCMDlojwk=xIqI_!QgXgf9k> zkR47F*BQ{$j`5_?4|f<0hs(tO9o*IyE)jw~RLxatAsdEyAq}L3W*D_Weir`)sLXYH zuA4$1673B7-|Y^AZ^-25upiEof&HMv>r|Aq2$|Oz^B&j>``|O!ueHApl@G!pI1D;G z)?u*@hvz^)=npT!GoZ8JM6i*FzajG%U<#QV1Jm*EqA)*^Q61CjSXM`_ci}CV0fk{8 z4277(}D`zW+JR5@wc%1iJEZZUx@z*8DECY>ZG*)CNAE9uQ5IYo$>1!^%VXB_z&PN zgP%ahE{aUYZnr6s{42@)Fz5zNK*xAjpgt6Z;!uH%Wd&vU7@UDeuvO!K8*B%IU^dKw zd7zx0L4FFpg0--l41Gc-*1-zk^vZi!wHh}Sq#$EIlCf*pz6CAdd8i4Mp(v z0UN=W_#5CJ#np_UnL#szW{c0ECc6E&npQNuY=R$X#H+9sj=~pk1T@gU2erPwjP$wg zeHZW?0ImMD3(#(0FPw)xa1M6EF4zfYVF#%Ho`&u4HK^C<*0l}Zg&7bFqhKUx%~T4s zTj&X0p+^k=X!of0?~L0P+CW2S0HuKfc@M2h;OYP?3etk6k>hX-G@TrUW}v%(?k2hu z6t%A6kW8p%b*=bfqWOoW@(ScOfm+ZMRQ(M>)n6a#K^>?Ks`eUC9m+vzP@ixj)^%AZ z1J$51RDeoQ9;C-M#{0lb3M=p$#r0Q3S0#>KnRFtHYxjH{8%M5!IDW@f#^v{AmiAXQ z$gu986h65VNF|O>@5oeY#UVQe<1yT+s+CpW%Cod^`b$v|`bUx02Ihha4I zg2A9?8wMuBBzOZ}gFf&o#DdZp1%qH941m^9xCQ^Tf|k%9`hgHXfuo{-a2e2HL!7BI&K7u$r{3OisY><2}lzz<(d^ z!S9d}euH1&6vUk5pJ(7M+=Lr&9e#joa239XEASPZgER0ooQAXT9Y}T=F2T1T{YAI{ zvQe$6^ylFl@RnZfFJTc-Kt=9ErU+!{BpBD9kew4Y&{mm?E4#(@&+wz#|2CdG@Dn(R z{fhqyJcftxhjky}22-s;-~!p{DS(~rAH4ly47 z$p%^DkPClq$P4+PAUq32Ksr5qDF%v6yBfKH=;l}-%0OMH1GS+hJP*~OnwHB|@l=7z zPzfqR1t<^Ype#5Olw79-Ds2sPwLr4I;7n5WtgHkwC-K(E0utwVm-cUoq6O##sTnkd zCeRofL41iT!H($KLp$gIouD*wrzVo%?}4lWWH`_o2j(^~l7NW38FJ!?FB8%Y{Y%`` ze_%vTEl9461x8klQd9<2eI+Olal<%?sI(g1Z^9ce9$tq@FcHSWI2pVOV_-B$_!`Jq z{s|T*uyl${a>*Q5@+mOcx|+=1!aohBf@7x)NG_Se$4D?8B%BK~;cb`$vq5{fS?~^Q zg?X?AHp43T6h48EU=u8bHLwESgAZXjEP@YUF)V=jfIh~nkU+)@t?L*p;re|mQv?d6 z1Z1mg$8MRumRvIF(5%BV6JH_S?|-_!Va0(|ThaL>k7b#8$5unyK*oCxA(LYb0W zt`nDB)s$-ND0~4&;4mD5gYY>Vh~c08@EPoby|4#%!!FnfJ77CF5h~L7aP*Gr*f`gY zE4ky3ag5}C1t;J*6ozBq7&{Y(+>>$Sr|`$UjvJqoh}>`DgmG+;$9NO?7UdsjAfp)%n!X)ZxAZ^_wdYH!dZt>Q{!^LOZqpH9St~ zmf{%+AHzU+1N3Bj5DWmFTIuxaN5b5K!PLf0-0PrexD4(Mn1y^h+{S+g`Vyuy6o#L; z))`-J$fx5n9p3lAScg7(s->q}ddj7zU3&VZBeGuz^gN`*p8_;2ljGjU)mi_qxOd@a zaBSVk^$b8Zzk$|@O8j@Y2P$>UA9x;s+{d7He*_QV30%TXXLEXxppqoTRc4dmhJYTu zX>Fh-wh{>CTIYFbKp9r%s^RKXOQ%^n)zXs+-Kr^Uj5jh>MJ~t**+C~u9?&^$4#)%2 za0D!4V_dCIyi(nxvfcY>Fro&Wt6O?nOB$M!~7Dj+m-O8{sAe+hHWIS$RrdS)< zk;xcu_2@#?G!2~E(6#*U!VGu|WGDYjxI_)SjXM|Sz&kJ-49o)6fD>H@;zGz^9M_UJ!L0>Np%K)FPtn)Itpl~8Ce(mWkO!`mr4N*C z$8HIqCNPsuq>3z1r)}IJ zn=uup9H_aRCR9ztI8~w2G(ez)8-fy4o2oQwdsTmYHK7EYYE_?b;!`{d>oi;3?VRjw z0eM``g%VWaf7iiO8kHchl<}3EJpHNjD&AuVW&7tsQ2**A%28l@{-=!XB|=rFN|{jR zoSJbG|4ZLc`#TwM+T0mhf7kA6JC)`iRI&FFfozr8z}nN?;LX4(Tbfaf`;l-IxuZLa zCmPuWT0d9aYbkn-GtXqNfgBB;^ZUoh<}oSUKP#!zqmssx<>)qlMp!baRzHz2R)W3dlnh`QU7^DZi8j%*#fF7HthE$Lel<+hEGne-N zzeK2-QH?mI&xt<=s7a-B3a_)d;88Ee~R&PFxCqNziMUswusOsn;^q8TE>$-rZ2=)XQWsk^G}~H>)5}0?j~y zqd{++l?Gjxf%PO%7Pm4~gbE;iFT#|^mAsOrDsCz=Qx|t`oI*IAwH9*vPE1e@JSs_b z(6-szlkv+yblnNR;_`;)TK%Id*UFeO+5=ty z6(q*#p;EjEaZ9L4M7eGOaZB0H+P#A7)P|a42-kx^^M*R8N~)$E0NSMV$L$4*&lWl+ z$cfB}NQ&Oj2l_(X49V89Q$z!$Ct;P;(G5nXgfw9(BPv)y7>teLl6`kvWpHRB+W%!d z%DD{HQ37j00cu#AxYOw7X3H z0UG}kaO2JauX3%qUYS=js#J;*(PZ={*Hd6JNai5jG-N5jvDaaQ;*hLT@tNm3K%B2(F zZLXbCsJdiqU?#i^b0H&vw@{Kf_}_uq@Cnx%Vv;Z5dJ!lC??Gu;2 z8*l_BLoAGj!!QZd?xTQwdXHz85Xm6?uR?1W2rWRp`(5Y@OR@O?%$mjSvS!C(cSy`~ zE>}Wee_Vmg>65C1vE)r%bJ%oA9Yx?*<;-b7G+?{_F4qTmv1tK8JfArlS8E_c-Vb z`zzd2Z~~6Omv9uMR|b_SC3X@{!#8ji&VXi=bKpJYb#kxVsstB7IaXjz7kch-3HMtV z2vPrAATLBfM$k_WVhZz5 zAy7&6TZ00S5Ar~6$OSne2V{qA;DPjz3Y-kMs-Rt0k8 z@=LCa76etb-n7pE%A~@|Rh&*ZwSRp67%9~G;zsHuq|T$xBAxtkZB#>*kyT=7$<@%) ztwv)zu3dF>PDY$eOJ;3iyrocriZG7CE0;{AQE%1ywGOVvu;z?L_!~k?Xb?x<6u)dW z4>ZBw7#v$oR`P3ut{ZMajepH}Q@Ci3LK9Fk+!k>HwZ^YExZ2>Z#a#_cVH0eGmyy?m zYA^%_Ltp3vpP}!Js~YHp+X0-(Nh7Si+FvDds-`2?imV1`IBNEaTb-&Y%>m8?qp`1! z=8Sud`7V$T6sKxWov$mXg9gSGBLhX!4K%5!hT)G-9ba}Md%4V z-~~|RicfLKFSj2!{c9k8=?8%N6KTXeoskrIe~3E~X+*?L(8;|Mxs#wus7PM~Wk~%+ z@eKmih%%vCRc))8MnP5Rtnsfqk<&hEn~^9gf~tN5?r_izWEk!%5VyIU3~4AjomeH* zkX5FXcmYt=t1ucRFT)TZ`k=+S{|_N@Cn9I!ar%QItqzVpZbFjlzOOZa)(2y_9t~Ot zXsw|2ffgxRplE%eQKLnO)(%=@Xf(uV)#9vJwAj!BLo1h90%$#;6^llQ7A6`}T9jM> zEi`m**Fr_LrbUW(MT3nNG)hczEk5+hSs5q-4Y3iQj8c(jV_n!7?OCaTB zWRpO-e*^b*m;e)DGqyR9cff8Y*Rsz76#r`Dy$3(5o_ByNxP4Sa(THM^-e23KM$`Ve=nc#3 zCd%dt&O^AANOXkCt|&IcSEmMlQts`XuHcGDQXtW4Uhei&snYGNKCQhgxC4@ONJd&s zg|LWAEq?g)JJKl;!N;U8Rtm|GydX;b-48NHx4Xk*no+i71iH5L_0;@7n)|tet0M_T z^0Jos{3UVM8o6moxr0PgDxzrdh$!!i2udi&($w1*R5??^pLCbLFAnV6hoj9+8;(B6 zKB16bqXUoRg!!&j(c!zYUU}bYXs6f~Ry8P7)RhM{Qf(%oVi844l2F3YEpnNTtKH$z z9}_keaTGdRx^uVdm6sDXQgzC%0F7$kGAkAK3>K=vugVlG zH0n@=Sze2x5k-B+zp9kqI(6~+DM6MzB!#4fsUE4iplROyX3N9K^Tv2#`{HeA*U%CCn`a8oPdDD%IC=tZZJwofy4-S5?SOI0HA%#YofV(5XY=J|PJ3qAK* zx6bHGP#@7u-3d00elw^rJqE6P@*wqxJ=3eS)FOKoG^*XBCDJ!-b?%oW>4PhjFM%q` z`y)r?@RgbUtEJfCn&}GOfUQ0z65Bc{OF2~S-z#)u;n_Rw+`$cysG{{#sm!>825&31 zr{JhOevN+Lm=#S_^1BzqUY^p`uhFj}J!rDr|0(v3Lz$ZUH3!jTN7JNttJr}*Po3e_ z#CQ|Bjw%PL!z)5E75XN{FaALK$x&`JzkE{crJCpdxaijuMx$(ZskiIxPM&9G`!)Ii zfD*biKB)JalTB0kHTor_(mB6Oi@Z(4<9%`lAar*=-gDCxlcG1(c1 z44$H9Tg#Aat}dxeueJ1}UR1BDW?GTflk=@FU)mKsDvGL)iYT=vm05s=t5+%$Su7;{ z#Wh$ewq$+F?YrLS9Y)Eh=!l}FX^ZVNgyx7{L6gd7n$~z)4w>Jz!Lp7*K z)6t|MO!qm>cMQugD2W<6Pke<3A%_zo%UYwRf86v?$Z+ubA(3ggHW@Au%5J}sxI2_^2SY(n?KEJ zEf_x$Y0O2%UJZ+IELPpRR`zzE)*tyT+S;)0DVsMQ%6PU3ny3iw3RFQF6S0x7Q?STM z*oBL`_S?64Xj&VVystu|K~p}%0Jy)?%&zR<(o6obRU~6w! z?`-ifjajYO(@>i++m%Xw?6Pk39Ru3+X>w?$?Y2%QY;sy#W969* zWlpruUcWpRQ4zYK1Z6;@wrY85b;>8jPn7d(qS6`9C*-Uq7OK@;nVNTQ)1biDev5AD zOhYW9hhvc!i%N4Q_sG2@<5s`L0yN6==V9}sR^FO;-LKi0&dgKTFKzm-EuTMq$HDVO z{1&%t*nS_TeEr7!g46sOcY1SEIm=F!D(|gRJV`pGZL1W1i?ZoWj!%ibDHdvx{)rFv zPd_aDnBSr|8ug;~jSkM7P_;pvQcX#3`Y7!CSg4O~DHJ^ZcD)Ue{;*rosI8VS3VWws z^cTK9>aES=>CG0xdTwK(xwB2Fj5iLS{pnkO*py-BN5!6>Zm3%A`NG<4gV$a-;J2uT zM(HkIHem6Nb=~4<+J>15n+f|e7V3%boS9uNeDmmR{;<>0C}&+?J~%T^mA2jdnx$c8 z5@9`iu+Y72?d`PL+vUl`^3UlBN5aewS)9d!s*nBXc&8i5c1P9mTigyaziei93(8=s zY;kwb6i!l_lu9hvpLg`hRE@DL%0v)R!tC6_$Bgo#R^z|^)1JE#_p*fNdb%Jhheny0 z-nQDj6UBen>DSaiL;A6uMyA@(?u!8{{hGk_l3(*;-$9*vvW!?CTPxMhLe)ojGh8~t zRV>J~+v?8ZYLmeX+sY*TLI$&Pt2>hat;l`Eo&hAqR2IAcN{SMd)-L&pU@Yf!n;(F1tTkx33C33X zX8lXMXA}xX!^({%hZ(-j-N5x}26Kq0J%=#UkX_l$q@SWe7zFJVBQ$uM)fyLAC`DIao6n_u*`^vGNZP;BV3Ug&C2cW9G+o)yHap=jM0~GJYOwlXQ(FfsA5dU z%$C`9xC`d_f@~_MV{ClG!^=MCIlgK4&|pWCGu-sr!F|;(fX%!e?##}GYgM>e&>$q- zwJY2lt&e*?-1OUt@suf|tymHyj4^QXKyz4O11F;2)o z#q8wONg+-x{`-FZr8S^O(6zB2Z#^HNxm=@Vm> zDv7d^OqAc3pPxTFlPtPry&_gaj??5f+Y~k@7Mhqwy}9;K_Eed3c`YI%N^+KGH7$q# zekEPZN6Ik;QE4mZHz~g$$BnRH`4PJ><=x8LBT7WRVKp;4WUScv+%whvniGl56on1kpM?e&^xlCui6X4lTCe=H{_C?9 zw~TTHM>6l0M%%c6IY(Gm&jMz_87AOVN8QD31BYcNU%3nU))CL0Tr}Xzmp*RmEmM@r zL>&)3O5Ns(Fe{D{L#YU}?mkj;s5hmhGrnE=G(xSwh+>%6un_@91l)gJvCXK6x;;({El2PFXm6E@tW;qaNSmZmwSA zZW-0|aN^~AvCvi@i@U{4EEdt*FdOTU1N+(jWs-<52!Jx@U zU)Kg(W5HZI?)JDCGJQ_aMA=TbGkdxZ>^7uZpFsmRO&yc*wc8WUyJgDyg44<3EOx*^J`mPBCI+Lv9~U5@|>iO zx|BBOCs9ZJPP&VG24SvleZ5ha;SJv?K=<+QmspV+niKkX(Ygs!v z7Ivy=QpWspl6%%%EOa-wAxx!HRQ?ZTP1{oh|GBKMM`U+hYTxMgZ4Un0bHb&Z{!!LU z#UeU!IbVBcN}IM<)7+^~U_phepJqVA0xNcT%2SV@R6SW7jUAQy%bD|pbsaBf?wz7c zt;?H2U(tgLS1`lAB1?BG_;%CJR5UBU!ftm(6S<3dFT9eknOZbDQLo+42fp>kxVVxz zx*Ll%Sg7|cedTfAVhgfVc6%#C*(qPyM1D<)I#%{s+I_|5?cOyn^@0#r@GvA=fWM3D9UsSU<{8#Ev=!;II#yXfMWbpin*6651#6^t zyMmV>QPTuhF^|uaZh5X%&5wEoukM$t+8M%f#-XtND$hJkA!}AKt4}g~$;N5|xQ*>^TV4aa5_=LVIdrsmY;r@Z7UCexI*~wv)cKh(2QDy4kgLvv11nsD=gS zO6r=ms{1Oe)xPM)*K6OuiAKwEiZ-W)=|z>gZsa%T^H4TB*?Afl@Rg}!tu85YO>Cyh zu${(dk-=O)!@y(D@#AdX6Ql6ux>SLki%DW?3w#kW7 zmG_0Ea5@OCghljaEVS_{w;@lid=p)}$+M0AXEdQ`DjcY|>fP>-Qg}5aP%6k(+e}f| zw6%Q}pY1fO`{pU|3Kk@<1J_(u(|d8ph4puzDrz-Micz)AIpwSh7HNq6VDYmNZH^@# zf`#ge*qhWgq2CaDS1i(E@ot}qw~qhT`HnaJ;+j~8SWW3OCdH(sbtn~i_J;kuw#o4= zE%SA4bL0Z<_q9!X;tGy34HkujnT{9SZrAEMCRVy)bxof*w$at<`qHca{J38-W=o}G zZEm)j6asIAO3$~2YhKqxs8`!`qPr7^hQs1!i=K-pxFF)jx({Sowqgax!d*l_*B1z9XpC1&L>8kzyP(McQm8sv1BR4ZOR z(s>;>M|EDBY6coL;{9iGtq-fT;73>R1S@gMVAC=wFS|2GpJ?J6L2o9_cI~}^?|((R zvAfrNbs3FD(6H_4FQ!iub{~zM>%Zxk@yy^Q zsRugMtmbB=!Y*mX#p3ClG9kg%T1 zEqoSDSIz&jQ0T>LSg4k<@Z~Vd6Hgp=W8Scp8Fhs+aX+0pfYJNe6?fK#8(aJ8^mh9r z7d!=)C_>Fw%sHQ-(P3JV&NGMIii~>bO{%me$_r@HpgGv}e$HvtFV99(jO7Of__4K_ znlU6ShDqfKMw)K6E-ewVZsOiJtL-#xd_AQ`{?z+#JYV~PO_Jb!I`!#WxXZwS)g!MQ z&2TC43ux>ierCdCBDNz>PPZ|iw3~y5iBY|)7#d|W=b?OCZ|zCpK~uDsPK|gE166WV zxj(Pjy~nBi>HxmIOHea3x>e6ik@@gjD>MJ1KvW}PUPO}y&2NQ@dNMC4{5%@F1sIM- zGtP%0&lGBZHZ%;4_x@#$whhV1$(SCAW-7?7K!1NZGcwb*yR+nR)Pwl{-F3#S#{6%V zFU+|4o_{M~?`)Y%1W^x#2My@f?VGV7@4Ykl2=R@t^so z+z&w|^7|I!-eETjjmr4_&K~Ip{8qUD;nfi-<40&zFa7)XN>w5}loc+e=K)30pp9s> zKzZiQt@;)2U6_VO9gu1sIOvsLJqPk?VeErHat~~lqy{Q(9^O*NIK}!)se>I&nfAWk zQ2l7@Ccl?$wa7+HH>-*Omz=EzzDHteiE7mZz}v`3@yd~{CB9QiML@_RGGN}YFCy7npD zwbP(Z*?O$V`s~TVziT0(=>AeRiq^iox;pLlO&8x-?Nx5Y%0V~N>rlY150a5NRaJ(^ zxvFnpb$kjM4%LaUSe9-xzh~ZhuA9mHJ!@v?D6M}tQ=jV)|It|j-Vxn-P~bhYbG?$q zJS489-y+fwF)vf*kx%BS%#_y~-z~YHVl$tI!zhG`(B@cUF zFp-%;!kQE<8gvw6-4YHqpLzJ$i7_2~?T2m94^~rbdY554-(5hXpF^TP`75p}b9B*@ zgVI0uR!jHO1d{bI{jRygVhEqThi}RF)tC%xk{vJP_8Uc`(L6Er$EAmA3^|nBuc>b} zr~mkUU-Da*U-N6aqtUFhDkkPy+GKZT`86?C^TOo8$0tqe5i5<>xnyA$s>}qcIWDMn zp$}TVD8LhV~L^8a)Dl7O#$!=n>uTr3i3 zTr~B2m~Gc|!?q0;(3Duj*Oa;)N79lerE2)&9wr~v|IZsJ5rf(E-{nr1eDVO^IigK} z9AGY9rv?8QVA9_ps;f+;8UXpeeVF=D!Aq<_oSBlN!p>NjVK>~NX2FSt=crl8OS zp8)waflrP6n!qPOeod~yzKoo#SiZvUW~Y<*HGg`^BZe1n{Hlbb8)2gxb?tP`MxPDI z;GLqlf%5uyzyo?Tx*5|g|CG?5Ui6tjY#Ot9s>J*{^YW(5toWQ4lk@?Tbg2IN-cVrX z{9*o^EZA3X!eagJ-i)#@-<)xohPRk@D8heJ?1b|f*e3oG#8*{j8)Mlsaft5*``0l% zafk`O?JnaAec3d<%|boULeq?Ik35uFJp*+bdVkf3XTGnMTl-(%85$?#p?dyv;x_AR zyWe(sZ;syQQs?y?D)6-2c}NzRp8xTgjevvx9d~>Ycn{5;fL3kK&XYT0=ed_vr^#fd zlDuaX9Yr0Y)7ag0J{Zt}o@ZBdLB0oM=@ThA#>wqNV?^QbMxA?Csk0%b=c}~8sVt{(PNUfPJ)LuL3PIM- z4qjC4$^H^!eOYHEqgQzT^gPCCr~laQ{~_xMG`|%mkk9{fFZ9&O;d{GbS)=}4s;B;K z{Qsbh0((zL-wwFel$M?8t-o!!+q$x{Arte*~w2sWZ zJ1~A6Gt$((&+RqIC=+|1(U5PHc}Le}Mwuh`IWi^&&jYS=#hQi>&^xY;*=%^gnSH_; z__O(+f)j1HY5WJ%A_pOT{&0ua95ULs9T?Hhy=v&d;Ty@N_au&2vK#ugEeBRr__S-& za+F;QdyS#L9+4y&V~+gc&f#h^#{Bq4oSc*63J=}gZHdCowudBrdW<>qkO~_2s;`2w zao&^`#+Hmvpwn_4ICPz}vYWMK!OEQksYmM{mHrw~j{pQBz z_joqqJx5^I(teyN^q9%79~N4_FD*2zVzHK=y1c3Bt+=4E<4i{^qTjJ`#3YHlQ})%` zyzxu+y^l3kq0!`?IsG5i4`m9^>eX;$!*A!tnUxCrH5O^H=(}k6_}=*%=(LZOvo>zm zZMyf@%*^}SxY{~*rc$&fc|6WMRL)Y3_ibpNy>RE}LHXus%SBi{bILy6L_Q(*l33`b zF!|2<8jHWJu-%`2eH-?JmX&*am1MLp&)!qj4&%)zh3$`pHW52UzdhsLr=yY*)*eH> zjwTFE&fxpom)HDFC-dY?)jNN@IYd}js_|w@B5Jw*_!<2B`gk)Ugu0GDzZyg{C%mm! zO$c^7*cYIZg3llS_FTfnPV_COdfdIf=hb2@xJBBG1kIZG_R)~==)j11C&pV5MS}v1 z>DB1{ZDlnZX~o|F+Pnk>#_BC5Pel05;&{`Gu%37;!h0e{>2=?U-OaB@ZCvx+ zm}A7DV=6k2uWD?G$&vo8{@pCE2hTiJISO}?FY7oHz%HHlYJ$6cz@Trj<2NuMB((zEt2~3HoEyGQAlyu z?a8K~n+)HZY}&g+>iTuiv9`V{OrKblWqVLSEOrdX1itF% zt>k!Lb@XeZ7?xT{Roq%S9m{CN5Kj($y&1-`Xac+bw2+OmZ8YdmOl zo_y+^uMhnG?VXEO(}dgOvsRV#RLF(m&%eqUzmrh3)jT(|Sd%w9W?b#pR7J!6EH-tQ z)YZpt&oaiZX}-YpOC8cBgj0oH3(VK4L#ibkiE$2$qs*MlA;t7~`DkWpd)9mAaYt6= z)xtwE8&4K)`+XOh##xvnzF25-lnV*V&e_Geg}xQ;Z}YZQpH%HeckdL%uX~CH-Ck%S z(uRa*O1#K-@;q?e^c1baGx}E8vM@Vmhh#`nQcJEJi_GdmBw2EiITwmsb&_XXd|t5ogJZ%56^Jk*;yv%M*kA;c4%=6#=bO1s`Y-Q_M~D}Vd2ts?U6bPrff z&a*|@=Wn0$DjNG-vhfm=DkII_b&08xk=35>d1|=l!V=%!>GXtrk1Mp9kc<$VR;eoQ zE-`x)!guJplqS`!_s&t>UVRdSGbHfv&$e=funU&$( z0&g$#6@2WI_4ED+59@=4-RphS-mDl$oK;Jkhgdqr%vK_($_ihsop$Bi`~0j->MiVj zw9>4z!i>sK74=o@&-& zWBA^4M~^=K&YO20Zj}mZv~p(Vkitp4+YH~+jj+sAN=e&NeP?}D2I^ldJoQf@ztM)o`Gku&myRS1Iu00dh`DWSn<)erG zlS5_QfTdQVBi@)dx$(p7oBftfz5U}{{7tGhHJ6ibrw;yc zjXw<;P~uDpmek}N6_VbVEFsNYcQ=?FT|?5BDaG)9yusK1Urqj7*Q$3ux$Ny_#WY_f z+vw}FGdI*(l(gg?9@g><*LzM9w$YU1IK{rPmcd;jsO%=wPm;Qu%B+AxCzXDAv0pP1jqZpY-Kkw!XXSADH8VDu zN?G+Nm7mPp;6uCbtLE9V_Zh#@b~M_W?Vc0bv&d-ko?mm)hRJ*G$)@gEpIr89dT%yM zvoi6l-E4l#O7`E_Vy4axi85`og`~}+=}SHNg{{6E2X)`J=hoEz=e#xL?a3pynz}hC z{p77?MK=1-yIAHV3&lrFp7ryHkGZGoxwO_`+qRmEviJszR9HNJsaN%aJ2z?@?6rst zy0O*7<_O8)jtsiD)kI_`)+F1^q};d}wwWEdLpabUo?P3^i0mQNT*bGUUD-p*xTiRv|*ty4JSEgn%aXbhUPq*yuqhdnc|YVN#nz&NzI_e>^Fp;+8_ zarn4oWuoUhd5X4q3U@gIRD)NWJ`BCGCpgf$&rgmKAu-Si~4?R&a0k%a{a05qJ#X^ z7|k2Sf129O&0O5m14>DKM+c?dYbwwN(Gh!nvqRreZ&g42LBYfR`c1eXPW#>6F!Rl) zdGR)DD!umU%g@v6=nngQ4Ykjc?(w@nq~>irr}Okjqy5~C54*iT@7VkePU5AQ`Au8J z#XG@6^3ad}T&}v#TELTYzi()@4qmsV*6!(Li9v6WP?vl5n~qv7e6imQ%S#M<4w(IU zX|JElnuo|e13vdn6CqdMj_xySWlBQY+a&KgN9H4>eGfS%;brT8v(8L7Oahu13X|}% zb_Op;?fja@Hnzrz z9))e#aGv#|6DENYse%%yyT7fvgtL%9LJ6c3@TY-rccxvp)X|Q>h?R+s9w{x@GRqQe<9P zcGPSv>|XUKnydtNZ7uR^=W4H=^oG?gHHb~@6vDcQBlJ0DiT95Bj@ongU%qY1(HY() zrPolF?PU=sBQ_tO5Jp=s8;9#ZTUv|ggyXvMrEj?08+`NRC&gz?^l!T#erZ-HAIXpT zmfhti-|f9?(^lQ=ibi-(=d&F%7oTGWl} zzWjE+T#+rl@do!kcez8v;S^_cu`5f)Z@Hi;Rug40*4;hK)S^^$V79#$-lo`h!gu$c zaq{clO*>EUy`Ratp=c15Q@1b`{qYy3V-(rHhGjmba{5-%v%}_n=3Se4<4ASV9KkZW z_(`8d+UI)Y%bGPuCci~%G+LeH{qXARSEor13-$`w|=W;FYvxHr2Z5 zkwtOuE%(q#(-4d3gwHkZ*s#+^4*aUmns9akPS~(hW}d>9J>@Iq`a6BY9^M#R-*3?l zjY@U!hZi1=y_A*r-U-Xwn|wF(l)0&#Eysc%D#b2)T=x4Z3xin4W1*)GK}SxR97Txz zE*1r`2%Vbaif6(6d|nIhBaEzH`N|#Hs9~G;zK!5n8DaG%V^D>!Odo~qibY<+zOZ`R z*5M^r@>EMtDlrK9Ysc0uM!KV4m@R}=x7B%$YvNbtN3Q?oHkj}k&3`j3$J>#M77Zb5 zbht^8A>qXX$FHxmMp3N5G4Be#Llx+S2h*=>NJf*oP)Jr6Lnamd_CF4paMzVDOO*cobO4;ZWPW!N`lprS z*gF|^t^>m-Bo8cjV7Py%_w@bF*Eg57ZZt8?l@I&t*)~T}bZ;kR+wAdlLFdNXOLW*_x@}uRJY%@VmsUb#2cAz8Tpt}Q3-B)#B&Wz=^_9PUXlclyz3({dpNli&N^chVL33;4iqQ}9xq z_gfTQu9_dq(e?l7Hz{7ZY9{I16anup{ey2&@IIIKD;EJFD>%QH|LcacNv^75cIre3h|{4&uC)Q%H`pvY0FOK zeQA#O#3$^!$A!%GKAi zv<>c%k01Hi+fz_LC;FR`r?G>_$(7U1{`=eocA9{^{N)od|9wp+6tk1}0=G;bK34NL zL*(D59auW2-u`_|_SIld@!R%2xX>o|#(unLg1;4=wsCrn^YI+#lPZA|-aqc=f75fE z5BxZF>wNgf)}_mtG@X9pd?3h~GMu^(sE>azH6~mSPWhcF(SB^`Z+f-UDo!b!+&Z}o z$lX7fsGK}FF*{otS1^xyQ+fHwG>e(-;79eY1``Z+77 zA33k}J0minuK#%{oeypOgBo?x{y*33UzQkwebPzc|Hu64j2ow)IHmfZk4#T|KM?tI zA9d!@fY$quBPE9?a13dk!T~NLAZ;3Rvl;iCz%N01S2EtcBVX0a(VQjEp$w*}T>A?Z zPvDn0b&SC$XuTg(_q$8U#&3?+gy21w3v&HxI<^W4%gn(==&!yt z+;cYuE|^j09oBFqc;Hjiyoo*}7s{O8chNjm@rDsuU@eCRSN+wTX%&*A=)ca0nYPCr z_V*FDZ5>i3`maCt<^WAUl>D2r^Qy+7*1T7gP`1;PZC`=1^!C;4{ywc|H#nSsn0FED zt=hB36mJt!&^6>Y)3FWt_}2v_hsS<1N85x%Wy$lq@2P&9S|dB(syl71w=;OpH=<0| zwjrZk>F${YZ8;WQdEeY_%agdfOML6du@z5bseN?+^8~Oje{8&OVmt8!q+vTg-Mizy z>DP{Kct4+cy&YZZ$bGYo>*%{1e5G1i4?c6>w~W7mt3%@Cq0x~=lD~S6 zUvEWm+)0OdaNneCPh24nOuqKS#rIBH%dPXkOpzS@#ao@PJWZt?!IZ98VO zWuY^TK%*WvZ(-UYtG4#ckUqH619Q7QuQ1+xU_v_(M~Od7!4Aa9L$da|_B|wf&MfM{ z!*pkZ?SAA-z?7(Waet|2_|bsATw0Vxi6M$h766Hf-$0!K?F1eDO!7 z;Rw1e{r}!0GeXA2G1kwjW-Tdr>-X=wGpab%^!_b=Y*x#n=o8;7Sj&Hi9a!o@k==fa zKfhoV>+)58xbq1qKI`E9sE(}~rv!RgrQ<+~_v;hm=}ZFrPYS%l_h`2IW2a}o?R`bK zG_TP{2JxYihFBy@5fp4jPYB7FC?lTjWAWt1Grx04#;ipMtXjR$YEA3&AAQYkQZ1&y zU9z zAHuw;z^~w0fM=)TU4v&Uv6S3REP9?cenR4&o5o&x(;rKGZSFgPc$zyW0aLDPNc}`R zgM-avjzpiiAC z_o{O)G}#IK((fCJSNOTgc)zByE7%n7P9q(_L~{ToUXxNfz~om(rV}P5 zlO^(*%;-J%NXFUyHPi4$9izHf1e*K=PF}E8+cbv@``%&TW&RTUfBJ&WDg_>nNe)bI zH*7KGTAE%>e1SEC%t51v5G`B3+I&;+db&5!a^utg9xB*8Qs!=9!hnd4XjJmR-m3M< zt7DSY9c+reK>StRzAPN=_RE6l*Y7^`o4kldzd+7%d{*ajOK12}_QwA+uhj zqM#V`VCeiOgG3mZ20iFcz5xH}I)MmPlkrFJ|0%$oo=>x!Knnk{64Z6#&vU$$5GO^i zp6f8ao6hj7^ioLF({cIN@uny4nf0#extEyJ>>O-T^$N-SS6f%fRHi+5iKiMfwHL3W z+E4B|_mHP!+>_e;_&V0k(2gFl)pwh!z5dC?^KCCS^YsUnG(Z3EN{!${FJ1U>OZ3|h z(CFxte$Cq}BYKB)e>$N!i6t~7U@RZ`Y8>!G~eq% z;jUAm=13oItTz~^nh5=0TGWxOUL{QUj$J;D@${wUYNat-`r)=oV;XWD9dFn6mS{kl zVDGDQAM^`5`&+7!!Q@AeLn!NoG-h63ZlKrFm}7Fep(gIfy5V$M)3hJg_Jy<@`tjKf z>QXn3)KzPoO&2o>M=_q4`h_HrzFC#6DvhGTdo-!b`Lzm3-+ z%nayHEAXC6=v%yT%+536S6xnqnN$Npx_jzm^xYVXq)ys7`?R&!$s%hnCG7jiP~E>Y zYQU}5bK1tRYWKogP4nmYniy=ius7kX@x74iyMH=2Cf9d-(|I7Z=RDbQZu8FdpI?d% zHycLqp7n@9tn%ai#-b`;%PB3Fp2lgjV;(D5t4zV(572zPWOK8=4U=d=Ky|Buk7qK) z2a^?>g{R}_U);*-IDt*{5YqZ1bFlBt-TE~?xbfz<#r-wp&SH8Ep;7nSFP$|U)oaVS zHEUO3q4gGppcNKi;j$IrNtGqo`}XaS7ExIzkEs7SmKrFub>{z9*_Xh_cy<40CVB1< zLC7LvB7&s0$-am+Qu`9I1=V7bjAWQh#>_;-8nm`jwB^{^##(Qw+J2@=NiD54LTjxm zZPE5sOR4?k|Gm#Li6rBF|Lx}^bDw+ex%ZxXwtMc@fNpWf*BVf@e6(NgzSgz(a!5;- ze3Xz~Hu@xANKjg?6j!L{>^*fDFJwERUhRHWxfNR)dDpF48$@>|gC<*pRN`u9t-n8+ zH%ynsZ9aFC(No#`Q11((O$9vQLYt_3lEIO77GC(;_SM@bw~Vs zDqoIAUzGYQ&KCfI-H!b5jkJr>m+{|F2E8XrtsXTIV=R}ZZ4+T1uwlFeAGODVB^DX~ zuKjDDn@{Egl`N2Qr?sxS6743j0?tQ^n=-NevymhwV*^UB&tH5I%fg}_gkF|Lo(p2E z8anajBs5EAxiXPatP1s?3@}BgD+;FUf~)0`ce=sUc#4psp4^y576Q3kT066`Bu!r~ zn)GvWQ9kxQh!TL2s)$z>c&BS0v;al9KKAeJb$iF&i98}u(UDi&cE!bYnyC1dDC~{G zK4%(-uid(0uT+?y{;qNdjc6=3HKxl`(e|$!)A4Drvab2DLN66kRuVt0PZO26$$rh5 z!_Dq(I~~DC1a-kw<~E^cAe445Ab6;v`^_C|-u&~^Bp$>N_d2^(JplDpGKDMBPj0pL zXj6&e|MqHBzsB^?XjI9K6DTfI*d;@?c2oK#0kk+-O7MEXIsi>X-@+!pJZ*wJ-hqV*qV(vTB(UwL#b#6NYCeYyJrXnC$fsY zm65-~TDhJgzCy`94I?i;$y1uDFqT^GZ)Ki{ulAbcXbt;f_pXxN=f%?!lGa>+6CT_J zTjE{VDdw%sU3e4#?K>5Q(;IbfPQ7PCqq||Y%UQVc zblo`y&Pl1085CvX6j^nx=2Ws84GL*a+Bx_u6NyshKwZMM7(E9%RADM*fSdzDf6fu6 zt82C(GaO`vZM1Pr3K53B^WD0AjZShkfVEiOsfr9GyVgOjS>73HP}tq+hHL#>NDO^o z#o1FccYXd@8AH3|8+TB|Vx4_gAK(!rfY*R+3F_{K?->tZh2%;q6a^$%}Do+KN&ZfS|HN zudQhLuqBgEpHTDH#O9QM()+C_@;!L= z8vw!0dC;Ps=8U2b^2;Fn_e!xXf1m5Uo6YXd&wz1PtE{>hwfgj2(Xz$izx zf?NjzokF2loIn>&3fe=33xrs@xv(mDV<34+FU4^KX5jhTtq@?#@tE#xbR~`-dieEJ@uzv4-C>v>3Q{_$N^3C+} zu6;EQ)D8atRFYpYpzvcJf#e-31jf65c_Gs@P7Fxyo1VFSvx)ILOz)zv7Vw`RK@0ORmk1+d|#m)R!E+*}K{_RFBXV{69C> z&o{V!9K!Xp4KAL1(83Y@8c`xHF$Xv2C)ZVVo`@!Jy%rTksu|cm`Ry@}hVd>gCQRTL z6H4KA+nnytcdTnVNv0p9f?riw>Jc#d^;pSodFHPkv?2YV+1DwvgOS5mHa=c7=<|%6 z3q#6sOma@fje--NMJvjcnI`9apZ`g(wEgybWo4Gc((@0&S(^aC;{;K+qmH-wX4~{K zh(oay4v6?GfZ)gD&c+m8onPGT?J|g`$l(F&8(EXCJuUeZA$mo>)Qh7{oVViRXcBO! z)8c3guWekMN_D54v$>^lr!|^ z%o*+U6%FEa!ZAEA;ROcdHpWUP(X7HxR|o1cWErJHfjBNb-es-}15M zbLVKcoZS)^)Tw`n0!mw-b z{B}TaAN^cJ;muhCu5sr_)(BNx%Otl9mqc|~3hc!w>y5I}^)`h~h?>Nwj9fPWdt32P zpPv9mFHN9%9C$k#%YCV}EiS*C*Q0sQvWl)IP%($N2XveoNxs1^cfx*Zk&yeNNFAJ|jCNa)v}7g5X$=6l4Su-g z!*1@w#@z%!Jc9Kg@Gj(VO;T6YocO5E=>82*f2?$NJ`6d07r5`c7k?Ra{Lng)0Aj32zPmdSjdZ;*6N}

    mfmRITkSLqwal-#dCTI|~q^CBSyfWb!V8){`%?$O0fA z=xz0bdwt{6L<2ziJoJ=g8ViZO@>Mo7J`N zB8NKnl&;i>L2HdJ$U-kGy3s32||)8~^IIJ(JUo(;O~UY%dxJi1-eG-~&8< z*FOnDn;skG{>Q`VLJsHl?MJ4*pPaQRR4xnJq3oWu7+BPDmev7>KLgJq^Xv3_yegXl`( zW(YU8Mu6em?N_VE8yVT>+5iSoao&qoDO3#n+67PuzJOrY{`MK zJ315c&-v3Z&!_GC$F`>fk}oDCB5YrEFCv$GhGVtT3rzmFGXC>ZcwmdN+S)E`LzH*&!c}66L|*ns4mWo#>sdZ{b3g&sD$!Q-g02Lk zq9%Rm*{4uxQGKb|2BA$=u`k&o74@Yl8^FuQQz>Z^dUz(4BL5G6uct2Ai9c=oE%_JZ z@o+zhs%Nytmj>)OxVEevmypAEOs}rBbXVT|-3S_q(uNVg-;YvIR=v1CHT(>J@Aapo zT~3tpt5X5$?nfp^ z<;r9*N<9UXm-^AB&jC}h>$*Wyybs^=2hsE0_`6^bd2!NwHi$mnQU)Jij91S&D<9o3 ztM+-Jn{)t99Gp_*a0j3?{-!GBm>+i$WEFI65WUB7Rp^FH?I(li(znRbevpFW>!5mzq*nAb3tbxtK;t z-(mv@g!x7q6`exi|D;jj7x-IiFj)=(Nzh=4t{eUyKWoUc5Pr0uHxObMJ(x-W8Q&8S zTrmcO-3Yn7aCsBCp{?EAGLgfD?E63S&;J@IKMx?P_i=+si*eP?2Lv}u)7BoSd(mS) zH#&UOpmx>{rc@5G6A*kRe#fic=@KJJ0ygzPrw|Du3OKzr&U9EY8 z_G|^D`3*_u|Eb?Bw70K6BSS)O=f6RB01-bM5S)eG2j$PbzC`;#hJaDui1Fe+kna!k zUf;X^*k5uE250FT)M1;mFMa~(gYFgcPs(4psY=>alhF`a_9QBmV_KPgw64h^QM{5= zy27ML`G3ooHJ+1yD=G(BQvb)#k}BahS(KIA!gY$Q+IsJV9;!esrKL`ViD50>DXD0& ztfHbN99p-L(tKb`jf*+s-`UBxRB`J8;#N?C!$wjO*NKXbkoq7BoPtu}AVqg8O;ft7 zNVW1^QbXeONbb?uFVUmx=~R3iH7UiUJf%ag&;@ckXx=V~E-*=A`(8%iq!_@xxmZbL z_NCIjf`?k^rWvY{XAaM#JIADD?WM9n6fN_N58gHJw#?M!I9SoElE8{)vD|oRUF*S@ zyZUOt{|%#Jek9B<4U^K}k$Vu-Yzqs_B{j59NYTNLmwd!d!8I3+MByG#K-TZFjus`h z98x7$>4CxviW$6~N~vEXxF|m#Nu^&yp#hjHBknF#9)YxMO8Oqp3>o+0=gA!A9YYIYrQ(l_k@knFFU_2^ zbWV&}t-|6dcURqhM@}P9)!gvjzBA{WCYI$?GtuQ@VR?Mzrw{ouGYqcV9Ft@%OZ>Wb zT6=%=?=qWVMC5w4uIrWEd_FNzfGa*;;Ek6@r8$ojxh&K+LKKa&Kvc)Ip_69R!ImGm zw4zVHtsgs}ztXjt_!7Dm3)JD&FC9Yj=#fNj)sP_@HT(T>eYS^fdr+>E5@*^;6S7FZcRzsnG!hUJF_dY5A0-|ag0x}4&DGL0Rs6irx@$1S>vwOeLlU?L_NTk!I+U zIEhp^OX^o|Wzs^OOW=0g&Zm(junjKeOYn_dlU&wL>dXS#3>>e}Qj-ekmlDJ)=0og# zKqJ4j6Th}5C8AVLY@!0(g5H;iEI1>Xt)Z`Tx(Z`ODEr-6(>AC zX6TnGQy60K7&n-n1`WElp|sPIgI!e&M3KaoyHrkP{x_%O<-W_%TGx?GISQKUI^^mi z{tTh0I&umrRpO&+Ib0jyDhf+aRtZ{hD}aC10cqO1UZ^MV+diYI^~Z=dNnu^J9D-KM z(O!yNExwMp@*^Q$zRuLI0nCM~Ffo;W1YF8`>CKOXK>w-(@ictD&@jN&22|Q~%A${8+Q-hC$23pr)bVcY~N6Hxj(A-kGTmtZ~dI^9Q1`G8yJS3vTqyt>U zq+`UFAUzf3J0y@U?e_>saQzgmVZ6nVVRv)8&??To-k&r1Q2^|!;455w^Glr{u%o^D zNV@U^R8}o-X`e%&Uud0aO$Hn6q*JEQsIyq8T3je8 zJcrJGUG0+x{svyzBVJudC4daQ1qeQlCa>LjUvw`$D%LEn3YPj{GY|et^=H&c@8LrY z&k3z4^_&nn^)hmJr0nh1amQ8TYdyE?ji!8~IkVfa(_yz-Zhh@s6DdMY&tsBJTYA;a z0l9fJr{|+1CgJv)ffe)ZHk~;))8O{@koZw`4qqN#Elh%6yfbKv|Ew*cK1g%&3>-D^ zP0zP?Ox>@=2Oh+px}wdw-}m=fpC}h7*tN4+zvFk#cSf4q@Ra1iCD#3k3kJyft*a-l zUcIqP_;5LY%TM_}ojNx-l7loiE~dr{{il30X7xm*>mp6Fiq{?98dWkMX@8_YJALcp z&qwrcQM?8p_)s4I?AeAK zgH3edB6ZdbJkCnLUJxRhVL2c?CQg@?XRz6I`aFAfc-(0Hgb5;ZjJ8<~`V70yn2~Fy zv8^=?Y3kQ3fWB$Ln$p6@f7Eti_)sgLL+K_QK%PD32nGNYl8IJ93G+7n{Ad{ zt6i6G6Kk{QQfdhBzer{ReREmxt5c~sUAQbXCv!4ml=!3IRT;q1{YSx9MRkK%W14bF z=ucb!#TwC_OG16R|0cfSmxiK$66#XeU^(f?`ALXS(Su+Bs(%&=IeHB(zbJ%OMo;@M z3VkWQC5le}SvW%0;rM0wRj5f5t_XF!<9YYEgRD@u-8G@1W7icS7w4(mfxq-u!JAfI z6>3w~FG7g(n?Du(CV0^PUxcR4Z}lnoS0Tcg)Y9Bvg)nC_N%|G}o6yGjO~fLDAfq1r z=QknPnd?oxuLu|2>3N<{n`EY56swfvUA*=QROm zxjOaLG^U8_Fx!u=3%;JwF>who2{EzpQRII^_|7ZSoT(dQv`1v*S`ChBzYA9cy8oBZ zv6{p+c{Yl^C0OyY`?_1gNIG|0=%9)v|2x8OK1o)6rg6M3&lq8`8gq~ zaTi1?eTab?&3|dTF|WF1z%n;x(w6SbpYA@u!RqOE!4#HLTswiz=G8C3EJ1F+?ZV5H%Xao*eVkX56Qr$Rga&4Wx& zgw7npc?EqI}9FJ=M7`G^C`4knu*LtdQ}`8Z_56ne@C(;vRoIc(WjyKb?2H;Qym>noqh!O7KMU-H$qu=GKPUMx^_hmio)0= z566&}Y>K<%mvB~H?PwW=eow{tMd%_F4mIeau*!?MBL+T+x78Lg+NR6M%^hPjB#qBa z&$SmABP8YlN9gj6nfC0ATuXt@j#tG^MoW6G-kLc|57n7zFj#bkv3Yuvn6zbsXL9qU zFHV%;AFfaH%wTw{O>fc}&31zo-walR%{alJ%T2c#tjN<@a!m!Jj3yK6HWBwr5RrRo0FIReq@hQ)F_JeUyTFodFzf9KD^THJ z7TluZSF=RWDn3)l10gKN6_=rAS6MV|2MhJ#?Xp7?$W7!w>25F!C5%%$KjrCn#bzlIeC$KbCoa15wdtXK4+G`ro{6v=E{N+iv6In}V z(x3XZVZqL%Cv{AO(OK07=0_>(P5-={?9mp2ujG2t;;M5#><&kn4$ioQz#m{&X2fVa`pfx3TJ(+}-gyd$|IyQ@+qQphDZm2jUu zX=EoBR=a}xOd;dY6w$k`&0XA*POJv)>%@Yp@N<9e#ClZmxn(jds2c6J$!MHC7_K5J z2&|&0_I38Qdt7*&W6kSql2QgL!P>*vghlzuH7!LUbAuByzn_q_O z_-{BtwK7;x~}ohql~Av4`0&HM6S`CMcTpMz*kA=_aEU+~}z<*+z>%7|^k*csex}WZ+y= zn@>qZb!9ccI+e@e4CKi{hcX@gHi)%whXtnMI86v`PQz$V-+{&|G-cF&3_NSGowvq7 zwib_J`!I1TUC;dJ&Kl-T6HKfN{bXXFQuOQK3t0Jo(e{HF^aUlXR&{YN9M_E$Jc0F~ zAv1t`-UK++S5|4BGZ}Gq1AbhNfzPvH{0-v_a5K%>diaN# zn8Vm|P2&s(n=U6eGY<;eWPnR!Kru7iALw^OmMJ%#JGfRu7UFwUJC_Bem~!=*a7(Ru z+$A>~a&>xJfjPsLfvAF>8&nhACxlJlB1?VISxk9ZMsvO%P2qi0K1==xEy{u3TsfI_ zq{Y)%8(ekEI#aEQj5)fGW9QsyMibDo`viPEIDx$-j!@Hy>}}fb!1wBt*f%s{G6eM` zFKd{>lF8Cg<3T>TY&cb)&SI%UEschz1Ze!|`4sjC{WzI*CI877fuoaHZ@Mv+1^dcI zOI%ltiHwYlijR$_z(O_?!@H`GeNS5-Km&bJPs8X#Gpj@HH@BF2qEy%!3asM*rtI{BpA+j!bwIThq90IQtVbo^@^R5Z( zR)fW)FL(*PnCVGJOi*OcSAae*mGY!p%b~^ltYo#F85)|ol7+}(<@kCf`;R*kyNlRh zdhjOt$89|&u40Q7$@AL|mGN*DG)T>D5SG_hvtY-&tJ!@4a=i+n9>?kp>_s)6xtahw zBQ8n09XUc`N|7WPxjC3haT_Fxmo8s#8e^~;ZG4Uoae%1MiR~vv$@ znYjbpc6|qHM=B@^G$?Eb>)>egf2^j8Qg^aYK@SHQ3w?ZoU$B#DYn6{)WY{Un!P4l; z9`wF=CtJFt3-h4EU$R8H?7(QgxfAo@l@8H6+3aAwfc}Yt>FKvHSihefAn5Ebp>ud# zYsoH*hxabHAltu!n5@{vEcDh_ER||}#riuQe#LfaXu&~b|9Svp8A*>0a5dcx-4MTC zJcxlovJrO>FT$SweTdCbQ&3B=T%8|<#*PVx*@Q$MIjBJco}n*dv;@A0KSDTSTn&z= zCmw)eLum{b%KIu!hx%d+ED5tDyWNOLfJAMbB`=H4s4;m9ch|IFws8iN-9Ue;HJ$04 zyGBcW)fz9#RU`P<-CdKYG3M~Npp6pLnx?c}t--s7?wabJdOK!h>3Nu=(R|=?JaX5} ztuD68vAU)PyMrSgzt+~=R#(H!Ay#BtZyzB4r>&m92ojtu(FZMF&kahrXj`u^@=MKO8%{Xm+Y7u#4tn+F%4(QaWgA(fDqfU=3fF;D(@4 zf+oPRvzz8;HP=#%zOSwErEWapv#zctma=_NYEPghPU8A^{}y~CdW%B9qaM(Pagsa7 zKRH(R)O_bbpY+uKIr-jR?MXDwYQT5^1 vgroQ8HlKH1&Z)Ls<6MEz(BxFj0MFLTr9w%|d7G`s&F6X7(1 diff --git a/package.json b/package.json index 42acf914..58c90a90 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,35 @@ { - "name": "learn-anything", - "scripts": { - "dev": "bun web", - "web": "cd web && bun dev", - "web:build": "bun run --filter '*' build", - "ts": "bun run --watch scripts/run.ts", - "seed": "bun --watch scripts/seed.ts", - "tauri": "tauri", - "app": "tauri dev", - "app:build": "bun tauri build -b dmg -v" - }, - "workspaces": [ - "web" - ], - "dependencies": { - "@clerk/themes": "^2.1.30", - "@tauri-apps/cli": "^2.0.0-rc.16", - "@tauri-apps/plugin-fs": "^2.0.0-rc.2", - "jazz-nodejs": "0.8.0" - }, - "devDependencies": { - "bun-types": "^1.1.29" - }, - "prettier": { - "plugins": [ - "prettier-plugin-tailwindcss" - ], - "useTabs": true, - "semi": false, - "trailingComma": "none", - "printWidth": 120, - "arrowParens": "avoid" - }, - "license": "MIT" + "name": "learn-anything", + "scripts": { + "dev": "bun web", + "web": "cd web && bun dev", + "web:build": "bun run --filter '*' build", + "ts": "bun run --watch scripts/run.ts", + "seed": "bun --watch scripts/seed.ts", + "tauri": "tauri", + "app": "tauri dev", + "app:build": "bun tauri build -b dmg -v" + }, + "workspaces": [ + "web" + ], + "dependencies": { + "@tauri-apps/cli": "^2.0.0-rc.17", + "@tauri-apps/plugin-fs": "^2.0.0-rc.2", + "jazz-nodejs": "0.8.0" + }, + "devDependencies": { + "bun-types": "^1.1.29" + }, + "prettier": { + "plugins": [ + "prettier-plugin-tailwindcss" + ], + "useTabs": true, + "semi": false, + "trailingComma": "none", + "printWidth": 120, + "arrowParens": "avoid" + }, + "license": "MIT" } diff --git a/q&a b/q&a new file mode 100644 index 00000000..e69de29b diff --git a/web/app/(pages)/community/[topicName]/page.tsx b/web/app/(pages)/community/[topicName]/page.tsx new file mode 100644 index 00000000..62e69371 --- /dev/null +++ b/web/app/(pages)/community/[topicName]/page.tsx @@ -0,0 +1,5 @@ +import { CommunityTopicRoute } from "@/components/routes/community/CommunityTopicRoute" + +export default function CommunityTopicPage({ params }: { params: { topicName: string } }) { + return +} diff --git a/web/app/(pages)/journal/page.tsx b/web/app/(pages)/journal/page.tsx new file mode 100644 index 00000000..0c2cd038 --- /dev/null +++ b/web/app/(pages)/journal/page.tsx @@ -0,0 +1,15 @@ +import { JournalRoute } from "@/components/routes/journal/JournalRoute" +import { currentUser } from "@clerk/nextjs/server" +import { notFound } from "next/navigation" +import { get } from "ronin" + +export default async function JournalPage() { + const user = await currentUser() + const flag = await get.featureFlag.with.name("JOURNAL") + + if (!user?.emailAddresses.some(email => flag?.emails.includes(email.emailAddress))) { + notFound() + } + + return +} diff --git a/web/app/globals.css b/web/app/globals.css index 0b181a0d..24faef84 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -74,3 +74,12 @@ @import "./command-palette.css"; @import "./custom.css"; + +.scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.scrollbar-hide::-webkit-scrollbar { + display: none; +} diff --git a/web/components/custom/GuideCommunityToggle.tsx b/web/components/custom/GuideCommunityToggle.tsx new file mode 100644 index 00000000..70adc389 --- /dev/null +++ b/web/components/custom/GuideCommunityToggle.tsx @@ -0,0 +1,50 @@ +import { useState, useEffect } from "react" +import { useRouter, usePathname } from "next/navigation" + +import { cn } from "@/lib/utils" + +interface GuideCommunityToggleProps { + topicName: string +} + +export const GuideCommunityToggle: React.FC = ({ topicName }) => { + const router = useRouter() + const pathname = usePathname() + const [view, setView] = useState<"guide" | "community">("guide") + + useEffect(() => { + setView(pathname.includes("/community/") ? "community" : "guide") + }, [pathname]) + + const handleToggle = (newView: "guide" | "community") => { + setView(newView) + router.push(newView === "community" ? `/community/${topicName}` : `/${topicName}`) + } + + return ( +

    +
    + + +
    + ) +} diff --git a/web/components/custom/QuestionList.tsx b/web/components/custom/QuestionList.tsx new file mode 100644 index 00000000..979c8af3 --- /dev/null +++ b/web/components/custom/QuestionList.tsx @@ -0,0 +1,65 @@ +import { useState, useEffect } from "react" +import { Input } from "../ui/input" +import { LaIcon } from "./la-icon" +import { cn } from "@/lib/utils" + +interface Question { + id: string + title: string + author: string + timestamp: string +} + +interface QuestionListProps { + topicName: string + onSelectQuestion: (question: Question) => void + selectedQuestionId?: string +} + +export function QuestionList({ topicName, onSelectQuestion, selectedQuestionId }: QuestionListProps) { + const [questions, setQuestions] = useState([]) + + useEffect(() => { + const mockQuestions: Question[] = Array(10) + .fill(null) + .map((_, index) => ({ + id: (index + 1).toString(), + title: "What can I do offline in Figma?", + author: "Ana", + timestamp: "13:35" + })) + setQuestions(mockQuestions) + }, [topicName]) + + return ( +
    +
    + {questions.map(question => ( +
    onSelectQuestion(question)} + > +
    +
    +
    +

    {question.author}

    +
    +

    {question.timestamp}

    +
    +

    {question.title}

    +
    + ))} +
    +
    + + +
    +
    + ) +} diff --git a/web/components/custom/QuestionThread.tsx b/web/components/custom/QuestionThread.tsx new file mode 100644 index 00000000..69e13df0 --- /dev/null +++ b/web/components/custom/QuestionThread.tsx @@ -0,0 +1,167 @@ +import { useState, useEffect, useRef } from "react" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" +import { LaIcon } from "./la-icon" +interface Answer { + id: string + author: string + content: string + timestamp: string + replies?: Answer[] +} + +interface QuestionThreadProps { + question: { + id: string + title: string + author: string + timestamp: string + } + onClose: () => void +} + +export function QuestionThread({ question, onClose }: QuestionThreadProps) { + const [answers, setAnswers] = useState([]) + const [newAnswer, setNewAnswer] = useState("") + const [replyTo, setReplyTo] = useState(null) + const [replyToAuthor, setReplyToAuthor] = useState(null) + const inputRef = useRef(null) + + useEffect(() => { + const mockAnswers: Answer[] = [ + { + id: "1", + author: "Noone", + content: + "Just press Command + Just press Command + Just press Command + Just press Command + Just press Command +", + timestamp: "14:40" + } + ] + setAnswers(mockAnswers) + }, [question.id]) + + const sendReply = (answer: Answer) => { + setReplyTo(answer) + setReplyToAuthor(answer.author) + setNewAnswer(`@${answer.author} `) + setTimeout(() => { + if (inputRef.current) { + inputRef.current.focus() + const length = inputRef.current.value.length + inputRef.current.setSelectionRange(length, length) + } + }, 0) + } + + const changeInput = (e: React.ChangeEvent) => { + const newValue = e.target.value + setNewAnswer(newValue) + + if (replyToAuthor && !newValue.startsWith(`@${replyToAuthor}`)) { + setReplyTo(null) + setReplyToAuthor(null) + } + } + + const sendAnswer = (e: React.FormEvent) => { + e.preventDefault() + if (newAnswer.trim()) { + const newReply: Answer = { + id: Date.now().toString(), + author: "Me", + content: newAnswer, + timestamp: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) + } + + if (replyTo) { + setAnswers(prevAnswers => + prevAnswers.map(answer => + answer.id === replyTo.id ? { ...answer, replies: [...(answer.replies || []), newReply] } : answer + ) + ) + } else { + setAnswers(prevAnswers => [...prevAnswers, newReply]) + } + setNewAnswer("") + setReplyTo(null) + setReplyToAuthor(null) + } + } + + const renderAnswers = (answers: Answer[], isReply = false) => ( +
    + {answers.map(answer => ( +
    +
    +
    +
    + {answer.author} +
    +
    + + + + +
    + + sendReply(answer)}> +
    + + Reply +
    +
    +
    +
    +
    + {answer.timestamp} +
    +
    +
    +

    {answer.content}

    + +
    + {answer.replies && renderAnswers(answer.replies, true)} +
    + ))} +
    + ) + + return ( +
    +
    +
    +
    +
    +
    +

    {question.author}

    +
    + +
    +

    {question.title}

    +

    {question.timestamp}

    +
    +
    +
    {renderAnswers(answers)}
    +
    + +
    + +
    + + +
    +
    + ) +} diff --git a/web/components/custom/sidebar/partial/journal-section.tsx b/web/components/custom/sidebar/partial/journal-section.tsx new file mode 100644 index 00000000..19879f84 --- /dev/null +++ b/web/components/custom/sidebar/partial/journal-section.tsx @@ -0,0 +1,113 @@ +import Link from "next/link" +import { usePathname } from "next/navigation" +import { useAccount } from "@/lib/providers/jazz-provider" +import { LaIcon } from "../../la-icon" +import { cn } from "@/lib/utils" +import { useEffect, useState } from "react" +import { useAuth, useUser } from "@clerk/nextjs" +import { getFeatureFlag } from "@/app/actions" + +export const JournalSection: React.FC = () => { + const { me } = useAccount() + const journalEntries = me?.root?.journalEntries + const pathname = usePathname() + const isActive = pathname === "/journal" + + const [isFetching, setIsFetching] = useState(false) + const [isFeatureActive, setIsFeatureActive] = useState(false) + const { isLoaded, isSignedIn } = useAuth() + const { user } = useUser() + + useEffect(() => { + async function checkFeatureFlag() { + setIsFetching(true) + + if (isLoaded && isSignedIn) { + const [data, err] = await getFeatureFlag({ name: "JOURNAL" }) + + if (err) { + console.error(err) + setIsFetching(false) + return + } + + if (user?.emailAddresses.some(email => data.flag?.emails.includes(email.emailAddress))) { + setIsFeatureActive(true) + } + setIsFetching(false) + } + } + + checkFeatureFlag() + }, [isLoaded, isSignedIn, user]) + + if (!isLoaded || !isSignedIn) { + return
    Loading...
    + } + + if (!me) return null + + if (!isFeatureActive) { + return null + } + + return ( +
    + + {journalEntries && journalEntries.length > 0 && } +
    + ) +} + +interface JournalHeaderProps { + entriesCount: number + isActive: boolean +} + +const JournalSectionHeader: React.FC = ({ entriesCount, isActive }) => ( +
    + +

    + Journal + {entriesCount > 0 && ({entriesCount})} +

    + +
    +) + +interface JournalEntryListProps { + entries: any[] +} + +const JournalEntryList: React.FC = ({ entries }) => { + return ( +
    + {entries.map((entry, index) => ( + + ))} +
    + ) +} + +interface JournalEntryItemProps { + entry: any +} + +const JournalEntryItem: React.FC = ({ entry }) => ( + +
    +
    + +

    {entry.title}

    +
    +
    + +) diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx index b1f70df1..dabc3e3a 100644 --- a/web/components/custom/sidebar/sidebar.tsx +++ b/web/components/custom/sidebar/sidebar.tsx @@ -16,6 +16,7 @@ import { ProfileSection } from "./partial/profile-section" import { TaskSection } from "./partial/task-section" import { useAccountOrGuest } from "@/lib/providers/jazz-provider" import { LaIcon } from "../la-icon" +import { JournalSection } from "./partial/journal-section" interface SidebarContextType { isCollapsed: boolean @@ -123,6 +124,7 @@ const SidebarContent: React.FC = React.memo(() => {
    {me._type === "Account" && } {me._type === "Account" && } + {me._type === "Account" && } {me._type === "Account" && } {me._type === "Account" && }
    diff --git a/web/components/routes/community/CommunityTopicRoute.tsx b/web/components/routes/community/CommunityTopicRoute.tsx new file mode 100644 index 00000000..cc9fa28b --- /dev/null +++ b/web/components/routes/community/CommunityTopicRoute.tsx @@ -0,0 +1,74 @@ +"use client" + +import { useMemo, useState } from "react" +import { useAccountOrGuest, useCoState } from "@/lib/providers/jazz-provider" +import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header" +import { GuideCommunityToggle } from "@/components/custom/GuideCommunityToggle" +import { QuestionList } from "@/components/custom/QuestionList" +import { QuestionThread } from "@/components/custom/QuestionThread" +import { Topic } from "@/lib/schema" +import { JAZZ_GLOBAL_GROUP_ID } from "@/lib/constants" + +interface CommunityTopicRouteProps { + topicName: string +} + +interface Question { + id: string + title: string + author: string + timestamp: string +} + +export function CommunityTopicRoute({ topicName }: CommunityTopicRouteProps) { + const { me } = useAccountOrGuest({ root: { personalLinks: [] } }) + const topicID = useMemo(() => me && Topic.findUnique({ topicName }, JAZZ_GLOBAL_GROUP_ID, me), [topicName, me]) + const topic = useCoState(Topic, topicID, { latestGlobalGuide: { sections: [] } }) + + const [selectedQuestion, setSelectedQuestion] = useState(null) + + if (!topic) { + return null + } + + return ( +
    + +
    + +
    +

    Topic

    + {topic.prettyName} +
    +
    +
    + + +
    +
    + setSelectedQuestion(question)} + /> +
    + {selectedQuestion && ( +
    + setSelectedQuestion(null)} + /> +
    + )} +
    +
    + ) +} diff --git a/web/components/routes/journal/JournalRoute.tsx b/web/components/routes/journal/JournalRoute.tsx new file mode 100644 index 00000000..4dcb644e --- /dev/null +++ b/web/components/routes/journal/JournalRoute.tsx @@ -0,0 +1,114 @@ +"use client" + +import { useState, useEffect } from "react" +import { JournalEntry, JournalEntryLists } from "@/lib/schema/journal" +import { useAccount } from "@/lib/providers/jazz-provider" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" +import { calendarFormatDate } from "@/lib/utils" +import { Calendar } from "@/components/ui/calendar" + +export function JournalRoute() { + const [date, setDate] = useState(new Date()) + const { me } = useAccount({ root: { journalEntries: [] } }) + const [newNote, setNewNote] = useState(null) + + const notes = me?.root?.journalEntries || (me ? JournalEntryLists.create([], { owner: me }) : []) + + useEffect(() => { + console.log("me:", me) + }, [me]) + + const selectDate = (selectedDate: Date | undefined) => { + if (selectedDate) { + setDate(selectedDate) + } + } + + const createNewNote = () => { + if (me) { + const newEntry = JournalEntry.create( + { + title: "", + content: "", + date: date, + createdAt: new Date(), + updatedAt: new Date() + }, + { owner: me._owner } + ) + setNewNote(newEntry) + } + } + + const handleNewNoteChange = (field: keyof JournalEntry, value: string) => { + if (newNote) { + setNewNote(prevNote => { + if (prevNote) { + return JournalEntry.create({ ...prevNote, [field]: value }, { owner: me!._owner }) + } + return prevNote + }) + } + } + + const saveNewNote = () => { + if (newNote && me?.root?.journalEntries) { + me.root.journalEntries.push(newNote) + setNewNote(null) + } + } + + return ( +
    +
    +
    + {newNote ? ( +
    + handleNewNoteChange("title", e.target.value)} + className="mb-2 w-full text-xl font-semibold" + /> +

    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 eq(eT`^r8y(siiX6V1_xA6&+PeKm8xgxl@4v diff --git a/package.json b/package.json index fbf1a31a..8dbb6a9e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "web" ], "dependencies": { + "@clerk/themes": "^2.1.27", "@tauri-apps/cli": "^2.0.0-rc.12", "@tauri-apps/plugin-fs": "^2.0.0-rc.2", "jazz-nodejs": "0.7.35-guest-auth.5", diff --git a/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx b/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx index 0b5feeb9..aa9403c5 100644 --- a/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx +++ b/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx @@ -1,9 +1,19 @@ -import { SignInClient } from "@/components/custom/clerk/sign-in-client" +// import { SignInClient } from "@/components/custom/clerk/sign-in-client" + +// export default async function Page() { +// return ( +//