diff --git a/packages/web/components/elements/MenuTrigger.tsx b/packages/web/components/elements/MenuTrigger.tsx
new file mode 100644
index 000000000..4ec61eef5
--- /dev/null
+++ b/packages/web/components/elements/MenuTrigger.tsx
@@ -0,0 +1,27 @@
+import { DotsThreeVertical } from 'phosphor-react'
+import { theme } from '../tokens/stitches.config'
+import { Box } from './LayoutPrimitives'
+
+export function MenuTrigger(): JSX.Element {
+ return (
+
+
+
+ )
+}
diff --git a/packages/web/components/elements/MenuTriggerButton.tsx b/packages/web/components/elements/MenuTriggerButton.tsx
deleted file mode 100644
index 29a1700c9..000000000
--- a/packages/web/components/elements/MenuTriggerButton.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { DotsThreeVertical, X } from 'phosphor-react'
-import { useState } from 'react'
-import { Button } from './Button'
-import { Box, SpanBox } from './LayoutPrimitives'
-
-export function MenuTrigger(): JSX.Element {
- const [hover, setHover] = useState(false)
-
- return (
- setHover(true)}
- onMouseOut={() => setHover(false)}
- >
-
- {/* color="#ADADAD" />
- */}
-
- )
-}
diff --git a/packages/web/components/elements/images/OmnivoreFullLogo.tsx b/packages/web/components/elements/images/OmnivoreFullLogo.tsx
index 7dc9d805a..5aa52ff11 100644
--- a/packages/web/components/elements/images/OmnivoreFullLogo.tsx
+++ b/packages/web/components/elements/images/OmnivoreFullLogo.tsx
@@ -1,5 +1,8 @@
import Link from 'next/link'
+import { useRouter } from 'next/router'
+import { useEffect, useState } from 'react'
import { config } from '../../tokens/stitches.config'
+import { OmnivoreLogoBase } from './OmnivoreLogoBase'
export type OmnivoreFullLogoProps = {
color?: string
@@ -9,64 +12,55 @@ export type OmnivoreFullLogoProps = {
export function OmnivoreFullLogo(props: OmnivoreFullLogoProps): JSX.Element {
const fillColor = props.color || config.theme.colors.graySolid
- const href = props.href || '/home'
return (
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/packages/web/components/elements/images/OmnivoreLogoBase.tsx b/packages/web/components/elements/images/OmnivoreLogoBase.tsx
new file mode 100644
index 000000000..9b0c7215d
--- /dev/null
+++ b/packages/web/components/elements/images/OmnivoreLogoBase.tsx
@@ -0,0 +1,42 @@
+import Link from 'next/link'
+import { useRouter } from 'next/router'
+import { ReactChildren } from 'react'
+import { config } from '../../tokens/stitches.config'
+
+export type OmnivoreLogoBaseProps = {
+ color?: string
+ href?: string
+ showTitle?: boolean
+ children: React.ReactNode
+}
+
+export function OmnivoreLogoBase(props: OmnivoreLogoBaseProps): JSX.Element {
+ const href = props.href || '/home'
+ const router = useRouter()
+
+ return (
+
+ {
+ const query = window.sessionStorage.getItem('q')
+ if (query) {
+ console.log(
+ 'going to: ',
+ window.sessionStorage.getItem('q'),
+ props.href
+ )
+ router.push(`/home?${query}`)
+ event.preventDefault()
+ }
+ }}
+ >
+ {props.children}
+
+
+ )
+}
diff --git a/packages/web/components/elements/images/OmnivoreNameLogo.tsx b/packages/web/components/elements/images/OmnivoreNameLogo.tsx
index 06e371390..cb43e1ab5 100644
--- a/packages/web/components/elements/images/OmnivoreNameLogo.tsx
+++ b/packages/web/components/elements/images/OmnivoreNameLogo.tsx
@@ -1,8 +1,7 @@
import { config } from '../../tokens/stitches.config'
import Image from 'next/image'
import { StyledText } from '../../elements/StyledText'
-import Link from 'next/link'
-import { SpanBox } from '../LayoutPrimitives'
+import { OmnivoreLogoBase } from './OmnivoreLogoBase'
export function OmnivoreNameLogoImage(): JSX.Element {
return (
@@ -66,27 +65,18 @@ export type OmnivoreNameLogoProps = {
export function OmnivoreNameLogo(props: OmnivoreNameLogoProps): JSX.Element {
const fillColor = props.color || config.theme.colors.graySolid
- const href = props.href || '/home'
return (
-
-
-
- {props.showTitle && (
-
- Omnivore
-
- )}
-
-
+
+
+ {props.showTitle && (
+
+ Omnivore
+
+ )}
+
)
}
diff --git a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx
index 23a13dfc0..8e1851ca4 100644
--- a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx
+++ b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx
@@ -1,5 +1,8 @@
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
+import { useMemo } from 'react'
+import { LibraryItemNode } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
+import { Box, SpanBox } from '../../elements/LayoutPrimitives'
dayjs.extend(relativeTime)
@@ -105,3 +108,40 @@ export const siteName = (
} catch {}
return ''
}
+
+type LibraryItemMetadataProps = {
+ item: LibraryItemNode
+ showProgress?: boolean
+}
+
+export function LibraryItemMetadata(
+ props: LibraryItemMetadataProps
+): JSX.Element {
+ const highlightCount = useMemo(() => {
+ return props.item.highlights?.length ?? 0
+ }, [props.item.highlights])
+
+ return (
+
+ {timeAgo(props.item.savedAt)}
+ {` `}
+ {props.item.wordsCount ?? 0 > 0
+ ? ` • ${Math.max(
+ 1,
+ Math.round((props.item.wordsCount ?? 0) / 235)
+ )} min read`
+ : null}
+ {(props.showProgress && props.item.readingProgressPercent) ?? 0 > 0 ? (
+ <>
+ {` • `}
+
+ {`${Math.round(props.item.readingProgressPercent)}%`}
+
+ >
+ ) : null}
+ {highlightCount > 0
+ ? ` • ${highlightCount} highlight${highlightCount > 1 ? 's' : ''}`
+ : null}
+
+ )
+}
diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx
index ebeb189e8..5d6de337a 100644
--- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx
+++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx
@@ -4,13 +4,14 @@ import type { LinkedItemCardProps } from './CardTypes'
import { CoverImage } from '../../elements/CoverImage'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
-import { useState } from 'react'
+import { useMemo, useState } from 'react'
import { DotsThreeVertical } from 'phosphor-react'
import Link from 'next/link'
import { CardMenu } from '../CardMenu'
import {
AuthorInfoStyle,
DescriptionStyle,
+ LibraryItemMetadata,
MenuStyle,
MetaStyle,
siteName,
@@ -102,19 +103,7 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element {
}}
distribution="start"
>
-
- {timeAgo(props.item.savedAt)}
- {` `}
- {props.item.wordsCount ?? 0 > 0
- ? ` • ${Math.max(
- 1,
- Math.round((props.item.wordsCount ?? 0) / 235)
- )} min read`
- : null}
- {props.item.highlights?.length ?? 0 > 0
- ? ` • ${props.item.highlights?.length} highlights`
- : null}
-
+
{props.item.author}
diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx
index 21f031335..abed965d4 100644
--- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx
+++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx
@@ -1,12 +1,13 @@
import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives'
import { LabelChip } from '../../elements/LabelChip'
import type { LinkedItemCardProps } from './CardTypes'
-import { useState } from 'react'
+import { useMemo, useState } from 'react'
import { DotsThree } from 'phosphor-react'
import Link from 'next/link'
import { CardMenu } from '../CardMenu'
import {
AuthorInfoStyle,
+ LibraryItemMetadata,
MenuStyle,
MetaStyle,
siteName,
@@ -22,6 +23,10 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element {
props.item.siteName ||
siteName(props.item.originalArticleUrl, props.item.url)
+ const highlightCount = useMemo(() => {
+ return props.item.highlights?.length ?? 0
+ }, [props.item.highlights])
+
return (
-
- {timeAgo(props.item.savedAt)}
- {` `}
- {props.item.wordsCount ?? 0 > 0
- ? ` • ${Math.max(
- 1,
- Math.round((props.item.wordsCount ?? 0) / 235)
- )} min read`
- : null}
- {props.item.readingProgressPercent ?? 0 > 0 ? (
- <>
- {` • `}
-
- {`${Math.round(props.item.readingProgressPercent)}%`}
-
- >
- ) : null}
- {props.item.highlights?.length ?? 0 > 0
- ? ` • ${props.item.highlights?.length} highlights`
- : null}
-
+
void
- setShowKeyboardCommandsModal: (showShareModal: boolean) => void
-}
-
-export function PrimaryHeader(props: HeaderProps): JSX.Element {
- const router = useRouter()
- const [isScrolled, setIsScrolled] = useState(false)
-
- function headerDropdownActionHandler(action: HeaderDropdownAction): void {
- switch (action) {
- case 'apply-darker-theme':
- updateTheme(ThemeId.Darker)
- break
- case 'apply-dark-theme':
- updateTheme(ThemeId.Dark)
- break
- case 'apply-lighter-theme':
- updateTheme(ThemeId.Lighter)
- break
- case 'apply-light-theme':
- updateTheme(ThemeId.Light)
- break
- case 'increaseFontSize':
- document.dispatchEvent(new Event('increaseFontSize'))
- break
- case 'decreaseFontSize':
- document.dispatchEvent(new Event('decreaseFontSize'))
- break
- case 'navigate-to-install':
- router.push('/settings/installation')
- break
- case 'navigate-to-emails':
- router.push('/settings/emails')
- break
- case 'navigate-to-labels':
- router.push('/settings/labels')
- break
- case 'navigate-to-profile':
- if (props.user) {
- router.push(`/${props.user.profile.username}`)
- }
- break
- case 'navigate-to-subscriptions':
- router.push('/settings/subscriptions')
- break
- case 'navigate-to-api':
- router.push('/settings/api')
- break
- case 'navigate-to-integrations':
- router.push('/settings/integrations')
- break
- case 'logout':
- props.setShowLogoutConfirmation(true)
- break
- default:
- break
- }
- }
-
- return (
- <>
-
-
-
-
-
-
- >
- )
-}
-
-// Separating out component so we can extract to design system
-type NavHeaderProps = {
- username?: string
- userInitials: string
- actionHandler: (action: HeaderDropdownAction) => void
- profileImageURL?: string
- isDisplayingShadow?: boolean
- isVisible?: boolean
- isTransparent: boolean
- toolbarControl?: JSX.Element
- alwaysDisplayToolbar?: boolean
-}
-
-function NavHeader(props: NavHeaderProps): JSX.Element {
- return (
-
- )
-}
-
-function FloatingNavHeader(props: NavHeaderProps): JSX.Element {
- return (
- <>
-
-
-
-
-
-
- {props.username && (
-
-
- }
- actionHandler={props.actionHandler}
- />
-
- )}
- >
- )
-}
diff --git a/packages/web/components/patterns/SettingsHeader.tsx b/packages/web/components/patterns/SettingsHeader.tsx
new file mode 100644
index 000000000..7dbb96f53
--- /dev/null
+++ b/packages/web/components/patterns/SettingsHeader.tsx
@@ -0,0 +1,52 @@
+import { Box, HStack, VStack } from '../elements/LayoutPrimitives'
+import { OmnivoreNameLogo } from '../elements/images/OmnivoreNameLogo'
+import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery'
+import { PrimaryDropdown } from '../templates/PrimaryDropdown'
+import {
+ HEADER_HEIGHT,
+ MOBILE_HEADER_HEIGHT,
+} from '../templates/homeFeed/HeaderSpacer'
+
+type HeaderProps = {
+ user?: UserBasicData
+}
+
+export function SettingsHeader(props: HeaderProps): JSX.Element {
+ return (
+
+ )
+}
diff --git a/packages/web/components/templates/SettingsLayout.tsx b/packages/web/components/templates/SettingsLayout.tsx
index 15d381bf3..84a898ced 100644
--- a/packages/web/components/templates/SettingsLayout.tsx
+++ b/packages/web/components/templates/SettingsLayout.tsx
@@ -1,6 +1,6 @@
-import { Box } from '../elements/LayoutPrimitives'
+import { Box, VStack } from '../elements/LayoutPrimitives'
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
-import { PrimaryHeader } from '../patterns/PrimaryHeader'
+import { SettingsHeader } from '../patterns/SettingsHeader'
import { navigationCommands } from '../../lib/keyboardShortcuts/navigationShortcuts'
import { useKeyboardShortcuts } from '../../lib/keyboardShortcuts/useKeyboardShortcuts'
import { useRouter } from 'next/router'
@@ -10,6 +10,7 @@ import { useState } from 'react'
import { ConfirmationModal } from '../patterns/ConfirmationModal'
import { KeyboardShortcutListModal } from './KeyboardShortcutListModal'
import { PageMetaData } from '../patterns/PageMetaData'
+import { HeaderSpacer, MOBILE_HEADER_HEIGHT } from './homeFeed/HeaderSpacer'
type SettingsLayoutProps = {
title?: string
@@ -20,7 +21,8 @@ export function SettingsLayout(props: SettingsLayoutProps): JSX.Element {
const { viewerData } = useGetViewerQuery()
const router = useRouter()
const [showLogoutConfirmation, setShowLogoutConfirmation] = useState(false)
- const [showKeyboardCommandsModal, setShowKeyboardCommandsModal] = useState(false)
+ const [showKeyboardCommandsModal, setShowKeyboardCommandsModal] =
+ useState(false)
useKeyboardShortcuts(navigationCommands(router))
applyStoredTheme(false)
@@ -41,39 +43,30 @@ export function SettingsLayout(props: SettingsLayoutProps): JSX.Element {
return (
<>
-
-
+
+
+
- {props.children}
- {showLogoutConfirmation ? (
- setShowLogoutConfirmation(false)}
- />
- ) : null}
- {showKeyboardCommandsModal ? (
- setShowKeyboardCommandsModal(false)}
- />
- ) : null}
-
+ >
+ {props.children}
+
+
+ {showLogoutConfirmation ? (
+ setShowLogoutConfirmation(false)}
+ />
+ ) : null}
+ {showKeyboardCommandsModal ? (
+ setShowKeyboardCommandsModal(false)}
+ />
+ ) : null}
>
)
}
diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx
index 6ee2145eb..675393b70 100644
--- a/packages/web/components/templates/article/HighlightsLayer.tsx
+++ b/packages/web/components/templates/article/HighlightsLayer.tsx
@@ -68,13 +68,15 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
>([])
const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 })
- const [focusedHighlight, setFocusedHighlight] =
- useState(undefined)
+ const [focusedHighlight, setFocusedHighlight] = useState<
+ Highlight | undefined
+ >(undefined)
const [selectionData, setSelectionData] = useSelection(highlightLocations)
- const [labelsTarget, setLabelsTarget] =
- useState(undefined)
+ const [labelsTarget, setLabelsTarget] = useState(
+ undefined
+ )
const canShareNative = useCanShareNative()
diff --git a/packages/web/components/templates/article/NotebookModal.tsx b/packages/web/components/templates/article/NotebookModal.tsx
index 6dadb8030..1aca83f46 100644
--- a/packages/web/components/templates/article/NotebookModal.tsx
+++ b/packages/web/components/templates/article/NotebookModal.tsx
@@ -23,6 +23,9 @@ import { updateHighlightMutation } from '../../../lib/networking/mutations/updat
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
import { diff_match_patch } from 'diff-match-patch'
import { HighlightNoteTextEditArea } from '../../elements/HighlightNoteTextEditArea'
+import { CloseButton } from '../../elements/CloseButton'
+import { MenuTrigger } from '../../elements/MenuTrigger'
+import { highlightsAsMarkdown, HighlightsMenu } from '../homeFeed/HighlightItem'
type NotebookModalProps = {
highlights: Highlight[]
@@ -46,6 +49,18 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element {
)
const [, updateState] = useState({})
+ const exportHighlights = useCallback(() => {
+ ;(async () => {
+ if (!props.highlights) {
+ showErrorToast('No highlights to export')
+ return
+ }
+ const markdown = highlightsAsMarkdown(props.highlights)
+ await navigator.clipboard.writeText(markdown)
+ showSuccessToast('Highlight copied')
+ })()
+ }, [props.highlights])
+
const sortedHighlights = useMemo(() => {
const sorted = (a: number, b: number) => {
if (a < b) {
@@ -85,7 +100,24 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element {
css={{ overflow: 'auto', px: '24px' }}
>
-
+
+ Notebook
+
+ }>
+ {
+ exportHighlights()
+ }}
+ title="Export"
+ />
+
+ props.onOpenChange(false)} />
+
+
{sortedHighlights.map((highlight) => (
{
@@ -174,9 +207,13 @@ function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element {
}, [props.highlight])
return (
- <>
+ setHover(true)}
+ onMouseLeave={() => setHover(false)}
+ >
-
+ {/*
@@ -201,7 +238,7 @@ function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element {
title="Delete"
/>
-
+ */}
- >
+
+
+
+
)
}
diff --git a/packages/web/components/templates/homeFeed/HighlightItem.tsx b/packages/web/components/templates/homeFeed/HighlightItem.tsx
index 531010027..7955f11dd 100644
--- a/packages/web/components/templates/homeFeed/HighlightItem.tsx
+++ b/packages/web/components/templates/homeFeed/HighlightItem.tsx
@@ -199,7 +199,7 @@ type HighlightsMenuProps = {
setShowConfirmDeleteHighlightId: (set: string) => void
}
-function HighlightsMenu(props: HighlightsMenuProps): JSX.Element {
+export function HighlightsMenu(props: HighlightsMenuProps): JSX.Element {
const copyHighlight = useCallback(() => {
;(async () => {
await navigator.clipboard.writeText(props.highlight.quote)
diff --git a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx
index 0d0c72d76..129d0f8f7 100644
--- a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx
+++ b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx
@@ -1,4 +1,4 @@
-import { DotsThreeVertical, HighlighterCircle } from 'phosphor-react'
+import { HighlighterCircle } from 'phosphor-react'
import { useCallback, useEffect, useState } from 'react'
import { Toaster } from 'react-hot-toast'
import { LibraryItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
@@ -7,6 +7,7 @@ import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
+import { MenuTrigger } from '../../elements/MenuTrigger'
import { StyledText } from '../../elements/StyledText'
import {
MetaStyle,
@@ -292,25 +293,7 @@ function HighlightList(props: HighlightListProps): JSX.Element {
>
HIGHLIGHTS
-
-
-
- }
- >
+ }>
{
exportHighlights()
diff --git a/packages/web/components/templates/reader/ReaderHeader.tsx b/packages/web/components/templates/reader/ReaderHeader.tsx
index e14e3a46c..e3345535f 100644
--- a/packages/web/components/templates/reader/ReaderHeader.tsx
+++ b/packages/web/components/templates/reader/ReaderHeader.tsx
@@ -1,4 +1,4 @@
-import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
+import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { DotsThreeOutline, TextAa } from 'phosphor-react'
import { PrimaryDropdown } from '../PrimaryDropdown'
diff --git a/packages/web/pages/index.tsx b/packages/web/pages/index.tsx
index ee60b8333..199614dc2 100644
--- a/packages/web/pages/index.tsx
+++ b/packages/web/pages/index.tsx
@@ -11,7 +11,7 @@ export default function LandingPage(): JSX.Element {
if (!isLoading && router.isReady && viewerData?.me) {
router.push('/home')
return <>>
- } else if (isLoading) {
+ } else if (isLoading || !router.isReady) {
return (
<>
-
+
Connect with other applications can help enhance and streamline your
experience with Omnivore, below are some useful apps to connect your
@@ -142,7 +142,7 @@ export default function Integrations(): JSX.Element {
css={{
width: '80%',
margin: '0 auto',
- height: '500px',
+ height: '100%',
'@smDown': {
width: '100%',
},
diff --git a/packages/web/pages/settings/webhooks.tsx b/packages/web/pages/settings/webhooks.tsx
index b4f1b5e10..60052b09a 100644
--- a/packages/web/pages/settings/webhooks.tsx
+++ b/packages/web/pages/settings/webhooks.tsx
@@ -13,6 +13,7 @@ import { deleteWebhookMutation } from '../../lib/networking/mutations/deleteWebh
import { FormModal } from '../../components/patterns/FormModal'
import { setWebhookMutation } from '../../lib/networking/mutations/setWebhookMutation'
import { FormInputProps } from '../../components/elements/FormElements'
+import { Box } from '../../components/elements/LayoutPrimitives'
interface Webhook {
id?: string
@@ -225,6 +226,7 @@ export default function Webhooks(): JSX.Element {
setOnEditWebhook(webhook)
}}
/>
+
)
}