Skip to content
Merged
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
71 changes: 71 additions & 0 deletions src/components/bookmarks/BookmarkList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import { FilterBar } from './FilterBar'
import { SelectionActionBar } from './SelectionActionBar'
import { SettingsView } from '../ui/SettingsView'
import { HelpModal } from '../ui/HelpModal'
import { QuickTagModal } from '../ui/QuickTagModal'
import { ToastContainer } from '../ui/Toast'
import { PackageOpen } from '../ui/Icons'
import {
getAllBookmarks,
deleteBookmark,
bulkDeleteBookmarks,
toggleReadLater,
} from '../../services/bookmarks'

export function BookmarkList() {
Expand Down Expand Up @@ -52,6 +54,8 @@ export function BookmarkList() {
const [searchQuery, setSearchQuery] = useState('')
const [isSidebarOpen, setIsSidebarOpen] = useState(false)
const [isHelpOpen, setIsHelpOpen] = useState(false)
const [isTagModalOpen, setIsTagModalOpen] = useState(false)
const [tagModalBookmark, setTagModalBookmark] = useState(null)
const [selectedIndex, setSelectedIndex] = useState(-1)
const [selectionMode, setSelectionMode] = useState(false)
const [selectedIds, setSelectedIds] = useState(new Set())
Expand Down Expand Up @@ -335,6 +339,63 @@ export function BookmarkList() {
}
}, [selectedIds, addToast, exitSelectionMode])

// Get the currently selected bookmark
const getSelectedBookmark = useCallback(() => {
if (selectedIndex >= 0 && selectedIndex < filteredBookmarks.length) {
return filteredBookmarks[selectedIndex]
}
return null
}, [selectedIndex, filteredBookmarks])

// Shift+T: Open tag edit modal for selected bookmark
const openTagModal = useCallback(() => {
if (filterView === 'inbox') return
if (isAddingNew || editingBookmarkId) return
const bookmark = getSelectedBookmark()
if (bookmark) {
setTagModalBookmark(bookmark)
setIsTagModalOpen(true)
}
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark])

const closeTagModal = useCallback(() => {
setIsTagModalOpen(false)
setTagModalBookmark(null)
}, [])

// Shift+L: Toggle read later for selected bookmark
const toggleReadLaterSelected = useCallback(() => {
if (filterView === 'inbox') return
if (isAddingNew || editingBookmarkId) return
const bookmark = getSelectedBookmark()
if (bookmark) {
try {
const newValue = toggleReadLater(bookmark._id)
addToast({
message: newValue ? 'Added to Read Later' : 'Removed from Read Later',
duration: 2000,
})
} catch (error) {
console.error('Failed to toggle read later:', error)
}
}
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark, addToast])

// c: Copy URL to clipboard
const copySelectedUrl = useCallback(() => {
if (filterView === 'inbox') return
if (isAddingNew || editingBookmarkId) return
const bookmark = getSelectedBookmark()
if (bookmark) {
navigator.clipboard.writeText(bookmark.url).then(() => {
addToast({ message: 'URL copied to clipboard', duration: 2000 })
}).catch((error) => {
console.error('Failed to copy URL:', error)
addToast({ message: 'Failed to copy URL', duration: 2000 })
})
}
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark, addToast])

useEffect(() => {
setSelectedIndex(-1)
}, [filteredBookmarks.length, filterView, selectedTag, debouncedSearchQuery])
Expand Down Expand Up @@ -364,8 +425,12 @@ export function BookmarkList() {
'shift+j': selectNextWithShift,
'shift+k': selectPrevWithShift,
'enter': openSelected,
'o': openSelected,
'e': editSelected,
'd': selectionMode && selectedIds.size > 0 ? handleBulkDelete : deleteSelected,
't': openTagModal,
'l': toggleReadLaterSelected,
'c': copySelectedUrl,
'mod+k': focusSearch,
'q': exitInbox,
'mod+z': handleUndo,
Expand Down Expand Up @@ -547,6 +612,12 @@ export function BookmarkList() {

<HelpModal isOpen={isHelpOpen} onClose={() => setIsHelpOpen(false)} />

<QuickTagModal
isOpen={isTagModalOpen}
onClose={closeTagModal}
bookmark={tagModalBookmark}
/>

<SelectionActionBar
selectedCount={selectedIds.size}
onDelete={handleBulkDelete}
Expand Down
13 changes: 13 additions & 0 deletions src/components/ui/HelpModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,35 @@ const HOTKEY_GROUPS = [
{ keys: ['j'], description: 'Select next bookmark' },
{ keys: ['k'], description: 'Select previous bookmark' },
{ keys: ['Enter'], description: 'Open selected bookmark' },
{ keys: ['o'], description: 'Open selected bookmark' },
{ keys: ['e'], description: 'Edit selected bookmark' },
{ keys: ['d'], description: 'Delete selected bookmark' },
{ keys: ['Ctrl', 'k'], description: 'Focus search bar', modifier: true },
],
},
{
title: 'Quick Actions',
hotkeys: [
{ keys: ['t'], description: 'Edit tags' },
{ keys: ['l'], description: 'Toggle read later' },
{ keys: ['c'], description: 'Copy URL' },
],
},
{
title: 'Go To',
hotkeys: [
{ keys: ['g', 'a'], description: 'All bookmarks' },
{ keys: ['g', 'l'], description: 'Read later' },
{ keys: ['g', 'i'], description: 'Inbox' },
{ keys: ['g', 's'], description: 'Settings' },
{ keys: ['g', 'n'], description: 'New bookmark' },
],
},
{
title: 'General',
hotkeys: [
{ keys: ['Ctrl', 'z'], description: 'Undo', modifier: true },
{ keys: ['Ctrl', 'Shift', 'z'], description: 'Redo', modifier: true },
{ keys: ['Ctrl', 'Enter'], description: 'Submit form', modifier: true },
{ keys: ['?'], description: 'Show this help' },
{ keys: ['Esc'], description: 'Close modal / Cancel' },
Expand Down
Loading