mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #1888 from omnivore-app/fix/notebooks-modal
Make notebook modal closer to highlights view, add exports
This commit is contained in:
commit
df04986a92
19 changed files with 335 additions and 481 deletions
27
packages/web/components/elements/MenuTrigger.tsx
Normal file
27
packages/web/components/elements/MenuTrigger.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
42
packages/web/components/elements/images/OmnivoreLogoBase.tsx
Normal file
42
packages/web/components/elements/images/OmnivoreLogoBase.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
52
packages/web/components/patterns/SettingsHeader.tsx
Normal file
52
packages/web/components/patterns/SettingsHeader.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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%',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue