Skip to content

Commit

Permalink
Update note schema
Browse files Browse the repository at this point in the history
  • Loading branch information
colebemis committed Dec 12, 2024
1 parent 1d33aad commit 3fb8466
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 98 deletions.
14 changes: 7 additions & 7 deletions src/components/command-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { useSaveNote } from "../hooks/note"
import { useSearchNotes } from "../hooks/search"
import { Note } from "../schema"
import { formatDate, formatDateDistance, toDateString, toWeekString } from "../utils/date"
import { checkIfPinned } from "../utils/pin"
import { pluralize } from "../utils/pluralize"
import {
CalendarDateIcon16,
Expand Down Expand Up @@ -219,7 +218,7 @@ export function CommandMenu() {
key={note.id}
note={note}
// Since they're all pinned, we don't need to show the pin icon
pinned={false}
hidePinIcon
onSelect={handleSelect(() =>
navigate({
to: "/notes/$",
Expand Down Expand Up @@ -312,7 +311,6 @@ export function CommandMenu() {
<NoteItem
key={note.id}
note={note}
pinned={checkIfPinned(note.content)}
onSelect={handleSelect(() =>
navigate({
to: "/notes/$",
Expand Down Expand Up @@ -408,22 +406,24 @@ function CommandItem({ children, value, icon, description, onSelect }: CommandIt

function NoteItem({
note,
pinned,
hidePinIcon,
onSelect,
}: {
note: Note
pinned: boolean
hidePinIcon?: boolean
onSelect: () => void
}) {
return (
<CommandItem
key={note.id}
value={note.id}
icon={<NoteFavicon noteId={note.id} content={note.content} />}
icon={<NoteFavicon note={note} />}
onSelect={onSelect}
>
<span className="flex items-center gap-2 truncate">
{pinned ? <PinFillIcon12 className="flex-shrink-0 text-[var(--orange-11)]" /> : null}
{!hidePinIcon && note.pinned ? (
<PinFillIcon12 className="flex-shrink-0 text-[var(--orange-11)]" />
) : null}
<span className="truncate">{note.displayName}</span>
</span>
</CommandItem>
Expand Down
2 changes: 1 addition & 1 deletion src/components/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ function NoteLink({ id, text }: NoteLinkProps) {
>
{isFirst && note && online ? (
<NoteFavicon
noteId={id}
note={note}
content={note.content}
className="mr-2 align-sub [h1>a>&]:align-baseline"
defaultFavicon={null}
Expand Down
2 changes: 1 addition & 1 deletion src/components/nav-items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function NavItems({ size = "medium" }: { size?: "medium" | "large" }) {
to={`/notes/$`}
params={{ _splat: note.id }}
search={{ mode: "read", query: undefined, view: "grid" }}
icon={<NoteFavicon noteId={note.id} content={note.content} />}
icon={<NoteFavicon note={note} />}
>
{note.displayName}
</NavLink>
Expand Down
45 changes: 17 additions & 28 deletions src/components/note-favicon.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,28 @@
import React from "react"
import { useNetworkState } from "react-use"
import { templateSchema } from "../schema"
import { Note } from "../schema"
import { cx } from "../utils/cx"
import { isValidDateString, isValidWeekString } from "../utils/date"
import { getLeadingEmoji } from "../utils/emoji"
import { parseNote } from "../utils/parse-note"
import { GitHubAvatar } from "./github-avatar"
import { CalendarDateIcon16, CalendarIcon16, NoteIcon16, NoteTemplateIcon16 } from "./icons"
import { WebsiteFavicon } from "./website-favicon"

type NoteFaviconProps = React.ComponentPropsWithoutRef<"span"> & {
noteId: string
content: string
note: Note
defaultFavicon?: React.ReactNode
}

const _defaultFavicon = <NoteIcon16 data-testid="favicon-default" />

export const NoteFavicon = React.memo(
({
noteId,
content,
className,
defaultFavicon = _defaultFavicon,
...props
}: NoteFaviconProps) => {
({ note, className, defaultFavicon = _defaultFavicon, ...props }: NoteFaviconProps) => {
console.log("NoteFavicon", note)
const { online } = useNetworkState()
const { frontmatter, title, url } = React.useMemo(() => parseNote(content), [content])

let icon = defaultFavicon

// Emoji
const leadingEmoji = getLeadingEmoji(title)
const leadingEmoji = getLeadingEmoji(note.title)
if (leadingEmoji) {
icon = (
<svg className="h-4 w-4 overflow-visible" viewBox="0 0 16 16">
Expand All @@ -43,45 +34,43 @@ export const NoteFavicon = React.memo(
}

// Daily note
if (isValidDateString(noteId)) {
icon = <CalendarDateIcon16 data-testid="favicon-daily" date={new Date(noteId).getUTCDate()} />
if (note.type === "daily") {
icon = (
<CalendarDateIcon16 data-testid="favicon-daily" date={new Date(note.id).getUTCDate()} />
)
}

// Weekly note
if (isValidWeekString(noteId)) {
if (note.type === "weekly") {
icon = <CalendarIcon16 data-testid="favicon-weekly" />
}

// GitHub
if (typeof frontmatter.github === "string" && online) {
icon = <GitHubAvatar data-testid="favicon-github" login={frontmatter.github} size={16} />
if (typeof note.frontmatter.github === "string" && online) {
icon = <GitHubAvatar data-testid="favicon-github" login={note.frontmatter.github} size={16} />
}

// URL
if (url && online) {
icon = <WebsiteFavicon data-testid="favicon-url" url={url} />
if (note.url && online) {
icon = <WebsiteFavicon data-testid="favicon-url" url={note.url} />
}

// Book
if (frontmatter.isbn && online) {
if (note.frontmatter.isbn && online) {
icon = (
<div
data-testid="favicon-isbn"
className="focus-ring inline-block aspect-[3/4] h-4 rounded-[2px] bg-bg-secondary bg-cover bg-center shadow-sm ring-1 ring-inset ring-border-secondary"
style={{
backgroundImage: `url(https://covers.openlibrary.org/b/isbn/${frontmatter.isbn}-S.jpg)`,
backgroundImage: `url(https://covers.openlibrary.org/b/isbn/${note.frontmatter.isbn}-S.jpg)`,
}}
aria-hidden
/>
)
}

// Template
const { success: isTemplate } = templateSchema
.omit({ body: true })
.safeParse(frontmatter.template)

if (isTemplate) {
if (note.type === "template") {
icon = <NoteTemplateIcon16 data-testid="favicon-template" />
}

Expand Down
7 changes: 3 additions & 4 deletions src/components/note-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import React, { useMemo, useState } from "react"
import { useInView } from "react-intersection-observer"
import { useDebounce } from "use-debounce"
import { parseQuery, useSearchNotes } from "../hooks/search"
import { checkIfPinned } from "../utils/pin"
import { formatNumber, pluralize } from "../utils/pluralize"
import { Button } from "./button"
import { Dice } from "./dice"
import { DropdownMenu } from "./dropdown-menu"
import { IconButton } from "./icon-button"
import { XIcon12, GridIcon16, ListIcon16, PinFillIcon12, TagIcon16 } from "./icons"
import { GridIcon16, ListIcon16, PinFillIcon12, TagIcon16, XIcon12 } from "./icons"
import { LinkHighlightProvider } from "./link-highlight-provider"
import { NoteFavicon } from "./note-favicon"
import { NotePreviewCard } from "./note-preview-card"
Expand Down Expand Up @@ -257,8 +256,8 @@ export function NoteList({
}}
className="focus-ring flex items-center rounded-lg p-3 leading-4 hover:bg-bg-secondary coarse:p-4"
>
<NoteFavicon noteId={note.id} content={note.content} className="mr-3" />
{checkIfPinned(note.content) ? (
<NoteFavicon note={note} className="mr-3" />
{note.pinned ? (
<PinFillIcon12 className="mr-2 flex-shrink-0 text-[var(--orange-11)]" />
) : null}
<span className="truncate text-text-secondary">
Expand Down
9 changes: 4 additions & 5 deletions src/components/note-preview-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useDeleteNote, useNoteById, useSaveNote } from "../hooks/note"
import { NoteId } from "../schema"
import { cx } from "../utils/cx"
import { exportAsGist } from "../utils/export-as-gist"
import { checkIfPinned, togglePin } from "../utils/pin"
import { togglePin } from "../utils/pin"
import { pluralize } from "../utils/pluralize"
import { DropdownMenu } from "./dropdown-menu"
import { IconButton } from "./icon-button"
Expand Down Expand Up @@ -53,7 +53,6 @@ const _NotePreviewCard = React.memo(function NoteCard({ id }: NoteCardProps) {
const githubUser = useAtomValue(githubUserAtom)
const githubRepo = useAtomValue(githubRepoAtom)
const isSignedOut = useAtomValue(isSignedOutAtom)
const isPinned = checkIfPinned(note?.content ?? "")
const saveNote = useSaveNote()
const deleteNote = useDeleteNote()
const [isDropdownOpen, setIsDropdownOpen] = React.useState(false)
Expand All @@ -80,19 +79,19 @@ const _NotePreviewCard = React.memo(function NoteCard({ id }: NoteCardProps) {
<div
className={cx(
"absolute right-1.5 top-1.5 rounded bg-bg-card opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100 coarse:opacity-100",
isPinned && "!opacity-100",
note.pinned && "!opacity-100",
)}
>
<IconButton
aria-label={isPinned ? "Unpin" : "Pin"}
aria-label={note.pinned ? "Unpin" : "Pin"}
tooltipSide="left"
disabled={isSignedOut}
onClick={() => {
if (isSignedOut) return
saveNote({ id, content: togglePin(note.content) })
}}
>
{isPinned ? <PinFillIcon16 className=" text-[var(--orange-11)]" /> : <PinIcon16 />}
{note.pinned ? <PinFillIcon16 className=" text-[var(--orange-11)]" /> : <PinIcon16 />}
</IconButton>
</div>
{note ? (
Expand Down
15 changes: 3 additions & 12 deletions src/global-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Searcher } from "fast-fuzzy"
import git, { WORKDIR } from "isomorphic-git"
import { atom } from "jotai"
import { atomWithMachine } from "jotai-xstate"
import { selectAtom, atomWithStorage } from "jotai/utils"
import { atomWithStorage, selectAtom } from "jotai/utils"
import { assign, createMachine, raise } from "xstate"
import { z } from "zod"
import {
Expand All @@ -27,11 +27,9 @@ import {
isRepoSynced,
} from "./utils/git"
import { parseNote } from "./utils/parse-note"
import { checkIfPinned } from "./utils/pin"
import { removeTemplateFrontmatter } from "./utils/remove-template-frontmatter"
import { getSampleMarkdownFiles } from "./utils/sample-markdown-files"
import { startTimer } from "./utils/timer"
import { removeLeadingEmoji } from "./utils/emoji"

// -----------------------------------------------------------------------------
// Constants
Expand Down Expand Up @@ -566,14 +564,7 @@ export const notesAtom = atom((get) => {
for (const filepath in markdownFiles) {
const id = filepath.replace(/\.md$/, "")
const content = markdownFiles[filepath]
const parsedNote = parseNote(content)
const parsedTemplate = templateSchema
.omit({ body: true })
.safeParse(parsedNote.frontmatter?.template)
const displayName = parsedTemplate.success
? `${parsedTemplate.data.name} template`
: removeLeadingEmoji(parsedNote.title) || id
notes.set(id, { id, content, displayName, ...parsedNote, backlinks: [] })
notes.set(id, parseNote(id, content))
}

// Derive backlinks
Expand All @@ -595,7 +586,7 @@ export const notesAtom = atom((get) => {

export const pinnedNotesAtom = atom((get) => {
const notes = get(notesAtom)
return [...notes.values()].filter((note) => checkIfPinned(note.content)).reverse()
return [...notes.values()].filter((note) => note.pinned).reverse()
})

export const sortedNotesAtom = atom((get) => {
Expand Down
21 changes: 17 additions & 4 deletions src/hooks/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ export function testQualifiers(qualifiers: Qualifier[], item: Note) {
break

case "tasks":
value = qualifier.values.some((value) => isInRange(item.openTasks, value))
value = qualifier.values.some((value) =>
isInRange(item.tasks.filter((task) => !task.completed).length, value),
)
break

case "no":
Expand All @@ -174,8 +176,6 @@ export function testQualifiers(qualifiers: Qualifier[], item: Note) {
case "backlinks":
if (!("backlinks" in item)) return true
return item.backlinks.length === 0
case "tasks":
return item.openTasks === 0
case "tag":
case "tags":
return item.tags.length === 0
Expand All @@ -185,6 +185,11 @@ export function testQualifiers(qualifiers: Qualifier[], item: Note) {
case "link":
case "links":
return item.links.length === 0
case "task":
case "tasks":
return item.tasks.filter((task) => !task.completed).length === 0
case "title":
return !item.title
default:
return !(value in frontmatter)
}
Expand All @@ -207,14 +212,22 @@ export function testQualifiers(qualifiers: Qualifier[], item: Note) {
case "link":
case "links":
return item.links.length === 0
case "task":
case "tasks":
return item.openTasks === 0
return item.tasks.filter((task) => !task.completed).length === 0
case "title":
return !item.title
default:
return !(value in frontmatter)
}
})
break

case "is":
case "type":
value = qualifier.values.includes(item.type)
break

default:
if (qualifier.key in frontmatter) {
// Match if the item's frontmatter value is in the qualifier's values
Expand Down
Loading

0 comments on commit 3fb8466

Please sign in to comment.