Skip to content

Commit

Permalink
feat: add bookmark functionality with new components and routing
Browse files Browse the repository at this point in the history
  • Loading branch information
vaayne committed Dec 11, 2024
1 parent 1cebb4a commit 1d9cea8
Show file tree
Hide file tree
Showing 14 changed files with 461 additions and 372 deletions.
Binary file added bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{ "dependencies": { "@mantine/notifications": "^7.12.1" } }
{ "dependencies": { "@mantine/notifications": "^7.12.1", "react-router-dom": "^7.0.2" } }
Binary file modified web/bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bookmark App</title>
<title>Vibrain</title>
<script>
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
Expand Down
1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-scroll-area": "^1.2.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.468.0",
Expand Down
170 changes: 10 additions & 160 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,167 +1,17 @@
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { ArrowLeft, Bookmark, Grid, List, Moon, PlusCircle, Sun } from 'lucide-react'
import { useTheme } from "next-themes"
import { useCallback, useState } from 'react'
import AddBookmarkModal from './components/AddBookmarkModal'
import BookmarkDetail from './components/BookmarkDetail'
import BookmarkGrid from './components/BookmarkGrid'
import BookmarkList from './components/BookmarkList'
import { ThemeProvider } from "./components/theme-provider"

interface Highlight {
id: string
text: string
startOffset: number
endOffset: number
note?: string
}

interface BookmarkType {
id: number
title: string
url: string
tags: string[]
content: string
image?: string
summary: string
highlights?: Highlight[]
}

// This would typically come from your backend
const initialBookmarks: BookmarkType[] = [
{
id: 1,
title: 'React Documentation',
url: 'https://reactjs.org',
tags: ['react', 'docs'],
content: '<p>React is a JavaScript library for building user interfaces. Learn what React is all about on our homepage or in the tutorial.</p>',
image: '/placeholder.svg?height=300&width=400',
summary: 'React is a popular JavaScript library for building user interfaces, particularly single page applications.'
},
{
id: 2,
title: 'Tailwind CSS',
url: 'https://tailwindcss.com',
tags: ['css', 'styling'],
content: '<p>Tailwind CSS is a utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup.</p>',
image: '/placeholder.svg?height=300&width=400',
summary: 'Tailwind CSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs.'
},
{
id: 3,
title: 'shadcn/ui',
url: 'https://ui.shadcn.com',
tags: ['ui', 'components'],
content: '<p>Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.</p>',
image: '/placeholder.svg?height=300&width=400',
summary: 'shadcn/ui provides a set of re-usable components that you can copy and paste into your apps, offering both functionality and customizable styling.'
},
]

function AppContent() {
const [bookmarks, setBookmarks] = useState<BookmarkType[]>(initialBookmarks)
const [view, setView] = useState<'list' | 'grid'>('list')
const [isModalOpen, setIsModalOpen] = useState(false)
const [searchTerm, setSearchTerm] = useState('')
const [selectedBookmark, setSelectedBookmark] = useState<number | null>(null)
const { setTheme, theme } = useTheme()

const filteredBookmarks = bookmarks.filter(bookmark =>
bookmark.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
bookmark.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()))
)

const addBookmark = (newBookmark: { title: string; url: string; tags: string[] }) => {
const completeBookmark: Omit<BookmarkType, 'id'> = {
...newBookmark,
content: '',
summary: `Bookmark for ${newBookmark.title}`,
highlights: []
}
setBookmarks([...bookmarks, { ...completeBookmark, id: bookmarks.length + 1 }])
}

const openBookmark = (id: number) => {
setSelectedBookmark(id)
}

const updateBookmarkHighlights = useCallback((id: number, highlights: Highlight[]) => {
setBookmarks(prevBookmarks => prevBookmarks.map(bookmark =>
bookmark.id === id ? { ...bookmark, highlights } : bookmark
))
}, [])

return (
<div className="min-h-screen bg-gradient-to-b from-background to-secondary/20">
<div className="container mx-auto p-4 max-w-4xl">
<header className="flex justify-between items-center mb-6 sticky top-0 bg-background/80 backdrop-blur-sm z-10 py-4">
<h1 className="text-2xl font-bold flex items-center gap-2">
<Bookmark className="h-6 w-6" />
Bookmark App
</h1>
<div className="flex items-center gap-2">
<Button variant="ghost" size="icon" onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
{theme === 'dark' ? <Sun className="h-[1.2rem] w-[1.2rem]" /> : <Moon className="h-[1.2rem] w-[1.2rem]" />}
</Button>
<Button onClick={() => setIsModalOpen(true)}>
<PlusCircle className="h-4 w-4 mr-2" />
Add Bookmark
</Button>
</div>
</header>

{selectedBookmark === null ? (
<>
<div className="mb-4 flex gap-4">
<Input
type="text"
placeholder="Search bookmarks..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="flex-grow"
/>
<Button variant="outline" onClick={() => setView('list')} aria-label="List view">
<List className="h-4 w-4" />
</Button>
<Button variant="outline" onClick={() => setView('grid')} aria-label="Grid view">
<Grid className="h-4 w-4" />
</Button>
</div>

{view === 'list' ? (
<BookmarkList bookmarks={filteredBookmarks} onBookmarkClick={openBookmark} />
) : (
<BookmarkGrid bookmarks={filteredBookmarks} onBookmarkClick={openBookmark} />
)}
</>
) : (
<>
<Button onClick={() => setSelectedBookmark(null)} className="mb-4">
<ArrowLeft className="h-4 w-4 mr-2" />
Back to List
</Button>
<BookmarkDetail
bookmark={bookmarks.find(b => b.id === selectedBookmark)!}
onUpdateBookmark={updateBookmarkHighlights}
/>
</>
)}

<AddBookmarkModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onAdd={addBookmark}
/>
</div>
</div>
)
}
import { ThemeProvider } from "@/components/theme-provider";
import { Route, BrowserRouter as Router, Routes } from 'react-router-dom';
import BookmarkPage from './pages/BookmarkPage';
import HomePage from './pages/HomePage';

export default function App() {
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<AppContent />
<Router>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/bookmarks/:id" element={<BookmarkPage />} />
</Routes>
</Router>
</ThemeProvider>
)
}
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/AddBookmarkModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useState } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import React, { useState } from 'react'

interface AddBookmarkModalProps {
isOpen: boolean
Expand Down
Loading

0 comments on commit 1d9cea8

Please sign in to comment.