One Calendar is a privacy-first calendar web app built with Next.js. It has modern security features, including e2ee, password-protected sharing, and self-destructing share links ๐Ÿ“… calendar.xyehr.cn
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #141 from EvanTechDev/codex/linear-mention-web-44-/app

perf: lazy-load /app Calendar and heavy calendar views; add Edge-friendly cache headers

authored by

Evan Huang and committed by
GitHub
cd6f467e 6cf7cad0

+37 -8
+5 -1
app/(app)/app/page.tsx
··· 1 1 "use client" 2 2 import AuthWaitingLoading from "@/components/app/auth-waiting-loading" 3 - import Calendar from "@/components/app/calendar" 3 + import dynamic from "next/dynamic" 4 4 import { useRouter } from "next/navigation" 5 5 import { useUser } from "@clerk/nextjs" 6 6 import { useEffect } from "react" 7 + 8 + const Calendar = dynamic(() => import("@/components/app/calendar"), { 9 + loading: () => <AuthWaitingLoading />, 10 + }) 7 11 8 12 export default function Home() { 9 13 const { isLoaded, isSignedIn } = useUser()
+1 -1
components/app/auth-waiting-loading.tsx
··· 22 22 return ( 23 23 <div className="flex min-h-screen items-center justify-center bg-white px-6 dark:bg-black"> 24 24 <div className="flex flex-col items-center gap-5 text-center"> 25 - <Image src="/icon.svg" alt="One Calendar" width={72} height={72} priority /> 25 + <Image src="/icon.svg" alt="One Calendar" width={84} height={84} priority /> 26 26 <p className="text-sm text-slate-700 dark:text-slate-300"> 27 27 {t.loadingCalendar}{".".repeat(dotCount)} 28 28 </p>
+8 -6
components/app/calendar.tsx
··· 3 3 import { checkPendingNotifications, clearAllNotificationTimers, type NOTIFICATION_SOUNDS } from "@/lib/notifications" 4 4 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectGroup } from "@/components/ui/select" 5 5 import { ChevronLeft, ChevronRight, Search, PanelLeft, BarChart2, Settings as SettingsIcon } from 'lucide-react' 6 + import dynamic from "next/dynamic" 6 7 import { readEncryptedLocalStorage, useLocalStorage, writeEncryptedLocalStorage } from "@/hooks/useLocalStorage" 7 8 import UserProfileButton, { type UserProfileSection } from "@/components/app/profile/user-profile-button" 8 9 import { useState, useEffect, useRef, useMemo } from "react" 9 10 import { useCalendar } from "@/components/providers/calendar-context" 10 - import AnalyticsView from "@/components/app/analytics/analytics-view" 11 11 import RightSidebar from "@/components/app/sidebar/right-sidebar" 12 12 import { addDays, addYears, subDays, subYears } from "date-fns" 13 13 import EventPreview from "@/components/app/event/event-preview" 14 14 import EventDialog from "@/components/app/event/event-dialog" 15 15 import DailyToast from "@/components/app/profile/daily-toast" 16 - import MonthView from "@/components/app/views/month-view" 17 - import Settings from "@/components/app/profile/settings" 18 16 import { ScrollArea } from "@/components/ui/scroll-area" 19 - import WeekView from "@/components/app/views/week-view" 20 - import YearView from "@/components/app/views/year-view" 21 17 import Sidebar from "@/components/app/sidebar/sidebar" 22 18 import { translations, useLanguage } from "@/lib/i18n" 23 - import DayView from "@/components/app/views/day-view" 24 19 import { Button } from "@/components/ui/button" 25 20 import { Input } from "@/components/ui/input" 26 21 import { cn } from "@/lib/utils" ··· 35 30 AlertDialogHeader, 36 31 AlertDialogTitle, 37 32 } from "@/components/ui/alert-dialog" 33 + 34 + const DayView = dynamic(() => import("@/components/app/views/day-view")) 35 + const WeekView = dynamic(() => import("@/components/app/views/week-view")) 36 + const MonthView = dynamic(() => import("@/components/app/views/month-view")) 37 + const YearView = dynamic(() => import("@/components/app/views/year-view")) 38 + const AnalyticsView = dynamic(() => import("@/components/app/analytics/analytics-view")) 39 + const Settings = dynamic(() => import("@/components/app/profile/settings")) 38 40 39 41 type ViewType = "day" | "week" | "four-day" | "month" | "year" | "analytics" | "settings" 40 42
+23
next.config.mjs
··· 31 31 NEXT_PUBLIC_GIT_COMMIT: getGitCommit(), 32 32 NEXT_PUBLIC_BUILD_TIME: new Date().toISOString(), 33 33 }, 34 + 35 + async headers() { 36 + return [ 37 + { 38 + source: "/_next/static/:path*", 39 + headers: [ 40 + { 41 + key: "Cache-Control", 42 + value: "public, max-age=31536000, immutable", 43 + }, 44 + ], 45 + }, 46 + { 47 + source: "/app", 48 + headers: [ 49 + { 50 + key: "Cache-Control", 51 + value: "public, s-maxage=120, stale-while-revalidate=600", 52 + }, 53 + ], 54 + }, 55 + ] 56 + }, 34 57 /* experimental: { 35 58 webpackBuildWorker: true, 36 59 parallelServerBuildTraces: true,