Merge pull request #1888 from omnivore-app/fix/notebooks-modal

Make notebook modal closer to highlights view, add exports
This commit is contained in:
Jackson Harper 2023-03-10 11:47:28 +08:00 committed by GitHub
commit df04986a92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 335 additions and 481 deletions

View file

@ -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 (
<Box
css={{
display: 'flex',
height: '20px',
width: '20px',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '1000px',
'&:hover': {
bg: '#898989',
},
}}
>
<DotsThreeVertical
size={20}
color={theme.colors.thTextContrast2.toString()}
weight="bold"
/>
</Box>
)
}

View file

@ -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 (
<SpanBox
css={{
display: 'flex',
height: '20px',
width: '20px',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '1000px',
background: '$thBackground2',
'&:hover': {
bg: '#898989',
},
}}
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
>
<DotsThreeVertical
size={25}
weight="bold"
color={hover ? '#EBEBEB' : '#898989'}
/>
{/* color="#ADADAD" />
<Button
css={{
cursor: 'pointer',
marginLeft: 'auto',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
style="ghost"
onClick={() => {
props.close()
}}
>
<X
width={10}
height={10}
weight="bold"
color={hover ? '#EBEBEB' : '#898989'}
className="xMark"
/>
</Button> */}
</SpanBox>
)
}

View file

@ -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 (
<Link passHref href={href}>
<a
style={{
textDecoration: 'none',
display: 'flex',
alignItems: 'center',
}}
<OmnivoreLogoBase {...props}>
<svg
width="129"
height="26"
viewBox="0 0 129 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<svg
width="129"
height="26"
viewBox="0 0 129 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M43.4131 13.2862C43.4131 9.75626 41.2214 7.74365 38.3606 7.74365C35.4839 7.74365 33.308 9.75626 33.308 13.2862C33.308 16.8004 35.4839 18.8288 38.3606 18.8288C41.2214 18.8288 43.4131 16.8162 43.4131 13.2862ZM41.1002 13.2862C41.1002 15.5728 40.0149 16.8109 38.3606 16.8109C36.701 16.8109 35.6209 15.5728 35.6209 13.2862C35.6209 10.9996 36.701 9.76153 38.3606 9.76153C40.0149 9.76153 41.1002 10.9996 41.1002 13.2862Z"
fill={fillColor}
/>
<path
d="M47.3285 7.89117V18.6813H49.5413V11.6319H49.6309L52.4232 18.6286H53.9301L56.7224 11.6582H56.812V18.6813H59.0248V7.89117H56.2114L53.2399 15.1408H53.1134L50.1419 7.89117H47.3285Z"
fill={fillColor}
/>
<path
d="M72.1549 7.89117H69.8842V14.6771H69.7893L65.1319 7.89117H63.1298V18.6813H65.4111V11.89H65.4901L70.1845 18.6813H72.1549V7.89117Z"
fill={fillColor}
/>
<path
d="M78.5465 7.89117H76.2652V18.6813H78.5465V7.89117Z"
fill={fillColor}
/>
<path
d="M84.5983 7.89117H82.0641L85.789 18.6813H88.7289L92.4485 7.89117H89.9196L87.3063 16.0891H87.2062L84.5983 7.89117Z"
fill={fillColor}
/>
<path
d="M105.28 13.2862C105.28 9.75626 103.088 7.74365 100.227 7.74365C97.3504 7.74365 95.1745 9.75626 95.1745 13.2862C95.1745 16.8004 97.3504 18.8288 100.227 18.8288C103.088 18.8288 105.28 16.8162 105.28 13.2862ZM102.967 13.2862C102.967 15.5728 101.881 16.8109 100.227 16.8109C98.5675 16.8109 97.4874 15.5728 97.4874 13.2862C97.4874 10.9996 98.5675 9.76153 100.227 9.76153C101.881 9.76153 102.967 10.9996 102.967 13.2862Z"
fill={fillColor}
/>
<path
d="M109.195 18.6813H111.476V14.8563H113.141L115.185 18.6813H117.704L115.412 14.4875C116.64 13.9606 117.319 12.8911 117.319 11.4159C117.319 9.27155 115.902 7.89117 113.452 7.89117H109.195V18.6813ZM111.476 13.0228V9.75626H113.015C114.332 9.75626 114.969 10.3411 114.969 11.4159C114.969 12.4854 114.332 13.0228 113.025 13.0228H111.476Z"
fill={fillColor}
/>
<path
d="M121.157 18.6813H128.449V16.8004H123.438V14.224H128.053V12.3431H123.438V9.77206H128.427V7.89117H121.157V18.6813Z"
fill={fillColor}
/>
<path
d="M8.42285 17.9061V10.5447C8.42285 9.91527 9.16173 9.55951 9.65432 9.99737L11.9257 13.3087C12.3909 13.6918 13.0477 13.6918 13.5129 13.3087L15.7296 10.0247C16.2222 9.61424 16.961 9.94263 16.961 10.5721V14.458C16.961 16.3463 18.2199 17.8788 20.1081 17.8788H20.1629C21.7775 17.8788 23.1731 16.7841 23.5563 15.2243C23.7478 14.4033 23.912 13.5549 23.912 12.8982C23.8847 6.46715 18.4388 1.596 11.9257 2.03385C6.39776 2.41698 1.9371 6.87764 1.55397 12.4056C1.11612 18.9187 6.26093 24.3645 12.7193 24.3645"
stroke={fillColor}
strokeWidth="2.18182"
strokeMiterlimit="10"
/>
</svg>
</a>
</Link>
<path
d="M43.4131 13.2862C43.4131 9.75626 41.2214 7.74365 38.3606 7.74365C35.4839 7.74365 33.308 9.75626 33.308 13.2862C33.308 16.8004 35.4839 18.8288 38.3606 18.8288C41.2214 18.8288 43.4131 16.8162 43.4131 13.2862ZM41.1002 13.2862C41.1002 15.5728 40.0149 16.8109 38.3606 16.8109C36.701 16.8109 35.6209 15.5728 35.6209 13.2862C35.6209 10.9996 36.701 9.76153 38.3606 9.76153C40.0149 9.76153 41.1002 10.9996 41.1002 13.2862Z"
fill={fillColor}
/>
<path
d="M47.3285 7.89117V18.6813H49.5413V11.6319H49.6309L52.4232 18.6286H53.9301L56.7224 11.6582H56.812V18.6813H59.0248V7.89117H56.2114L53.2399 15.1408H53.1134L50.1419 7.89117H47.3285Z"
fill={fillColor}
/>
<path
d="M72.1549 7.89117H69.8842V14.6771H69.7893L65.1319 7.89117H63.1298V18.6813H65.4111V11.89H65.4901L70.1845 18.6813H72.1549V7.89117Z"
fill={fillColor}
/>
<path
d="M78.5465 7.89117H76.2652V18.6813H78.5465V7.89117Z"
fill={fillColor}
/>
<path
d="M84.5983 7.89117H82.0641L85.789 18.6813H88.7289L92.4485 7.89117H89.9196L87.3063 16.0891H87.2062L84.5983 7.89117Z"
fill={fillColor}
/>
<path
d="M105.28 13.2862C105.28 9.75626 103.088 7.74365 100.227 7.74365C97.3504 7.74365 95.1745 9.75626 95.1745 13.2862C95.1745 16.8004 97.3504 18.8288 100.227 18.8288C103.088 18.8288 105.28 16.8162 105.28 13.2862ZM102.967 13.2862C102.967 15.5728 101.881 16.8109 100.227 16.8109C98.5675 16.8109 97.4874 15.5728 97.4874 13.2862C97.4874 10.9996 98.5675 9.76153 100.227 9.76153C101.881 9.76153 102.967 10.9996 102.967 13.2862Z"
fill={fillColor}
/>
<path
d="M109.195 18.6813H111.476V14.8563H113.141L115.185 18.6813H117.704L115.412 14.4875C116.64 13.9606 117.319 12.8911 117.319 11.4159C117.319 9.27155 115.902 7.89117 113.452 7.89117H109.195V18.6813ZM111.476 13.0228V9.75626H113.015C114.332 9.75626 114.969 10.3411 114.969 11.4159C114.969 12.4854 114.332 13.0228 113.025 13.0228H111.476Z"
fill={fillColor}
/>
<path
d="M121.157 18.6813H128.449V16.8004H123.438V14.224H128.053V12.3431H123.438V9.77206H128.427V7.89117H121.157V18.6813Z"
fill={fillColor}
/>
<path
d="M8.42285 17.9061V10.5447C8.42285 9.91527 9.16173 9.55951 9.65432 9.99737L11.9257 13.3087C12.3909 13.6918 13.0477 13.6918 13.5129 13.3087L15.7296 10.0247C16.2222 9.61424 16.961 9.94263 16.961 10.5721V14.458C16.961 16.3463 18.2199 17.8788 20.1081 17.8788H20.1629C21.7775 17.8788 23.1731 16.7841 23.5563 15.2243C23.7478 14.4033 23.912 13.5549 23.912 12.8982C23.8847 6.46715 18.4388 1.596 11.9257 2.03385C6.39776 2.41698 1.9371 6.87764 1.55397 12.4056C1.11612 18.9187 6.26093 24.3645 12.7193 24.3645"
stroke={fillColor}
strokeWidth="2.18182"
strokeMiterlimit="10"
/>
</svg>
</OmnivoreLogoBase>
)
}

View file

@ -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 (
<Link passHref href={href}>
<a
style={{
textDecoration: 'none',
display: 'flex',
alignItems: 'center',
}}
onClick={(event) => {
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}
</a>
</Link>
)
}

View file

@ -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 (
<Link passHref href={href}>
<a
style={{
textDecoration: 'none',
display: 'flex',
alignItems: 'center',
}}
>
<OmnivoreLogoIcon size={27} strokeColor={fillColor}></OmnivoreLogoIcon>
{props.showTitle && (
<StyledText
style="logoTitle"
css={{ color: fillColor, paddingLeft: '12px' }}
>
Omnivore
</StyledText>
)}
</a>
</Link>
<OmnivoreLogoBase {...props}>
<OmnivoreLogoIcon size={27} strokeColor={fillColor}></OmnivoreLogoIcon>
{props.showTitle && (
<StyledText
style="logoTitle"
css={{ color: fillColor, paddingLeft: '12px' }}
>
Omnivore
</StyledText>
)}
</OmnivoreLogoBase>
)
}

View file

@ -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 (
<Box>
{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 ? (
<>
{``}
<SpanBox css={{ color: '#55B938' }}>
{`${Math.round(props.item.readingProgressPercent)}%`}
</SpanBox>
</>
) : null}
{highlightCount > 0
? `${highlightCount} highlight${highlightCount > 1 ? 's' : ''}`
: null}
</Box>
)
}

View file

@ -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"
>
<Box>
{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}
</Box>
<LibraryItemMetadata item={props.item} />
<Box
css={{
...MenuStyle,
@ -152,7 +141,7 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element {
<SpanBox
css={{
...AuthorInfoStyle,
m: '0px',
mt: '10px',
}}
>
{props.item.author}

View file

@ -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 (
<VStack
css={{
@ -62,27 +67,7 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element {
style={{ textDecoration: 'unset', width: '100%', height: '100%' }}
>
<HStack css={MetaStyle} distribution="start">
<Box>
{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 ? (
<>
{``}
<SpanBox css={{ color: '#55B938' }}>
{`${Math.round(props.item.readingProgressPercent)}%`}
</SpanBox>
</>
) : null}
{props.item.highlights?.length ?? 0 > 0
? `${props.item.highlights?.length} highlights`
: null}
</Box>
<LibraryItemMetadata item={props.item} showProgress={true} />
<Box
css={{
...MenuStyle,

View file

@ -1,243 +0,0 @@
import { Box, HStack, SpanBox } from '../elements/LayoutPrimitives'
import { OmnivoreNameLogo } from './../elements/images/OmnivoreNameLogo'
import { DropdownMenu, HeaderDropdownAction } from './../patterns/DropdownMenu'
import { updateTheme } from '../../lib/themeUpdater'
import { AvatarDropdown } from './../elements/AvatarDropdown'
import { ThemeId } from './../tokens/stitches.config'
import { useState } from 'react'
import { useRouter } from 'next/router'
import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery'
type HeaderProps = {
user?: UserBasicData
userInitials: string
hideHeader?: boolean
profileImageURL?: string
isTransparent: boolean
toolbarControl?: JSX.Element
alwaysDisplayToolbar?: boolean
setShowLogoutConfirmation: (showShareModal: boolean) => 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 (
<>
<SpanBox
css={{
display: props.alwaysDisplayToolbar ? 'none' : 'flex',
'@lgDown': {
display: 'none',
},
}}
>
<FloatingNavHeader
{...props}
isVisible={true}
username={props.user?.profile.username}
actionHandler={headerDropdownActionHandler}
isDisplayingShadow={isScrolled}
toolbarControl={props.toolbarControl}
alwaysDisplayToolbar={props.alwaysDisplayToolbar}
/>
</SpanBox>
<SpanBox
css={{
'@lg': {
display: props.alwaysDisplayToolbar ? 'flex' : 'none',
},
}}
>
<NavHeader
{...props}
isVisible={true}
username={props.user?.profile.username}
actionHandler={headerDropdownActionHandler}
isDisplayingShadow={isScrolled}
toolbarControl={props.toolbarControl}
alwaysDisplayToolbar={props.alwaysDisplayToolbar}
/>
</SpanBox>
</>
)
}
// 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 (
<nav>
<HStack
alignment="center"
distribution="between"
css={{
zIndex: 5,
width: '100%',
boxShadow: props.isDisplayingShadow ? '$panelShadow' : 'unset',
p: '0px $3 0px $3',
height: '48px',
position: 'fixed',
bg: props.isTransparent ? 'transparent' : '$grayBase',
'@smDown': {
p: '0px 18px 0px 16px',
},
'@lgDown': {
bg: '$grayBase',
},
}}
>
<HStack alignment="center" distribution="start">
<Box
css={{
display: 'flex',
alignItems: 'center',
paddingRight: '10px',
}}
>
<OmnivoreNameLogo href={props.username ? '/home' : '/login'} />
</Box>
</HStack>
{props.toolbarControl && (
<HStack
distribution="end"
alignment="center"
css={{
height: '100%',
width: '100%',
mr: '16px',
display: props.alwaysDisplayToolbar ? 'flex' : 'none',
'@lgDown': {
display: 'flex',
},
}}
>
{props.toolbarControl}
</HStack>
)}
{props.username && (
<HStack
alignment="center"
css={{ display: 'flex', alignItems: 'center' }}
>
<DropdownMenu
username={props.username}
triggerElement={
<AvatarDropdown userInitials={props.userInitials} />
}
actionHandler={props.actionHandler}
/>
</HStack>
)}
</HStack>
</nav>
)
}
function FloatingNavHeader(props: NavHeaderProps): JSX.Element {
return (
<>
<HStack alignment="center" distribution="start">
<Box
css={{
top: '13px',
left: '18px',
position: 'fixed',
display: 'flex',
alignItems: 'center',
zIndex: 5,
}}
>
<OmnivoreNameLogo href={props.username ? '/home' : '/login'} />
</Box>
</HStack>
{props.username && (
<HStack
alignment="center"
css={{
top: '13px',
right: '18px',
position: 'fixed',
display: 'flex',
alignItems: 'center',
}}
>
<DropdownMenu
username={props.username}
triggerElement={
<AvatarDropdown userInitials={props.userInitials} />
}
actionHandler={props.actionHandler}
/>
</HStack>
)}
</>
)
}

View file

@ -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 (
<nav>
<HStack
alignment="center"
distribution="start"
css={{
top: '0',
left: '0',
zIndex: 1,
display: 'flex',
position: 'fixed',
width: '100%',
px: '25px',
height: MOBILE_HEADER_HEIGHT,
bg: '$thBackground3',
borderBottom: '1px solid $thBorderColor',
'@mdDown': {
px: '15px',
},
}}
>
<Box
css={{
display: 'flex',
alignItems: 'center',
paddingRight: '10px',
}}
>
<OmnivoreNameLogo href={props.user ? '/home' : '/login'} />
</Box>
<HStack css={{ ml: 'auto' }}>
<PrimaryDropdown showThemeSection={true} />
</HStack>
</HStack>
</nav>
)
}

View file

@ -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 (
<>
<PageMetaData path='settings' title='Settings' />
<PrimaryHeader
user={viewerData?.me}
isTransparent={false}
userInitials={viewerData?.me?.name.charAt(0) ?? ''}
profileImageURL={viewerData?.me?.profile.pictureUrl}
setShowLogoutConfirmation={setShowLogoutConfirmation}
setShowKeyboardCommandsModal={setShowKeyboardCommandsModal}
/>
<PageMetaData path="settings" title="Settings" />
<SettingsHeader user={viewerData?.me} />
<VStack css={{ width: '100%', height: '100%' }}>
<Box
css={{
top: '48px',
position: 'fixed',
overflowY: 'auto',
height: '100%',
width: '100vw',
height: MOBILE_HEADER_HEIGHT,
bg: '$grayBase',
}}
>
{props.children}
{showLogoutConfirmation ? (
<ConfirmationModal
message={'Are you sure you want to log out?'}
onAccept={logout}
onOpenChange={() => setShowLogoutConfirmation(false)}
/>
) : null}
{showKeyboardCommandsModal ? (
<KeyboardShortcutListModal
onOpenChange={() => setShowKeyboardCommandsModal(false)}
/>
) : null}
</Box>
></Box>
{props.children}
<Box css={{ height: '120px', width: '100%' }} />
</VStack>
{showLogoutConfirmation ? (
<ConfirmationModal
message={'Are you sure you want to log out?'}
onAccept={logout}
onOpenChange={() => setShowLogoutConfirmation(false)}
/>
) : null}
{showKeyboardCommandsModal ? (
<KeyboardShortcutListModal
onOpenChange={() => setShowKeyboardCommandsModal(false)}
/>
) : null}
</>
)
}

View file

@ -68,13 +68,15 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
>([])
const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 })
const [focusedHighlight, setFocusedHighlight] =
useState<Highlight | undefined>(undefined)
const [focusedHighlight, setFocusedHighlight] = useState<
Highlight | undefined
>(undefined)
const [selectionData, setSelectionData] = useSelection(highlightLocations)
const [labelsTarget, setLabelsTarget] =
useState<Highlight | undefined>(undefined)
const [labelsTarget, setLabelsTarget] = useState<Highlight | undefined>(
undefined
)
const canShareNative = useCanShareNative()

View file

@ -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' }}
>
<VStack distribution="start" css={{ height: '100%' }}>
<ModalTitleBar title="Notebook" onOpenChange={props.onOpenChange} />
<HStack
distribution="between"
alignment="center"
css={{ height: '50px', width: '100%' }}
>
<StyledText style="modalHeadline">Notebook</StyledText>
<HStack css={{ ml: 'auto', gap: '10px' }}>
<Dropdown triggerElement={<MenuTrigger />}>
<DropdownOption
onSelect={() => {
exportHighlights()
}}
title="Export"
/>
</Dropdown>
<CloseButton close={() => props.onOpenChange(false)} />
</HStack>
</HStack>
<Box css={{ overflow: 'auto', width: '100%' }}>
{sortedHighlights.map((highlight) => (
<ModalHighlightView
@ -167,6 +199,7 @@ type ModalHighlightViewProps = {
}
function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element {
const [hover, setHover] = useState(false)
const [isEditing, setIsEditing] = useState(false)
const copyHighlight = useCallback(async () => {
@ -174,9 +207,13 @@ function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element {
}, [props.highlight])
return (
<>
<HStack
css={{ width: '100%', py: '20px', cursor: 'pointer' }}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<VStack>
<SpanBox css={{ marginLeft: 'auto' }}>
{/* <SpanBox css={{ marginLeft: 'auto' }}>
<Dropdown
triggerElement={
<DotsThree size={24} color={theme.colors.readerFont.toString()} />
@ -201,7 +238,7 @@ function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element {
title="Delete"
/>
</Dropdown>
</SpanBox>
</SpanBox> */}
<HighlightView
scrollToHighlight={props.scrollToHighlight}
@ -233,6 +270,24 @@ function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element {
)}
<SpanBox css={{ mt: '$2', mb: '$4' }} />
</VStack>
</>
<SpanBox
css={{
marginLeft: 'auto',
width: '20px',
visibility: hover ? 'unset' : 'hidden',
'@media (hover: none)': {
visibility: 'unset',
},
}}
>
<HighlightsMenu
highlight={props.highlight}
setLabelsTarget={props.setSetLabelsTarget}
setShowConfirmDeleteHighlightId={
props.setShowConfirmDeleteHighlightId
}
/>
</SpanBox>
</HStack>
)
}

View file

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

View file

@ -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
</StyledText>
<Dropdown
triggerElement={
<Box
css={{
display: 'flex',
height: '20px',
width: '20px',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '1000px',
'&:hover': {
bg: '#898989',
},
}}
>
<DotsThreeVertical size={20} color="#EBEBEB" weight="bold" />
</Box>
}
>
<Dropdown triggerElement={<MenuTrigger />}>
<DropdownOption
onSelect={() => {
exportHighlights()

View file

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

View file

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

View file

@ -131,7 +131,7 @@ export default function Integrations(): JSX.Element {
top: '5rem',
}}
/>
<Header css={{ textAlign: 'center' }}>Integrations</Header>
<Header css={{ textAlign: 'center', width: '100%' }}>Integrations</Header>
<Subheader>
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%',
},

View file

@ -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)
}}
/>
<Box css={{ height: '120px' }} />
</SettingsLayout>
)
}