From d0140b33d95e23471934c8ce81e1fc6354f93bc3 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 20 Jun 2023 10:18:29 +0800 Subject: [PATCH 1/8] Min font size of 16px to prevent zoom on iOS --- packages/web/components/elements/LabelsPicker.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/web/components/elements/LabelsPicker.tsx b/packages/web/components/elements/LabelsPicker.tsx index c76924e1c..7f6b6548d 100644 --- a/packages/web/components/elements/LabelsPicker.tsx +++ b/packages/web/components/elements/LabelsPicker.tsx @@ -112,6 +112,9 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => { marginTop: '0px', marginBottom: '0px', }, + '>input': { + fontSize: '16px', + }, }} onMouseDown={(event) => { inputRef.current?.focus() @@ -153,10 +156,14 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => { }} > { inputRef.current = ref }} + inputStyle={{ + fontSize: '16px', + minWidth: '100px', + }} onFocus={() => { if (props.onFocus) { props.onFocus() From 074efe3b4399ae3a9828ed101b463c41b616c76d Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 20 Jun 2023 10:19:40 +0800 Subject: [PATCH 2/8] Show error messages in labels modal --- .../templates/article/SetLabelsControl.tsx | 35 ++++++++++- .../templates/article/SetLabelsModal.tsx | 58 +++++++++++++++---- 2 files changed, 80 insertions(+), 13 deletions(-) diff --git a/packages/web/components/templates/article/SetLabelsControl.tsx b/packages/web/components/templates/article/SetLabelsControl.tsx index 9e17a646e..1e03fb872 100644 --- a/packages/web/components/templates/article/SetLabelsControl.tsx +++ b/packages/web/components/templates/article/SetLabelsControl.tsx @@ -6,7 +6,13 @@ import { StyledText } from '../../elements/StyledText' import { styled, theme } from '../../tokens/stitches.config' import { Label } from '../../../lib/networking/fragments/labelFragment' import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery' -import { Check, Circle, PencilSimple, Plus } from 'phosphor-react' +import { + Check, + Circle, + PencilSimple, + Plus, + WarningCircle, +} from 'phosphor-react' import { createLabelMutation } from '../../../lib/networking/mutations/createLabelMutation' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects' @@ -39,6 +45,8 @@ type SetLabelsControlProps = { deleteLastLabel: () => void selectOrCreateLabel: (value: string) => void + + errorMessage?: string } type HeaderProps = { @@ -75,7 +83,8 @@ function Header(props: HeaderProps): JSX.Element { @@ -414,6 +423,28 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { selectOrCreateLabel={props.selectOrCreateLabel} clearInputState={props.clearInputState} /> + + {props.errorMessage && ( + <> + {props.errorMessage} + + + )} + ( + undefined + ) + const errorTimeoutRef = useRef() const [highlightLastLabel, setHighlightLastLabel] = useState(false) const [selectedLabels, setSelectedLabels] = useState( @@ -47,9 +51,35 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { [props, selectedLabels] ) - const showMessage = useCallback((msg: string) => { - console.log('showMessage: ', msg) - }, []) + const showMessage = useCallback( + (msg: string, timeout?: number) => { + if (errorTimeoutRef.current) { + clearTimeout(errorTimeoutRef.current) + errorTimeoutRef.current = undefined + } + setErrorMessage(msg) + if (timeout) { + errorTimeoutRef.current = setTimeout(() => { + setErrorMessage(undefined) + if (errorTimeoutRef.current) { + clearTimeout(errorTimeoutRef.current) + errorTimeoutRef.current = undefined + } + }, timeout) + } + }, + [errorTimeoutRef] + ) + + useEffect(() => { + const maxLengthMessage = 'Max label length: 48 chars' + + if (inputValue.length >= 48) { + showMessage(maxLengthMessage) + } else if (errorMessage === maxLengthMessage) { + setErrorMessage(undefined) + } + }, [inputValue, showMessage]) const clearInputState = useCallback(() => { setTabCount(-1) @@ -59,15 +89,15 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { }, [tabCount, tabStartValue, highlightLastLabel]) const createLabelAsync = useCallback( - (tempLabel: Label) => { + (newLabels: Label[], tempLabel: Label) => { ;(async () => { - const currentLabels = selectedLabels + const currentLabels = newLabels const newLabel = await createLabelMutation( tempLabel.name, tempLabel.color ) + const idx = currentLabels.findIndex((l) => l.id === tempLabel.id) if (newLabel) { - const idx = currentLabels.findIndex((l) => l.id === tempLabel.id) showSuccessToast(`Created label ${newLabel.name}`, { position: 'bottom-right', }) @@ -78,7 +108,11 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { setSelectedLabels([...currentLabels, newLabel]) } } else { - showMessage(`Error creating label ${tempLabel.name}`) + showMessage(`Error creating label ${tempLabel.name}`, 5000) + if (idx !== -1) { + currentLabels.splice(idx, 1) + setSelectedLabels([...currentLabels]) + } } })() }, @@ -105,7 +139,7 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { setSelectedLabels([...current, existing]) clearInputState() } else { - showMessage(`label ${value} already added.`) + showMessage(`label ${value} already added.`, 5000) } } else { const tempLabel = { @@ -116,10 +150,11 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { createdAt: new Date(), _temporary: true, } - setSelectedLabels([...current, tempLabel]) + const newLabels = [...current, tempLabel] + setSelectedLabels(newLabels) clearInputState() - createLabelAsync(tempLabel) + createLabelAsync(newLabels, tempLabel) } }, [ @@ -183,6 +218,7 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { setHighlightLastLabel={setHighlightLastLabel} deleteLastLabel={deleteLastLabel} selectOrCreateLabel={selectOrCreateLabel} + errorMessage={errorMessage} /> From 103e676c7d753fee52c0d15cc2ac1c667fd8b3f6 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 20 Jun 2023 10:28:15 +0800 Subject: [PATCH 3/8] Clear last focused state on the labels in the selector --- packages/web/components/elements/LabelsPicker.tsx | 2 ++ .../templates/article/SetLabelsControl.tsx | 15 ++++++++++----- .../templates/article/SetLabelsModal.tsx | 4 ++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/web/components/elements/LabelsPicker.tsx b/packages/web/components/elements/LabelsPicker.tsx index 7f6b6548d..c0d973cec 100644 --- a/packages/web/components/elements/LabelsPicker.tsx +++ b/packages/web/components/elements/LabelsPicker.tsx @@ -118,6 +118,7 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => { }} onMouseDown={(event) => { inputRef.current?.focus() + props.setHighlightLastLabel(false) inputRef.current?.setSelectionRange( inputRef.current?.value.length, inputRef.current?.value.length @@ -126,6 +127,7 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => { }} onDoubleClick={(event) => { inputRef.current?.focus() + props.setHighlightLastLabel(false) inputRef.current?.setSelectionRange(0, inputRef.current?.value.length) }} > diff --git a/packages/web/components/templates/article/SetLabelsControl.tsx b/packages/web/components/templates/article/SetLabelsControl.tsx index 1e03fb872..621fbfc82 100644 --- a/packages/web/components/templates/article/SetLabelsControl.tsx +++ b/packages/web/components/templates/article/SetLabelsControl.tsx @@ -264,6 +264,10 @@ function Footer(props: FooterProps): JSX.Element { export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { const router = useRouter() const { labels, revalidate } = useGetLabelsQuery() + // Move focus through the labels list on tab or arrow up/down keys + const [focusedIndex, setFocusedIndex] = useState( + undefined + ) useEffect(() => { setFocusedIndex(undefined) @@ -278,6 +282,12 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { [props.selectedLabels] ) + useEffect(() => { + if (focusedIndex === 0) { + props.setHighlightLastLabel(false) + } + }, [focusedIndex]) + const toggleLabel = useCallback( async (label: Label) => { let newSelectedLabels = [...props.selectedLabels] @@ -314,11 +324,6 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { }) }, [labels, props.inputValue]) - // Move focus through the labels list on tab or arrow up/down keys - const [focusedIndex, setFocusedIndex] = useState( - undefined - ) - const createLabelFromFilterText = useCallback( async (text: string) => { const label = await createLabelMutation(text, randomLabelColorHex(), '') diff --git a/packages/web/components/templates/article/SetLabelsModal.tsx b/packages/web/components/templates/article/SetLabelsModal.tsx index 4f6ff5e2f..50401afef 100644 --- a/packages/web/components/templates/article/SetLabelsModal.tsx +++ b/packages/web/components/templates/article/SetLabelsModal.tsx @@ -79,6 +79,10 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { } else if (errorMessage === maxLengthMessage) { setErrorMessage(undefined) } + + if (inputValue.length > 0) { + setHighlightLastLabel(false) + } }, [inputValue, showMessage]) const clearInputState = useCallback(() => { From 9dd2a430172135833863d163aea5b585a7a6c4df Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 20 Jun 2023 10:35:40 +0800 Subject: [PATCH 4/8] Dont watch all props for save operation --- packages/web/components/templates/article/SetLabelsModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/components/templates/article/SetLabelsModal.tsx b/packages/web/components/templates/article/SetLabelsModal.tsx index 50401afef..3ac556988 100644 --- a/packages/web/components/templates/article/SetLabelsModal.tsx +++ b/packages/web/components/templates/article/SetLabelsModal.tsx @@ -187,7 +187,7 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { await props.save(selectedLabels) })() } - }, [props, selectedLabels]) + }, [props.save, selectedLabels]) return ( From b8886c96da62d9ac232a6a73a3856edc6e85578c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 20 Jun 2023 10:55:41 +0800 Subject: [PATCH 5/8] Only set minWidth for placeholder if all elements are empty --- packages/web/components/elements/LabelsPicker.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/web/components/elements/LabelsPicker.tsx b/packages/web/components/elements/LabelsPicker.tsx index c0d973cec..c489efe23 100644 --- a/packages/web/components/elements/LabelsPicker.tsx +++ b/packages/web/components/elements/LabelsPicker.tsx @@ -164,7 +164,10 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => { }} inputStyle={{ fontSize: '16px', - minWidth: '100px', + minWidth: + props.inputValue.length == 0 && props.selectedLabels.length == 0 + ? '100px' + : '2px', }} onFocus={() => { if (props.onFocus) { From e270d4acf98a2192e23e019587961ecf8f8b3308 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 20 Jun 2023 13:36:36 +0800 Subject: [PATCH 6/8] Warnings clean up --- .../web/components/elements/LabelChip.tsx | 2 +- .../elements/LabelColorDropdown.tsx | 18 +---- .../web/components/elements/LabelsPicker.tsx | 66 ++++++++++----- packages/web/components/patterns/CardMenu.tsx | 1 - .../web/components/patterns/DropdownMenu.tsx | 6 +- .../patterns/LibraryCards/LibraryGridCard.tsx | 7 +- .../LibraryCards/LibraryHighlightGridCard.tsx | 2 +- .../patterns/LibraryCards/LibraryListCard.tsx | 5 +- .../components/patterns/SettingsHeader.tsx | 2 +- .../web/components/templates/UploadModal.tsx | 2 +- .../templates/article/ArticleActionsMenu.tsx | 43 +--------- .../templates/article/ArticleContainer.tsx | 14 +--- .../templates/article/HighlightViewItem.tsx | 5 +- .../templates/article/HighlightsLayer.tsx | 21 ++--- .../components/templates/article/Notebook.tsx | 32 ++------ .../templates/article/NotebookModal.tsx | 5 +- .../article/ReaderSettingsControl.tsx | 58 ++++++++------ .../templates/article/SetLabelsControl.tsx | 80 +++++++------------ .../templates/article/SetLabelsModal.tsx | 69 ++++++---------- .../article/SetLabelsModalPresenter.tsx | 61 ++++++++++++++ .../article/VerticalArticleActions.tsx | 1 - .../templates/homeFeed/HighlightItem.tsx | 4 +- .../templates/homeFeed/HomeFeedContainer.tsx | 24 +----- .../templates/homeFeed/LibraryHeader.tsx | 14 ---- .../web/lib/hooks/useSetHighlightLabels.tsx | 69 ++++++++++++++++ packages/web/lib/hooks/useSetPageLabels.tsx | 71 ++++++++++++++++ .../web/pages/[username]/[slug]/index.tsx | 39 +++++---- 27 files changed, 388 insertions(+), 333 deletions(-) create mode 100644 packages/web/components/templates/article/SetLabelsModalPresenter.tsx create mode 100644 packages/web/lib/hooks/useSetHighlightLabels.tsx create mode 100644 packages/web/lib/hooks/useSetPageLabels.tsx diff --git a/packages/web/components/elements/LabelChip.tsx b/packages/web/components/elements/LabelChip.tsx index 29c29e0cb..22b10f510 100644 --- a/packages/web/components/elements/LabelChip.tsx +++ b/packages/web/components/elements/LabelChip.tsx @@ -1,4 +1,4 @@ -import { getLuminance, lighten, parseToRgba, toHsla } from 'color2k' +import { getLuminance } from 'color2k' import { useRouter } from 'next/router' import { Button } from './Button' import { SpanBox, HStack } from './LayoutPrimitives' diff --git a/packages/web/components/elements/LabelColorDropdown.tsx b/packages/web/components/elements/LabelColorDropdown.tsx index 28beae07f..26e015bff 100644 --- a/packages/web/components/elements/LabelColorDropdown.tsx +++ b/packages/web/components/elements/LabelColorDropdown.tsx @@ -1,6 +1,6 @@ -import React, { useMemo, useRef, useState } from 'react' +import React, { useRef, useState } from 'react' import { styled } from '../tokens/stitches.config' -import { Box, HStack, SpanBox } from './LayoutPrimitives' +import { Box, HStack } from './LayoutPrimitives' import { StyledText } from './StyledText' import { LabelColorDropdownProps, @@ -116,7 +116,6 @@ export const LabelColorDropdown = (props: LabelColorDropdownProps) => { } function LabelOption(props: LabelOptionProps): JSX.Element { - const { color, isDropdownOption, isCreateMode, labelId } = props return ( void onFocus?: () => void - setSelectedLabels: (labels: Label[]) => void + dispatchLabels: LabelsDispatcher deleteLastLabel: () => void selectOrCreateLabel: (value: string) => void @@ -33,32 +33,42 @@ type LabelsPickerProps = { export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => { const inputRef = useRef() const availableLabels = useGetLabelsQuery() + const { + focused, + inputValue, + tabCount, + tabStartValue, + selectedLabels, + setInputValue, + setTabCount, + setTabStartValue, + } = props useEffect(() => { - if (!isTouchScreenDevice() && props.focused && inputRef.current) { + if (!isTouchScreenDevice() && focused && inputRef.current) { inputRef.current.focus() } - }, [props.focused]) + }, [focused]) const autoComplete = useCallback(() => { - const lowerCasedValue = props.inputValue.toLowerCase() + const lowerCasedValue = inputValue.toLowerCase() if (lowerCasedValue.length < 1) { return } - let _tabCount = props.tabCount - let _tabStartValue = props.tabStartValue.toLowerCase() + let _tabCount = tabCount + let _tabStartValue = tabStartValue.toLowerCase() if (_tabCount === -1) { _tabCount = 0 _tabStartValue = lowerCasedValue - props.setTabCount(0) - props.setTabStartValue(lowerCasedValue) + setTabCount(0) + setTabStartValue(lowerCasedValue) } else { - _tabCount = props.tabCount + 1 - props.setTabCount(_tabCount) + _tabCount = tabCount + 1 + setTabCount(_tabCount) } const matches = availableLabels.labels.filter((l) => @@ -66,21 +76,29 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => { ) if (_tabCount < matches.length) { - props.setInputValue(matches[_tabCount].name) + setInputValue(matches[_tabCount].name) } else if (matches.length > 0) { - props.setTabCount(0) - props.setInputValue(matches[0].name) + setTabCount(0) + setInputValue(matches[0].name) } - }, [props.inputValue, availableLabels, props.tabCount, props.tabStartValue]) + }, [ + inputValue, + availableLabels, + tabCount, + tabStartValue, + setInputValue, + setTabCount, + setTabStartValue, + ]) const clearTabState = useCallback(() => { - props.setTabCount(-1) - props.setTabStartValue('') - }, []) + setTabCount(-1) + setTabStartValue('') + }, [setTabCount, setTabStartValue]) const isEmpty = useMemo(() => { - return props.selectedLabels.length === 0 && props.inputValue.length === 0 - }, [props.inputValue, props.selectedLabels]) + return selectedLabels.length === 0 && inputValue.length === 0 + }, [inputValue, selectedLabels]) return ( { inputRef.current?.focus() props.setHighlightLastLabel(false) inputRef.current?.setSelectionRange(0, inputRef.current?.value.length) + event.preventDefault() }} > {props.selectedLabels.map((label, idx) => ( @@ -144,7 +163,10 @@ export const LabelsPicker = (props: LabelsPickerProps): JSX.Element => { if (idx !== -1) { const _selectedLabels = props.selectedLabels _selectedLabels.splice(idx, 1) - props.setSelectedLabels([..._selectedLabels]) + props.dispatchLabels({ + type: 'SAVE', + labels: [..._selectedLabels], + }) } }} /> diff --git a/packages/web/components/patterns/CardMenu.tsx b/packages/web/components/patterns/CardMenu.tsx index d4adea69d..c28b62097 100644 --- a/packages/web/components/patterns/CardMenu.tsx +++ b/packages/web/components/patterns/CardMenu.tsx @@ -2,7 +2,6 @@ import type { ReactNode } from 'react' import { Dropdown, DropdownOption } from '../elements/DropdownElements' import { LibraryItemNode } from '../../lib/networking/queries/useGetLibraryItemsQuery' import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery' -import { isVipUser } from '../../lib/featureFlag' export type CardMenuDropdownAction = | 'mark-read' diff --git a/packages/web/components/patterns/DropdownMenu.tsx b/packages/web/components/patterns/DropdownMenu.tsx index 7f6ffe6eb..de09ce225 100644 --- a/packages/web/components/patterns/DropdownMenu.tsx +++ b/packages/web/components/patterns/DropdownMenu.tsx @@ -1,10 +1,6 @@ import { ReactNode, useMemo, useState } from 'react' import { HStack, VStack } from './../elements/LayoutPrimitives' -import { - Dropdown, - DropdownOption, - DropdownSeparator, -} from '../elements/DropdownElements' +import { Dropdown, DropdownOption } from '../elements/DropdownElements' import { StyledText } from '../elements/StyledText' import { Button } from '../elements/Button' import { currentThemeName } from '../../lib/themeUpdater' diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 66bf0b96b..b2fd95ac7 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -4,7 +4,7 @@ import type { LinkedItemCardProps } from './CardTypes' import { CoverImage } from '../../elements/CoverImage' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' -import { useCallback, useMemo, useState } from 'react' +import { useCallback, useState } from 'react' import { DotsThreeVertical } from 'phosphor-react' import Link from 'next/link' import { CardMenu } from '../CardMenu' @@ -104,12 +104,13 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { } const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => { + const { isChecked, setIsChecked, item } = props const [menuOpen, setMenuOpen] = useState(false) const originText = siteName(props.item.originalArticleUrl, props.item.url) const handleCheckChanged = useCallback(() => { - props.setIsChecked(props.item.id, !props.isChecked) - }, [props.isChecked]) + setIsChecked(item.id, !isChecked) + }, [setIsChecked, isChecked]) return ( <> diff --git a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx index 39613cbcb..0d2de7109 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx @@ -1,5 +1,5 @@ import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives' -import { useCallback, useMemo, useState } from 'react' +import { useMemo, useState } from 'react' import { CaretDown, CaretUp } from 'phosphor-react' import { MetaStyle, timeAgo, TitleStyle } from './LibraryCardStyles' import { styled } from '@stitches/react' diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index 02a8cedce..205e65f93 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -76,12 +76,13 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { export function LibraryListCardContent( props: LinkedItemCardProps ): JSX.Element { + const { isChecked, setIsChecked, item } = props const [menuOpen, setMenuOpen] = useState(false) const originText = siteName(props.item.originalArticleUrl, props.item.url) const handleCheckChanged = useCallback(() => { - props.setIsChecked(props.item.id, !props.isChecked) - }, [props.isChecked]) + setIsChecked(item.id, !isChecked) + }, [isChecked, setIsChecked, item]) return ( <> diff --git a/packages/web/components/patterns/SettingsHeader.tsx b/packages/web/components/patterns/SettingsHeader.tsx index 03b603470..bd98428de 100644 --- a/packages/web/components/patterns/SettingsHeader.tsx +++ b/packages/web/components/patterns/SettingsHeader.tsx @@ -1,4 +1,4 @@ -import { Box, HStack, VStack } from '../elements/LayoutPrimitives' +import { Box, HStack } from '../elements/LayoutPrimitives' import { OmnivoreNameLogo } from '../elements/images/OmnivoreNameLogo' import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery' import { PrimaryDropdown } from '../templates/PrimaryDropdown' diff --git a/packages/web/components/templates/UploadModal.tsx b/packages/web/components/templates/UploadModal.tsx index 296ba0dbe..1e065edf0 100644 --- a/packages/web/components/templates/UploadModal.tsx +++ b/packages/web/components/templates/UploadModal.tsx @@ -13,7 +13,7 @@ import * as Progress from '@radix-ui/react-progress' import { theme } from '../tokens/stitches.config' import { uploadFileRequestMutation } from '../../lib/networking/mutations/uploadFileMutation' import axios from 'axios' -import { CheckCircle, File } from 'phosphor-react' +import { File } from 'phosphor-react' import { showErrorToast } from '../../lib/toastHelpers' const DragnDropContainer = styled('div', { diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx index 402cb7d62..71fd01d78 100644 --- a/packages/web/components/templates/article/ArticleActionsMenu.tsx +++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx @@ -1,23 +1,12 @@ import { Separator } from '@radix-ui/react-separator' -import { - ArchiveBox, - Notebook, - Info, - TagSimple, - Trash, - Tray, - Tag, -} from 'phosphor-react' +import { ArchiveBox, Notebook, Info, Trash, Tray, Tag } from 'phosphor-react' import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery' import { Button } from '../../elements/Button' import { Box, SpanBox } from '../../elements/LayoutPrimitives' import { TooltipWrapped } from '../../elements/Tooltip' import { styled, theme } from '../../tokens/stitches.config' -import { useReaderSettings } from '../../../lib/hooks/useReaderSettings' +import { ReaderSettings } from '../../../lib/hooks/useReaderSettings' import { useRef } from 'react' -import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation' -import { Label } from '../../../lib/networking/fragments/labelFragment' -import { SetLabelsModal } from './SetLabelsModal' export type ArticleActionsMenuLayout = 'top' | 'side' @@ -25,6 +14,7 @@ type ArticleActionsMenuProps = { article?: ArticleAttributes layout: ArticleActionsMenuLayout showReaderDisplaySettings?: boolean + readerSettings: ReaderSettings articleActionHandler: (action: string, arg?: unknown) => void } @@ -45,7 +35,6 @@ const MenuSeparator = (props: MenuSeparatorProps): JSX.Element => { export function ArticleActionsMenu( props: ArticleActionsMenuProps ): JSX.Element { - const readerSettings = useReaderSettings() const displaySettingsButtonRef = useRef(null) return ( @@ -73,7 +62,7 @@ export function ArticleActionsMenu( <> */} - - {props.article && readerSettings.showSetLabelsModal && ( - { - readerSettings.setShowSetLabelsModal(false) - }} - onLabelsUpdated={(labels: Label[]) => { - props.articleActionHandler('refreshLabels', labels) - }} - save={async (labels: Label[]) => { - if (props.article?.id) { - const result = - (await setLabelsMutation( - props.article?.id, - labels.map((l) => l.id) - )) ?? [] - props.article.labels = result - return Promise.resolve(result) - } - return Promise.resolve(labels) - }} - /> - )} ) } diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx index 5960dd17d..c60a2c180 100644 --- a/packages/web/components/templates/article/ArticleContainer.tsx +++ b/packages/web/components/templates/article/ArticleContainer.tsx @@ -12,19 +12,13 @@ import { Button } from '../../elements/Button' import { useEffect, useState, useRef, useMemo, useCallback } from 'react' import { ReportIssuesModal } from './ReportIssuesModal' import { reportIssueMutation } from '../../../lib/networking/mutations/reportIssueMutation' -import { - currentTheme, - updateTheme, - updateThemeLocally, -} from '../../../lib/themeUpdater' +import { updateTheme, updateThemeLocally } from '../../../lib/themeUpdater' import { ArticleMutations } from '../../../lib/articleActions' import { LabelChip } from '../../elements/LabelChip' import { Label } from '../../../lib/networking/fragments/labelFragment' import { Recommendation } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { Avatar } from '../../elements/Avatar' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' -import Downshift from 'downshift' -import { LabelsPicker } from '../../elements/LabelsPicker' type ArticleContainerProps = { viewer: UserBasicData @@ -145,9 +139,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element { const updateFontSize = useCallback( (newFontSize: number) => { - if (fontSize !== newFontSize) { - setFontSize(newFontSize) - } + setFontSize(newFontSize) }, [setFontSize] ) @@ -155,7 +147,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element { useEffect(() => { setLabels(props.labels) updateFontSize(props.fontSize ?? 20) - }, [props.labels, props.fontSize]) + }, [props.labels, props.fontSize, updateFontSize]) // Listen for preference change events sent from host apps (ios, macos...) useEffect(() => { diff --git a/packages/web/components/templates/article/HighlightViewItem.tsx b/packages/web/components/templates/article/HighlightViewItem.tsx index fa9f71550..8a4186c43 100644 --- a/packages/web/components/templates/article/HighlightViewItem.tsx +++ b/packages/web/components/templates/article/HighlightViewItem.tsx @@ -1,9 +1,6 @@ import { useState } from 'react' import { Highlight } from '../../../lib/networking/fragments/highlightFragment' -import { - LibraryItem, - ReadableItem, -} from '../../../lib/networking/queries/useGetLibraryItemsQuery' +import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { HighlightView } from '../../patterns/HighlightView' diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index f79fc3cf2..a0f13faee 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -22,12 +22,9 @@ import { NotebookModal } from './NotebookModal' import { showErrorToast } from '../../../lib/toastHelpers' import { ArticleMutations } from '../../../lib/articleActions' import { isTouchScreenDevice } from '../../../lib/deviceType' -import { SetLabelsModal } from './SetLabelsModal' -import { setLabelsForHighlight } from '../../../lib/networking/mutations/setLabelsForHighlight' -import { Label } from '../../../lib/networking/fragments/labelFragment' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' -import { useRegisterActions } from 'kbar' +import { SetHighlightLabelsModalPresenter } from './SetLabelsModalPresenter' type HighlightsLayerProps = { viewer: UserBasicData @@ -673,18 +670,10 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { if (labelsTarget) { return ( - { - const result = setLabelsForHighlight( - labelsTarget.id, - labels.map((label) => label.id) - ) - return result - }} + setLabelsTarget(undefined)} /> ) } diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 4410847a1..1d17f8d55 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -4,27 +4,21 @@ import { theme } from '../../tokens/stitches.config' import type { Highlight } from '../../../lib/networking/fragments/highlightFragment' import { useCallback, useEffect, useMemo, useReducer, useState } from 'react' import { BookOpen, PencilLine, X } from 'phosphor-react' -import { SetLabelsModal } from './SetLabelsModal' -import { Label } from '../../../lib/networking/fragments/labelFragment' -import { setLabelsForHighlight } from '../../../lib/networking/mutations/setLabelsForHighlight' import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { diff_match_patch } from 'diff-match-patch' -import { highlightsAsMarkdown } from '../homeFeed/HighlightItem' import 'react-markdown-editor-lite/lib/index.css' import { createHighlightMutation } from '../../../lib/networking/mutations/createHighlightMutation' import { v4 as uuidv4 } from 'uuid' import { nanoid } from 'nanoid' import { deleteHighlightMutation } from '../../../lib/networking/mutations/deleteHighlightMutation' -import { HighlightNoteBox, MarkdownNote } from '../../patterns/HighlightNotes' +import { HighlightNoteBox } from '../../patterns/HighlightNotes' import { HighlightViewItem } from './HighlightViewItem' import { ConfirmationModal } from '../../patterns/ConfirmationModal' import { TrashIcon } from '../../elements/images/TrashIcon' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' -import { - LibraryItem, - ReadableItem, -} from '../../../lib/networking/queries/useGetLibraryItemsQuery' +import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' +import { SetHighlightLabelsModalPresenter } from './SetLabelsModalPresenter' type NotebookProps = { viewer: UserBasicData @@ -289,6 +283,7 @@ export function Notebook(props: NotebookProps): JSX.Element { }, [annotations, props.item] ) + return ( )} {labelsTarget && ( - { - const result = setLabelsForHighlight( - labelsTarget.id, - labels.map((label) => label.id) - ) - return result - }} + setLabelsTarget(undefined)} /> )} {props.showConfirmDeleteNote && ( diff --git a/packages/web/components/templates/article/NotebookModal.tsx b/packages/web/components/templates/article/NotebookModal.tsx index 93fa62701..c6a8f4282 100644 --- a/packages/web/components/templates/article/NotebookModal.tsx +++ b/packages/web/components/templates/article/NotebookModal.tsx @@ -3,7 +3,7 @@ import { ModalOverlay, ModalContent, } from '../../elements/ModalPrimitives' -import { HStack, SpanBox } from '../../elements/LayoutPrimitives' +import { HStack } from '../../elements/LayoutPrimitives' import { Button } from '../../elements/Button' import { StyledText } from '../../elements/StyledText' import { theme } from '../../tokens/stitches.config' @@ -19,7 +19,6 @@ import 'react-markdown-editor-lite/lib/index.css' import { Notebook } from './Notebook' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' -import { MarkdownNote } from '../../patterns/HighlightNotes' type NotebookModalProps = { viewer: UserBasicData @@ -49,7 +48,7 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element { const handleClose = useCallback(() => { props.onClose(allAnnotations ?? [], deletedAnnotations ?? []) - }, [allAnnotations, deletedAnnotations]) + }, [props, allAnnotations, deletedAnnotations]) const handleAnnotationsChange = useCallback( (allAnnotations, deletedAnnotations) => { diff --git a/packages/web/components/templates/article/ReaderSettingsControl.tsx b/packages/web/components/templates/article/ReaderSettingsControl.tsx index 4e79d65cd..b9c10ef83 100644 --- a/packages/web/components/templates/article/ReaderSettingsControl.tsx +++ b/packages/web/components/templates/article/ReaderSettingsControl.tsx @@ -12,7 +12,7 @@ import { import { TickedRangeSlider } from '../../elements/TickedRangeSlider' import { showSuccessToast } from '../../../lib/toastHelpers' import { ReaderSettings } from '../../../lib/hooks/useReaderSettings' -import { useCallback, useMemo, useState } from 'react' +import { useCallback, useState } from 'react' import { currentThemeName, updateTheme } from '../../../lib/themeUpdater' import { LineHeightIncreaseIcon } from '../../elements/images/LineHeightIncreaseIconProps' import { LineHeightDecreaseIcon } from '../../elements/images/LineHeightDecreaseIcon' @@ -70,6 +70,8 @@ export function ReaderSettingsControl(props: ReaderSettingsProps): JSX.Element { } function AdvancedSettings(props: SettingsProps): JSX.Element { + const { readerSettings } = props + return ( { - props.readerSettings.setJustifyText(checked) + readerSettings.setJustifyText(checked) }} > @@ -153,9 +155,9 @@ function AdvancedSettings(props: SettingsProps): JSX.Element { { - props.readerSettings.setHighContrastText(checked) + readerSettings.setHighContrastText(checked) }} > @@ -196,17 +198,18 @@ const Label = styled('label', { }) function BasicSettings(props: SettingsProps): JSX.Element { + const { readerSettings } = props return ( - + - + - + @@ -228,10 +231,10 @@ function BasicSettings(props: SettingsProps): JSX.Element { }, }} onClick={() => { - props.readerSettings.setFontFamily('Inter') - props.readerSettings.setMarginWidth(290) - props.readerSettings.setLineHeight(150) - props.readerSettings.actionHandler('resetReaderSettings') + readerSettings.setFontFamily('Inter') + readerSettings.setMarginWidth(290) + readerSettings.setLineHeight(150) + readerSettings.actionHandler('resetReaderSettings') showSuccessToast('Reader Preferences Reset', { position: 'bottom-right', }) @@ -272,6 +275,7 @@ type FontControlsProps = { } function FontControls(props: FontControlsProps): JSX.Element { + const { readerSettings } = props const FontSelect = styled('select', { pl: '5px', height: '30px', @@ -281,16 +285,16 @@ function FontControls(props: FontControlsProps): JSX.Element { fontSize: '12px', background: '$thBackground', border: '1px solid $thBorderColor', - fontFamily: props.readerSettings.fontFamily, + fontFamily: readerSettings.fontFamily, textTransform: 'capitalize', borderRadius: '4px', }) const handleFontSizeChange = useCallback( (value) => { - props.readerSettings.actionHandler('setFontSize', value) + readerSettings.actionHandler('setFontSize', value) }, - [props.readerSettings.actionHandler] + [readerSettings] ) return ( @@ -304,13 +308,13 @@ function FontControls(props: FontControlsProps): JSX.Element { ) => { const font = e.currentTarget.value if (FONT_FAMILIES.indexOf(font) < 0) { return } - props.readerSettings.setFontFamily(font) + readerSettings.setFontFamily(font) }} > {FONT_FAMILIES.map((family) => ( @@ -329,7 +333,7 @@ function FontControls(props: FontControlsProps): JSX.Element { style="plainIcon" css={{ py: '0px', width: '60px' }} onClick={() => { - props.readerSettings.actionHandler('decrementFontSize') + readerSettings.actionHandler('decrementFontSize') }} > { - props.readerSettings.setMarginWidth(value) + readerSettings.setMarginWidth(value) }, - [props.readerSettings.actionHandler, props.readerSettings.setMarginWidth] + [readerSettings] ) return ( @@ -425,10 +431,10 @@ function LayoutControls(props: LayoutControlsProps): JSX.Element { css={{ py: '0px', width: '60px' }} onClick={() => { const newMarginWith = Math.max( - props.readerSettings.marginWidth - 45, + readerSettings.marginWidth - 45, 200 ) - props.readerSettings.setMarginWidth(newMarginWith) + readerSettings.setMarginWidth(newMarginWith) }} > @@ -437,7 +443,7 @@ function LayoutControls(props: LayoutControlsProps): JSX.Element { min={200} max={560} step={45} - value={props.readerSettings.marginWidth} + value={readerSettings.marginWidth} onChange={handleMarginWidthChange} /> - {/* */}