mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #3538 from omnivore-app/feat/web-multiselect-header
Improvements to the web multiselect header
This commit is contained in:
commit
d8194363eb
45 changed files with 343 additions and 698 deletions
|
|
@ -1,10 +1,6 @@
|
|||
import { getLuminance } from 'color2k'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Button } from './Button'
|
||||
import { SpanBox, HStack } from './LayoutPrimitives'
|
||||
import { Circle, X } from 'phosphor-react'
|
||||
import { isDarkTheme } from '../../lib/themeUpdater'
|
||||
import { theme } from '../tokens/stitches.config'
|
||||
|
||||
type LabelChipProps = {
|
||||
text: string
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import {
|
|||
import { Box, HStack } from '../elements/LayoutPrimitives'
|
||||
import { StyledText, StyledAnchor } from '../elements/StyledText'
|
||||
import { TooltipWrapped } from './Tooltip'
|
||||
import Link from 'next/link'
|
||||
|
||||
const TooltipStyle = {
|
||||
backgroundColor: '#F9D354',
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import { ReactNode, useEffect, useMemo, useRef } from 'react'
|
||||
import { styled } from '../tokens/stitches.config'
|
||||
import { Box, HStack, VStack } from './LayoutPrimitives'
|
||||
import { HStack, VStack } from './LayoutPrimitives'
|
||||
import { Button } from './Button'
|
||||
import { Dropdown, DropdownOption } from './DropdownElements'
|
||||
import { CaretDownIcon } from './icons/CaretDownIcon'
|
||||
|
||||
type ShowLinkMode = 'none' | 'link' | 'pdf'
|
||||
|
|
|
|||
|
|
@ -4,8 +4,28 @@ import { IconProps } from './IconProps'
|
|||
import { SpanBox } from '../LayoutPrimitives'
|
||||
|
||||
import React from 'react'
|
||||
import { MultiSelectMode } from '../../templates/homeFeed/LibraryHeader'
|
||||
|
||||
export class HeaderCheckboxIcon extends React.Component<IconProps> {
|
||||
type HeaderCheckboxIconProps = {
|
||||
multiSelectMode: MultiSelectMode
|
||||
}
|
||||
|
||||
export const HeaderCheckboxIcon = (
|
||||
props: HeaderCheckboxIconProps
|
||||
): JSX.Element => {
|
||||
switch (props.multiSelectMode) {
|
||||
case 'search':
|
||||
case 'visible':
|
||||
return <HeaderCheckboxCheckedIcon />
|
||||
case 'none':
|
||||
case 'off':
|
||||
return <HeaderCheckboxUncheckedIcon />
|
||||
case 'some':
|
||||
return <HeaderCheckboxHalfCheckedIcon />
|
||||
}
|
||||
}
|
||||
|
||||
export class HeaderCheckboxUncheckedIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
return (
|
||||
<SpanBox
|
||||
|
|
@ -54,3 +74,89 @@ export class HeaderCheckboxIcon extends React.Component<IconProps> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class HeaderCheckboxCheckedIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
return (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
'--inner-color': 'var(--colors-thHeaderIconInner)',
|
||||
'--ring-color': 'var(--colors-thHeaderIconRing)',
|
||||
'&:hover': {
|
||||
'--ring-fill': '#007AFF10',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="41"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="39"
|
||||
height="39"
|
||||
rx="19.5"
|
||||
style={{
|
||||
fill: 'var(--ring-fill)',
|
||||
stroke: 'var(--ring-color)',
|
||||
}}
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
d="M25.9341 11.6667C27.5674 11.6667 28.9007 12.9476 28.9857 14.5601L28.9899 14.7226V25.2776C28.9899 26.9109 27.7091 28.2442 26.0966 28.3292L25.9341 28.3334H15.3791C14.5967 28.3335 13.8442 28.0334 13.2764 27.4951C12.7087 26.9569 12.369 26.2213 12.3274 25.4401L12.3232 25.2776V14.7226C12.3232 13.0892 13.6041 11.7559 15.2166 11.6709L15.3791 11.6667H25.9341ZM23.7457 17.7442C23.5895 17.588 23.3775 17.5003 23.1566 17.5003C22.9356 17.5003 22.7237 17.588 22.5674 17.7442L19.8232 20.4876L18.7457 19.4109L18.6674 19.3417C18.4999 19.2122 18.2894 19.1513 18.0786 19.1714C17.8679 19.1915 17.6726 19.291 17.5326 19.4498C17.3926 19.6087 17.3183 19.8148 17.3247 20.0264C17.3312 20.2381 17.418 20.4393 17.5674 20.5892L19.2341 22.2559L19.3124 22.3251C19.4727 22.4495 19.673 22.5111 19.8755 22.4983C20.078 22.4856 20.2689 22.3994 20.4124 22.2559L23.7457 18.9226L23.8149 18.8442C23.9393 18.6839 24.0009 18.4837 23.9881 18.2812C23.9754 18.0787 23.8892 17.8877 23.7457 17.7442Z"
|
||||
fill="#007AFF"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</SpanBox>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class HeaderCheckboxHalfCheckedIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
return (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
'--inner-color': '#007AFF',
|
||||
'--ring-color': 'var(--colors-thHeaderIconRing)',
|
||||
'&:hover': {
|
||||
'--ring-fill': '#007AFF10',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="41"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="39"
|
||||
height="39"
|
||||
rx="19.5"
|
||||
style={{
|
||||
fill: 'var(--ring-fill)',
|
||||
stroke: 'var(--ring-color)',
|
||||
}}
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
d="M25.8332 11.7496C26.4962 11.7496 27.1321 12.013 27.6009 12.4819C28.0698 12.9507 28.3332 13.5866 28.3332 14.2496V25.9163C28.3332 26.5793 28.0698 27.2152 27.6009 27.6841C27.1321 28.1529 26.4962 28.4163 25.8332 28.4163H14.1665C13.5035 28.4163 12.8676 28.1529 12.3987 27.6841C11.9299 27.2152 11.6665 26.5793 11.6665 25.9163V14.2496C11.6665 13.5866 11.9299 12.9507 12.3987 12.4819C12.8676 12.013 13.5035 11.7496 14.1665 11.7496H25.8332ZM22.4998 19.2496H17.4998L17.4023 19.2555C17.1914 19.2806 16.998 19.3852 16.8617 19.5481C16.7254 19.711 16.6564 19.9198 16.6689 20.1318C16.6813 20.3438 16.7743 20.5431 16.9287 20.6889C17.0831 20.8347 17.2874 20.9161 17.4998 20.9163H22.4998L22.5973 20.9105C22.8082 20.8854 23.0016 20.7807 23.1379 20.6178C23.2743 20.4549 23.3433 20.2462 23.3308 20.0341C23.3184 19.8221 23.2254 19.6228 23.071 19.477C22.9165 19.3312 22.7122 19.2499 22.4998 19.2496Z"
|
||||
fill="#007AFF"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</SpanBox>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,22 +14,21 @@ export function OmnivoreFestiveLogo(
|
|||
const href = props.href || '/home'
|
||||
|
||||
return (
|
||||
(<Link
|
||||
<Link
|
||||
passHref
|
||||
href={href}
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/static/images/omnivore-logo-santa.png"
|
||||
width="27"
|
||||
height="27"
|
||||
alt=""
|
||||
/>
|
||||
|
||||
</Link>)
|
||||
);
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
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'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,8 @@
|
|||
import { isAndroid } from '../../lib/deviceType'
|
||||
|
||||
import { styled, theme } from '../tokens/stitches.config'
|
||||
|
||||
import { StyledText } from '../elements/StyledText'
|
||||
import { Button } from '../elements/Button'
|
||||
import { HStack, Box } from '../elements/LayoutPrimitives'
|
||||
import { PenWithColorIcon } from '../elements/images/PenWithColorIcon'
|
||||
import {
|
||||
Note,
|
||||
Tag,
|
||||
Trash,
|
||||
Copy,
|
||||
Circle,
|
||||
CopySimple,
|
||||
CheckCircle,
|
||||
} from 'phosphor-react'
|
||||
import { TrashIcon } from '../elements/icons/TrashIcon'
|
||||
import { Circle, CheckCircle } from 'phosphor-react'
|
||||
import { LabelIcon } from '../elements/icons/LabelIcon'
|
||||
import { NotebookIcon } from '../elements/icons/NotebookIcon'
|
||||
import { highlightColor, highlightColors } from '../../lib/themeUpdater'
|
||||
|
|
|
|||
|
|
@ -1,20 +1,8 @@
|
|||
import { useState } from 'react'
|
||||
import { Box, SpanBox } from '../elements/LayoutPrimitives'
|
||||
import { LibraryItemNode } from '../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { Box } from '../elements/LayoutPrimitives'
|
||||
import { Button } from '../elements/Button'
|
||||
import { theme } from '../tokens/stitches.config'
|
||||
import {
|
||||
ArchiveBox,
|
||||
Book,
|
||||
BookOpen,
|
||||
Copy,
|
||||
DotsThree,
|
||||
Notebook,
|
||||
Tag,
|
||||
Trash,
|
||||
Tray,
|
||||
} from 'phosphor-react'
|
||||
//import { CardMenu } from '../CardMenu'
|
||||
import { BookOpen, Copy } from 'phosphor-react'
|
||||
import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { Highlight } from '../../lib/networking/fragments/highlightFragment'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
|
|
|
|||
|
|
@ -9,15 +9,11 @@ import {
|
|||
SpanBox,
|
||||
HStack,
|
||||
} from '../elements/LayoutPrimitives'
|
||||
import { styled, theme } from '../tokens/stitches.config'
|
||||
import { styled } from '../tokens/stitches.config'
|
||||
import { HighlightViewNote } from './HighlightNotes'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import {
|
||||
highlightColor,
|
||||
highlightColorVar,
|
||||
isDarkTheme,
|
||||
} from '../../lib/themeUpdater'
|
||||
import { highlightColorVar } from '../../lib/themeUpdater'
|
||||
import { ReadableItem } from '../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import {
|
||||
|
|
@ -54,7 +50,6 @@ const StyledQuote = styled(Blockquote, {
|
|||
})
|
||||
|
||||
export function HighlightView(props: HighlightViewProps): JSX.Element {
|
||||
const isDark = isDarkTheme()
|
||||
const [noteMode, setNoteMode] = useState<'preview' | 'edit'>('preview')
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { ChangeEvent, useMemo } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { LibraryItemNode } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { HStack, SpanBox } from '../../elements/LayoutPrimitives'
|
||||
import { RecommendedFlairIcon } from '../../elements/icons/RecommendedFlairIcon'
|
||||
import { PinnedFlairIcon } from '../../elements/icons/PinnedFlairIcon'
|
||||
import { FavoriteFlairIcon } from '../../elements/icons/FavoriteFlairIcon'
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => {
|
|||
const handleCheckChanged = useCallback(() => {
|
||||
const newValue = !isChecked
|
||||
setIsChecked(item.id, newValue)
|
||||
}, [setIsChecked, isChecked, props])
|
||||
}, [item, setIsChecked, isChecked, props])
|
||||
|
||||
return (
|
||||
<VStack css={{ p: '0px', m: '0px', width: '100%' }}>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,7 @@ import { LibraryItemNode } from '../../../lib/networking/queries/useGetLibraryIt
|
|||
import { LinkedItemCardAction } from './CardTypes'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import {
|
||||
ArchiveBox,
|
||||
DotsThree,
|
||||
Notebook,
|
||||
Tag,
|
||||
Trash,
|
||||
Tray,
|
||||
} from 'phosphor-react'
|
||||
import { DotsThree } from 'phosphor-react'
|
||||
import { CardMenu } from '../CardMenu'
|
||||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { ArchiveIcon } from '../../elements/icons/ArchiveIcon'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { Box, HStack } from '../elements/LayoutPrimitives'
|
||||
import { OmnivoreNameLogo } from '../elements/images/OmnivoreNameLogo'
|
||||
import { HStack } from '../elements/LayoutPrimitives'
|
||||
import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { PrimaryDropdown } from '../templates/PrimaryDropdown'
|
||||
import { DEFAULT_HEADER_HEIGHT } from '../templates/homeFeed/HeaderSpacer'
|
||||
import { LogoBox } from '../elements/LogoBox'
|
||||
|
||||
|
|
|
|||
|
|
@ -131,10 +131,11 @@ export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element {
|
|||
|
||||
return (
|
||||
<Dropdown
|
||||
side="top"
|
||||
triggerElement={
|
||||
props.children ?? <TriggerButton name={viewerData?.me?.name ?? 'O'} />
|
||||
}
|
||||
css={{ width: '240px' }}
|
||||
css={{ width: '240px', ml: '15px' }}
|
||||
>
|
||||
<HStack
|
||||
alignment="center"
|
||||
|
|
@ -278,7 +279,7 @@ function ThemeSection(props: PrimaryDropdownProps): JSX.Element {
|
|||
updateTheme(newTheme)
|
||||
setDisplayTheme(newTheme)
|
||||
},
|
||||
[displayTheme, setDisplayTheme]
|
||||
[setDisplayTheme]
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ import { useKeyboardShortcuts } from '../../lib/keyboardShortcuts/useKeyboardSho
|
|||
import { useRouter } from 'next/router'
|
||||
import { ConfirmationModal } from '../patterns/ConfirmationModal'
|
||||
import { KeyboardShortcutListModal } from './KeyboardShortcutListModal'
|
||||
import { logoutMutation } from '../../lib/networking/mutations/logoutMutation'
|
||||
import { deinitAnalytics, setupAnalytics } from '../../lib/analytics'
|
||||
import { setupAnalytics } from '../../lib/analytics'
|
||||
import { primaryCommands } from '../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { logout } from '../../lib/logout'
|
||||
import { useApplyLocalTheme } from '../../lib/hooks/useApplyLocalTheme'
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,8 @@ export function AddBulkLabelsModal(
|
|||
const [tabCount, setTabCount] = useState(-1)
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [tabStartValue, setTabStartValue] = useState('')
|
||||
const [errorMessage, setErrorMessage] = useState<string | undefined>(
|
||||
undefined
|
||||
)
|
||||
const [errorMessage, setErrorMessage] =
|
||||
useState<string | undefined>(undefined)
|
||||
const errorTimeoutRef = useRef<NodeJS.Timeout | undefined>()
|
||||
const [highlightLastLabel, setHighlightLastLabel] = useState(false)
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useState } from 'react'
|
|||
import { Highlight } from '../../../lib/networking/fragments/highlightFragment'
|
||||
import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { HighlightView } from '../../patterns/HighlightView'
|
||||
|
||||
type HighlightViewItemProps = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Box, HStack, VStack, SpanBox } from '../../elements/LayoutPrimitives'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import type { Highlight } from '../../../lib/networking/fragments/highlightFragment'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
|
@ -17,7 +16,6 @@ import { TrashIcon } from '../../elements/icons/TrashIcon'
|
|||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { SetHighlightLabelsModalPresenter } from './SetLabelsModalPresenter'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { ArticleNotes } from '../../patterns/ArticleNotes'
|
||||
import { useGetArticleQuery } from '../../../lib/networking/queries/useGetArticleQuery'
|
||||
import { formattedShortTime } from '../../../lib/dateFormatting'
|
||||
|
|
@ -59,9 +57,8 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element {
|
|||
const [noteText, setNoteText] = useState<string>('')
|
||||
const [showConfirmDeleteHighlightId, setShowConfirmDeleteHighlightId] =
|
||||
useState<undefined | string>(undefined)
|
||||
const [labelsTarget, setLabelsTarget] = useState<Highlight | undefined>(
|
||||
undefined
|
||||
)
|
||||
const [labelsTarget, setLabelsTarget] =
|
||||
useState<Highlight | undefined>(undefined)
|
||||
const noteState = useRef<NoteState>({
|
||||
isCreating: false,
|
||||
note: undefined,
|
||||
|
|
@ -87,34 +84,37 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element {
|
|||
}
|
||||
})()
|
||||
},
|
||||
[]
|
||||
[props]
|
||||
)
|
||||
|
||||
const createNote = useCallback((text: string) => {
|
||||
noteState.current.isCreating = true
|
||||
noteState.current.createStarted = new Date()
|
||||
;(async () => {
|
||||
try {
|
||||
const success = await createHighlightMutation({
|
||||
id: newNoteId,
|
||||
shortId: nanoid(8),
|
||||
type: 'NOTE',
|
||||
articleId: props.item.id,
|
||||
annotation: text,
|
||||
})
|
||||
if (success) {
|
||||
noteState.current.note = success
|
||||
const createNote = useCallback(
|
||||
(text: string) => {
|
||||
noteState.current.isCreating = true
|
||||
noteState.current.createStarted = new Date()
|
||||
;(async () => {
|
||||
try {
|
||||
const success = await createHighlightMutation({
|
||||
id: newNoteId,
|
||||
shortId: nanoid(8),
|
||||
type: 'NOTE',
|
||||
articleId: props.item.id,
|
||||
annotation: text,
|
||||
})
|
||||
if (success) {
|
||||
noteState.current.note = success
|
||||
noteState.current.isCreating = false
|
||||
} else {
|
||||
setErrorSaving('Error creating note')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('error creating note: ', error)
|
||||
noteState.current.isCreating = false
|
||||
} else {
|
||||
setErrorSaving('Error creating note')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('error creating note: ', error)
|
||||
noteState.current.isCreating = false
|
||||
setErrorSaving('Error creating note')
|
||||
}
|
||||
})()
|
||||
}, [])
|
||||
})()
|
||||
},
|
||||
[props, newNoteId]
|
||||
)
|
||||
|
||||
const highlights = useMemo(() => {
|
||||
const result = articleData?.article.article.highlights
|
||||
|
|
@ -133,7 +133,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element {
|
|||
if (highlights && props.onAnnotationsChanged) {
|
||||
props.onAnnotationsChanged(highlights)
|
||||
}
|
||||
}, [highlights])
|
||||
}, [props, highlights])
|
||||
|
||||
const sortedHighlights = useMemo(() => {
|
||||
const sorted = (a: number, b: number) => {
|
||||
|
|
@ -188,7 +188,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element {
|
|||
}
|
||||
createNote(text)
|
||||
},
|
||||
[noteText, noteState, createNote, updateNote, highlights]
|
||||
[noteState, createNote, updateNote]
|
||||
)
|
||||
|
||||
const deleteDocumentNote = useCallback(() => {
|
||||
|
|
@ -204,7 +204,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element {
|
|||
noteState.current.note = undefined
|
||||
})()
|
||||
setNoteText('')
|
||||
}, [noteState, highlights])
|
||||
}, [props, noteState, highlights])
|
||||
|
||||
const [errorSaving, setErrorSaving] = useState<string | undefined>(undefined)
|
||||
const [lastChanged, setLastChanged] = useState<Date | undefined>(undefined)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Sidebar } from 'phosphor-react'
|
|||
import { theme } from '../../tokens/stitches.config'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { ExportIcon } from '../../elements/icons/ExportIcon'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { useGetArticleQuery } from '../../../lib/networking/queries/useGetArticleQuery'
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { StyledText } from '../../elements/StyledText'
|
|||
import { theme } from '../../tokens/stitches.config'
|
||||
import type { Highlight } from '../../../lib/networking/fragments/highlightFragment'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { ArrowsIn, ArrowsOut, X } from 'phosphor-react'
|
||||
import { X } from 'phosphor-react'
|
||||
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
|
||||
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
|
||||
import { diff_match_patch } from 'diff-match-patch'
|
||||
|
|
@ -37,17 +37,15 @@ export const getHighlightLocation = (patch: string): number | undefined => {
|
|||
|
||||
export function NotebookModal(props: NotebookModalProps): JSX.Element {
|
||||
const [showConfirmDeleteNote, setShowConfirmDeleteNote] = useState(false)
|
||||
const [allAnnotations, setAllAnnotations] = useState<Highlight[] | undefined>(
|
||||
undefined
|
||||
)
|
||||
const [allAnnotations, setAllAnnotations] =
|
||||
useState<Highlight[] | undefined>(undefined)
|
||||
|
||||
const [deletedHighlights, setDeletedAnnotations] = useState<
|
||||
Highlight[] | undefined
|
||||
>(undefined)
|
||||
const [deletedHighlights, setDeletedAnnotations] =
|
||||
useState<Highlight[] | undefined>(undefined)
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
props.onClose(allAnnotations ?? [], deletedHighlights ?? [])
|
||||
}, [props, allAnnotations])
|
||||
}, [props, allAnnotations, deletedHighlights])
|
||||
|
||||
const handleAnnotationsChange = useCallback((allAnnotations: Highlight[]) => {
|
||||
setAllAnnotations(allAnnotations)
|
||||
|
|
@ -161,11 +159,6 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element {
|
|||
)
|
||||
}
|
||||
|
||||
type SizeToggleProps = {
|
||||
mode: 'normal' | 'maximized'
|
||||
setMode: (mode: 'normal' | 'maximized') => void
|
||||
}
|
||||
|
||||
function CloseButton(props: { close: () => void }): JSX.Element {
|
||||
return (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -13,15 +13,10 @@ import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
|
|||
import { showSuccessToast } from '../../../lib/toastHelpers'
|
||||
import { ReaderSettings } from '../../../lib/hooks/useReaderSettings'
|
||||
import { useCallback, useState } from 'react'
|
||||
import {
|
||||
getCurrentLocalTheme,
|
||||
currentThemeName,
|
||||
updateTheme,
|
||||
} from '../../../lib/themeUpdater'
|
||||
import { updateTheme } from '../../../lib/themeUpdater'
|
||||
import { LineHeightIncreaseIcon } from '../../elements/images/LineHeightIncreaseIconProps'
|
||||
import { LineHeightDecreaseIcon } from '../../elements/images/LineHeightDecreaseIcon'
|
||||
import * as Switch from '@radix-ui/react-switch'
|
||||
import { Checkbox } from '@radix-ui/react-checkbox'
|
||||
import { useCurrentTheme } from '../../../lib/hooks/useCurrentTheme'
|
||||
import { useDarkModeListener } from '../../../lib/hooks/useDarkModeListener'
|
||||
|
||||
|
|
@ -571,7 +566,7 @@ function LayoutControls(props: LayoutControlsProps): JSX.Element {
|
|||
}
|
||||
|
||||
function ThemeSelector(): JSX.Element {
|
||||
const isDarkMode = useDarkModeListener()
|
||||
useDarkModeListener()
|
||||
const { currentTheme, setCurrentTheme, resetSystemTheme } = useCurrentTheme()
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ function Footer(props: FooterProps): JSX.Element {
|
|||
alignment="center"
|
||||
distribution="start"
|
||||
css={{ gap: '8px', fontSize: '12px', pointer: 'cursor' }}
|
||||
onClick={async (event) => {
|
||||
onClick={async () => {
|
||||
switch (textMatch) {
|
||||
case 'available':
|
||||
await props.selectEnteredLabel()
|
||||
|
|
@ -414,7 +414,6 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element {
|
|||
filteredLabels,
|
||||
focusedIndex,
|
||||
createLabelFromFilterText,
|
||||
router,
|
||||
toggleLabel,
|
||||
]
|
||||
)
|
||||
|
|
@ -423,7 +422,7 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element {
|
|||
const _filterText = inputValue
|
||||
setInputValue('')
|
||||
return createLabelFromFilterText(_filterText)
|
||||
}, [inputValue])
|
||||
}, [inputValue, setInputValue, createLabelFromFilterText])
|
||||
|
||||
const selectEnteredLabel = useCallback(() => {
|
||||
const label = labels.find(
|
||||
|
|
@ -433,7 +432,7 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element {
|
|||
return Promise.resolve()
|
||||
}
|
||||
return toggleLabel(label)
|
||||
}, [labels, inputValue])
|
||||
}, [labels, inputValue, toggleLabel])
|
||||
|
||||
return (
|
||||
<VStack
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { Tray } from 'phosphor-react'
|
||||
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { HStack } from '../../elements/LayoutPrimitives'
|
||||
import { TooltipWrapped } from '../../elements/Tooltip'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { ReaderDropdownMenu } from '../../patterns/ReaderDropdownMenu'
|
||||
import { ArchiveIcon } from '../../elements/icons/ArchiveIcon'
|
||||
|
|
|
|||
|
|
@ -1,22 +1,17 @@
|
|||
import { useCallback, useRef, useState } from 'react'
|
||||
import * as Progress from '@radix-ui/react-progress'
|
||||
import { File, Info } from 'phosphor-react'
|
||||
import toast from 'react-hot-toast'
|
||||
import { locale, timeZone } from '../../../lib/dateFormatting'
|
||||
import { saveUrlMutation } from '../../../lib/networking/mutations/saveUrlMutation'
|
||||
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { FormInput } from '../../elements/FormElements'
|
||||
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import {
|
||||
ModalButtonBar,
|
||||
ModalContent,
|
||||
ModalOverlay,
|
||||
ModalRoot,
|
||||
ModalTitleBar,
|
||||
} from '../../elements/ModalPrimitives'
|
||||
import { CloseButton } from '../../elements/CloseButton'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { styled } from '@stitches/react'
|
||||
import Dropzone, {
|
||||
Accept,
|
||||
|
|
@ -76,8 +71,8 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element {
|
|||
<Box css={{ width: '100%' }}>
|
||||
{selectedTab == 'link' && <AddLinkTab {...props} />}
|
||||
{selectedTab == 'feed' && <AddFeedTab {...props} />}
|
||||
{selectedTab == 'opml' && <UploadOPMLTab {...props} />}
|
||||
{selectedTab == 'pdf' && <UploadPDFTab {...props} />}
|
||||
{selectedTab == 'opml' && <UploadOPMLTab />}
|
||||
{selectedTab == 'pdf' && <UploadPDFTab />}
|
||||
{selectedTab == 'import' && <UploadImportTab {...props} />}
|
||||
</Box>
|
||||
</VStack>
|
||||
|
|
@ -95,7 +90,7 @@ const AddLinkTab = (props: AddLinkModalProps): JSX.Element => {
|
|||
await props.handleLinkSubmission(link, timeZone, locale)
|
||||
props.onOpenChange(false)
|
||||
},
|
||||
[errorMessage, setErrorMessage]
|
||||
[props, errorMessage, setErrorMessage]
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
@ -144,7 +139,7 @@ const AddFeedTab = (props: AddLinkModalProps): JSX.Element => {
|
|||
showSuccessToast('New feed has been added.')
|
||||
props.onOpenChange(false)
|
||||
},
|
||||
[errorMessage, setErrorMessage]
|
||||
[props, errorMessage, setErrorMessage]
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
@ -167,20 +162,17 @@ type AddFromURLProps = {
|
|||
const AddFromURL = (props: AddFromURLProps): JSX.Element => {
|
||||
const [url, setURL] = useState('')
|
||||
|
||||
const validateURL = useCallback(
|
||||
(link: string) => {
|
||||
try {
|
||||
const url = new URL(link)
|
||||
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
||||
return false
|
||||
}
|
||||
} catch (e) {
|
||||
const validateURL = useCallback((link: string) => {
|
||||
try {
|
||||
const url = new URL(link)
|
||||
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
[url]
|
||||
)
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<VStack css={{ width: '100%', height: '180px' }}>
|
||||
|
|
@ -258,7 +250,7 @@ const AddFromURL = (props: AddFromURLProps): JSX.Element => {
|
|||
)
|
||||
}
|
||||
|
||||
const UploadOPMLTab = (props: AddLinkModalProps): JSX.Element => {
|
||||
const UploadOPMLTab = (): JSX.Element => {
|
||||
return (
|
||||
<VStack
|
||||
alignment="start"
|
||||
|
|
@ -278,7 +270,7 @@ const UploadOPMLTab = (props: AddLinkModalProps): JSX.Element => {
|
|||
)
|
||||
}
|
||||
|
||||
const UploadPDFTab = (props: AddLinkModalProps): JSX.Element => {
|
||||
const UploadPDFTab = (): JSX.Element => {
|
||||
return (
|
||||
<VStack
|
||||
alignment="start"
|
||||
|
|
|
|||
|
|
@ -1,29 +1,8 @@
|
|||
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
|
||||
import { PinnedSearch } from '../../../pages/settings/pinned-searches'
|
||||
import { Box } from '../../elements/LayoutPrimitives'
|
||||
|
||||
export const DEFAULT_HEADER_HEIGHT = '85px'
|
||||
|
||||
// export const useGetHeaderHeight = () => {
|
||||
// const [hidePinnedSearches] = usePersistedState({
|
||||
// key: '--library-hide-pinned-searches',
|
||||
// initialValue: false,
|
||||
// isSessionStorage: false,
|
||||
// })
|
||||
// const [pinnedSearches] = usePersistedState<PinnedSearch[] | null>({
|
||||
// key: `--library-pinned-searches`,
|
||||
// initialValue: [],
|
||||
// isSessionStorage: false,
|
||||
// })
|
||||
|
||||
// if (hidePinnedSearches || !pinnedSearches?.length) {
|
||||
// return '90px'
|
||||
// }
|
||||
// return '90px'
|
||||
// }
|
||||
|
||||
export function HeaderSpacer(): JSX.Element {
|
||||
// const headerHeight = useGetHeaderHeight()
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
|
|
|
|||
|
|
@ -37,28 +37,17 @@ import { EditLibraryItemModal } from './EditItemModals'
|
|||
import { EmptyLibrary } from './EmptyLibrary'
|
||||
import { HighlightItemsLayout } from './HighlightsLayout'
|
||||
import { LibraryFilterMenu } from '../navMenu/LibraryMenu'
|
||||
import {
|
||||
LibraryHeader,
|
||||
MultiSelectMode,
|
||||
headerControlWidths,
|
||||
} from './LibraryHeader'
|
||||
import { LibraryHeader, MultiSelectMode } from './LibraryHeader'
|
||||
import { UploadModal } from '../UploadModal'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import {
|
||||
showErrorToast,
|
||||
showSuccessToast,
|
||||
showSuccessToastWithUndo,
|
||||
} from '../../../lib/toastHelpers'
|
||||
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
|
||||
import { SetPageLabelsModalPresenter } from '../article/SetLabelsModalPresenter'
|
||||
import { NotebookPresenter } from '../article/NotebookPresenter'
|
||||
import { saveUrlMutation } from '../../../lib/networking/mutations/saveUrlMutation'
|
||||
import { articleQuery } from '../../../lib/networking/queries/useGetArticleQuery'
|
||||
import { searchQuery } from '../../../lib/networking/queries/search'
|
||||
import { MoreOptionsIcon } from '../../elements/images/MoreOptionsIcon'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { PinnedSearch } from '../../../pages/settings/pinned-searches'
|
||||
import { PinnedButtons } from './PinnedButtons'
|
||||
import { PinnedSearch } from '../../../pages/settings/pinned-searches'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
export type LibraryMode = 'reads' | 'highlights'
|
||||
|
|
|
|||
|
|
@ -1,24 +1,15 @@
|
|||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { FormInput } from '../../elements/FormElements'
|
||||
import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
|
||||
import { locale, timeZone } from '../../../lib/dateFormatting'
|
||||
import { Button, IconButton } from '../../elements/Button'
|
||||
import {
|
||||
FunnelSimple,
|
||||
MagnifyingGlass,
|
||||
Prohibit,
|
||||
Plus,
|
||||
X,
|
||||
} from 'phosphor-react'
|
||||
import { FunnelSimple, MagnifyingGlass, Plus, X } from 'phosphor-react'
|
||||
import { LayoutType } from './HomeFeedContainer'
|
||||
import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo'
|
||||
import { DEFAULT_HEADER_HEIGHT, HeaderSpacer } from './HeaderSpacer'
|
||||
import { LIBRARY_LEFT_MENU_WIDTH } from '../navMenu/LibraryMenu'
|
||||
import { CardCheckbox } from '../../patterns/LibraryCards/LibraryCardStyles'
|
||||
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { ConfirmationModal } from '../../patterns/ConfirmationModal'
|
||||
import { AddBulkLabelsModal } from '../article/AddBulkLabelsModal'
|
||||
|
|
@ -26,12 +17,6 @@ import { Label } from '../../../lib/networking/fragments/labelFragment'
|
|||
import { ArchiveIcon } from '../../elements/icons/ArchiveIcon'
|
||||
import { TrashIcon } from '../../elements/icons/TrashIcon'
|
||||
import { LabelIcon } from '../../elements/icons/LabelIcon'
|
||||
import { ListViewIcon } from '../../elements/icons/ListViewIcon'
|
||||
import { GridViewIcon } from '../../elements/icons/GridViewIcon'
|
||||
import { CaretDownIcon } from '../../elements/icons/CaretDownIcon'
|
||||
import { PinnedButtons } from './PinnedButtons'
|
||||
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
|
||||
import { PinnedSearch } from '../../../pages/settings/pinned-searches'
|
||||
import { HeaderCheckboxIcon } from '../../elements/icons/HeaderCheckboxIcon'
|
||||
import { HeaderSearchIcon } from '../../elements/icons/HeaderSearchIcon'
|
||||
import { HeaderToggleGridIcon } from '../../elements/icons/HeaderToggleGridIcon'
|
||||
|
|
@ -134,9 +119,10 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element {
|
|||
}}
|
||||
>
|
||||
{props.multiSelectMode !== 'off' ? (
|
||||
<HStack alignment="center" css={{ width: '100% ' }}>
|
||||
<>
|
||||
<CheckBoxButton {...props} />
|
||||
<MultiSelectControls {...props} />
|
||||
</HStack>
|
||||
</>
|
||||
) : (
|
||||
<HeaderControls {...props} />
|
||||
)}
|
||||
|
|
@ -144,6 +130,31 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element {
|
|||
)
|
||||
}
|
||||
|
||||
const CheckBoxButton = (props: LibraryHeaderProps): JSX.Element => {
|
||||
return (
|
||||
<Button
|
||||
title="Select multiple"
|
||||
style="plainIcon"
|
||||
css={{ display: 'flex', '&:hover': { opacity: '1.0' } }}
|
||||
onClick={(e) => {
|
||||
switch (props.multiSelectMode) {
|
||||
case 'off':
|
||||
case 'none':
|
||||
case 'some':
|
||||
props.setMultiSelectMode('visible')
|
||||
break
|
||||
default:
|
||||
props.setMultiSelectMode('off')
|
||||
break
|
||||
}
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<HeaderCheckboxIcon multiSelectMode={props.multiSelectMode} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
const HeaderControls = (props: LibraryHeaderProps): JSX.Element => {
|
||||
const [showSearchBar, setShowSearchBar] = useState(false)
|
||||
return (
|
||||
|
|
@ -156,17 +167,7 @@ const HeaderControls = (props: LibraryHeaderProps): JSX.Element => {
|
|||
>
|
||||
<MenuHeaderButton {...props} />
|
||||
</SpanBox>
|
||||
<Button
|
||||
title="Select multiple"
|
||||
style="plainIcon"
|
||||
css={{ display: 'flex', '&:hover': { opacity: '1.0' } }}
|
||||
onClick={(e) => {
|
||||
props.setMultiSelectMode('visible')
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<HeaderCheckboxIcon />
|
||||
</Button>
|
||||
<CheckBoxButton {...props} />
|
||||
|
||||
{showSearchBar ? (
|
||||
<SearchBox {...props} setShowSearchBar={setShowSearchBar} />
|
||||
|
|
@ -441,262 +442,125 @@ type ControlButtonBoxProps = {
|
|||
function MultiSelectControls(props: ControlButtonBoxProps): JSX.Element {
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const [showLabelsModal, setShowLabelsModal] = useState(false)
|
||||
|
||||
return (
|
||||
<HStack alignment="center" distribution="center" css={{ gap: '20px' }}>
|
||||
<Button
|
||||
style="outline"
|
||||
onClick={(e) => {
|
||||
props.performMultiSelectAction(BulkAction.ARCHIVE)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<ArchiveIcon
|
||||
size={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
<SpanBox css={{ '@lgDown': { display: 'none' } }}>Archive</SpanBox>
|
||||
</Button>
|
||||
<Button
|
||||
style="outline"
|
||||
onClick={(e) => {
|
||||
setShowLabelsModal(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<LabelIcon size={20} color={theme.colors.thTextContrast2.toString()} />
|
||||
<SpanBox css={{ '@lgDown': { display: 'none' } }}>Add Labels</SpanBox>
|
||||
</Button>
|
||||
<Button
|
||||
style="outline"
|
||||
onClick={(e) => {
|
||||
setShowConfirmDelete(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<TrashIcon size={20} color={theme.colors.thTextContrast2.toString()} />
|
||||
<SpanBox css={{ '@lgDown': { display: 'none' } }}>Delete</SpanBox>
|
||||
</Button>
|
||||
<Button
|
||||
style="cancel"
|
||||
onClick={(e) => {
|
||||
props.setMultiSelectMode('off')
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<Prohibit
|
||||
width={20}
|
||||
height={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
<SpanBox css={{ '@lgDown': { display: 'none' } }}>Cancel</SpanBox>
|
||||
</Button>
|
||||
{showConfirmDelete && (
|
||||
<ConfirmationModal
|
||||
message={`You are about to delete ${props.numItemsSelected} items. All associated notes and highlights will be deleted.`}
|
||||
acceptButtonLabel={'Delete'}
|
||||
onAccept={() => {
|
||||
props.performMultiSelectAction(BulkAction.DELETE)
|
||||
}}
|
||||
onOpenChange={(open: boolean) => {
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showLabelsModal && (
|
||||
<AddBulkLabelsModal
|
||||
bulkSetLabels={(labels: Label[]) => {
|
||||
const labelIds = labels.map((l) => l.id)
|
||||
props.performMultiSelectAction(BulkAction.ADD_LABELS, labelIds)
|
||||
}}
|
||||
onOpenChange={(open: boolean) => {
|
||||
setShowLabelsModal(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
||||
type SearchControlButtonBoxProps = ControlButtonBoxProps
|
||||
|
||||
function SearchControlButtonBox(
|
||||
props: SearchControlButtonBoxProps
|
||||
): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
style="plainIcon"
|
||||
css={{ display: 'flex', marginLeft: 'auto' }}
|
||||
onClick={(e) => {
|
||||
props.updateLayout(
|
||||
props.layout == 'GRID_LAYOUT' ? 'LIST_LAYOUT' : 'GRID_LAYOUT'
|
||||
)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
{props.layout == 'GRID_LAYOUT' ? (
|
||||
<ListViewIcon size={30} color={'#898989'} />
|
||||
) : (
|
||||
<GridViewIcon size={30} color={'#898989'} />
|
||||
)}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const MuliSelectControl = (props: ControlButtonBoxProps): JSX.Element => {
|
||||
const [isChecked, setIsChecked] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (props.multiSelectMode === 'off' || props.multiSelectMode === 'none') {
|
||||
setIsChecked(false)
|
||||
}
|
||||
}, [props.multiSelectMode])
|
||||
const compact = false
|
||||
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
display: 'flex',
|
||||
padding: '10px',
|
||||
height: '38px',
|
||||
width: '100%',
|
||||
maxWidth: '521px',
|
||||
bg: '$thLibrarySearchbox',
|
||||
borderRadius: '6px',
|
||||
|
||||
boxShadow: '0px 1px 2px 0px rgba(0, 0, 0, 0.05);',
|
||||
'@mdDown': {
|
||||
mx: '5px',
|
||||
},
|
||||
borderRadius: '100px',
|
||||
border: '2px solid transparent',
|
||||
boxShadow:
|
||||
'0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);',
|
||||
}}
|
||||
>
|
||||
<SpanBox
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="end"
|
||||
css={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
gap: '5px',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
gap: '15px',
|
||||
pl: compact ? '10px' : '15px',
|
||||
pr: compact ? '5px' : '10px',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<CardCheckbox
|
||||
isChecked={isChecked}
|
||||
handleChanged={() => {
|
||||
const newValue = !isChecked
|
||||
props.setMultiSelectMode(newValue ? 'visible' : 'off')
|
||||
setIsChecked(newValue)
|
||||
<SpanBox
|
||||
css={{
|
||||
fontSize: '14px',
|
||||
fontFamily: '$display',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
/>
|
||||
<SpanBox css={{ display: 'flex', pb: '2px' }}>
|
||||
<Dropdown
|
||||
triggerElement={
|
||||
<CaretDownIcon
|
||||
size={9}
|
||||
color={theme.colors.graySolid.toString()}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DropdownOption
|
||||
onSelect={() => {
|
||||
setIsChecked(true)
|
||||
props.setMultiSelectMode('visible')
|
||||
}}
|
||||
title="All"
|
||||
/>
|
||||
{/* <DropdownOption
|
||||
onSelect={() => {
|
||||
setIsChecked(true)
|
||||
props.setMultiSelectMode('search')
|
||||
}}
|
||||
title="All matching search"
|
||||
/> */}
|
||||
</Dropdown>
|
||||
>
|
||||
{props.numItemsSelected} items selected
|
||||
</SpanBox>
|
||||
</SpanBox>
|
||||
<Button
|
||||
title="Archive"
|
||||
css={{ display: 'flex' }}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
props.performMultiSelectAction(BulkAction.ARCHIVE)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<ArchiveIcon
|
||||
size={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
title="Add labels"
|
||||
css={{ display: 'flex' }}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
setShowLabelsModal(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<LabelIcon
|
||||
size={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
title="Delete"
|
||||
css={{ display: 'flex' }}
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
setShowConfirmDelete(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<TrashIcon
|
||||
size={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
</Button>
|
||||
{showConfirmDelete && (
|
||||
<ConfirmationModal
|
||||
message={`You are about to delete ${props.numItemsSelected} items. All associated notes and highlights will be deleted.`}
|
||||
acceptButtonLabel={'Delete'}
|
||||
onAccept={() => {
|
||||
props.performMultiSelectAction(BulkAction.DELETE)
|
||||
}}
|
||||
onOpenChange={(open: boolean) => {
|
||||
setShowConfirmDelete(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showLabelsModal && (
|
||||
<AddBulkLabelsModal
|
||||
bulkSetLabels={(labels: Label[]) => {
|
||||
const labelIds = labels.map((l) => l.id)
|
||||
props.performMultiSelectAction(BulkAction.ADD_LABELS, labelIds)
|
||||
}}
|
||||
onOpenChange={(open: boolean) => {
|
||||
setShowLabelsModal(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
title="Cancel"
|
||||
css={{ display: 'flex', mr: '10px' }}
|
||||
style="plainIcon"
|
||||
onClick={(event) => {
|
||||
props.setMultiSelectMode('off')
|
||||
}}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<X
|
||||
width={20}
|
||||
height={20}
|
||||
color={theme.colors.thTextContrast2.toString()}
|
||||
/>
|
||||
</Button>
|
||||
</HStack>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
// function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
// return (
|
||||
// <>
|
||||
|
||||
// <SpanBox
|
||||
// css={{
|
||||
// flex: 1,
|
||||
// display: 'flex',
|
||||
// gap: '2px',
|
||||
// alignItems: 'center',
|
||||
// }}
|
||||
// >
|
||||
// <SpanBox
|
||||
// css={{
|
||||
// color: '#55B938',
|
||||
// paddingLeft: '5px',
|
||||
// fontSize: '12px',
|
||||
// fontWeight: '600',
|
||||
// fontFamily: '$inter',
|
||||
// '@xlgDown': {
|
||||
// paddingLeft: '5px',
|
||||
// },
|
||||
// }}
|
||||
// >
|
||||
// {props.numItemsSelected}{' '}
|
||||
// <SpanBox
|
||||
// css={{
|
||||
// '@media (max-width: 1280px)': { display: 'none' },
|
||||
// }}
|
||||
// >
|
||||
// selected
|
||||
// </SpanBox>
|
||||
// </SpanBox>
|
||||
// </SpanBox>
|
||||
// )}
|
||||
// {/* {props.multiSelectMode !== 'off' ? (
|
||||
// <>
|
||||
// <MultiSelectControls {...props} />
|
||||
// <SpanBox css={{ flex: 1 }}></SpanBox>
|
||||
// </>
|
||||
// ) : (
|
||||
// <SearchControlButtonBox {...props} />
|
||||
// )} */}
|
||||
// {/* </HStack> */}
|
||||
|
||||
// // {props.setShowInlineSearch && props.multiSelectMode === 'off' && (
|
||||
// // <HStack
|
||||
// // alignment="center"
|
||||
// // distribution="end"
|
||||
// // css={{
|
||||
// // marginLeft: 'auto',
|
||||
// // marginRight: '20px',
|
||||
// // width: '100px',
|
||||
// // height: '100%',
|
||||
// // gap: '20px',
|
||||
// // '@md': {
|
||||
// // display: 'none',
|
||||
// // },
|
||||
// // }}
|
||||
// // >
|
||||
// // <Button
|
||||
// // style="ghost"
|
||||
// // onClick={() => {
|
||||
// // props.setShowInlineSearch && props.setShowInlineSearch(true)
|
||||
// // }}
|
||||
// // css={{
|
||||
// // display: 'flex',
|
||||
// // }}
|
||||
// // >
|
||||
// // <MagnifyingGlass
|
||||
// // size={20}
|
||||
// // color={theme.colors.graySolid.toString()}
|
||||
// // />
|
||||
// // </Button>
|
||||
// // <PrimaryDropdown
|
||||
// // showThemeSection={true}
|
||||
// // layout={props.layout}
|
||||
// // updateLayout={props.updateLayout}
|
||||
// // />
|
||||
// // </HStack>
|
||||
// // )}
|
||||
// </>
|
||||
// )
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,185 +0,0 @@
|
|||
import { ReactNode, useEffect, useRef, useState } from 'react'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { Box, HStack, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { SearchIcon } from '../../elements/images/SearchIcon'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
|
||||
import { FormInput } from '../../elements/FormElements'
|
||||
import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { X } from 'phosphor-react'
|
||||
|
||||
type LibrarySearchBarProps = {
|
||||
searchTerm?: string
|
||||
applySearchQuery: (searchQuery: string) => void
|
||||
}
|
||||
|
||||
type LibraryFilter =
|
||||
| 'in:inbox'
|
||||
| 'in:all'
|
||||
| 'in:archive'
|
||||
| 'type:file'
|
||||
| 'type:highlights'
|
||||
| `saved:${string}`
|
||||
| `sort:read`
|
||||
|
||||
// get last week's date
|
||||
const recentlySavedStartDate = new Date(
|
||||
new Date().getTime() - 7 * 24 * 60 * 60 * 1000
|
||||
).toLocaleDateString('en-US')
|
||||
|
||||
const FOCUSED_BOXSHADOW = '0px 0px 2px 2px rgba(255, 234, 159, 0.56)'
|
||||
|
||||
export function LibrarySearchBar(props: LibrarySearchBarProps): JSX.Element {
|
||||
const [focused, setFocused] = useState(false)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [searchTerm, setSearchTerm] = useState(props.searchTerm || '')
|
||||
|
||||
useEffect(() => {
|
||||
setSearchTerm(props.searchTerm || '')
|
||||
}, [props.searchTerm])
|
||||
|
||||
return (
|
||||
<VStack css={{ width: '100%' }}>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="start"
|
||||
css={{
|
||||
bg: '$grayBg',
|
||||
width: '100%',
|
||||
borderRadius: '8px',
|
||||
flex: 1,
|
||||
border: '1px solid $grayBorder',
|
||||
maxHeight: '40px',
|
||||
boxShadow: focused ? FOCUSED_BOXSHADOW : 'unset',
|
||||
}}
|
||||
>
|
||||
<DropdownFilterMenu
|
||||
triggerElement={
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution="between"
|
||||
css={{
|
||||
width: '5em',
|
||||
height: '100%',
|
||||
bg: '$grayBgActive',
|
||||
px: '$3',
|
||||
borderRadius: '8px 0px 0px 8px',
|
||||
'&:hover:': { bg: '$grayBgHover' },
|
||||
}}
|
||||
>
|
||||
<StyledText style="controlButton" css={{ m: '8px', ml: '0px' }}>
|
||||
Filters
|
||||
</StyledText>
|
||||
<Box
|
||||
css={{ width: '8px', color: '$grayText' }}
|
||||
className="dropdown-arrow"
|
||||
/>
|
||||
</HStack>
|
||||
}
|
||||
onFilterChange={(filter: LibraryFilter) => {
|
||||
props.applySearchQuery(filter)
|
||||
}}
|
||||
/>
|
||||
<Box css={{ mx: '$2', pt: '$1' }}>
|
||||
<span>
|
||||
<SearchIcon strokeColor={theme.colors.graySolid.toString()} />
|
||||
</span>
|
||||
</Box>
|
||||
<Box css={{ width: '100%' }}>
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault()
|
||||
props.applySearchQuery(searchTerm || '')
|
||||
inputRef.current?.blur()
|
||||
}}
|
||||
>
|
||||
<FormInput
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
placeholder="Search"
|
||||
onFocus={(event) => {
|
||||
event.target.select()
|
||||
setFocused(true)
|
||||
}}
|
||||
onBlur={() => {
|
||||
setFocused(false)
|
||||
}}
|
||||
onChange={(event) => {
|
||||
setSearchTerm(event.target.value)
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
</Box>
|
||||
{searchTerm && (
|
||||
<Button
|
||||
style="plainIcon"
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
setSearchTerm('')
|
||||
props.applySearchQuery('')
|
||||
inputRef.current?.blur()
|
||||
}}
|
||||
css={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
mr: '8px',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<X
|
||||
width={16}
|
||||
height={16}
|
||||
color={theme.colors.grayTextContrast.toString()}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</HStack>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
||||
type DropdownFilterMenuProps = {
|
||||
triggerElement: ReactNode
|
||||
onFilterChange: (filter: LibraryFilter) => void
|
||||
}
|
||||
|
||||
export function DropdownFilterMenu(
|
||||
props: DropdownFilterMenuProps
|
||||
): JSX.Element {
|
||||
return (
|
||||
<Dropdown triggerElement={props.triggerElement}>
|
||||
<DropdownOption
|
||||
onSelect={() => props.onFilterChange('in:inbox')}
|
||||
title="Inbox"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => props.onFilterChange('in:all')}
|
||||
title="All"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => props.onFilterChange('in:archive')}
|
||||
title="Archived"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => props.onFilterChange('type:file')}
|
||||
title="Files"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => props.onFilterChange('type:highlights')}
|
||||
title="Highlights"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => props.onFilterChange(`saved:${recentlySavedStartDate}`)}
|
||||
title="Recently Saved"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => props.onFilterChange(`sort:read`)}
|
||||
title="Recently Read"
|
||||
/>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { HStack, SpanBox } from '../../elements/LayoutPrimitives'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { Button } from '../../elements/Button'
|
||||
|
|
|
|||
|
|
@ -19,9 +19,6 @@ import { SavedSearch } from '../../../lib/networking/fragments/savedSearchFragme
|
|||
import { ToggleCaretDownIcon } from '../../elements/icons/ToggleCaretDownIcon'
|
||||
import Link from 'next/link'
|
||||
import { ToggleCaretRightIcon } from '../../elements/icons/ToggleCaretRightIcon'
|
||||
import { SplitButton } from '../../elements/SplitButton'
|
||||
import { AvatarDropdown } from '../../elements/AvatarDropdown'
|
||||
import { PrimaryDropdown } from '../PrimaryDropdown'
|
||||
import { NavMenuFooter } from './Footer'
|
||||
|
||||
export const LIBRARY_LEFT_MENU_WIDTH = '275px'
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
|||
import { StyledText } from '../../elements/StyledText'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { SettingsLayout } from '../SettingsLayout'
|
||||
import { SuggestionBox } from '../../elements/SuggestionBox'
|
||||
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
|
||||
import { FeatureHelpBox } from '../../elements/FeatureHelpBox'
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ export const useCopyLink = (
|
|||
url || (typeof window !== 'undefined' ? window.location.toString() : '')
|
||||
|
||||
const copyLink = useCallback(async (): Promise<void> => {
|
||||
const res = await navigator.clipboard.writeText(link)
|
||||
await navigator.clipboard.writeText(link)
|
||||
setIsLinkCopied(true)
|
||||
window.setTimeout(() => setIsLinkCopied(false), 5000)
|
||||
}, [link, type])
|
||||
}, [link])
|
||||
|
||||
return { link, copyLink, isLinkCopied }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,12 @@ export function useCurrentTheme() {
|
|||
setCurrentThemeInternal(themeId)
|
||||
updateThemeLocally(themeId)
|
||||
},
|
||||
[setCurrentThemeInternal]
|
||||
[
|
||||
currentThemeInternal,
|
||||
setCurrentThemeInternal,
|
||||
setPreferredDarkTheme,
|
||||
setPreferredLightTheme,
|
||||
]
|
||||
)
|
||||
|
||||
// This is used when the user disables "System" theme
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ export type ReaderSettings = {
|
|||
export const useReaderSettings = (): ReaderSettings => {
|
||||
applyStoredTheme()
|
||||
|
||||
const [, updateState] = useState({})
|
||||
|
||||
const [fontSize, setFontSize] = usePersistedState({
|
||||
key: 'fontSize',
|
||||
initialValue: 20,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'
|
||||
import { useCallback, useEffect, useReducer } from 'react'
|
||||
import { setLabelsMutation } from '../networking/mutations/setLabelsMutation'
|
||||
import { Label } from '../networking/fragments/labelFragment'
|
||||
import { showErrorToast } from '../toastHelpers'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { State } from '../fragments/articleFragment'
|
||||
|
||||
export type UpdateUserInput = {
|
||||
name: string
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { State } from '../fragments/articleFragment'
|
||||
|
||||
export type UpdateUserProfileInput = {
|
||||
userId: string
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import useSWRImmutable, { Cache } from 'swr'
|
||||
import {
|
||||
gqlFetcher,
|
||||
makeGqlFetcher,
|
||||
RequestContext,
|
||||
ssrFetcher,
|
||||
} from '../networkHelpers'
|
||||
import { Cache } from 'swr'
|
||||
import { gqlFetcher, makeGqlFetcher } from '../networkHelpers'
|
||||
import {
|
||||
articleFragment,
|
||||
ContentReader,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
"@stitches/react": "^1.2.5",
|
||||
"antd": "4.24.3",
|
||||
"axios": "^1.2.0",
|
||||
"color2k": "^2.0.0",
|
||||
"cookie": "^0.5.0",
|
||||
"csv-file-validator": "^2.1.0",
|
||||
"dayjs": "^1.11.7",
|
||||
|
|
|
|||
|
|
@ -1,16 +1,7 @@
|
|||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
useGetArticleQuery,
|
||||
ArticleAttributes,
|
||||
} from '../../../lib/networking/queries/useGetArticleQuery'
|
||||
import { useGetArticleQuery } from '../../../lib/networking/queries/useGetArticleQuery'
|
||||
import { applyStoredTheme } from '../../../lib/themeUpdater'
|
||||
import { useMemo } from 'react'
|
||||
import {
|
||||
EmptySettingsRow,
|
||||
SettingsTable,
|
||||
SettingsTableRow,
|
||||
} from '../../../components/templates/settings/SettingsTable'
|
||||
import { StyledText } from '../../../components/elements/StyledText'
|
||||
import {
|
||||
Box,
|
||||
HStack,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { useGetArticleQuery } from '../../../../lib/networking/queries/useGetArticleQuery'
|
||||
import { Box, VStack } from './../../../../components/elements/LayoutPrimitives'
|
||||
import { ArticleContainer } from './../../../../components/templates/article/ArticleContainer'
|
||||
import { useRef, useState, useEffect } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { webBaseURL } from '../../../../lib/appConfig'
|
||||
import { LoadingView } from '../../../../components/patterns/LoadingView'
|
||||
import { cookieValue } from '../../../../lib/cookieHelpers'
|
||||
import { applyStoredTheme } from '../../../../lib/themeUpdater'
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { SettingsLayout } from '../../components/templates/SettingsLayout'
|
|||
|
||||
import { ConfirmationModal } from '../../components/patterns/ConfirmationModal'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import { HStack, VStack } from '../../components/elements/LayoutPrimitives'
|
||||
import { VStack } from '../../components/elements/LayoutPrimitives'
|
||||
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { Loader } from '../../components/templates/SavingRequest'
|
||||
import { deleteAccountMutation } from '../../lib/networking/mutations/deleteAccountMutation'
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import {
|
|||
SettingsTableRow,
|
||||
} from '../../../components/templates/settings/SettingsTable'
|
||||
import { ConfirmationModal } from '../../../components/patterns/ConfirmationModal'
|
||||
import { SuggestionBox } from '../../../components/elements/SuggestionBox'
|
||||
|
||||
enum TextType {
|
||||
EmailAddress,
|
||||
|
|
|
|||
|
|
@ -17,13 +17,7 @@ import { applyStoredTheme, isDarkTheme } from '../../lib/themeUpdater'
|
|||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
import { Label, LabelColor } from '../../lib/networking/fragments/labelFragment'
|
||||
import { StyledText } from '../../components/elements/StyledText'
|
||||
import {
|
||||
ArrowClockwise,
|
||||
DotsThree,
|
||||
PencilSimple,
|
||||
Trash,
|
||||
Plus,
|
||||
} from 'phosphor-react'
|
||||
import { ArrowClockwise, DotsThree, PencilSimple, Trash } from 'phosphor-react'
|
||||
import { GenericTableCardProps } from '../../utils/settings-page/labels/types'
|
||||
import { labelColorObjects } from '../../utils/settings-page/labels/labelColorObjects'
|
||||
import { TooltipWrapped } from '../../components/elements/Tooltip'
|
||||
|
|
@ -35,7 +29,6 @@ import {
|
|||
import { LabelChip } from '../../components/elements/LabelChip'
|
||||
import { ConfirmationModal } from '../../components/patterns/ConfirmationModal'
|
||||
import { InfoLink } from '../../components/elements/InfoLink'
|
||||
import { SuggestionBox } from '../../components/elements/SuggestionBox'
|
||||
import { usePersistedState } from '../../lib/hooks/usePersistedState'
|
||||
import { FeatureHelpBox } from '../../components/elements/FeatureHelpBox'
|
||||
|
||||
|
|
|
|||
|
|
@ -11842,11 +11842,6 @@ color-support@^1.1.2, color-support@^1.1.3:
|
|||
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
|
||||
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
|
||||
|
||||
color2k@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/color2k/-/color2k-2.0.2.tgz#ac2b4aea11c822a6bcb70c768b5a289f4fffcebb"
|
||||
integrity sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==
|
||||
|
||||
color@3.0.x:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a"
|
||||
|
|
|
|||
Loading…
Reference in a new issue