Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions src/components/calendar-header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { addDays, addWeeks, parseISO, startOfToday } from "date-fns"
import { useNavigate, useSearch } from "@tanstack/react-router"
import React from "react"
import { useBuildCalendarNoteId } from "../hooks/config"
import { getCalendarNoteBasename, isCalendarNoteId } from "../utils/config"

Check failure on line 5 in src/components/calendar-header.tsx

View workflow job for this annotation

GitHub Actions / lint

'isCalendarNoteId' is defined but never used
import {
formatDate,
formatDateDistance,
Expand All @@ -21,41 +23,43 @@
export function CalendarHeader({ activeNoteId }: CalendarHeaderProps) {
const navigate = useNavigate()
const searchParams = useSearch({ strict: false })
const isWeekly = isValidWeekString(activeNoteId)
const buildId = useBuildCalendarNoteId()

const primaryText = isWeekly ? formatWeek(activeNoteId) : formatDate(activeNoteId)
const secondaryText = isWeekly
? formatWeekDistance(activeNoteId)
: formatDateDistance(activeNoteId)
// Get the basename (date/week part) for formatting
const basename = getCalendarNoteBasename(activeNoteId)
const isWeekly = isValidWeekString(basename)

const primaryText = isWeekly ? formatWeek(basename) : formatDate(basename)
const secondaryText = isWeekly ? formatWeekDistance(basename) : formatDateDistance(basename)

const today = startOfToday()
const todayString = toDateString(today)
const thisWeekString = toWeekString(today)
const todayNoteId = buildId(toDateString(today))
const thisWeekNoteId = buildId(toWeekString(today))

const navigateByInterval = React.useCallback(
(direction: "previous" | "next") => {
const date = parseISO(activeNoteId)
const date = parseISO(basename)
const increment = direction === "next" ? 1 : -1

const target = isWeekly
const targetBasename = isWeekly
? toWeekString(addWeeks(date, increment))
: toDateString(addDays(date, increment))

navigate({
to: "/notes/$",
params: { _splat: target },
params: { _splat: buildId(targetBasename) },
search: {
mode: searchParams.mode ?? "read",
query: undefined,
view: searchParams.view === "list" ? "list" : "grid",
},
})
},
[isWeekly, activeNoteId, navigate, searchParams.mode, searchParams.view],
[isWeekly, basename, navigate, searchParams.mode, searchParams.view, buildId],
)

const navigateToCurrentPeriod = React.useCallback(() => {
const target = isWeekly ? thisWeekString : todayString
const target = isWeekly ? thisWeekNoteId : todayNoteId
navigate({
to: "/notes/$",
params: { _splat: target },
Expand All @@ -65,7 +69,7 @@
view: searchParams.view === "list" ? "list" : "grid",
},
})
}, [isWeekly, thisWeekString, todayString, navigate, searchParams.mode, searchParams.view])
}, [isWeekly, thisWeekNoteId, todayNoteId, navigate, searchParams.mode, searchParams.view])

return (
<div className="flex items-start justify-between gap-4">
Expand All @@ -78,7 +82,7 @@
<div className="flex gap-2">
<Button
onClick={navigateToCurrentPeriod}
disabled={isWeekly ? activeNoteId === thisWeekString : activeNoteId === todayString}
disabled={isWeekly ? activeNoteId === thisWeekNoteId : activeNoteId === todayNoteId}
>
{isWeekly ? "This week" : "Today"}
</Button>
Expand Down
70 changes: 42 additions & 28 deletions src/components/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import { useAtom } from "jotai"
import React from "react"
import { Link, useSearch } from "@tanstack/react-router"
import { calendarLayoutAtom } from "../global-state"
import { useBuildCalendarNoteId } from "../hooks/config"
import { useBacklinksForId, useNoteById } from "../hooks/note"
import { Note } from "../schema"
import { getCalendarNoteBasename } from "../utils/config"
import { cx } from "../utils/cx"
import {
DAY_NAMES,
Expand All @@ -42,7 +44,10 @@ export function Calendar({
activeNoteId: string
className?: string
}) {
const date = parseISO(activeNoteId)
const buildId = useBuildCalendarNoteId()
// Extract the date/week part from the noteId (handles paths like "journal/2025-01-26")
const activeBasename = getCalendarNoteBasename(activeNoteId)
const date = parseISO(activeBasename)
const [layout, setLayout] = useAtom(calendarLayoutAtom)

// Local state for the displayed date anchor (independent of activeNoteId)
Expand Down Expand Up @@ -164,14 +169,14 @@ export function Calendar({
<div className="flex gap-1.5 items-center">
<CalendarWeek
startOfWeek={displayedWeekStart}
isActive={toWeekString(displayedWeekStart) === activeNoteId}
isActive={buildId(toWeekString(displayedWeekStart)) === activeNoteId}
/>
<div role="separator" className="h-8 w-px shrink-0 bg-border-secondary" />
{daysOfWeek.map((day) => (
<CalendarDate
key={day.toISOString()}
date={day}
isActive={toDateString(day) === activeNoteId}
isActive={buildId(toDateString(day)) === activeNoteId}
/>
))}
</div>
Expand All @@ -196,19 +201,21 @@ function CalendarWeek({
startOfWeek: Date
isActive?: boolean
}) {
const buildId = useBuildCalendarNoteId()
const weekString = toWeekString(startOfWeek)
const weekNoteId = buildId(weekString)
const weekNumber = getISOWeek(startOfWeek)
const label = formatWeek(weekString)
const existingNote = useNoteById(weekString)
const backlinks = useBacklinksForId(weekString)
const existingNote = useNoteById(weekNoteId)
const backlinks = useBacklinksForId(weekNoteId)
const hasNotes = Boolean(existingNote) || backlinks.length > 0
const anchorRef = React.useContext(CalendarContainerContext)

// Create note object for hover card (fallback if note doesn't exist)
const note: Note = React.useMemo(() => {
if (existingNote) return existingNote
return {
id: weekString,
id: weekNoteId,
content: "",
type: "weekly",
displayName: formatWeek(weekString),
Expand All @@ -224,12 +231,12 @@ function CalendarWeek({
tasks: [],
backlinks,
}
}, [existingNote, weekString, backlinks])
}, [existingNote, weekNoteId, weekString, backlinks])

return (
<CalendarItem
key={weekString}
id={weekString}
key={weekNoteId}
id={weekNoteId}
aria-label={label}
name="Week"
shortName="W"
Expand All @@ -244,9 +251,11 @@ function CalendarWeek({
}

function CalendarDate({ date, isActive = false }: { date: Date; isActive?: boolean }) {
const buildId = useBuildCalendarNoteId()
const dateString = toDateString(date)
const existingNote = useNoteById(dateString)
const backlinks = useBacklinksForId(dateString)
const dateNoteId = buildId(dateString)
const existingNote = useNoteById(dateNoteId)
const backlinks = useBacklinksForId(dateNoteId)
const hasNotes = Boolean(existingNote) || backlinks.length > 0
const dayName = DAY_NAMES[date.getDay()]
const monthName = MONTH_NAMES[date.getMonth()]
Expand All @@ -259,7 +268,7 @@ function CalendarDate({ date, isActive = false }: { date: Date; isActive?: boole
const note: Note = React.useMemo(() => {
if (existingNote) return existingNote
return {
id: dateString,
id: dateNoteId,
content: "",
type: "daily",
displayName: formatDate(dateString),
Expand All @@ -275,12 +284,12 @@ function CalendarDate({ date, isActive = false }: { date: Date; isActive?: boole
tasks: [],
backlinks,
}
}, [existingNote, dateString, backlinks])
}, [existingNote, dateNoteId, dateString, backlinks])

return (
<CalendarItem
key={date.toISOString()}
id={dateString}
id={dateNoteId}
aria-label={label}
name={dayName.slice(0, 3)}
shortName={dayName.slice(0, 2)}
Expand Down Expand Up @@ -434,14 +443,16 @@ function MonthWeekRow({
activeNoteId: string
isLastRow: boolean
}) {
const buildId = useBuildCalendarNoteId()
// Get the Monday of this week (weekStart should already be Monday from eachWeekOfInterval)
const mondayOfWeek = startOfISOWeek(weekStart)
const weekString = toWeekString(mondayOfWeek)
const weekNoteId = buildId(weekString)
const weekNumber = getISOWeek(mondayOfWeek)
const label = formatWeek(weekString)

const existingNote = useNoteById(weekString)
const backlinks = useBacklinksForId(weekString)
const existingNote = useNoteById(weekNoteId)
const backlinks = useBacklinksForId(weekNoteId)
const hasWeekNotes = Boolean(existingNote) || backlinks.length > 0

const daysOfWeek = React.useMemo(() => {
Expand All @@ -450,14 +461,14 @@ function MonthWeekRow({
}, [mondayOfWeek])

const searchParams = useSearch({ strict: false })
const isWeekActive = weekString === activeNoteId
const isWeekActive = weekNoteId === activeNoteId
const anchorRef = React.useContext(CalendarContainerContext)

// Create note object for hover card (fallback if note doesn't exist)
const note: Note = React.useMemo(() => {
if (existingNote) return existingNote
return {
id: weekString,
id: weekNoteId,
content: "",
type: "weekly",
displayName: formatWeek(weekString),
Expand All @@ -473,12 +484,12 @@ function MonthWeekRow({
tasks: [],
backlinks,
}
}, [existingNote, weekString, backlinks])
}, [existingNote, weekNoteId, weekString, backlinks])

const weekLink = (
<Link
to="/notes/$"
params={{ _splat: weekString }}
params={{ _splat: weekNoteId }}
search={{
mode: searchParams.mode ?? "read",
query: undefined,
Expand Down Expand Up @@ -531,7 +542,7 @@ function MonthWeekRow({
key={day.toISOString()}
date={day}
isOutsideMonth={day.getMonth() !== displayedMonth}
isActive={toDateString(day) === activeNoteId}
activeNoteId={activeNoteId}
/>
))}
</div>
Expand All @@ -541,15 +552,18 @@ function MonthWeekRow({
function MonthDateCell({
date,
isOutsideMonth = false,
isActive = false,
activeNoteId,
}: {
date: Date
isOutsideMonth?: boolean
isActive?: boolean
activeNoteId: string
}) {
const buildId = useBuildCalendarNoteId()
const dateString = toDateString(date)
const existingNote = useNoteById(dateString)
const backlinks = useBacklinksForId(dateString)
const dateNoteId = buildId(dateString)
const isActive = dateNoteId === activeNoteId
const existingNote = useNoteById(dateNoteId)
const backlinks = useBacklinksForId(dateNoteId)
const hasNotes = Boolean(existingNote) || backlinks.length > 0
const dayName = DAY_NAMES[date.getDay()]
const monthName = MONTH_NAMES[date.getMonth()]
Expand All @@ -564,7 +578,7 @@ function MonthDateCell({
const note: Note = React.useMemo(() => {
if (existingNote) return existingNote
return {
id: dateString,
id: dateNoteId,
content: "",
type: "daily",
displayName: formatDate(dateString),
Expand All @@ -580,12 +594,12 @@ function MonthDateCell({
tasks: [],
backlinks,
}
}, [existingNote, dateString, backlinks])
}, [existingNote, dateNoteId, dateString, backlinks])

const link = (
<Link
to="/notes/$"
params={{ _splat: dateString }}
params={{ _splat: dateNoteId }}
search={{
mode: searchParams.mode ?? "read",
query: undefined,
Expand Down
24 changes: 20 additions & 4 deletions src/components/command-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@
import { Command } from "cmdk"
import copy from "copy-to-clipboard"
import { atom, useAtom, useAtomValue } from "jotai"
import { selectAtom, useAtomCallback } from "jotai/utils"

Check failure on line 6 in src/components/command-menu.tsx

View workflow job for this annotation

GitHub Actions / lint

'selectAtom' is defined but never used
import { useCallback, useMemo, useRef, useState } from "react"
import { useHotkeys } from "react-hotkeys-hook"
import { useDebounce } from "use-debounce"
import { githubRepoAtom, notesAtom, pinnedNotesAtom, tagSearcherAtom } from "../global-state"
import {
calendarNotesDirAtom,
githubRepoAtom,
notesAtom,
pinnedNotesAtom,
tagSearcherAtom,
} from "../global-state"
import { useBuildCalendarNoteId } from "../hooks/config"
import { useNoteById, useSaveNote } from "../hooks/note"
import { useSearchNotes } from "../hooks/search-notes"
import { Note } from "../schema"
Expand All @@ -29,9 +36,17 @@
} from "./icons"
import { NoteFavicon } from "./note-favicon"

import { buildCalendarNoteId } from "../utils/config"

export const isCommandMenuOpenAtom = atom(false)

const hasDailyNoteAtom = selectAtom(notesAtom, (notes) => notes.has(toDateString(new Date())))
// Check if daily note exists, considering calendar notes directory
const hasDailyNoteAtom = atom((get) => {
const notes = get(notesAtom)
const calendarNotesDir = get(calendarNotesDirAtom)
const todayId = buildCalendarNoteId(toDateString(new Date()), calendarNotesDir)
return notes.has(todayId)
})

export function CommandMenu() {
const navigate = useNavigate()
Expand All @@ -41,6 +56,7 @@
const saveNote = useSaveNote()
const pinnedNotes = useAtomValue(pinnedNotesAtom)
const getHasDailyNote = useAtomCallback(useCallback((get) => get(hasDailyNoteAtom), []))
const buildCalendarNoteId = useBuildCalendarNoteId()
const [isOpen, setIsOpen] = useAtom(isCommandMenuOpenAtom)

// Get the current note if we're on a note page.
Expand Down Expand Up @@ -115,7 +131,7 @@
navigate({
to: "/notes/$",
params: {
_splat: toDateString(new Date()),
_splat: buildCalendarNoteId(toDateString(new Date())),
},
search: {
mode: getHasDailyNote() ? "read" : "write",
Expand Down Expand Up @@ -149,7 +165,7 @@
},
},
]
}, [navigate, getHasDailyNote])

Check warning on line 168 in src/components/command-menu.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook useMemo has a missing dependency: 'buildCalendarNoteId'. Either include it or remove the dependency array

const filteredNavItems = useMemo(() => {
return navItems.filter((item) => {
Expand Down Expand Up @@ -326,7 +342,7 @@
navigate({
to: "/notes/$",
params: {
_splat: dateString,
_splat: buildCalendarNoteId(dateString),
},
search: {
mode: "read",
Expand Down
Loading
Loading