Merge pull request #2395 from omnivore-app/fix/web-home-notebooks
Allow opening notebooks from the library
This commit is contained in:
@ -185,6 +185,9 @@ export function Dropdown(
|
||||
sideOffset={sideOffset}
|
||||
align={align ? align : 'center'}
|
||||
alignOffset={alignOffset}
|
||||
onCloseAutoFocus={(event) => {
|
||||
event.preventDefault()
|
||||
}}
|
||||
>
|
||||
{labelText && <StyledLabel>{labelText}</StyledLabel>}
|
||||
{children}
|
||||
|
||||
@ -14,8 +14,10 @@ type EditLabelChipProps = {
|
||||
export function EditLabelLabelChip(props: EditLabelChipProps): JSX.Element {
|
||||
const isDark = isDarkTheme()
|
||||
|
||||
const selectedBorder = '#FFEA9F'
|
||||
const unSelectedBorder = 'transparent'
|
||||
const selectedBorder = isDark ? '#FFEA9F' : '#D9D9D9'
|
||||
const unSelectedBorder = isDark ? 'transparent' : '#DEDEDE'
|
||||
const xUnselectedColor = isDark ? '#6A6968' : '#DEDEDE'
|
||||
|
||||
return (
|
||||
<SpanBox
|
||||
css={{
|
||||
@ -33,7 +35,7 @@ export function EditLabelLabelChip(props: EditLabelChipProps): JSX.Element {
|
||||
borderStyle: 'solid',
|
||||
color: isDark ? '#EBEBEB' : '#2A2A2A',
|
||||
borderColor: props.isSelected ? selectedBorder : unSelectedBorder,
|
||||
backgroundColor: isDark ? '#2A2A2A' : '#F5F5F5',
|
||||
backgroundColor: isDark ? '#2A2A2A' : '#F9F9F9',
|
||||
}}
|
||||
>
|
||||
<HStack alignment="center" css={{ gap: '7px' }}>
|
||||
@ -49,11 +51,7 @@ export function EditLabelLabelChip(props: EditLabelChipProps): JSX.Element {
|
||||
>
|
||||
<X
|
||||
size={14}
|
||||
color={
|
||||
props.isSelected
|
||||
? '#FFEA9F'
|
||||
: theme.colors.thBorderSubtle.toString()
|
||||
}
|
||||
color={props.isSelected ? '#FFEA9F' : xUnselectedColor}
|
||||
/>
|
||||
</Button>
|
||||
</HStack>
|
||||
|
||||
@ -48,7 +48,7 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => {
|
||||
if (!isTouchScreenDevice() && focused && inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
}
|
||||
}, [focused])
|
||||
}, [inputRef.current, focused])
|
||||
|
||||
const autoComplete = useCallback(() => {
|
||||
const lowerCasedValue = inputValue.toLowerCase()
|
||||
@ -104,7 +104,7 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => {
|
||||
<Box
|
||||
css={{
|
||||
display: 'inline-block',
|
||||
bg: '#3D3D3D',
|
||||
bg: '$thBackground2', // '#3D3D3D' '#D9D9D9',
|
||||
border: '1px transparent solid',
|
||||
borderRadius: '6px',
|
||||
verticalAlign: 'center',
|
||||
@ -112,7 +112,7 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => {
|
||||
lineHeight: '2',
|
||||
width: '100%',
|
||||
cursor: 'text',
|
||||
color: '#EBEBEB',
|
||||
color: '$thTextContrast2',
|
||||
fontSize: '12px',
|
||||
fontFamily: '$inter',
|
||||
input: {
|
||||
@ -124,7 +124,7 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => {
|
||||
},
|
||||
'&:focus-within': {
|
||||
outline: 'none',
|
||||
border: '1px solid $thLibraryMenuUnselected',
|
||||
border: '1px solid #898989',
|
||||
},
|
||||
'>span': {
|
||||
marginTop: '0px',
|
||||
|
||||
@ -10,6 +10,7 @@ export type CardMenuDropdownAction =
|
||||
| 'unarchive'
|
||||
| 'delete'
|
||||
| 'set-labels'
|
||||
| 'open-notebook'
|
||||
| 'showOriginal'
|
||||
| 'unsubscribe'
|
||||
| 'editTitle'
|
||||
@ -45,6 +46,12 @@ export function CardMenu(props: CardMenuProps): JSX.Element {
|
||||
}}
|
||||
title="Set Labels"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => {
|
||||
props.actionHandler('open-notebook')
|
||||
}}
|
||||
title="Open Notebook"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => props.actionHandler('showOriginal')}
|
||||
title="Open Original"
|
||||
|
||||
@ -12,6 +12,7 @@ export type LinkedItemCardAction =
|
||||
| 'mark-read'
|
||||
| 'mark-unread'
|
||||
| 'set-labels'
|
||||
| 'open-notebook'
|
||||
| 'unsubscribe'
|
||||
| 'update-item'
|
||||
|
||||
|
||||
@ -82,6 +82,7 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element {
|
||||
<ModalRoot defaultOpen onOpenChange={handleClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent
|
||||
tabIndex={-1}
|
||||
onInteractOutside={(event) => {
|
||||
event.preventDefault()
|
||||
}}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
import { Highlight } from '../../../lib/networking/fragments/highlightFragment'
|
||||
import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import {
|
||||
UserBasicData,
|
||||
useGetViewerQuery,
|
||||
} from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { NotebookModal } from './NotebookModal'
|
||||
|
||||
type NotebookPresenterProps = {
|
||||
viewer: UserBasicData
|
||||
|
||||
item: ReadableItem
|
||||
highlights: Highlight[]
|
||||
|
||||
onClose: (highlights: Highlight[]) => void
|
||||
}
|
||||
|
||||
export const NotebookPresenter = (props: NotebookPresenterProps) => {
|
||||
return (
|
||||
<NotebookModal
|
||||
viewer={props.viewer}
|
||||
item={props.item}
|
||||
highlights={props.highlights}
|
||||
onClose={(highlights: Highlight[], deletedAnnotations: Highlight[]) => {
|
||||
console.log('NotebookModal: ', highlights, deletedAnnotations)
|
||||
props.onClose(highlights)
|
||||
}}
|
||||
viewHighlightInReader={(highlightId) => {
|
||||
window.location.href = `/${props.viewer.profile.username}/${props.item.slug}#${highlightId}`
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -241,9 +241,7 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element {
|
||||
props
|
||||
const { labels, revalidate } = useGetLabelsQuery()
|
||||
// Move focus through the labels list on tab or arrow up/down keys
|
||||
const [focusedIndex, setFocusedIndex] = useState<number | undefined>(
|
||||
undefined
|
||||
)
|
||||
const [focusedIndex, setFocusedIndex] = useState<number | undefined>(0)
|
||||
|
||||
useEffect(() => {
|
||||
setFocusedIndex(undefined)
|
||||
|
||||
@ -173,6 +173,7 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element {
|
||||
<ModalRoot defaultOpen onOpenChange={props.onOpenChange}>
|
||||
<ModalOverlay />
|
||||
<ModalContent
|
||||
tabIndex={0}
|
||||
css={{
|
||||
border: '1px solid $grayBorder',
|
||||
backgroundColor: '$thBackground',
|
||||
|
||||
@ -43,6 +43,8 @@ import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation
|
||||
import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
|
||||
import { SetPageLabelsModalPresenter } from '../article/SetLabelsModalPresenter'
|
||||
import { NotebookPresenter } from '../article/NotebookPresenter'
|
||||
import { Highlight } from '../../../lib/networking/fragments/highlightFragment'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
export type LibraryMode = 'reads' | 'highlights'
|
||||
@ -79,6 +81,10 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
undefined
|
||||
)
|
||||
|
||||
const [notebookTarget, setNotebookTarget] = useState<LibraryItem | undefined>(
|
||||
undefined
|
||||
)
|
||||
|
||||
const [showAddLinkModal, setShowAddLinkModal] = useState(false)
|
||||
const [showEditTitleModal, setShowEditTitleModal] = useState(false)
|
||||
const [linkToRemove, setLinkToRemove] = useState<LibraryItem>()
|
||||
@ -328,6 +334,9 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
case 'set-labels':
|
||||
setLabelsTarget(item)
|
||||
break
|
||||
case 'open-notebook':
|
||||
setNotebookTarget(item)
|
||||
break
|
||||
case 'unsubscribe':
|
||||
performActionOnItem('unsubscribe', item)
|
||||
case 'update-item':
|
||||
@ -464,6 +473,9 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
case 'showEditLabelsModal':
|
||||
handleCardAction('set-labels', activeItem)
|
||||
break
|
||||
case 'openNotebook':
|
||||
handleCardAction('open-notebook', activeItem)
|
||||
break
|
||||
case 'sortDescending':
|
||||
setQueryInputs({ ...queryInputs, sortDescending: true })
|
||||
break
|
||||
@ -510,6 +522,12 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
shortcut: ['l'],
|
||||
perform: () => handleCardAction('set-labels', activeItem),
|
||||
}),
|
||||
createAction({
|
||||
section: 'Library',
|
||||
name: 'Open Notebook',
|
||||
shortcut: ['t'],
|
||||
perform: () => handleCardAction('open-notebook', activeItem),
|
||||
}),
|
||||
createAction({
|
||||
section: 'Library',
|
||||
name: 'Mark item as read',
|
||||
@ -705,6 +723,8 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
isValidating={isValidating}
|
||||
labelsTarget={labelsTarget}
|
||||
setLabelsTarget={setLabelsTarget}
|
||||
notebookTarget={notebookTarget}
|
||||
setNotebookTarget={setNotebookTarget}
|
||||
showAddLinkModal={showAddLinkModal}
|
||||
setShowAddLinkModal={setShowAddLinkModal}
|
||||
showEditTitleModal={showEditTitleModal}
|
||||
@ -740,6 +760,10 @@ type HomeFeedContentProps = {
|
||||
loadMore: () => void
|
||||
labelsTarget: LibraryItem | undefined
|
||||
setLabelsTarget: (target: LibraryItem | undefined) => void
|
||||
|
||||
notebookTarget: LibraryItem | undefined
|
||||
setNotebookTarget: (target: LibraryItem | undefined) => void
|
||||
|
||||
showAddLinkModal: boolean
|
||||
setShowAddLinkModal: (show: boolean) => void
|
||||
showEditTitleModal: boolean
|
||||
@ -1015,6 +1039,19 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{props.viewer && props.notebookTarget?.node.id && (
|
||||
<NotebookPresenter
|
||||
viewer={props.viewer}
|
||||
item={props.notebookTarget?.node}
|
||||
highlights={props.notebookTarget?.node.highlights ?? []}
|
||||
onClose={(highlights: Highlight[]) => {
|
||||
if (props.notebookTarget?.node.highlights) {
|
||||
props.notebookTarget.node.highlights = highlights
|
||||
}
|
||||
props.setNotebookTarget(undefined)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showUploadModal && (
|
||||
<UploadModal onOpenChange={() => setShowUploadModal(false)} />
|
||||
)}
|
||||
@ -1055,13 +1092,17 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element {
|
||||
display: 'grid',
|
||||
width: '100%',
|
||||
gridAutoRows: 'auto',
|
||||
borderRadius: '5px',
|
||||
borderRadius: '6px',
|
||||
gridGap: props.layout == 'LIST_LAYOUT' ? '0' : '20px',
|
||||
marginTop: '10px',
|
||||
marginBottom: '0px',
|
||||
paddingTop: '0',
|
||||
paddingBottom: '0px',
|
||||
overflow: 'hidden',
|
||||
boxShadow:
|
||||
props.layout == 'LIST_LAYOUT'
|
||||
? '0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);'
|
||||
: 'unset',
|
||||
'@xlgDown': {
|
||||
border: 'unset',
|
||||
borderRadius: props.layout == 'LIST_LAYOUT' ? 0 : undefined,
|
||||
|
||||
@ -83,7 +83,7 @@ export function LibraryHeader(props: LibraryHeaderProps): JSX.Element {
|
||||
zIndex: 5,
|
||||
position: 'fixed',
|
||||
height: HEADER_HEIGHT,
|
||||
bg: showBackground ? '$thBackground' : 'transparent',
|
||||
bg: '$thLibraryBackground',
|
||||
'@mdDown': {
|
||||
left: '0px',
|
||||
right: '0',
|
||||
@ -242,14 +242,6 @@ export function SearchBox(props: SearchBoxProps): JSX.Element {
|
||||
setSearchTerm(props.searchTerm ?? '')
|
||||
}, [props.searchTerm])
|
||||
|
||||
const border = props.compact
|
||||
? focused
|
||||
? '1px solid $omnivoreCtaYellow'
|
||||
: '1px solid black'
|
||||
: focused
|
||||
? '1px solid $omnivoreCtaYellow'
|
||||
: '1px solid $thBorderColor'
|
||||
|
||||
useKeyboardShortcuts(
|
||||
searchBarCommands((action) => {
|
||||
if (action === 'focusSearchBar' && inputRef.current) {
|
||||
@ -270,7 +262,12 @@ export function SearchBox(props: SearchBoxProps): JSX.Element {
|
||||
maxWidth: '521px',
|
||||
bg: '$thLibrarySearchbox',
|
||||
borderRadius: '6px',
|
||||
border: border,
|
||||
border: focused
|
||||
? '2px solid $omnivoreCtaYellow'
|
||||
: '2px solid transparent',
|
||||
boxShadow: focused
|
||||
? 'none'
|
||||
: '0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);',
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
|
||||
@ -89,6 +89,7 @@ type LibraryListKeyboardAction =
|
||||
| 'shareItem'
|
||||
| 'showAddLinkModal'
|
||||
| 'showEditLabelsModal'
|
||||
| 'openNotebook'
|
||||
| 'beginMultiSelect'
|
||||
| 'endMultiSelect'
|
||||
|
||||
|
||||
@ -815,7 +815,18 @@ function init() {
|
||||
|
||||
browserApi.contextMenus.create({
|
||||
id: 'save-selection',
|
||||
title: 'Save to Omnivore',
|
||||
title: 'Save link to Omnivore',
|
||||
contexts: ['link'],
|
||||
onclick: async function (obj) {
|
||||
executeAction(async function (currentTab) {
|
||||
await saveUrl(currentTab, obj.linkUrl)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
browserApi.contextMenus.create({
|
||||
id: 'save-selection',
|
||||
title: 'Save link to Omnivore',
|
||||
contexts: ['link'],
|
||||
onclick: async function (obj) {
|
||||
executeAction(async function (currentTab) {
|
||||
|
||||
Reference in New Issue
Block a user