Skip to content

Commit 19d5558

Browse files
committed
feat: remove inbox feature, keep paste-to-bookmark
Replace inbox triage workflow with direct bookmark creation on paste. Pasting a URL now creates a regular bookmark (title=domain) and asynchronously fetches suggestions if the service is enabled. - Delete InboxView, InboxItem components - Remove inbox flag from bookmark schema and validation - Remove getInboxBookmarks, createInboxItem, moveFromInbox - Rename usePasteToInbox → usePasteToBookmark with new behavior - Remove inbox route, sidebar button, and keyboard shortcuts - Update all tests (49 files, 1244 tests passing)
1 parent 52208f9 commit 19d5558

22 files changed

Lines changed: 207 additions & 978 deletions

src/app.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useCallback } from 'react'
22
import { useYjs } from './hooks/useYjs'
33
import { useNostrSync } from './hooks/useNostrSync'
4-
import { usePasteToInbox } from './hooks/usePasteToInbox'
4+
import { usePasteToBookmark } from './hooks/usePasteToBookmark'
55
import { useOnlineStatus } from './hooks/useOnlineStatus'
66
import { useRelayErrorToasts } from './hooks/useRelayErrorToasts'
77
import { BookmarkList } from './components/bookmarks/BookmarkList'
@@ -15,14 +15,14 @@ function AppContent() {
1515

1616
const handlePasteSuccess = useCallback((url) => {
1717
const domain = new URL(url).hostname.replace('www.', '')
18-
addToast({ message: `Added "${domain}" to inbox`, type: 'success' })
18+
addToast({ message: `Added "${domain}"`, type: 'success' })
1919
}, [addToast])
2020

2121
const handlePasteDuplicate = useCallback(() => {
2222
addToast({ message: 'Already bookmarked', type: 'warning' })
2323
}, [addToast])
2424

25-
usePasteToInbox(handlePasteSuccess, handlePasteDuplicate)
25+
usePasteToBookmark(handlePasteSuccess, handlePasteDuplicate)
2626

2727
const isOnline = useOnlineStatus()
2828

src/components/bookmarks/BookmarkList.jsx

Lines changed: 66 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { useBookmarkKeyboardNav } from '../../hooks/useBookmarkKeyboardNav'
99
import { BookmarkItem } from './BookmarkItem'
1010
import { BookmarkInlineCard } from './BookmarkInlineCard'
1111
import { BookmarkContextMenu } from './BookmarkContextMenu'
12-
import { InboxView } from './InboxView'
1312
import { TagSidebar } from './TagSidebar'
1413
import { FilterBar } from './FilterBar'
1514
import { SelectionActionBar } from './SelectionActionBar'
@@ -74,7 +73,6 @@ export function BookmarkList() {
7473
const [bulkTagIds, setBulkTagIds] = useState(null)
7574
const [contextMenu, setContextMenu] = useState(null)
7675
const searchInputRef = useRef(null)
77-
const inboxViewRef = useRef(null)
7876

7977
// Extracted hooks
8078
const {
@@ -88,7 +86,6 @@ export function BookmarkList() {
8886
filteredBookmarks,
8987
goToAllBookmarks,
9088
goToReadLater,
91-
goToInbox,
9289
handleFilterChange,
9390
handleTagSelect,
9491
handleTagClick,
@@ -113,7 +110,6 @@ export function BookmarkList() {
113110
getSelectedBookmark,
114111
} = useBookmarkKeyboardNav(filteredBookmarks, {
115112
filterView,
116-
inboxViewRef,
117113
selectedTag,
118114
debouncedSearchQuery,
119115
})
@@ -153,30 +149,20 @@ export function BookmarkList() {
153149

154150
const openSelected = useCallback(() => {
155151
if (isAddingNew || editingBookmarkId) return
156-
if (filterView === 'inbox') {
157-
inboxViewRef.current?.handleEnter()
158-
} else if (selectedIndex >= 0 && selectedIndex < filteredBookmarks.length) {
152+
if (selectedIndex >= 0 && selectedIndex < filteredBookmarks.length) {
159153
const bookmark = filteredBookmarks[selectedIndex]
160154
window.open(bookmark.url, '_blank', 'noopener,noreferrer')
161155
}
162-
}, [selectedIndex, filteredBookmarks, filterView, isAddingNew, editingBookmarkId])
163-
164-
const exitInbox = useCallback(() => {
165-
if (filterView === 'inbox') {
166-
goToAllBookmarks()
167-
}
168-
}, [filterView, goToAllBookmarks])
156+
}, [selectedIndex, filteredBookmarks, isAddingNew, editingBookmarkId])
169157

170158
const editSelected = useCallback(() => {
171-
if (filterView === 'inbox') return
172159
if (selectedIndex >= 0 && selectedIndex < filteredBookmarks.length) {
173160
const bookmark = filteredBookmarks[selectedIndex]
174161
setEditingBookmarkId(bookmark._id)
175162
}
176-
}, [selectedIndex, filteredBookmarks, filterView])
163+
}, [selectedIndex, filteredBookmarks])
177164

178165
const deleteSelected = useCallback(() => {
179-
if (filterView === 'inbox') return
180166
if (selectedIndex >= 0 && selectedIndex < filteredBookmarks.length) {
181167
const bookmark = filteredBookmarks[selectedIndex]
182168
try {
@@ -192,7 +178,7 @@ export function BookmarkList() {
192178
console.error('Failed to delete bookmark:', error)
193179
}
194180
}
195-
}, [selectedIndex, filteredBookmarks, filterView, addToast])
181+
}, [selectedIndex, filteredBookmarks, addToast])
196182

197183
const handleUndo = useCallback(() => {
198184
if (undo()) {
@@ -254,14 +240,13 @@ export function BookmarkList() {
254240
}, [selectedIds])
255241

256242
const openTagModal = useCallback(() => {
257-
if (filterView === 'inbox') return
258243
if (isAddingNew || editingBookmarkId) return
259244
const bookmark = getSelectedBookmark()
260245
if (bookmark) {
261246
setTagModalBookmark(bookmark)
262247
setIsTagModalOpen(true)
263248
}
264-
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark])
249+
}, [isAddingNew, editingBookmarkId, getSelectedBookmark])
265250

266251
const closeTagModal = useCallback(() => {
267252
setIsTagModalOpen(false)
@@ -274,7 +259,6 @@ export function BookmarkList() {
274259
}, [suppressHoverBriefly, bulkTagIds, exitSelectionMode])
275260

276261
const toggleReadLaterSelected = useCallback(() => {
277-
if (filterView === 'inbox') return
278262
if (isAddingNew || editingBookmarkId) return
279263
const bookmark = getSelectedBookmark()
280264
if (bookmark) {
@@ -289,10 +273,9 @@ export function BookmarkList() {
289273
console.error('Failed to toggle read later:', error)
290274
}
291275
}
292-
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark, addToast])
276+
}, [isAddingNew, editingBookmarkId, getSelectedBookmark, addToast])
293277

294278
const copySelectedUrl = useCallback(() => {
295-
if (filterView === 'inbox') return
296279
if (isAddingNew || editingBookmarkId) return
297280
const bookmark = getSelectedBookmark()
298281
if (bookmark) {
@@ -303,17 +286,16 @@ export function BookmarkList() {
303286
addToast({ message: 'Failed to copy URL', type: 'error', duration: 2000 })
304287
})
305288
}
306-
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark, addToast])
289+
}, [isAddingNew, editingBookmarkId, getSelectedBookmark, addToast])
307290

308291
const openContextMenuForSelected = useCallback(() => {
309-
if (filterView === 'inbox') return
310292
if (isAddingNew || editingBookmarkId) return
311293
const bookmark = getSelectedBookmark()
312294
if (bookmark && selectedItemRef.current) {
313295
const rect = selectedItemRef.current.getBoundingClientRect()
314296
setContextMenu({ bookmark, position: { x: rect.right - 50, y: rect.bottom } })
315297
}
316-
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark, selectedItemRef])
298+
}, [isAddingNew, editingBookmarkId, getSelectedBookmark, selectedItemRef])
317299

318300
// Close inline card and exit selection mode when view/filter changes
319301
useEffect(() => {
@@ -325,7 +307,6 @@ export function BookmarkList() {
325307
useHotkeys({
326308
'g n': openNewBookmarkForm,
327309
'g a': goToAllBookmarks,
328-
'g i': goToInbox,
329310
'g l': goToReadLater,
330311
'g s': () => navigate('#/settings'),
331312
'g g': goToTop,
@@ -344,7 +325,6 @@ export function BookmarkList() {
344325
'c': copySelectedUrl,
345326
'.': openContextMenuForSelected,
346327
'mod+k': focusSearch,
347-
'q': exitInbox,
348328
'mod+z': handleUndo,
349329
'mod+shift+z': handleRedo,
350330
'escape': exitSelectionMode,
@@ -439,75 +419,66 @@ export function BookmarkList() {
439419

440420
<div className="flex-1 overflow-y-auto bg-background">
441421
<div className="px-4 pb-12 pt-1 space-y-1">
442-
{filterView === 'inbox' ? (
443-
<InboxView
444-
ref={inboxViewRef}
445-
bookmarks={filteredBookmarks}
422+
{/* New bookmark inline card at top */}
423+
{isAddingNew && (
424+
<div className="mb-3 pt-2">
425+
<BookmarkInlineCard
426+
isNew={true}
427+
onDone={handleCloseInlineCard}
428+
onDiscard={handleCloseInlineCard}
446429
/>
447-
) : (
448-
<>
449-
{/* New bookmark inline card at top */}
450-
{isAddingNew && (
451-
<div className="mb-3 pt-2">
452-
<BookmarkInlineCard
453-
isNew={true}
454-
onDone={handleCloseInlineCard}
455-
onDiscard={handleCloseInlineCard}
456-
/>
457-
</div>
458-
)}
459-
460-
{filteredBookmarks.length === 0 && !isAddingNew ? (
461-
isFirstRun && filterView === 'all' ? (
462-
<WelcomeState
463-
onAddBookmark={openNewBookmarkForm}
464-
onImport={() => navigate('#/settings')}
465-
onPairDevice={() => navigate('#/settings')}
466-
/>
467-
) : (
468-
<div className="flex flex-col items-center justify-center py-20 opacity-50">
469-
<PackageOpen className="w-12 h-12 mb-4 stroke-1" />
470-
<p className="text-sm font-medium">No bookmarks found</p>
471-
{filterView !== 'all' && (
472-
<button
473-
onClick={() => handleFilterChange('all')}
474-
className="mt-2 text-sm text-primary hover:underline"
475-
>
476-
Clear filters
477-
</button>
478-
)}
479-
</div>
480-
)
481-
) : (
482-
filteredBookmarks.map((bookmark, index) => (
483-
editingBookmarkId === bookmark._id ? (
484-
<BookmarkInlineCard
485-
key={bookmark._id}
486-
ref={index === selectedIndex ? selectedItemRef : null}
487-
bookmark={bookmark}
488-
onDone={handleCloseInlineCard}
489-
onDiscard={handleCloseInlineCard}
490-
/>
491-
) : (
492-
<BookmarkItem
493-
key={bookmark._id}
494-
ref={index === selectedIndex ? selectedItemRef : null}
495-
bookmark={bookmark}
496-
isSelected={index === selectedIndex}
497-
isChecked={selectedIds.has(bookmark._id)}
498-
selectionMode={selectionMode}
499-
keyboardNavActive={keyboardNavActive}
500-
onEdit={handleEdit}
501-
onTagClick={handleTagClick}
502-
onToggleSelect={toggleSelectBookmark}
503-
onMouseEnter={() => handleBookmarkHover(index)}
504-
onContextMenu={handleContextMenu}
505-
/>
506-
)
507-
))
430+
</div>
431+
)}
432+
433+
{filteredBookmarks.length === 0 && !isAddingNew ? (
434+
isFirstRun && filterView === 'all' ? (
435+
<WelcomeState
436+
onAddBookmark={openNewBookmarkForm}
437+
onImport={() => navigate('#/settings')}
438+
onPairDevice={() => navigate('#/settings')}
439+
/>
440+
) : (
441+
<div className="flex flex-col items-center justify-center py-20 opacity-50">
442+
<PackageOpen className="w-12 h-12 mb-4 stroke-1" />
443+
<p className="text-sm font-medium">No bookmarks found</p>
444+
{filterView !== 'all' && (
445+
<button
446+
onClick={() => handleFilterChange('all')}
447+
className="mt-2 text-sm text-primary hover:underline"
448+
>
449+
Clear filters
450+
</button>
508451
)}
509-
</>
510-
)}
452+
</div>
453+
)
454+
) : (
455+
filteredBookmarks.map((bookmark, index) => (
456+
editingBookmarkId === bookmark._id ? (
457+
<BookmarkInlineCard
458+
key={bookmark._id}
459+
ref={index === selectedIndex ? selectedItemRef : null}
460+
bookmark={bookmark}
461+
onDone={handleCloseInlineCard}
462+
onDiscard={handleCloseInlineCard}
463+
/>
464+
) : (
465+
<BookmarkItem
466+
key={bookmark._id}
467+
ref={index === selectedIndex ? selectedItemRef : null}
468+
bookmark={bookmark}
469+
isSelected={index === selectedIndex}
470+
isChecked={selectedIds.has(bookmark._id)}
471+
selectionMode={selectionMode}
472+
keyboardNavActive={keyboardNavActive}
473+
onEdit={handleEdit}
474+
onTagClick={handleTagClick}
475+
onToggleSelect={toggleSelectBookmark}
476+
onMouseEnter={() => handleBookmarkHover(index)}
477+
onContextMenu={handleContextMenu}
478+
/>
479+
)
480+
))
481+
)}
511482
</div>
512483
</div>
513484
</>

0 commit comments

Comments
 (0)