Merge pull request #2395 from omnivore-app/fix/web-home-notebooks

Allow opening notebooks from the library
This commit is contained in:
Jackson Harper
2023-06-20 18:25:34 +08:00
committed by GitHub
13 changed files with 119 additions and 27 deletions

View File

@ -185,6 +185,9 @@ export function Dropdown(
sideOffset={sideOffset}
align={align ? align : 'center'}
alignOffset={alignOffset}
onCloseAutoFocus={(event) => {
event.preventDefault()
}}
>
{labelText && <StyledLabel>{labelText}</StyledLabel>}
{children}

View File

@ -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>

View File

@ -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',

View File

@ -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"

View File

@ -12,6 +12,7 @@ export type LinkedItemCardAction =
| 'mark-read'
| 'mark-unread'
| 'set-labels'
| 'open-notebook'
| 'unsubscribe'
| 'update-item'

View File

@ -82,6 +82,7 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element {
<ModalRoot defaultOpen onOpenChange={handleClose}>
<ModalOverlay />
<ModalContent
tabIndex={-1}
onInteractOutside={(event) => {
event.preventDefault()
}}

View File

@ -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}`
}}
/>
)
}

View File

@ -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)

View File

@ -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',

View File

@ -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,

View File

@ -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

View File

@ -89,6 +89,7 @@ type LibraryListKeyboardAction =
| 'shareItem'
| 'showAddLinkModal'
| 'showEditLabelsModal'
| 'openNotebook'
| 'beginMultiSelect'
| 'endMultiSelect'

View File

@ -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) {