From 2123b248c90cbe4f2af5bd3147334626f9935387 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 6 Jun 2024 13:24:48 +0800 Subject: [PATCH 01/23] Setup new left navigation system --- .../components/templates/NavigationLayout.tsx | 199 ++++++++++++++++++ .../templates/navMenu/NavigationMenu.tsx | 54 ++--- packages/web/pages/highlights/index.tsx | 29 +++ packages/web/pages/justread/index.tsx | 173 +++++---------- packages/web/pages/library/index.tsx | 33 +++ packages/web/pages/subscriptions/index.tsx | 29 +++ 6 files changed, 368 insertions(+), 149 deletions(-) create mode 100644 packages/web/components/templates/NavigationLayout.tsx create mode 100644 packages/web/pages/highlights/index.tsx create mode 100644 packages/web/pages/library/index.tsx create mode 100644 packages/web/pages/subscriptions/index.tsx diff --git a/packages/web/components/templates/NavigationLayout.tsx b/packages/web/components/templates/NavigationLayout.tsx new file mode 100644 index 000000000..a52290ca2 --- /dev/null +++ b/packages/web/components/templates/NavigationLayout.tsx @@ -0,0 +1,199 @@ +import { PageMetaData, PageMetaDataProps } from '../patterns/PageMetaData' +import { Box, VStack } from '../elements/LayoutPrimitives' +import { ReactNode, useEffect, useState, useCallback } from 'react' +import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery' +import { navigationCommands } from '../../lib/keyboardShortcuts/navigationShortcuts' +import { useKeyboardShortcuts } from '../../lib/keyboardShortcuts/useKeyboardShortcuts' +import { NextRouter, useRouter } from 'next/router' +import { ConfirmationModal } from '../patterns/ConfirmationModal' +import { KeyboardShortcutListModal } from './KeyboardShortcutListModal' +import { setupAnalytics } from '../../lib/analytics' +import { primaryCommands } from '../../lib/keyboardShortcuts/navigationShortcuts' +import { logout } from '../../lib/logout' +import { useApplyLocalTheme } from '../../lib/hooks/useApplyLocalTheme' +import { updateTheme } from '../../lib/themeUpdater' +import { Priority, useRegisterActions } from 'kbar' +import { ThemeId, theme } from '../tokens/stitches.config' +import { NavigationMenu } from './navMenu/NavigationMenu' +import { DEFAULT_HEADER_HEIGHT } from './homeFeed/HeaderSpacer' +import { Button } from '../elements/Button' +import { List } from 'phosphor-react' +import { usePersistedState } from '../../lib/hooks/usePersistedState' + +export type NavigationSection = + | 'justread' + | 'home' + | 'library' + | 'subscriptions' + | 'highlights' + +type NavigationLayoutProps = { + children: ReactNode + section: NavigationSection + pageMetaDataProps?: PageMetaDataProps +} + +export function NavigationLayout(props: NavigationLayoutProps): JSX.Element { + useApplyLocalTheme() + + const { viewerData } = useGetViewerQuery() + const router = useRouter() + const [showLogoutConfirmation, setShowLogoutConfirmation] = useState(false) + const [showKeyboardCommandsModal, setShowKeyboardCommandsModal] = + useState(false) + const [showLeftMenu, setShowLeftMenu] = usePersistedState({ + key: 'nav-show-menu', + isSessionStorage: false, + initialValue: true, + }) + + useKeyboardShortcuts(navigationCommands(router)) + + useKeyboardShortcuts( + primaryCommands((action) => { + switch (action) { + case 'toggleShortcutHelpModalDisplay': + setShowKeyboardCommandsModal(true) + break + } + }) + ) + + useRegisterActions( + [ + { + id: 'home', + section: 'Navigation', + name: 'Go to Home (Library) ', + shortcut: ['g h'], + keywords: 'go home', + perform: () => router?.push('/home'), + }, + { + id: 'lightTheme', + section: 'Preferences', + name: 'Change theme (light) ', + shortcut: ['v', 'l'], + keywords: 'light theme', + priority: Priority.LOW, + perform: () => updateTheme(ThemeId.Light), + }, + { + id: 'darkTheme', + section: 'Preferences', + name: 'Change theme (dark) ', + shortcut: ['v', 'd'], + keywords: 'dark theme', + priority: Priority.LOW, + perform: () => updateTheme(ThemeId.Dark), + }, + ], + [router] + ) + + // Attempt to identify the user if they are logged in. + useEffect(() => { + setupAnalytics(viewerData?.me) + }, [viewerData?.me]) + + const showLogout = useCallback(() => { + setShowLogoutConfirmation(true) + }, [setShowLogoutConfirmation]) + + useEffect(() => { + document.addEventListener('logout', showLogout) + + return () => { + document.removeEventListener('logout', showLogout) + } + }, [showLogout]) + + return ( + <> + {props.pageMetaDataProps ? ( + + ) : null} + +
{ + setShowLeftMenu(!showLeftMenu) + }} + /> + {showLeftMenu && ( + {}} + searchTerm={''} + // eslint-disable-next-line @typescript-eslint/no-empty-function + applySearchQuery={(searchQuery: string) => {}} + showFilterMenu={showLeftMenu} + setShowFilterMenu={(show) => { + setShowLeftMenu(show) + }} + /> + )} + {props.children} + {showLogoutConfirmation ? ( + setShowLogoutConfirmation(false)} + /> + ) : null} + {showKeyboardCommandsModal ? ( + setShowKeyboardCommandsModal(false)} + /> + ) : null} + + + ) +} + +type HeaderProps = { + toggleMenu: () => void +} + +const Header = (props: HeaderProps): JSX.Element => { + const small = false + + return ( + + + + + + ) +} diff --git a/packages/web/components/templates/navMenu/NavigationMenu.tsx b/packages/web/components/templates/navMenu/NavigationMenu.tsx index 47df7da78..72ab6d15a 100644 --- a/packages/web/components/templates/navMenu/NavigationMenu.tsx +++ b/packages/web/components/templates/navMenu/NavigationMenu.tsx @@ -32,10 +32,13 @@ import { Dropdown, DropdownOption } from '../../elements/DropdownElements' import { useRouter } from 'next/router' import { DiscoverIcon } from '../../elements/icons/DiscoverIcon' import { escapeQuotes } from '../../../utils/helper' +import { NavigationSection } from '../NavigationLayout' export const LIBRARY_LEFT_MENU_WIDTH = '275px' type LibraryFilterMenuProps = { + section: NavigationSection + setShowAddLinkModal: (show: boolean) => void searchTerm: string | undefined @@ -206,33 +209,31 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { } /> } /> } /> } /> - } - /> ) } @@ -731,11 +732,8 @@ type NavButtonProps = { text: string icon: ReactNode - filterTerm: string - searchTerm: string | undefined - - applySearchQuery: (searchTerm: string) => void - setShowFilterMenu: (show: boolean) => void + isSelected: boolean + section: NavigationSection } type NavButtonRedirectProps = { @@ -803,15 +801,7 @@ function NavRedirectButton(props: NavButtonRedirectProps): JSX.Element { } function NavButton(props: NavButtonProps): JSX.Element { - const isInboxFilter = (filter: string) => { - return filter === '' || filter === 'in:inbox' - } - const selected = useMemo(() => { - if (isInboxFilter(props.filterTerm) && !props.searchTerm) { - return true - } - return props.searchTerm === props.filterTerm - }, [props.searchTerm, props.filterTerm]) + const router = useRouter() return ( { - props.applySearchQuery(props.filterTerm) - props.setShowFilterMenu(false) - e.preventDefault() + router.push(`/` + props.section) }} > {props.icon} diff --git a/packages/web/pages/highlights/index.tsx b/packages/web/pages/highlights/index.tsx new file mode 100644 index 000000000..f9e38bd1d --- /dev/null +++ b/packages/web/pages/highlights/index.tsx @@ -0,0 +1,29 @@ +import { NavigationLayout } from '../../components/templates/NavigationLayout' +import { PrimaryLayout } from '../../components/templates/PrimaryLayout' +import { HomeFeedContainer } from '../../components/templates/homeFeed/HomeFeedContainer' +import { VStack } from '../../components/elements/LayoutPrimitives' + +export default function Highlights(): JSX.Element { + return ( + + +
Highlights will go here
+
+
+ ) +} diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index ef491bf40..c5d6c41df 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -30,97 +30,77 @@ import { SpanBox, VStack, } from './../../components/elements/LayoutPrimitives' -import { List, ThumbsDown, ThumbsUp } from 'phosphor-react' -import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers' import { Toaster } from 'react-hot-toast' -import { DEFAULT_HEADER_HEIGHT } from '../../components/templates/homeFeed/HeaderSpacer' -import { NavigationMenu } from '../../components/templates/navMenu/NavigationMenu' +import { NavigationLayout } from '../../components/templates/NavigationLayout' export default function Home(): JSX.Element { - const [showLeftMenu, setShowLeftMenu] = useState(false) const homeData = useGetHomeItems() useApplyLocalTheme() return ( - - -
{ - setShowLeftMenu(!showLeftMenu) - }} - /> - {showLeftMenu && ( - {}} - searchTerm={''} - // eslint-disable-next-line @typescript-eslint/no-empty-function - applySearchQuery={(searchQuery: string) => {}} - showFilterMenu={showLeftMenu} - setShowFilterMenu={(show) => { - setShowLeftMenu(show) - }} - /> - )} + - {homeData.sections?.map((homeSection, idx) => { - if (homeSection.items.length < 1) { - return <> - } - switch (homeSection.layout) { - case 'just_added': - return ( - - ) - case 'top_picks': - return ( - - ) - case 'quick_links': - return ( - - ) - case 'hidden': - return ( - - ) - default: + + + {homeData.sections?.map((homeSection, idx) => { + if (homeSection.items.length < 1) { return <> - } - })} + } + switch (homeSection.layout) { + case 'just_added': + return ( + + ) + case 'top_picks': + return ( + + ) + case 'quick_links': + return ( + + ) + case 'hidden': + return ( + + ) + default: + return <> + } + })} + - + ) } @@ -753,44 +733,3 @@ const SubscriptionSourceHoverContent = ( // // ) // } - -type HeaderProps = { - toggleMenu: () => void -} - -const Header = (props: HeaderProps): JSX.Element => { - const small = false - - return ( - - - - - - ) -} diff --git a/packages/web/pages/library/index.tsx b/packages/web/pages/library/index.tsx new file mode 100644 index 000000000..7d7eb06ad --- /dev/null +++ b/packages/web/pages/library/index.tsx @@ -0,0 +1,33 @@ +import { NavigationLayout } from '../../components/templates/NavigationLayout' +import { PrimaryLayout } from '../../components/templates/PrimaryLayout' +import { HomeFeedContainer } from '../../components/templates/homeFeed/HomeFeedContainer' +import { VStack } from '../../components/elements/LayoutPrimitives' + +export default function Home(): JSX.Element { + return +} + +function LoadedContent(): JSX.Element { + return ( + + + + + + ) +} diff --git a/packages/web/pages/subscriptions/index.tsx b/packages/web/pages/subscriptions/index.tsx new file mode 100644 index 000000000..0a68a24cf --- /dev/null +++ b/packages/web/pages/subscriptions/index.tsx @@ -0,0 +1,29 @@ +import { NavigationLayout } from '../../components/templates/NavigationLayout' +import { PrimaryLayout } from '../../components/templates/PrimaryLayout' +import { HomeFeedContainer } from '../../components/templates/homeFeed/HomeFeedContainer' +import { VStack } from '../../components/elements/LayoutPrimitives' + +export default function Subscriptions(): JSX.Element { + return ( + + +
Subscriptions will go here
+
+
+ ) +} From 4295e8228d30f3525db1342015b5207b2f90e5fc Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 6 Jun 2024 17:10:55 +0800 Subject: [PATCH 02/23] Start adding allotment cleaning up navigation --- .../patterns/LibraryCards/LibraryGridCard.tsx | 2 +- .../patterns/LibraryCards/LibraryListCard.tsx | 26 +- .../templates/{homeFeed => }/AddLinkModal.tsx | 28 +- .../components/templates/NavigationLayout.tsx | 76 +- .../discoverFeed/DiscoverContainer.tsx | 46 +- .../templates/homeFeed/HomeFeedContainer.tsx | 6 +- .../templates/homeFeed/LibraryHeader.tsx | 2 +- .../homeFeed/MultiSelectControls.tsx | 29 +- .../templates/library/LibraryContainer.tsx | 1275 +++++++++++++++++ .../templates/library/LibraryHeader.tsx | 418 ++++++ .../library/LibraryItemsContainer.tsx | 23 + .../templates/library/LibrarySideBar.tsx | 62 + .../templates/navMenu/NavigationMenu.tsx | 21 +- packages/web/package.json | 1 + packages/web/pages/justread/index.tsx | 2 +- packages/web/pages/library/index.tsx | 35 +- yarn.lock | 46 +- 17 files changed, 1971 insertions(+), 127 deletions(-) rename packages/web/components/templates/{homeFeed => }/AddLinkModal.tsx (95%) create mode 100644 packages/web/components/templates/library/LibraryContainer.tsx create mode 100644 packages/web/components/templates/library/LibraryHeader.tsx create mode 100644 packages/web/components/templates/library/LibraryItemsContainer.tsx create mode 100644 packages/web/components/templates/library/LibrarySideBar.tsx diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index c824c82e9..9c260b5cc 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -64,7 +64,7 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { css={{ pl: '0px', padding: '0px', - width: '293px', + width: '100%', height: '100%', minHeight: '270px', background: 'white', diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index 00374e9f4..007b71919 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -68,19 +68,19 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { borderStyle: 'none', borderBottom: 'none', borderRadius: '6px', - width: '100vw', - '@media (min-width: 768px)': { - width: `calc(100vw - ${LIBRARY_LEFT_MENU_WIDTH})`, - }, - '@media (min-width: 930px)': { - width: '580px', - }, - '@media (min-width: 1280px)': { - width: '890px', - }, - '@media (min-width: 1600px)': { - width: '1200px', - }, + width: '100%', + // '@media (min-width: 768px)': { + // width: `calc(100vw - ${LIBRARY_LEFT_MENU_WIDTH})`, + // }, + // '@media (min-width: 930px)': { + // width: '580px', + // }, + // '@media (min-width: 1280px)': { + // width: '890px', + // }, + // '@media (min-width: 1600px)': { + // width: '1200px', + // }, '@media (max-width: 930px)': { borderRadius: '0px', }, diff --git a/packages/web/components/templates/homeFeed/AddLinkModal.tsx b/packages/web/components/templates/AddLinkModal.tsx similarity index 95% rename from packages/web/components/templates/homeFeed/AddLinkModal.tsx rename to packages/web/components/templates/AddLinkModal.tsx index a4a6b0dce..063406ebb 100644 --- a/packages/web/components/templates/homeFeed/AddLinkModal.tsx +++ b/packages/web/components/templates/AddLinkModal.tsx @@ -1,17 +1,17 @@ import { useCallback, useRef, useState } from 'react' import * as Progress from '@radix-ui/react-progress' import { File, Info } from 'phosphor-react' -import { locale, timeZone } from '../../../lib/dateFormatting' -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 { locale, timeZone } from '../../lib/dateFormatting' +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 { ModalContent, ModalOverlay, ModalRoot, -} from '../../elements/ModalPrimitives' -import { CloseButton } from '../../elements/CloseButton' +} from '../elements/ModalPrimitives' +import { CloseButton } from '../elements/CloseButton' import { styled } from '@stitches/react' import Dropzone, { Accept, @@ -20,17 +20,17 @@ import Dropzone, { FileRejection, } from 'react-dropzone' import { v4 as uuidv4 } from 'uuid' -import { validateCsvFile } from '../../../utils/csvValidator' +import { validateCsvFile } from '../../utils/csvValidator' import { uploadImportFileRequestMutation, UploadImportFileType, -} from '../../../lib/networking/mutations/uploadImportFileMutation' -import { uploadFileRequestMutation } from '../../../lib/networking/mutations/uploadFileMutation' +} from '../../lib/networking/mutations/uploadImportFileMutation' +import { uploadFileRequestMutation } from '../../lib/networking/mutations/uploadFileMutation' import axios from 'axios' -import { theme } from '../../tokens/stitches.config' -import { formatMessage } from '../../../locales/en/messages' -import { subscribeMutation } from '../../../lib/networking/mutations/subscribeMutation' -import { SubscriptionType } from '../../../lib/networking/queries/useGetSubscriptionsQuery' +import { theme } from '../tokens/stitches.config' +import { formatMessage } from '../../locales/en/messages' +import { subscribeMutation } from '../../lib/networking/mutations/subscribeMutation' +import { SubscriptionType } from '../../lib/networking/queries/useGetSubscriptionsQuery' type TabName = 'link' | 'feed' | 'opml' | 'pdf' | 'import' diff --git a/packages/web/components/templates/NavigationLayout.tsx b/packages/web/components/templates/NavigationLayout.tsx index a52290ca2..d892364ad 100644 --- a/packages/web/components/templates/NavigationLayout.tsx +++ b/packages/web/components/templates/NavigationLayout.tsx @@ -1,5 +1,5 @@ import { PageMetaData, PageMetaDataProps } from '../patterns/PageMetaData' -import { Box, VStack } from '../elements/LayoutPrimitives' +import { Box, HStack, VStack } from '../elements/LayoutPrimitives' import { ReactNode, useEffect, useState, useCallback } from 'react' import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery' import { navigationCommands } from '../../lib/keyboardShortcuts/navigationShortcuts' @@ -19,6 +19,10 @@ import { DEFAULT_HEADER_HEIGHT } from './homeFeed/HeaderSpacer' import { Button } from '../elements/Button' import { List } from 'phosphor-react' import { usePersistedState } from '../../lib/hooks/usePersistedState' +import { Allotment } from 'allotment' +import 'allotment/dist/style.css' +import { LibrarySideBar } from './library/LibrarySideBar' +import NoSsr from './NoSsr' export type NavigationSection = | 'justread' @@ -29,6 +33,7 @@ export type NavigationSection = type NavigationLayoutProps = { children: ReactNode + rightPane?: ReactNode section: NavigationSection pageMetaDataProps?: PageMetaDataProps } @@ -41,7 +46,8 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element { const [showLogoutConfirmation, setShowLogoutConfirmation] = useState(false) const [showKeyboardCommandsModal, setShowKeyboardCommandsModal] = useState(false) - const [showLeftMenu, setShowLeftMenu] = usePersistedState({ + + const [showNavMenu, setShowNavMenu] = usePersistedState({ key: 'nav-show-menu', isSessionStorage: false, initialValue: true, @@ -109,51 +115,37 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element { }, [showLogout]) return ( - <> + {props.pageMetaDataProps ? ( ) : null} - { + setShowNavMenu(!showNavMenu) }} - > -
{ - setShowLeftMenu(!showLeftMenu) - }} + /> + {}} + searchTerm="" + applySearchQuery={() => {}} + showFilterMenu={showNavMenu} + setShowFilterMenu={setShowNavMenu} + /> + {props.children} + {showLogoutConfirmation ? ( + setShowLogoutConfirmation(false)} /> - {showLeftMenu && ( - {}} - searchTerm={''} - // eslint-disable-next-line @typescript-eslint/no-empty-function - applySearchQuery={(searchQuery: string) => {}} - showFilterMenu={showLeftMenu} - setShowFilterMenu={(show) => { - setShowLeftMenu(show) - }} - /> - )} - {props.children} - {showLogoutConfirmation ? ( - setShowLogoutConfirmation(false)} - /> - ) : null} - {showKeyboardCommandsModal ? ( - setShowKeyboardCommandsModal(false)} - /> - ) : null} - - + ) : null} + {showKeyboardCommandsModal ? ( + setShowKeyboardCommandsModal(false)} + /> + ) : null} + ) } diff --git a/packages/web/components/templates/discoverFeed/DiscoverContainer.tsx b/packages/web/components/templates/discoverFeed/DiscoverContainer.tsx index e3ab2dafd..76174e877 100644 --- a/packages/web/components/templates/discoverFeed/DiscoverContainer.tsx +++ b/packages/web/components/templates/discoverFeed/DiscoverContainer.tsx @@ -2,7 +2,7 @@ import { Box, HStack, VStack } from '../../elements/LayoutPrimitives' import { LibraryFilterMenu } from '../navMenu/LibraryMenu' import { DiscoverHeader } from './DiscoverHeader/DiscoverHeader' import { useRouter } from 'next/router' -import React, { useCallback, useEffect, useState } from "react" +import React, { useCallback, useEffect, useState } from 'react' import { DiscoverItemFeed } from './DiscoverFeed/DiscoverFeed' import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery' import toast from 'react-hot-toast' @@ -10,13 +10,13 @@ import { Button } from '../../elements/Button' import { showErrorToast } from '../../../lib/toastHelpers' import { saveDiscoverArticleMutation, - SaveDiscoverArticleOutput -} from "../../../lib/networking/mutations/saveDiscoverArticle" -import { saveUrlMutation } from "../../../lib/networking/mutations/saveUrlMutation" -import { useFetchMore } from "../../../lib/hooks/useFetchMoreScroll" -import { AddLinkModal } from "../homeFeed/AddLinkModal" -import { useGetDiscoverFeedItems } from "../../../lib/networking/queries/useGetDiscoverFeedItems" -import { useGetDiscoverFeeds } from "../../../lib/networking/queries/useGetDiscoverFeeds" + SaveDiscoverArticleOutput, +} from '../../../lib/networking/mutations/saveDiscoverArticle' +import { saveUrlMutation } from '../../../lib/networking/mutations/saveUrlMutation' +import { useFetchMore } from '../../../lib/hooks/useFetchMoreScroll' +import { AddLinkModal } from '../AddLinkModal' +import { useGetDiscoverFeedItems } from '../../../lib/networking/queries/useGetDiscoverFeedItems' +import { useGetDiscoverFeeds } from '../../../lib/networking/queries/useGetDiscoverFeeds' export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT' @@ -27,8 +27,8 @@ export function DiscoverContainer(): JSX.Element { const viewer = useGetViewerQuery() const [showFilterMenu, setShowFilterMenu] = useState(false) const [layoutType, setLayoutType] = useState('GRID_LAYOUT') - const [showAddLinkModal, setShowAddLinkModal] = useState(false); - const {feeds, revalidate, isValidating} = useGetDiscoverFeeds() + const [showAddLinkModal, setShowAddLinkModal] = useState(false) + const { feeds, revalidate, isValidating } = useGetDiscoverFeeds() const topics = [ { title: 'Popular', @@ -73,8 +73,16 @@ export function DiscoverContainer(): JSX.Element { }, ] - const [selectedFeed, setSelectedFeed] = useState("All Feeds"); - const { discoverItems, setTopic, activeTopic, isLoading, hasMore, setPage, page } = useGetDiscoverFeedItems(topics[1], selectedFeed) + const [selectedFeed, setSelectedFeed] = useState('All Feeds') + const { + discoverItems, + setTopic, + activeTopic, + isLoading, + hasMore, + setPage, + page, + } = useGetDiscoverFeedItems(topics[1], selectedFeed) const handleFetchMore = useCallback(() => { if (isLoading || !hasMore) { return @@ -88,7 +96,11 @@ export function DiscoverContainer(): JSX.Element { timezone: string, locale: string ): Promise => { - const result = await saveDiscoverArticleMutation({discoverArticleId, timezone, locale}) + const result = await saveDiscoverArticleMutation({ + discoverArticleId, + timezone, + locale, + }) if (result?.saveDiscoverArticle) { toast( () => ( @@ -160,8 +172,8 @@ export function DiscoverContainer(): JSX.Element { }, []) const setTopicAndReturnToTop = (topic: TopicTabData) => { - window.scroll(0,0); - setTopic(topic); + window.scroll(0, 0) + setTopic(topic) } return ( @@ -204,12 +216,12 @@ export function DiscoverContainer(): JSX.Element { items={discoverItems ?? []} viewer={viewer.viewerData?.me} /> - { showAddLinkModal && + {showAddLinkModal && ( setShowAddLinkModal(false)} /> - } + )} ) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index cc6014c41..0d221e34f 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -32,13 +32,13 @@ import { ConfirmationModal } from '../../patterns/ConfirmationModal' import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes' import { LinkedItemCard } from '../../patterns/LibraryCards/LinkedItemCard' import { Box, HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives' -import { AddLinkModal } from './AddLinkModal' +import { AddLinkModal } from '../AddLinkModal' import { EditLibraryItemModal } from './EditItemModals' import { EmptyLibrary } from './EmptyLibrary' import { HighlightItemsLayout } from './HighlightsLayout' import { LibraryFilterMenu } from '../navMenu/LibraryMenu' import { LibraryLegacyMenu } from '../navMenu/LibraryLegacyMenu' -import { LibraryHeader, MultiSelectMode } from './LibraryHeader' +import { LegacyLibraryHeader, MultiSelectMode } from './LibraryHeader' import { UploadModal } from '../UploadModal' import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation' import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation' @@ -975,7 +975,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { }} > {props.mode != 'highlights' && ( - { diff --git a/packages/web/components/templates/homeFeed/MultiSelectControls.tsx b/packages/web/components/templates/homeFeed/MultiSelectControls.tsx index e1cbb2113..fd624edcc 100644 --- a/packages/web/components/templates/homeFeed/MultiSelectControls.tsx +++ b/packages/web/components/templates/homeFeed/MultiSelectControls.tsx @@ -9,12 +9,29 @@ import { TrashIcon } from '../../elements/icons/TrashIcon' import { ConfirmationModal } from '../../patterns/ConfirmationModal' import { AddBulkLabelsModal } from '../article/AddBulkLabelsModal' import { X } from 'phosphor-react' -import { LibraryHeaderProps } from './LibraryHeader' +import { MultiSelectMode } from './LibraryHeader' import { HeaderCheckboxIcon } from '../../elements/icons/HeaderCheckboxIcon' import { Label } from '../../../lib/networking/fragments/labelFragment' import { MarkAsReadIcon } from '../../elements/icons/MarkAsReadIcon' +import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' -export const MultiSelectControls = (props: LibraryHeaderProps): JSX.Element => { +export type MultiSelectProps = { + viewer: UserBasicData | undefined + + searchTerm: string | undefined + applySearchQuery: (searchQuery: string) => void + + showFilterMenu: boolean + setShowFilterMenu: (show: boolean) => void + + numItemsSelected: number + multiSelectMode: MultiSelectMode + setMultiSelectMode: (mode: MultiSelectMode) => void + + performMultiSelectAction: (action: BulkAction, labelIds?: string[]) => void +} + +export const MultiSelectControls = (props: MultiSelectProps): JSX.Element => { const [showConfirmDelete, setShowConfirmDelete] = useState(false) const [showLabelsModal, setShowLabelsModal] = useState(false) // Don't change on immediate hover, the button has to be blurred at least once @@ -146,7 +163,7 @@ export const MultiSelectControls = (props: LibraryHeaderProps): JSX.Element => { ) } -export const CheckBoxButton = (props: LibraryHeaderProps): JSX.Element => { +export const CheckBoxButton = (props: MultiSelectProps): JSX.Element => { return ( + ) : ( + + )} + + + + {props.showEditTitleModal && ( + + props.actionHandler('update-item', item) + } + onOpenChange={() => { + props.setShowEditTitleModal(false) + props.setLinkToEdit(undefined) + }} + item={props.linkToEdit as LibraryItem} + /> + )} + {showUnsubscribeConfirmation && ( + setShowUnsubscribeConfirmation(false)} + /> + )} + {props.labelsTarget?.node.id && ( + { + if (props.labelsTarget) { + const activate = props.labelsTarget + props.setActiveItem(activate) + props.setLabelsTarget(undefined) + } + }} + /> + )} + {props.viewer && props.notebookTarget?.node.id && ( + { + // onClose={(highlights: Highlight[]) => { + // if (props.notebookTarget?.node.highlights) { + // props.notebookTarget.node.highlights = highlights + // } + props.setNotebookTarget(open ? props.notebookTarget : undefined) + }} + /> + )} + {showUploadModal && ( + setShowUploadModal(false)} /> + )} + + ) +} + +type LibraryItemsProps = { + items: LibraryItem[] + layout: LayoutType + viewer: UserBasicData | undefined + + gridContainerRef: React.RefObject + + setShowEditTitleModal: (show: boolean) => void + setLinkToEdit: (set: LibraryItem | undefined) => void + setShowUnsubscribeConfirmation: (show: true) => void + setLinkToUnsubscribe: (set: LibraryItem | undefined) => void + + isChecked: (itemId: string) => boolean + setIsChecked: (itemId: string, set: boolean) => void + multiSelectMode: MultiSelectMode + + actionHandler: ( + action: LinkedItemCardAction, + item: LibraryItem | undefined + ) => Promise +} + +function LibraryItems(props: LibraryItemsProps): JSX.Element { + return ( + + {props.items.map((linkedItem) => ( + div': { + bg: '$thLeftMenuBackground', + // bg: '$thLibraryBackground', + }, + '&:focus': { + outline: 'none', + '> div': { + outline: 'none', + bg: '$thBackgroundActive', + }, + }, + '&:hover': { + '> div': { + bg: '$thBackgroundActive', + boxShadow: '$cardBoxShadow', + }, + '> a': { + bg: '$thBackgroundActive', + }, + }, + }} + > + {props.viewer && ( + { + if (action === 'editTitle') { + props.setShowEditTitleModal(true) + props.setLinkToEdit(linkedItem) + } else if (action == 'unsubscribe') { + props.setShowUnsubscribeConfirmation(true) + props.setLinkToUnsubscribe(linkedItem) + } else { + props.actionHandler(action, linkedItem) + } + document.body.style.removeProperty('pointer-events') + }} + /> + )} + + ))} + + ) +} diff --git a/packages/web/components/templates/library/LibraryHeader.tsx b/packages/web/components/templates/library/LibraryHeader.tsx new file mode 100644 index 000000000..26318bb6a --- /dev/null +++ b/packages/web/components/templates/library/LibraryHeader.tsx @@ -0,0 +1,418 @@ +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 { Button, IconButton } from '../../elements/Button' +import { FunnelSimple, X } from 'phosphor-react' +import { LayoutType, LibraryMode } from '../homeFeed/HomeFeedContainer' +import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo' +import { DEFAULT_HEADER_HEIGHT, HeaderSpacer } from '../homeFeed/HeaderSpacer' +import { LIBRARY_LEFT_MENU_WIDTH } from '../navMenu/LibraryMenu' +import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation' +import { HeaderToggleGridIcon } from '../../elements/icons/HeaderToggleGridIcon' +import { HeaderToggleListIcon } from '../../elements/icons/HeaderToggleListIcon' +import { HeaderToggleTLDRIcon } from '../../elements/icons/HeaderToggleTLDRIcon' +import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' +import { userHasFeature } from '../../../lib/featureFlag' +import { + MultiSelectControls, + CheckBoxButton, +} from '../homeFeed/MultiSelectControls' + +export type MultiSelectMode = 'off' | 'none' | 'some' | 'visible' | 'search' + +export type LibraryHeaderProps = { + viewer: UserBasicData | undefined + + layout: LayoutType + updateLayout: (layout: LayoutType) => void + + searchTerm: string | undefined + applySearchQuery: (searchQuery: string) => void + + showFilterMenu: boolean + setShowFilterMenu: (show: boolean) => void + + numItemsSelected: number + multiSelectMode: MultiSelectMode + setMultiSelectMode: (mode: MultiSelectMode) => void + + performMultiSelectAction: (action: BulkAction, labelIds?: string[]) => void +} + +export const headerControlWidths = ( + layout: LayoutType, + multiSelectMode: MultiSelectMode +) => { + return { + width: '95%', + '@mdDown': { + width: '100%', + }, + '@media (min-width: 930px)': { + width: '620px', + }, + '@media (min-width: 1280px)': { + width: '940px', + }, + '@media (min-width: 1600px)': { + width: '1232px', + }, + } +} + +export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { + const [small, setSmall] = useState(false) + + useEffect(() => { + const handleScroll = () => { + setSmall(window.scrollY > 40) + } + if (typeof window !== 'undefined') { + window.addEventListener('scroll', handleScroll) + } + return () => { + window.removeEventListener('scroll', handleScroll) + } + }, []) + + return ( + <> + + + + + {/* This spacer is put in to push library content down + below the fixed header height. */} + + + ) +} + +function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element { + return ( + + {props.multiSelectMode !== 'off' ? ( + <> + + + ) : ( + + )} + + ) +} + +const HeaderControls = (props: LibraryHeaderProps): JSX.Element => { + const [searchBoxFocused, setSearchBoxFocused] = useState(false) + + return ( + <> + {!searchBoxFocused && ( + + + + )} + + + + + {/* {userHasFeature(props.viewer, 'ai-summaries') && ( + + )} */} + + + + + ) +} + +type MenuHeaderButtonProps = { + showFilterMenu: boolean + setShowFilterMenu: (show: boolean) => void +} + +export function MenuHeaderButton(props: MenuHeaderButtonProps): JSX.Element { + return ( + { + props.setShowFilterMenu(!props.showFilterMenu) + }} + > + + + + ) +} + +type SearchBoxProps = LibraryHeaderProps & { + searchBoxFocused: boolean + setSearchBoxFocused: (show: boolean) => void +} + +export function SearchBox(props: SearchBoxProps): JSX.Element { + const inputRef = useRef(null) + const [searchTerm, setSearchTerm] = useState(props.searchTerm ?? '') + + useEffect(() => { + setSearchTerm(props.searchTerm ?? '') + }, [props.searchTerm]) + + useKeyboardShortcuts( + searchBarCommands((action) => { + if (action === 'focusSearchBar' && inputRef.current) { + inputRef.current.select() + } + if (action == 'clearSearch' && inputRef.current) { + setSearchTerm('') + props.applySearchQuery('') + } + }) + ) + + return ( + + + + + + +
{ + event.preventDefault() + props.applySearchQuery(searchTerm || '') + inputRef.current?.blur() + }} + style={{ width: '100%' }} + > + { + event.target.select() + props.setSearchBoxFocused(true) + }} + onBlur={() => { + props.setSearchBoxFocused(false) + }} + onChange={(event) => { + setSearchTerm(event.target.value) + }} + onKeyDown={(event) => { + const key = event.key.toLowerCase() + if (key == 'escape') { + event.currentTarget.blur() + } + }} + /> + + + { + setSearchTerm('in:inbox') + props.applySearchQuery('') + inputRef.current?.blur() + }} + /> + +
+
+
+ ) +} + +type CancelSearchButtonProps = { + onClick: () => void +} + +const CancelSearchButton = (props: CancelSearchButtonProps): JSX.Element => { + const [color, setColor] = useState( + theme.colors.thTextContrast2.toString() + ) + return ( + + ) +} diff --git a/packages/web/components/templates/library/LibraryItemsContainer.tsx b/packages/web/components/templates/library/LibraryItemsContainer.tsx new file mode 100644 index 000000000..26b806515 --- /dev/null +++ b/packages/web/components/templates/library/LibraryItemsContainer.tsx @@ -0,0 +1,23 @@ +import { Allotment } from 'allotment' +import 'allotment/dist/style.css' +import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery' +import { useRouter } from 'next/router' +import { useKBar } from 'kbar' +import { useState } from 'react' +import { LibraryContainer } from './LibraryContainer' +import { LibrarySideBar } from './LibrarySideBar' + +export function LibraryItemsContainer(): JSX.Element { + const router = useRouter() + + return ( + + + + + + + + + ) +} diff --git a/packages/web/components/templates/library/LibrarySideBar.tsx b/packages/web/components/templates/library/LibrarySideBar.tsx new file mode 100644 index 000000000..d59b5a1f1 --- /dev/null +++ b/packages/web/components/templates/library/LibrarySideBar.tsx @@ -0,0 +1,62 @@ +import { Action, createAction, useKBar, useRegisterActions } from 'kbar' +import debounce from 'lodash/debounce' +import { useRouter } from 'next/router' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { Toaster } from 'react-hot-toast' +import TopBarProgress from 'react-topbar-progress-indicator' +import { useFetchMore } from '../../../lib/hooks/useFetchMoreScroll' +import { usePersistedState } from '../../../lib/hooks/usePersistedState' +import { libraryListCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' +import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts' +import { + PageType, + State, +} from '../../../lib/networking/fragments/articleFragment' +import { + SearchItem, + TypeaheadSearchItemsData, + typeaheadSearchQuery, +} from '../../../lib/networking/queries/typeaheadSearch' +import type { + LibraryItem, + LibraryItemsQueryInput, +} from '../../../lib/networking/queries/useGetLibraryItemsQuery' +import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery' +import { + useGetViewerQuery, + UserBasicData, +} from '../../../lib/networking/queries/useGetViewerQuery' +import { Button } from '../../elements/Button' +import { StyledText } from '../../elements/StyledText' +import { ConfirmationModal } from '../../patterns/ConfirmationModal' +import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes' +import { LinkedItemCard } from '../../patterns/LibraryCards/LinkedItemCard' +import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' +import { AddLinkModal } from '../AddLinkModal' +import { EditLibraryItemModal } from '../homeFeed/EditItemModals' +import { EmptyLibrary } from '../homeFeed/EmptyLibrary' +import { LegacyLibraryHeader, MultiSelectMode } from '../homeFeed/LibraryHeader' +import { UploadModal } from '../UploadModal' +import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation' +import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation' +import { + showErrorToast, + showSuccessToast, + showSuccessToastWithAction, +} 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 { PinnedButtons } from '../homeFeed/PinnedButtons' +import { PinnedSearch } from '../../../pages/settings/pinned-searches' +import { FetchItemsError } from '../homeFeed/FetchItemsError' +import { LibraryHeader } from './LibraryHeader' + +type LibrarySideBarProps = { + text: string +} + +export function LibrarySideBar(props: LibrarySideBarProps): JSX.Element { + return {props.text} +} diff --git a/packages/web/components/templates/navMenu/NavigationMenu.tsx b/packages/web/components/templates/navMenu/NavigationMenu.tsx index 72ab6d15a..87db74d30 100644 --- a/packages/web/components/templates/navMenu/NavigationMenu.tsx +++ b/packages/web/components/templates/navMenu/NavigationMenu.tsx @@ -105,9 +105,9 @@ export function NavigationMenu(props: LibraryFilterMenuProps): JSX.Element { left: '0px', top: '0px', position: 'fixed', - bg: '$thLeftMenuBackground', height: '100%', width: LIBRARY_LEFT_MENU_WIDTH, + bg: '$thLeftMenuBackground', overflowY: 'auto', overflowX: 'hidden', '&::-webkit-scrollbar': { @@ -200,7 +200,7 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { gap: '5px', width: '100%', borderBottom: '1px solid $thBorderColor', - px: '15px', + px: '0px', pb: '25px', }} alignment="start" @@ -213,13 +213,6 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { isSelected={props.section == 'home'} icon={} /> - } - /> { isSelected={props.section == 'library'} icon={} /> + } + /> { m: '0px', gap: '8px', width: '100%', - px: '15px', + px: '0px', pb: '25px', }} alignment="start" @@ -815,6 +815,7 @@ function NavButton(props: NavButtonProps): JSX.Element { width: '100%', maxWidth: '100%', height: '34px', + px: '15px', backgroundColor: props.isSelected ? '$thLibrarySelectionColor' diff --git a/packages/web/package.json b/packages/web/package.json index 2afe9b389..cfdc9f0ae 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -31,6 +31,7 @@ "@radix-ui/react-switch": "^1.0.1", "@sentry/nextjs": "^7.42.0", "@stitches/react": "^1.2.5", + "allotment": "^1.20.2", "antd": "4.24.3", "axios": "^1.2.0", "cookie": "^0.5.0", diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index c5d6c41df..a7edcb344 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -1,7 +1,7 @@ import * as HoverCard from '@radix-ui/react-hover-card' import { styled } from '@stitches/react' import { useRouter } from 'next/router' -import { useCallback, useMemo, useState } from 'react' +import { useMemo, useState } from 'react' import { Button } from '../../components/elements/Button' import { AddToLibraryActionIcon } from '../../components/elements/icons/home/AddToLibraryActionIcon' import { ArchiveActionIcon } from '../../components/elements/icons/home/ArchiveActionIcon' diff --git a/packages/web/pages/library/index.tsx b/packages/web/pages/library/index.tsx index 7d7eb06ad..67d14c46b 100644 --- a/packages/web/pages/library/index.tsx +++ b/packages/web/pages/library/index.tsx @@ -1,13 +1,14 @@ import { NavigationLayout } from '../../components/templates/NavigationLayout' import { PrimaryLayout } from '../../components/templates/PrimaryLayout' import { HomeFeedContainer } from '../../components/templates/homeFeed/HomeFeedContainer' -import { VStack } from '../../components/elements/LayoutPrimitives' +import { Box, VStack } from '../../components/elements/LayoutPrimitives' +import { LibraryContainer } from '../../components/templates/library/LibraryContainer' +import { LibraryItemsContainer } from '../../components/templates/library/LibraryItemsContainer' +import { LibrarySideBar } from '../../components/templates/library/LibrarySideBar' +import { Allotment, LayoutPriority } from 'allotment' +import 'allotment/dist/style.css' -export default function Home(): JSX.Element { - return -} - -function LoadedContent(): JSX.Element { +export default function Library(): JSX.Element { return ( - - - + {/* + */} + + + + {/* + + + + */} ) } diff --git a/yarn.lock b/yarn.lock index 76355e48b..6a2fe7a16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3793,6 +3793,11 @@ dependencies: lodash "^4.17.21" +"@juggle/resize-observer@^3.3.1": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" + integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== + "@langchain/anthropic@^0.1.16": version "0.1.16" resolved "https://registry.yarnpkg.com/@langchain/anthropic/-/anthropic-0.1.16.tgz#c2a9d3dd4e02df7118dd97cf2503c9bd1a4de5ad" @@ -9649,6 +9654,18 @@ ajv@^8.11.0: require-from-string "^2.0.2" uri-js "^4.2.2" +allotment@^1.20.2: + version "1.20.2" + resolved "https://registry.yarnpkg.com/allotment/-/allotment-1.20.2.tgz#5ea3a630b3265479debb69156658244711f83843" + integrity sha512-TaCuHfYNcsJS9EPk04M7TlG5Rl3vbAdHeAyrTE9D5vbpzV+wxnRoUrulDbfnzaQcPIZKpHJNixDOoZNuzliKEA== + dependencies: + classnames "^2.3.0" + eventemitter3 "^5.0.0" + lodash.clamp "^4.0.0" + lodash.debounce "^4.0.0" + lodash.isequal "^4.5.0" + use-resize-observer "^9.0.0" + ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" @@ -12147,6 +12164,11 @@ classnames@^2.2.6: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== +classnames@^2.3.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + cld@^2.9.1: version "2.9.1" resolved "https://registry.yarnpkg.com/cld/-/cld-2.9.1.tgz#0c6685672d9f4612dfeb75eabfdd17bf282a87a6" @@ -15330,6 +15352,11 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + eventid@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/eventid/-/eventid-2.0.1.tgz#574e860149457a79a2efe788c459f0c3062d02ec" @@ -21076,12 +21103,17 @@ lodash.capitalize@^4.2.1: resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" integrity sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw== +lodash.clamp@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/lodash.clamp/-/lodash.clamp-4.0.3.tgz#5c24bedeeeef0753560dc2b4cb4671f90a6ddfaa" + integrity sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg== + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= -lodash.debounce@^4.0.8: +lodash.debounce@^4.0.0, lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= @@ -21126,6 +21158,11 @@ lodash.isboolean@^3.0.3: resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.isfunction@^3.0.9: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" @@ -31088,6 +31125,13 @@ use-latest@^1.0.0: dependencies: use-isomorphic-layout-effect "^1.0.0" +use-resize-observer@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/use-resize-observer/-/use-resize-observer-9.1.0.tgz#14735235cf3268569c1ea468f8a90c5789fc5c6c" + integrity sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow== + dependencies: + "@juggle/resize-observer" "^3.3.1" + use-sidecar@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2" From 3f6e7c924a1766521fb648794018fa509f1f9789 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 10 Jun 2024 17:34:38 +0800 Subject: [PATCH 03/23] Work on new left menu w/shortcuts --- .../api/src/entity/user_personalization.ts | 20 + packages/api/src/routers/shortcuts_router.ts | 107 ++ packages/api/src/server.ts | 2 + .../api/src/services/user_personalization.ts | 137 ++- .../elements/AndroidInstallHelp.tsx | 2 +- packages/web/components/elements/Button.tsx | 7 + .../web/components/elements/CloseButton.tsx | 2 +- .../web/components/elements/DiscoverMenu.tsx | 2 +- .../web/components/elements/EditLabelChip.tsx | 2 +- .../elements/EditLabelChipStack.tsx | 2 +- .../components/elements/FeatureHelpBox.tsx | 2 +- .../components/elements/IOSInstallHelp.tsx | 2 +- .../web/components/elements/LabelChip.tsx | 2 +- .../web/components/elements/MenuTrigger.tsx | 2 +- .../components/elements/OutlinedLabelChip.tsx | 2 +- packages/web/components/elements/Table.tsx | 2 +- .../elements/images/MoreOptionsIcon.tsx | 10 +- .../web/components/patterns/HighlightBar.tsx | 2 +- .../patterns/HighlightHoverActions.tsx | 2 +- .../patterns/LibraryCards/LibraryGridCard.tsx | 2 +- .../LibraryCards/LibraryHighlightGridCard.tsx | 6 +- .../LibraryCards/LibraryHoverActions.tsx | 2 +- .../patterns/LibraryCards/LibraryListCard.tsx | 2 +- .../web/components/patterns/LinkHoverBar.tsx | 2 +- .../patterns/MDEditorSavePlugin.tsx | 2 +- .../web/components/templates/AddLinkModal.tsx | 2 +- .../components/templates/NavigationLayout.tsx | 6 +- .../components/templates/PrimaryDropdown.tsx | 2 +- .../components/templates/SettingsLayout.tsx | 2 +- .../web/components/templates/UploadModal.tsx | 2 +- .../templates/article/NotebookHeader.tsx | 2 +- .../templates/article/NotebookModal.tsx | 12 +- .../article/ReaderSettingsControl.tsx | 2 +- .../templates/article/SetLabelsControl.tsx | 2 +- .../templates/article/ShareModal.tsx | 2 +- .../DiscoverHeader/SmallTopicBar.tsx | 2 +- .../DiscoverHeader/SmallerHeaderLayout.tsx | 2 +- .../discoverFeed/DiscoverHeader/TopicBar.tsx | 2 +- .../DiscoverItems/DiscoverHoverActions.tsx | 23 +- .../DiscoverItems/DiscoverItemGridCard.tsx | 26 +- .../DiscoverItems/DiscoverItemListCard.tsx | 24 +- .../templates/homeFeed/EmptyHighlights.tsx | 2 +- .../templates/homeFeed/HighlightItem.tsx | 7 +- .../templates/homeFeed/HighlightsLayout.tsx | 7 +- .../templates/homeFeed/LibraryHeader.tsx | 2 +- .../homeFeed/MultiSelectControls.tsx | 2 +- .../templates/homeFeed/TLDRLayout.tsx | 2 +- .../templates/integrations/Webhooks.tsx | 2 +- .../templates/library/LibraryHeader.tsx | 2 +- .../templates/navMenu/LibraryLegacyMenu.tsx | 2 +- .../templates/navMenu/LibraryMenu.tsx | 2 +- .../templates/navMenu/NavigationMenu.tsx | 1063 ++++++++--------- .../templates/navMenu/SettingsDropdown.tsx | 2 +- .../templates/navMenu/SettingsMenu.tsx | 2 +- .../templates/settings/SettingsTable.tsx | 2 +- packages/web/lib/networking/networkHelpers.ts | 11 +- packages/web/lib/toastHelpers.tsx | 2 +- packages/web/package.json | 10 +- packages/web/pages/_app.tsx | 1 + .../pages/settings/discover-feeds/index.tsx | 2 +- packages/web/pages/settings/emails/index.tsx | 7 +- packages/web/pages/settings/features/beta.tsx | 2 +- packages/web/pages/settings/feeds/index.tsx | 2 +- packages/web/pages/settings/integrations.tsx | 6 +- packages/web/pages/settings/labels.tsx | 7 +- .../web/pages/settings/pinned-searches.tsx | 2 +- .../web/pages/settings/saved-searches.tsx | 33 +- packages/web/pages/settings/shortcuts.tsx | 13 +- .../web/pages/tools/import/matter-archive.tsx | 2 +- yarn.lock | 127 +- 70 files changed, 1043 insertions(+), 721 deletions(-) create mode 100644 packages/api/src/routers/shortcuts_router.ts diff --git a/packages/api/src/entity/user_personalization.ts b/packages/api/src/entity/user_personalization.ts index 7fa36b470..6411b3885 100644 --- a/packages/api/src/entity/user_personalization.ts +++ b/packages/api/src/entity/user_personalization.ts @@ -8,6 +8,23 @@ import { UpdateDateColumn, } from 'typeorm' import { User } from './user' +import { Label } from './label' + +export type ShortcutType = 'search' | 'label' | 'newsletter' | 'feed' | 'folder' + +export type Shortcut = { + type: ShortcutType + + id: string + name: string + section: string + filter?: string + + icon?: string + label?: Label + + children?: Shortcut[] +} @Entity({ name: 'user_personalization' }) export class UserPersonalization { @@ -59,4 +76,7 @@ export class UserPersonalization { @Column('jsonb') digestConfig?: any | null + + @Column({ type: 'jsonb', nullable: true }) + shortcuts?: any | null // Explicitly allow null values } diff --git a/packages/api/src/routers/shortcuts_router.ts b/packages/api/src/routers/shortcuts_router.ts new file mode 100644 index 000000000..a6dd88f8a --- /dev/null +++ b/packages/api/src/routers/shortcuts_router.ts @@ -0,0 +1,107 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import cors from 'cors' +import express from 'express' +import { env } from '../env' +import { getClaimsByToken, getTokenByRequest } from '../utils/auth' +import { corsConfig } from '../utils/corsConfig' +import { logger } from '../utils/logger' +import { + getShortcuts, + resetShortcuts, + setShortcuts, +} from '../services/user_personalization' + +export function shortcutsRouter() { + const router = express.Router() + + router.get('/', cors(corsConfig), async (req, res) => { + logger.info('get shortcuts router') + const token = getTokenByRequest(req) + + let claims + try { + claims = await getClaimsByToken(token) + if (!claims) { + logger.info('failed to authorize') + return res.status(401).send('UNAUTHORIZED') + } + } catch (e) { + logger.info('failed to authorize', e) + return res.status(401).send('UNAUTHORIZED') + } + + try { + const shortcuts = await getShortcuts(claims.uid) + return res.send({ + shortcuts: shortcuts ?? [], + }) + } catch (e) { + logger.info('error getting shortcuts', e) + } + + return res.status(500).send('UNKNOWN') + }) + + router.options('/', cors({ ...corsConfig, maxAge: 600 })) + router.put('/', cors(corsConfig), async (req, res) => { + logger.info('put shortcuts router') + const token = getTokenByRequest(req) + + let claims + try { + claims = await getClaimsByToken(token) + if (!claims) { + logger.info('failed to authorize') + return res.status(401).send('UNAUTHORIZED') + } + } catch (e) { + logger.info('failed to authorize', e) + return res.status(401).send('UNAUTHORIZED') + } + + try { + const shortcuts = await setShortcuts(claims.uid, req.body.shortcuts) + return res.send({ + shortcuts: shortcuts ?? [], + }) + } catch (e) { + logger.info('error settings shortcuts', e) + } + + return res.status(500).send('UNKNOWN') + }) + + router.delete('/', cors(corsConfig), async (req, res) => { + logger.info('delete shortcuts router') + const token = getTokenByRequest(req) + + let claims + try { + claims = await getClaimsByToken(token) + if (!claims) { + logger.info('failed to authorize') + return res.status(401).send('UNAUTHORIZED') + } + } catch (e) { + logger.info('failed to authorize', e) + return res.status(401).send('UNAUTHORIZED') + } + + try { + const success = await resetShortcuts(claims.uid) + if (success) { + const shortcuts = await getShortcuts(claims.uid) + return res.send({ + shortcuts: shortcuts ?? [], + }) + } + } catch (e) { + logger.info('error settings shortcuts', e) + } + + return res.status(500).send('UNKNOWN') + }) + + return router +} diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts index 7952cf5f8..3efaf69af 100755 --- a/packages/api/src/server.ts +++ b/packages/api/src/server.ts @@ -47,6 +47,7 @@ import { corsConfig } from './utils/corsConfig' import { getClientFromUserAgent } from './utils/helpers' import { buildLogger, buildLoggerTransport, logger } from './utils/logger' import { apiLimiter, authLimiter } from './utils/rate_limit' +import { shortcutsRouter } from './routers/shortcuts_router' const PORT = process.env.PORT || 4000 @@ -94,6 +95,7 @@ export const createApp = (): Express => { app.use('/api/mobile-auth', authLimiter, mobileAuthRouter()) app.use('/api/page', pageRouter()) app.use('/api/user', userRouter()) + app.use('/api/shortcuts', shortcutsRouter()) app.use('/api/article', articleRouter()) app.use('/api/ai-summary', aiSummariesRouter()) app.use('/api/explain', explainRouter()) diff --git a/packages/api/src/services/user_personalization.ts b/packages/api/src/services/user_personalization.ts index 40e39e459..d517215bb 100644 --- a/packages/api/src/services/user_personalization.ts +++ b/packages/api/src/services/user_personalization.ts @@ -1,6 +1,10 @@ -import { DeepPartial } from 'typeorm' -import { UserPersonalization } from '../entity/user_personalization' +import { DeepPartial, IsNull } from 'typeorm' +import { Shortcut, UserPersonalization } from '../entity/user_personalization' import { authTrx } from '../repository' +import { findLabelsByUserId } from './labels' +import { findSubscriptionById } from './subscriptions' +import { Filter } from '../entity/filter' +import { Subscription, SubscriptionStatus } from '../entity/subscription' export const findUserPersonalization = async (userId: string) => { return authTrx( @@ -34,3 +38,132 @@ export const saveUserPersonalization = async ( userId ) } + +export const getShortcuts = async (userId: string): Promise => { + const personalization = await authTrx( + (t) => + t.getRepository(UserPersonalization).findOneBy({ + user: { id: userId }, + }), + undefined, + userId + ) + if (personalization?.shortcuts) { + return personalization?.shortcuts as Shortcut[] + } + + return await userDefaultShortcuts(userId) +} + +export const resetShortcuts = async (userId: string): Promise => { + const result = await authTrx( + (t) => { + return t + .createQueryBuilder() + .update(UserPersonalization) + .set({ shortcuts: () => 'null' }) // Use a raw SQL string to set the value to null + .where({ + user: { id: userId }, + }) + .execute() + }, + undefined, + userId + ) + if (!result) { + throw Error('Could not update shortcuts') + } + return true +} + +export const setShortcuts = async ( + userId: string, + shortcuts: Shortcut[] +): Promise => { + const result = await authTrx( + (t) => + t.getRepository(UserPersonalization).update( + { + user: { id: userId }, + }, + { + shortcuts: shortcuts, + } + ), + undefined, + userId + ) + if (!result.affected || result.affected < 1) { + throw Error('Could not update shortcuts') + } + return shortcuts +} + +const userDefaultShortcuts = async (userId: string): Promise => { + const labels = await findLabelsByUserId(userId) + const savedSearches = await authTrx((t) => + t.getRepository(Filter).find({ + where: { user: { id: userId } }, + order: { position: 'ASC' }, + }) + ) + const subscriptions = await authTrx((t) => + t.getRepository(Subscription).find({ + where: { user: { id: userId }, status: SubscriptionStatus.Active }, + order: { mostRecentItemDate: 'DESC' }, + }) + ) + + return [ + { + id: '1', + type: 'folder', + name: 'Labels', + section: 'library', + children: labels.map((label) => { + return { + id: label.id, + type: 'label', + name: label.name, + section: 'library', + label: label, + filter: `in:all label:"${label.name}"`, + } + }), + }, + { + id: '2', + type: 'folder', + name: 'Subscriptions', + section: 'subscriptions', + children: subscriptions.map((subscription) => { + return { + id: subscription.id, + type: subscription.type == 'NEWSLETTER' ? 'newsletter' : 'feed', + name: subscription.name, + section: 'subscriptions', + icon: subscription.icon ?? undefined, + filter: + subscription.type == 'NEWSLETTER' + ? `in:following subscription:"${subscription.name}"` + : `in:following rss:"${subscription.url}"`, + } + }), + }, + { + id: '3', + type: 'folder', + name: 'Saved Searches', + section: 'library', + children: savedSearches.map((search) => { + return { + id: search.id, + type: 'search', + name: search.name, + section: 'library', + filter: search.filter, + } + }), + }, + ] +} diff --git a/packages/web/components/elements/AndroidInstallHelp.tsx b/packages/web/components/elements/AndroidInstallHelp.tsx index f8acd5ece..c79cee33f 100644 --- a/packages/web/components/elements/AndroidInstallHelp.tsx +++ b/packages/web/components/elements/AndroidInstallHelp.tsx @@ -3,7 +3,7 @@ import { Desktop, DeviceTabletSpeaker, DeviceMobileCamera, -} from 'phosphor-react' +} from '@phosphor-icons/react' import { Box, HStack } from './LayoutPrimitives' import { StyledText, StyledAnchor } from './StyledText' diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index e0a999ce3..010e0bdba 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -438,6 +438,13 @@ export const Button = styled('button', { borderRadius: '5px', '&:hover': { bg: '$readerHoverBg', opacity: '1' }, }, + menuAction: { + display: 'flex', + border: 'none', + bg: 'transparent', + cursor: 'pointer', + '&:hover': { bg: 'transparent', opacity: '1' }, + }, themeSwitch: { p: '0px', m: '0px', diff --git a/packages/web/components/elements/CloseButton.tsx b/packages/web/components/elements/CloseButton.tsx index d4468c629..9dabf9102 100644 --- a/packages/web/components/elements/CloseButton.tsx +++ b/packages/web/components/elements/CloseButton.tsx @@ -1,4 +1,4 @@ -import { X } from 'phosphor-react' +import { X } from '@phosphor-icons/react' import { useState } from 'react' import { Button } from './Button' import { Box } from './LayoutPrimitives' diff --git a/packages/web/components/elements/DiscoverMenu.tsx b/packages/web/components/elements/DiscoverMenu.tsx index 9d28e6fc4..c178e955d 100644 --- a/packages/web/components/elements/DiscoverMenu.tsx +++ b/packages/web/components/elements/DiscoverMenu.tsx @@ -1,6 +1,6 @@ import { HStack, SpanBox, VStack } from './LayoutPrimitives' import { StyledText } from './StyledText' -import { NewspaperClipping } from 'phosphor-react' +import { NewspaperClipping } from '@phosphor-icons/react' import { theme } from '../tokens/stitches.config' import { useEffect, useState } from 'react' import { useRouter } from 'next/router' diff --git a/packages/web/components/elements/EditLabelChip.tsx b/packages/web/components/elements/EditLabelChip.tsx index 930f9f739..605d292be 100644 --- a/packages/web/components/elements/EditLabelChip.tsx +++ b/packages/web/components/elements/EditLabelChip.tsx @@ -1,6 +1,6 @@ import { Button } from './Button' import { SpanBox, HStack } from './LayoutPrimitives' -import { Circle, X } from 'phosphor-react' +import { Circle, X } from '@phosphor-icons/react' import { isDarkTheme } from '../../lib/themeUpdater' import { theme } from '../tokens/stitches.config' diff --git a/packages/web/components/elements/EditLabelChipStack.tsx b/packages/web/components/elements/EditLabelChipStack.tsx index 2844f2264..535cfd568 100644 --- a/packages/web/components/elements/EditLabelChipStack.tsx +++ b/packages/web/components/elements/EditLabelChipStack.tsx @@ -1,6 +1,6 @@ import { Button } from './Button' import { SpanBox, HStack } from './LayoutPrimitives' -import { Circle, X } from 'phosphor-react' +import { Circle, X } from '@phosphor-icons/react' import { isDarkTheme } from '../../lib/themeUpdater' import { Label } from '../../lib/networking/fragments/labelFragment' import { useMemo } from 'react' diff --git a/packages/web/components/elements/FeatureHelpBox.tsx b/packages/web/components/elements/FeatureHelpBox.tsx index 9390dddb3..0bb350259 100644 --- a/packages/web/components/elements/FeatureHelpBox.tsx +++ b/packages/web/components/elements/FeatureHelpBox.tsx @@ -3,7 +3,7 @@ import { theme } from '../tokens/stitches.config' import { Button } from './Button' import { CloseIcon } from './icons/CloseIcon' import { HelpfulSlothImage } from './images/HelpfulSlothImage' -import { ArrowSquareOut } from 'phosphor-react' +import { ArrowSquareOut } from '@phosphor-icons/react' import { useEffect, useState } from 'react' type FeatureHelpBoxProps = { diff --git a/packages/web/components/elements/IOSInstallHelp.tsx b/packages/web/components/elements/IOSInstallHelp.tsx index 5eb96fc36..1c7ad9c28 100644 --- a/packages/web/components/elements/IOSInstallHelp.tsx +++ b/packages/web/components/elements/IOSInstallHelp.tsx @@ -3,7 +3,7 @@ import { Desktop, DeviceTabletSpeaker, DeviceMobileCamera, -} from 'phosphor-react' +} from '@phosphor-icons/react' import { Box, HStack } from './LayoutPrimitives' import { StyledText, StyledAnchor } from './StyledText' diff --git a/packages/web/components/elements/LabelChip.tsx b/packages/web/components/elements/LabelChip.tsx index 9aa959897..c807ef3ac 100644 --- a/packages/web/components/elements/LabelChip.tsx +++ b/packages/web/components/elements/LabelChip.tsx @@ -1,5 +1,5 @@ import { SpanBox, HStack } from './LayoutPrimitives' -import { Circle, X } from 'phosphor-react' +import { Circle, X } from '@phosphor-icons/react' type LabelChipProps = { text: string diff --git a/packages/web/components/elements/MenuTrigger.tsx b/packages/web/components/elements/MenuTrigger.tsx index 4ec61eef5..63f105653 100644 --- a/packages/web/components/elements/MenuTrigger.tsx +++ b/packages/web/components/elements/MenuTrigger.tsx @@ -1,4 +1,4 @@ -import { DotsThreeVertical } from 'phosphor-react' +import { DotsThreeVertical } from '@phosphor-icons/react' import { theme } from '../tokens/stitches.config' import { Box } from './LayoutPrimitives' diff --git a/packages/web/components/elements/OutlinedLabelChip.tsx b/packages/web/components/elements/OutlinedLabelChip.tsx index 5991fd378..cd7da0827 100644 --- a/packages/web/components/elements/OutlinedLabelChip.tsx +++ b/packages/web/components/elements/OutlinedLabelChip.tsx @@ -1,5 +1,5 @@ import { SpanBox, HStack } from './LayoutPrimitives' -import { Circle, X } from 'phosphor-react' +import { Circle, X } from '@phosphor-icons/react' type LabelChipProps = { text: string diff --git a/packages/web/components/elements/Table.tsx b/packages/web/components/elements/Table.tsx index ce81966bf..468076597 100644 --- a/packages/web/components/elements/Table.tsx +++ b/packages/web/components/elements/Table.tsx @@ -8,7 +8,7 @@ import { Td, } from 'react-super-responsive-table' import 'react-super-responsive-table/dist/SuperResponsiveTableStyle.css' -import { PencilSimple, Plus, Trash } from 'phosphor-react' +import { PencilSimple, Plus, Trash } from '@phosphor-icons/react' import { Box, SpanBox, VStack } from './LayoutPrimitives' import { styled } from '../tokens/stitches.config' import { StyledText } from './StyledText' diff --git a/packages/web/components/elements/images/MoreOptionsIcon.tsx b/packages/web/components/elements/images/MoreOptionsIcon.tsx index c51229426..c4494ab32 100644 --- a/packages/web/components/elements/images/MoreOptionsIcon.tsx +++ b/packages/web/components/elements/images/MoreOptionsIcon.tsx @@ -1,4 +1,4 @@ -import { DotsThree, DotsThreeVertical } from 'phosphor-react' +import { DotsThree, DotsThreeVertical } from '@phosphor-icons/react' type Orientation = 'horizontal' | 'vertical' @@ -10,8 +10,8 @@ type MoreOptionsIconProps = { export function MoreOptionsIcon(props: MoreOptionsIconProps): JSX.Element { return props.orientation == 'horizontal' ? ( - - ) : ( - - ) + + ) : ( + + ) } diff --git a/packages/web/components/patterns/HighlightBar.tsx b/packages/web/components/patterns/HighlightBar.tsx index c85f41301..4fc42080d 100644 --- a/packages/web/components/patterns/HighlightBar.tsx +++ b/packages/web/components/patterns/HighlightBar.tsx @@ -2,7 +2,7 @@ import { isAndroid } from '../../lib/deviceType' import { styled, theme } from '../tokens/stitches.config' import { Button } from '../elements/Button' import { HStack, Box } from '../elements/LayoutPrimitives' -import { Circle, CheckCircle } from 'phosphor-react' +import { Circle, CheckCircle } from '@phosphor-icons/react' import { LabelIcon } from '../elements/icons/LabelIcon' import { NotebookIcon } from '../elements/icons/NotebookIcon' import { highlightColor, highlightColors } from '../../lib/themeUpdater' diff --git a/packages/web/components/patterns/HighlightHoverActions.tsx b/packages/web/components/patterns/HighlightHoverActions.tsx index 4903fa116..95cdfc173 100644 --- a/packages/web/components/patterns/HighlightHoverActions.tsx +++ b/packages/web/components/patterns/HighlightHoverActions.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { Box } from '../elements/LayoutPrimitives' import { Button } from '../elements/Button' import { theme } from '../tokens/stitches.config' -import { BookOpen, Copy } from 'phosphor-react' +import { BookOpen, Copy } from '@phosphor-icons/react' import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery' import { Highlight } from '../../lib/networking/fragments/highlightFragment' import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers' diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 9c260b5cc..c5e388d81 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -26,7 +26,7 @@ import { autoUpdate, } from '@floating-ui/react' import { CardMenu } from '../CardMenu' -import { DotsThree } from 'phosphor-react' +import { DotsThree } from '@phosphor-icons/react' import { isTouchScreenDevice } from '../../../lib/deviceType' import { LoadingBarOverlay, ProgressBarOverlay } from './LibraryListCard' import { GridFallbackImage } from './FallbackImage' diff --git a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx index 0ad21d885..4145c7d41 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx @@ -1,6 +1,6 @@ import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives' import { useCallback, useMemo, useState } from 'react' -import { CaretDown, CaretUp } from 'phosphor-react' +import { CaretDown, CaretUp } from '@phosphor-icons/react' import { MetaStyle, timeAgo, TitleStyle } from './LibraryCardStyles' import { styled } from '@stitches/react' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' @@ -197,7 +197,9 @@ export function LibraryHighlightGridCard( event.preventDefault() }} > - {`View ${highlightCount} highlight${highlightCount > 1 ? 's' : ''}`} + {`View ${highlightCount} highlight${ + highlightCount > 1 ? 's' : '' + }`} {}} searchTerm="" + // eslint-disable-next-line @typescript-eslint/no-empty-function applySearchQuery={() => {}} showFilterMenu={showNavMenu} setShowFilterMenu={setShowNavMenu} diff --git a/packages/web/components/templates/PrimaryDropdown.tsx b/packages/web/components/templates/PrimaryDropdown.tsx index f5698a5f5..e2510ea7c 100644 --- a/packages/web/components/templates/PrimaryDropdown.tsx +++ b/packages/web/components/templates/PrimaryDropdown.tsx @@ -1,5 +1,5 @@ import { useRouter } from 'next/router' -import { Moon, Sun } from 'phosphor-react' +import { Moon, Sun } from '@phosphor-icons/react' import { ReactNode, useCallback } from 'react' import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery' import { Avatar } from '../elements/Avatar' diff --git a/packages/web/components/templates/SettingsLayout.tsx b/packages/web/components/templates/SettingsLayout.tsx index 71f8f44c6..a6716af9d 100644 --- a/packages/web/components/templates/SettingsLayout.tsx +++ b/packages/web/components/templates/SettingsLayout.tsx @@ -13,7 +13,7 @@ import { SettingsMenu } from './navMenu/SettingsMenu' import { SettingsDropdown } from './navMenu/SettingsDropdown' import { useVerifyAuth } from '../../lib/hooks/useVerifyAuth' import Link from 'next/link' -import { CaretLeft } from 'phosphor-react' +import { CaretLeft } from '@phosphor-icons/react' type SettingsLayoutProps = { title?: string diff --git a/packages/web/components/templates/UploadModal.tsx b/packages/web/components/templates/UploadModal.tsx index e19c74e04..a3dc64937 100644 --- a/packages/web/components/templates/UploadModal.tsx +++ b/packages/web/components/templates/UploadModal.tsx @@ -1,7 +1,7 @@ import * as Progress from '@radix-ui/react-progress' import { styled } from '@stitches/react' import axios from 'axios' -import { File } from 'phosphor-react' +import { File } from '@phosphor-icons/react' import { useCallback, useRef, useState } from 'react' import Dropzone, { DropEvent, DropzoneRef, FileRejection } from 'react-dropzone' import { v4 as uuidv4 } from 'uuid' diff --git a/packages/web/components/templates/article/NotebookHeader.tsx b/packages/web/components/templates/article/NotebookHeader.tsx index e0dbaf3c3..1ff0b98f0 100644 --- a/packages/web/components/templates/article/NotebookHeader.tsx +++ b/packages/web/components/templates/article/NotebookHeader.tsx @@ -1,6 +1,6 @@ import { HStack } from '../../elements/LayoutPrimitives' import { StyledText } from '../../elements/StyledText' -import { Sidebar } from 'phosphor-react' +import { Sidebar } from '@phosphor-icons/react' import { theme } from '../../tokens/stitches.config' import { Button } from '../../elements/Button' import { ExportIcon } from '../../elements/icons/ExportIcon' diff --git a/packages/web/components/templates/article/NotebookModal.tsx b/packages/web/components/templates/article/NotebookModal.tsx index 0250a2b44..4b865af66 100644 --- a/packages/web/components/templates/article/NotebookModal.tsx +++ b/packages/web/components/templates/article/NotebookModal.tsx @@ -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 { X } from 'phosphor-react' +import { X } from '@phosphor-icons/react' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { diff_match_patch } from 'diff-match-patch' @@ -37,11 +37,13 @@ export const getHighlightLocation = (patch: string): number | undefined => { export function NotebookModal(props: NotebookModalProps): JSX.Element { const [showConfirmDeleteNote, setShowConfirmDeleteNote] = useState(false) - const [allAnnotations, setAllAnnotations] = - useState(undefined) + const [allAnnotations, setAllAnnotations] = useState( + undefined + ) - const [deletedHighlights, setDeletedAnnotations] = - useState(undefined) + const [deletedHighlights, setDeletedAnnotations] = useState< + Highlight[] | undefined + >(undefined) const handleClose = useCallback(() => { props.onClose(allAnnotations ?? [], deletedHighlights ?? []) diff --git a/packages/web/components/templates/article/ReaderSettingsControl.tsx b/packages/web/components/templates/article/ReaderSettingsControl.tsx index 279605c87..ec956daad 100644 --- a/packages/web/components/templates/article/ReaderSettingsControl.tsx +++ b/packages/web/components/templates/article/ReaderSettingsControl.tsx @@ -8,7 +8,7 @@ import { CaretLeft, CaretRight, Check, -} from 'phosphor-react' +} from '@phosphor-icons/react' import { TickedRangeSlider } from '../../elements/TickedRangeSlider' import { showSuccessToast } from '../../../lib/toastHelpers' import { ReaderSettings } from '../../../lib/hooks/useReaderSettings' diff --git a/packages/web/components/templates/article/SetLabelsControl.tsx b/packages/web/components/templates/article/SetLabelsControl.tsx index e7ad24f86..b8ae855a0 100644 --- a/packages/web/components/templates/article/SetLabelsControl.tsx +++ b/packages/web/components/templates/article/SetLabelsControl.tsx @@ -5,7 +5,7 @@ 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, Plus, WarningCircle } from 'phosphor-react' +import { Check, Circle, Plus, WarningCircle } from '@phosphor-icons/react' import { createLabelMutation } from '../../../lib/networking/mutations/createLabelMutation' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects' diff --git a/packages/web/components/templates/article/ShareModal.tsx b/packages/web/components/templates/article/ShareModal.tsx index 9706bd95a..488002ced 100644 --- a/packages/web/components/templates/article/ShareModal.tsx +++ b/packages/web/components/templates/article/ShareModal.tsx @@ -16,7 +16,7 @@ import { useCopyLink } from '../../../lib/hooks/useCopyLink' import { CloseIcon } from '../../elements/images/CloseIcon' import { OmnivoreLogoIcon } from '../../elements/images/OmnivoreNameLogo' import { useState } from 'react' -import { TwitterLogo, FacebookLogo } from 'phosphor-react' +import { TwitterLogo, FacebookLogo } from '@phosphor-icons/react' type ShareType = 'link' | 'highlight' diff --git a/packages/web/components/templates/discoverFeed/DiscoverHeader/SmallTopicBar.tsx b/packages/web/components/templates/discoverFeed/DiscoverHeader/SmallTopicBar.tsx index 70617d593..7e7eefb34 100644 --- a/packages/web/components/templates/discoverFeed/DiscoverHeader/SmallTopicBar.tsx +++ b/packages/web/components/templates/discoverFeed/DiscoverHeader/SmallTopicBar.tsx @@ -1,6 +1,6 @@ import { HStack } from '../../../elements/LayoutPrimitives' import { TopicTab } from './TopicTab' -import { CaretLeft, CaretRight } from 'phosphor-react' +import { CaretLeft, CaretRight } from '@phosphor-icons/react' import React, { useEffect, useRef, useState } from 'react' import { TopicTabData } from '../DiscoverContainer' diff --git a/packages/web/components/templates/discoverFeed/DiscoverHeader/SmallerHeaderLayout.tsx b/packages/web/components/templates/discoverFeed/DiscoverHeader/SmallerHeaderLayout.tsx index 4dad58221..c389a2a00 100644 --- a/packages/web/components/templates/discoverFeed/DiscoverHeader/SmallerHeaderLayout.tsx +++ b/packages/web/components/templates/discoverFeed/DiscoverHeader/SmallerHeaderLayout.tsx @@ -2,7 +2,7 @@ import React from 'react' import { HStack } from '../../../elements/LayoutPrimitives' import { OmnivoreSmallLogo } from '../../../elements/images/OmnivoreNameLogo' import { theme } from '../../../tokens/stitches.config' -import { FunnelSimple } from 'phosphor-react' +import { FunnelSimple } from '@phosphor-icons/react' import { DiscoverHeaderProps } from './DiscoverHeader' import { SmallTopicBar } from './SmallTopicBar' import { PrimaryDropdown } from '../../PrimaryDropdown' diff --git a/packages/web/components/templates/discoverFeed/DiscoverHeader/TopicBar.tsx b/packages/web/components/templates/discoverFeed/DiscoverHeader/TopicBar.tsx index 814e09048..f22d9f5b2 100644 --- a/packages/web/components/templates/discoverFeed/DiscoverHeader/TopicBar.tsx +++ b/packages/web/components/templates/discoverFeed/DiscoverHeader/TopicBar.tsx @@ -1,6 +1,6 @@ import { Box, HStack } from '../../../elements/LayoutPrimitives' import { TopicTab } from './TopicTab' -import { CaretLeft, CaretRight } from 'phosphor-react' +import { CaretLeft, CaretRight } from '@phosphor-icons/react' import React, { useEffect, useRef, useState } from 'react' import { TopicTabData } from '../DiscoverContainer' diff --git a/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverHoverActions.tsx b/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverHoverActions.tsx index 465283e22..9ee8d6d6d 100644 --- a/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverHoverActions.tsx +++ b/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverHoverActions.tsx @@ -7,12 +7,12 @@ import { Browsers, MinusCircle, PlusCircle, -} from 'phosphor-react' +} from '@phosphor-icons/react' import { timeZone, locale } from '../../../../lib/dateFormatting' import React from 'react' -import { SaveDiscoverArticleOutput } from "../../../../lib/networking/mutations/saveDiscoverArticle" -import { DiscoverFeedItem } from "../../../../lib/networking/queries/useGetDiscoverFeedItems" -import { BrowserIcon } from "../../../elements/icons/BrowserIcon" +import { SaveDiscoverArticleOutput } from '../../../../lib/networking/mutations/saveDiscoverArticle' +import { DiscoverFeedItem } from '../../../../lib/networking/queries/useGetDiscoverFeedItems' +import { BrowserIcon } from '../../../elements/icons/BrowserIcon' type DiscoverHoverActionsProps = { viewer?: UserBasicData @@ -29,7 +29,7 @@ type DiscoverHoverActionsProps = { setSavedUrl: (url: string) => void savedUrl?: string - deleteDiscoverItem: (item: DiscoverFeedItem) => Promise, + deleteDiscoverItem: (item: DiscoverFeedItem) => Promise } export const DiscoverHoverActions = (props: DiscoverHoverActionsProps) => { @@ -60,14 +60,14 @@ export const DiscoverHoverActions = (props: DiscoverHoverActionsProps) => { > ) diff --git a/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverItemGridCard.tsx b/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverItemGridCard.tsx index ffe3d585c..68c3dd2b9 100644 --- a/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverItemGridCard.tsx +++ b/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverItemGridCard.tsx @@ -22,10 +22,13 @@ import { siteName, TitleStyle, } from '../../../patterns/LibraryCards/LibraryCardStyles' -import { DiscoverItemCardProps, DiscoverItemSubCardProps } from "./DiscoverItemCard" +import { + DiscoverItemCardProps, + DiscoverItemSubCardProps, +} from './DiscoverItemCard' import { DiscoverItemMetadata } from './DiscoverItemMetadata' import { DiscoverHoverActions } from './DiscoverHoverActions' -import { CheckCircle, Circle } from 'phosphor-react' +import { CheckCircle, Circle } from '@phosphor-icons/react' export function DiscoverGridCard(props: DiscoverItemSubCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) @@ -100,7 +103,12 @@ export function DiscoverGridCard(props: DiscoverItemSubCardProps): JSX.Element { /> )} - + ) } @@ -122,7 +130,15 @@ const DiscoverGridCardContent = ( } return ( - + <> { setDisplayFallback(true) diff --git a/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverItemListCard.tsx b/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverItemListCard.tsx index f4819d6b5..14756f17a 100644 --- a/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverItemListCard.tsx +++ b/packages/web/components/templates/discoverFeed/DiscoverItems/DiscoverItemListCard.tsx @@ -23,8 +23,11 @@ import { siteName, TitleStyle, } from '../../../patterns/LibraryCards/LibraryCardStyles' -import { CheckCircle, Circle } from 'phosphor-react' -import { DiscoverItemCardProps, DiscoverItemSubCardProps } from "./DiscoverItemCard" +import { CheckCircle, Circle } from '@phosphor-icons/react' +import { + DiscoverItemCardProps, + DiscoverItemSubCardProps, +} from './DiscoverItemCard' import { DiscoverItemMetadata } from './DiscoverItemMetadata' import { DiscoverHoverActions } from './DiscoverHoverActions' @@ -103,13 +106,17 @@ export function DiscoverItemListCard( /> )} - + ) } export function DiscoverListCardContent( - props: DiscoverItemCardProps & { savedId?: string; savedUrl? : string } + props: DiscoverItemCardProps & { savedId?: string; savedUrl?: string } ): JSX.Element { const originText = siteName(props.item.url, props.item.url) const [displayFallback, setDisplayFallback] = useState( @@ -123,7 +130,14 @@ export function DiscoverListCardContent( } return ( - + + legacyBehavior + > { console.log('event.ctrlKey: ', event.ctrlKey, event.metaKey) @@ -129,7 +130,7 @@ export function HighlightsMenu(props: HighlightsMenuProps): JSX.Element { - ); + ) } const sortHighlights = (highlights: Highlight[]) => { diff --git a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx index 1b66e1f91..390805326 100644 --- a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx +++ b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx @@ -1,5 +1,5 @@ import { useRouter } from 'next/router' -import { HighlighterCircle } from 'phosphor-react' +import { HighlighterCircle } from '@phosphor-icons/react' import { useCallback, useEffect, useReducer, useState } from 'react' import { Toaster } from 'react-hot-toast' import { Highlight } from '../../../lib/networking/fragments/highlightFragment' @@ -36,8 +36,9 @@ type HighlightItemsLayoutProps = { export function HighlightItemsLayout( props: HighlightItemsLayoutProps ): JSX.Element { - const [currentItem, setCurrentItem] = - useState(undefined) + const [currentItem, setCurrentItem] = useState( + undefined + ) const listReducer = ( state: LibraryItem[], diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 7ae44439d..b8636a80f 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -5,7 +5,7 @@ import { FormInput } from '../../elements/FormElements' import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts' import { Button, IconButton } from '../../elements/Button' -import { FunnelSimple, X } from 'phosphor-react' +import { FunnelSimple, X } from '@phosphor-icons/react' import { LayoutType, LibraryMode } from './HomeFeedContainer' import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo' import { DEFAULT_HEADER_HEIGHT, HeaderSpacer } from './HeaderSpacer' diff --git a/packages/web/components/templates/homeFeed/MultiSelectControls.tsx b/packages/web/components/templates/homeFeed/MultiSelectControls.tsx index fd624edcc..a2d48fd05 100644 --- a/packages/web/components/templates/homeFeed/MultiSelectControls.tsx +++ b/packages/web/components/templates/homeFeed/MultiSelectControls.tsx @@ -8,7 +8,7 @@ import { LabelIcon } from '../../elements/icons/LabelIcon' import { TrashIcon } from '../../elements/icons/TrashIcon' import { ConfirmationModal } from '../../patterns/ConfirmationModal' import { AddBulkLabelsModal } from '../article/AddBulkLabelsModal' -import { X } from 'phosphor-react' +import { X } from '@phosphor-icons/react' import { MultiSelectMode } from './LibraryHeader' import { HeaderCheckboxIcon } from '../../elements/icons/HeaderCheckboxIcon' import { Label } from '../../../lib/networking/fragments/labelFragment' diff --git a/packages/web/components/templates/homeFeed/TLDRLayout.tsx b/packages/web/components/templates/homeFeed/TLDRLayout.tsx index 29a3a26cc..13d8881f1 100644 --- a/packages/web/components/templates/homeFeed/TLDRLayout.tsx +++ b/packages/web/components/templates/homeFeed/TLDRLayout.tsx @@ -12,7 +12,7 @@ import { BrowserIcon } from '../../elements/icons/BrowserIcon' import { styled } from '@stitches/react' import { siteName } from '../../patterns/LibraryCards/LibraryCardStyles' import { theme } from '../../tokens/stitches.config' -import { DotsThree } from 'phosphor-react' +import { DotsThree } from '@phosphor-icons/react' import { useState } from 'react' type TLDRLayoutProps = { diff --git a/packages/web/components/templates/integrations/Webhooks.tsx b/packages/web/components/templates/integrations/Webhooks.tsx index ce8bcd95e..83ace3980 100644 --- a/packages/web/components/templates/integrations/Webhooks.tsx +++ b/packages/web/components/templates/integrations/Webhooks.tsx @@ -4,7 +4,7 @@ import Image from 'next/image' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { Button } from '../../elements/Button' -import { Link, Plus } from 'phosphor-react' +import { Link, Plus } from '@phosphor-icons/react' import { useGetWebhooksQuery } from '../../../lib/networking/queries/useGetWebhooksQuery' import { useMemo } from 'react' diff --git a/packages/web/components/templates/library/LibraryHeader.tsx b/packages/web/components/templates/library/LibraryHeader.tsx index 26318bb6a..ecb4eef59 100644 --- a/packages/web/components/templates/library/LibraryHeader.tsx +++ b/packages/web/components/templates/library/LibraryHeader.tsx @@ -5,7 +5,7 @@ import { FormInput } from '../../elements/FormElements' import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts' import { Button, IconButton } from '../../elements/Button' -import { FunnelSimple, X } from 'phosphor-react' +import { FunnelSimple, X } from '@phosphor-icons/react' import { LayoutType, LibraryMode } from '../homeFeed/HomeFeedContainer' import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo' import { DEFAULT_HEADER_HEIGHT, HeaderSpacer } from '../homeFeed/HeaderSpacer' diff --git a/packages/web/components/templates/navMenu/LibraryLegacyMenu.tsx b/packages/web/components/templates/navMenu/LibraryLegacyMenu.tsx index 8590c3578..5b1dedfe7 100644 --- a/packages/web/components/templates/navMenu/LibraryLegacyMenu.tsx +++ b/packages/web/components/templates/navMenu/LibraryLegacyMenu.tsx @@ -2,7 +2,7 @@ import { ReactNode, useEffect, useMemo, useRef } from 'react' import { StyledText } from '../../elements/StyledText' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { Button } from '../../elements/Button' -import { Circle, NewspaperClipping, X } from 'phosphor-react' +import { Circle, NewspaperClipping, X } from '@phosphor-icons/react' import { Subscription, SubscriptionType, diff --git a/packages/web/components/templates/navMenu/LibraryMenu.tsx b/packages/web/components/templates/navMenu/LibraryMenu.tsx index d852a2087..7832c220e 100644 --- a/packages/web/components/templates/navMenu/LibraryMenu.tsx +++ b/packages/web/components/templates/navMenu/LibraryMenu.tsx @@ -2,7 +2,7 @@ import { ReactNode, useEffect, useMemo, useRef, useState } from 'react' import { StyledText } from '../../elements/StyledText' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { Button } from '../../elements/Button' -import { Circle, DotsThree, MagnifyingGlass, X } from 'phosphor-react' +import { Circle, DotsThree, MagnifyingGlass, X } from '@phosphor-icons/react' import { Subscription, SubscriptionType, diff --git a/packages/web/components/templates/navMenu/NavigationMenu.tsx b/packages/web/components/templates/navMenu/NavigationMenu.tsx index 87db74d30..a3a3131c7 100644 --- a/packages/web/components/templates/navMenu/NavigationMenu.tsx +++ b/packages/web/components/templates/navMenu/NavigationMenu.tsx @@ -1,41 +1,73 @@ -import { ReactNode, useEffect, useMemo, useRef, useState } from 'react' +import { + CSSProperties, + ReactNode, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' import { StyledText } from '../../elements/StyledText' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { Button } from '../../elements/Button' -import { Circle, DotsThree, List, MagnifyingGlass, X } from 'phosphor-react' +import { + DotsThree, + List, + X, + Folder, + FolderOpen, + Tag, +} from '@phosphor-icons/react' import { Subscription, - SubscriptionType, useGetSubscriptionsQuery, } from '../../../lib/networking/queries/useGetSubscriptionsQuery' import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery' import { Label } from '../../../lib/networking/fragments/labelFragment' import { theme } from '../../tokens/stitches.config' -import { useRegisterActions } from 'kbar' -import { LogoBox } from '../../elements/LogoBox' import { usePersistedState } from '../../../lib/hooks/usePersistedState' import { useGetSavedSearchQuery } from '../../../lib/networking/queries/useGetSavedSearchQuery' import { SavedSearch } from '../../../lib/networking/fragments/savedSearchFragment' -import { ToggleCaretDownIcon } from '../../elements/icons/ToggleCaretDownIcon' import Link from 'next/link' -import { ToggleCaretRightIcon } from '../../elements/icons/ToggleCaretRightIcon' import { NavMenuFooter } from './Footer' import { FollowingIcon } from '../../elements/icons/FollowingIcon' import { HomeIcon } from '../../elements/icons/HomeIcon' import { LibraryIcon } from '../../elements/icons/LibraryIcon' import { HighlightsIcon } from '../../elements/icons/HighlightsIcon' import { CoverImage } from '../../elements/CoverImage' -import { Shortcut } from '../../../pages/settings/shortcuts' -import { OutlinedLabelChip } from '../../elements/OutlinedLabelChip' import { NewsletterIcon } from '../../elements/icons/NewsletterIcon' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' import { useRouter } from 'next/router' -import { DiscoverIcon } from '../../elements/icons/DiscoverIcon' -import { escapeQuotes } from '../../../utils/helper' import { NavigationSection } from '../NavigationLayout' +import { NodeApi, SimpleTree, Tree, TreeApi } from 'react-arborist' +import { ListMagnifyingGlass } from '@phosphor-icons/react' +import React from 'react' +import useSWR from 'swr' +import useSWRMutation from 'swr/mutation' +import { fetchEndpoint } from '../../../lib/appConfig' +import { requestHeaders } from '../../../lib/networking/networkHelpers' +import { v4 as uuidv4 } from 'uuid' +import { showErrorToast } from '../../../lib/toastHelpers' +import { OpenMap } from 'react-arborist/dist/module/state/open-slice' export const LIBRARY_LEFT_MENU_WIDTH = '275px' +export type ShortcutType = 'search' | 'label' | 'newsletter' | 'feed' | 'folder' + +export type Shortcut = { + type: ShortcutType + + id: string + name: string + section: string + filter: string + + icon?: string + label?: Label + + join?: string +} + type LibraryFilterMenuProps = { section: NavigationSection @@ -210,7 +242,7 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { {...props} text="Home" section="justread" - isSelected={props.section == 'home'} + isSelected={props.section == 'justread'} icon={} /> { } const Shortcuts = (props: LibraryFilterMenuProps): JSX.Element => { - const router = useRouter() - const [shortcuts] = usePersistedState({ - key: 'library-shortcuts', - isSessionStorage: false, - initialValue: [], - }) + const treeRef = useRef | undefined>(undefined) + const { trigger: resetShortcutsTrigger } = useSWRMutation( + '/api/shortcuts', + resetShortcuts + ) - // const shortcuts: Shortcut[] = [ - // { - // id: '12asdfasdf', - // name: 'Omnivore Blog', - // icon: 'https://substackcdn.com/image/fetch/w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F052c15c4-ecfd-4d32-87db-13bcac9afad5_512x512.png', - // filter: 'subscription:"Money Talk"', - // type: 'feed', - // }, - // { - // id: 'sdfsdfgdsfg', - // name: 'Follow the Money | Arne & Harr', - // filter: 'subscription:"Money Talk"', - // type: 'feed', - // }, - // { - // id: 'sdfasdfasdfsdfsdfsgasdfg', - // name: 'Andrew Kenneson from Center for the Study of Partisanship and Ideology', - // // icon: 'https://substackcdn.com/image/fetch/w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F052c15c4-ecfd-4d32-87db-13bcac9afad5_512x512.png', - // filter: 'in:all label:"Hockey"', - // type: 'newsletter', - // }, - // { - // id: 'sdfasdfasdfsdfsdfsgasdfg', - // name: 'Robert的博客', - // // icon: 'https://substackcdn.com/image/fetch/w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F052c15c4-ecfd-4d32-87db-13bcac9afad5_512x512.png', - // filter: 'in:all label:"Hockey"', - // type: 'feed', - // }, - // { - // id: 'sdfasdfasdfasdfasf', - // name: 'Oldest First', - // // icon: 'https://substackcdn.com/image/fetch/w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F052c15c4-ecfd-4d32-87db-13bcac9afad5_512x512.png', - // filter: 'in:all label:"Hockey"', - // type: 'search', - // }, - // { - // id: 'sdfasdfasdfgasdfg', - // name: 'Hockey', - // // icon: 'https://substackcdn.com/image/fetch/w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F052c15c4-ecfd-4d32-87db-13bcac9afad5_512x512.png', - // filter: 'in:all label:"Hockey"', - // type: 'label', - // label: { - // id: 'sdfsdfsdf', - // name: 'Hockey', - // color: '#E98B8B', - // createdAt: new Date(), - // }, - // }, - // ] - // + const createNewFolder = useCallback(async () => { + if (treeRef.current) { + const result = await treeRef.current.create({ + type: 'internal', + index: 0, + }) + console.log('create leaf: ', result) + } + }, [treeRef]) + + const resetShortcutsToDefault = useCallback(async () => { + resetShortcutsTrigger(null, { + revalidate: true, + }) + }, []) return ( { fontSize: '14px', lineHeight: '125%', color: '$thLibraryMenuPrimary', - pl: '10px', - // mt: '20px', mb: '10px', + px: '15px', }} > SHORTCUTS - + } css={{ ml: 'auto' }} > { - router.push(`/settings/shortcuts`) - }} - title="Edit shortcuts" + onSelect={resetShortcutsToDefault} + title="Reset to default" + /> + - {shortcuts.map((shortcut) => { - const selected = props.searchTerm === shortcut.filter - return ( - { - props.applySearchQuery(shortcut.filter) - props.setShowFilterMenu(false) - e.preventDefault() - }} - > - {(shortcut.type == 'feed' || shortcut.type == 'newsletter') && ( - - )} - {shortcut.type == 'search' && ( - - )} - {shortcut.type == 'label' && } - - ) - })} + + + ) } +type ShortcutsTreeProps = { + treeRef: React.MutableRefObject | undefined> +} + +async function getShortcuts(path: string): Promise { + const url = new URL(path, fetchEndpoint) + try { + const response = await fetch(url.toString(), { + method: 'GET', + headers: requestHeaders(), + credentials: 'include', + mode: 'cors', + }) + const payload = await response.json() + if ('shortcuts' in payload) { + return payload['shortcuts'] as Shortcut[] + } + return [] + } catch (err) { + console.log('error getting shortcuts: ', err) + throw err + } +} + +async function setShortcuts( + path: string, + { arg }: { arg: { shortcuts: Shortcut[] } } +): Promise { + const url = new URL(path, fetchEndpoint) + try { + const response = await fetch(url, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + ...requestHeaders(), + }, + credentials: 'include', + mode: 'cors', + body: JSON.stringify(arg), + }) + const payload = await response.json() + if (!('shortcuts' in payload)) { + throw new Error('Error syncing shortcuts') + } + return payload['shortcuts'] as Shortcut[] + } catch (err) { + showErrorToast('Error syncing shortcut changes.') + } + return arg.shortcuts +} + +async function resetShortcuts(path: string): Promise { + const url = new URL(path, fetchEndpoint) + try { + const response = await fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + ...requestHeaders(), + }, + credentials: 'include', + mode: 'cors', + }) + const payload = await response.json() + if (!('shortcuts' in payload)) { + throw new Error('Error syncing shortcuts') + } + return payload['shortcuts'] as Shortcut[] + } catch (err) { + showErrorToast('Error syncing shortcut changes.') + } + return [] +} + +const cachedShortcutsData = (): Shortcut[] | undefined => { + if (typeof localStorage !== 'undefined') { + const str = localStorage.getItem('/api/shortcuts') + if (str) { + return JSON.parse(str) as Shortcut[] + } + } + return undefined +} + +const ShortcutsTree = (props: ShortcutsTreeProps): JSX.Element => { + const router = useRouter() + + const { isValidating, data } = useSWR('/api/shortcuts', getShortcuts, { + fallbackData: cachedShortcutsData(), + onSuccess(data) { + localStorage.setItem('/api/shortcuts', JSON.stringify(data)) + }, + }) + const { trigger, isMutating } = useSWRMutation('/api/shortcuts', setShortcuts) + const [folderOpenState, setFolderOpenState] = usePersistedState< + Record + >({ + key: 'nav-menu-open-state', + isSessionStorage: false, + initialValue: {}, + }) + const tree = useMemo(() => { + const result = new SimpleTree((data ?? []) as Shortcut[]) + return result + }, [data]) + + const syncTreeData = (data: Shortcut[]) => { + trigger( + { shortcuts: data }, + { + optimisticData: data, + rollbackOnError: true, + populateCache: (updatedShortcuts) => { + return updatedShortcuts + }, + revalidate: false, + } + ) + } + + const onMove = useCallback( + (args: { dragIds: string[]; parentId: null | string; index: number }) => { + for (const id of args.dragIds) { + tree?.move({ id, parentId: args.parentId, index: args.index }) + } + syncTreeData(tree.data) + }, + [tree, data] + ) + + const onCreate = useCallback( + (args: { parentId: string | null; index: number; type: string }) => { + const data = { id: uuidv4(), name: '', type: 'folder' } as any + if (args.type === 'internal') { + data.children = [] + } + tree.create({ parentId: args.parentId, index: args.index, data }) + syncTreeData(tree.data) + return data + }, + [tree, data] + ) + + const onDelete = useCallback( + (args: { ids: string[] }) => { + args.ids.forEach((id) => tree.drop({ id })) + syncTreeData(tree.data) + }, + [tree, data] + ) + + const onRename = useCallback( + (args: { name: string; id: string }) => { + tree.update({ id: args.id, changes: { name: args.name } as any }) + syncTreeData(tree.data) + }, + [tree, data] + ) + + const onToggle = useCallback( + (id: string) => { + if (id && props.treeRef.current) { + const isOpen = props.treeRef.current?.isOpen(id) + const newItem: OpenMap = {} + newItem[id] = isOpen + setFolderOpenState({ ...folderOpenState, ...newItem }) + } + }, + [props, folderOpenState, setFolderOpenState] + ) + + const onActivate = useCallback( + (node: NodeApi) => { + console.log('onActivate: ', node) + if (node.data.type == 'folder') { + const join = node.data.join + if (join == 'or') { + const query = node.children + ?.map((child) => { + return `(${child.data.filter})` + }) + .join(' OR ') + console.log('query: ', query) + } + } else if (node.data.section != null && node.data.filter != null) { + router.push(`/${node.data.section}?q=${node.data.filter}`) + } + }, + [tree, router] + ) + + return ( + <> + {!isValidating && ( + + {NodeRenderer} + + )} + + ) +} + +function NodeRenderer(args: { + style: CSSProperties + node: NodeApi + tree: TreeApi + dragHandle?: (el: HTMLDivElement | null) => void + preview?: boolean +}) { + const isSelected = false + const [menuVisible, setMenuVisible] = useState(false) + const [menuOpened, setMenuOpened] = useState(false) + + const router = useRouter() + + return ( + { + setMenuVisible(true) + }} + onMouseLeave={() => { + setMenuVisible(false) + }} + title={args.node.data.name} + onClick={(e) => { + // router.push(`/` + props.section) + }} + > + + + + } + css={{ ml: 'auto' }} + onOpenChange={(open) => { + setMenuOpened(open) + }} + > + { + args.tree.delete(args.node) + }} + title="Remove" + /> + {/* {args.node.data.type == 'folder' && ( + { + args.node.data.join = 'or' + }} + title="Folder query: OR" + /> + )} */} + + + + + ) +} + +type NodeItemContentsProps = { + node: NodeApi +} + +const NodeItemContents = (props: NodeItemContentsProps): JSX.Element => { + if (props.node.isEditing) { + return ( + e.currentTarget.select()} + onBlur={() => props.node.reset()} + onKeyDown={(e) => { + if (e.key === 'Escape') { + props.node.reset() + } + if (e.key === 'Enter') { + // props.node.data = { + // id: 'new-folder', + // type: 'folder', + // name: e.currentTarget.value, + // } + props.node.submit(e.currentTarget.value) + props.node.activate() + } + }} + /> + ) + } + if (props.node.isLeaf) { + const shortcut = props.node.data + if (shortcut) { + switch (shortcut.type) { + case 'feed': + case 'newsletter': + return ( + + + + ) + case 'label': + return ( + + + + ) + case 'search': + return ( + + + + ) + } + } + } else { + return ( + { + props.node.toggle() + event.preventDefault() + }} + > + {props.node.isClosed ? ( + + ) : ( + + )} + {props.node.data.name} + + ) + } + return <> +} + type ShortcutItemProps = { shortcut: Shortcut } @@ -435,7 +806,7 @@ const SearchShortcut = (props: ShortcutItemProps): JSX.Element => { { alignment="center" css={{ minWidth: '20px' }} > - + {props.shortcut.name} @@ -451,283 +822,29 @@ const SearchShortcut = (props: ShortcutItemProps): JSX.Element => { } const LabelShortcut = (props: ShortcutItemProps): JSX.Element => { + // return ( - + + {props.shortcut.name} + ) } -function SavedSearches( - props: LibraryFilterMenuProps & { savedSearches: SavedSearch[] | undefined } -): JSX.Element { - const sortedSearches = useMemo(() => { - return props.savedSearches - ?.filter((it) => it.visible) - ?.sort( - (left: SavedSearch, right: SavedSearch) => - left.position - right.position - ) - }, [props.savedSearches]) - - useRegisterActions( - (sortedSearches ?? []).map((item, idx) => { - const key = String(idx + 1) - return { - id: `saved_search_${key}`, - name: item.name, - shortcut: [key], - section: 'Saved Searches', - keywords: '?' + item.name, - perform: () => { - props.applySearchQuery(item.filter) - }, - } - }), - [props.savedSearches] - ) - - const [collapsed, setCollapsed] = usePersistedState({ - key: `--saved-searches-collapsed`, - initialValue: false, - }) - - return ( - - {!collapsed && - sortedSearches && - sortedSearches?.map((item) => ( - - ))} - {!collapsed && sortedSearches !== undefined && ( - - )} - - - - ) -} - -function Subscriptions( - props: LibraryFilterMenuProps & { subscriptions: Subscription[] | undefined } -): JSX.Element { - const [collapsed, setCollapsed] = usePersistedState({ - key: `--subscriptions-collapsed`, - initialValue: false, - }) - - const sortedSubscriptions = useMemo(() => { - if (!props.subscriptions) { - return [] - } - return props.subscriptions - .filter((s) => s.status == 'ACTIVE') - .sort((a, b) => a.name.localeCompare(b.name)) - }, [props.subscriptions]) - - useRegisterActions( - (sortedSubscriptions ?? []).map((subscription, idx) => { - const key = String(idx + 1) - const name = subscription.name - return { - id: `subscription_${key}`, - section: 'Subscriptions', - name: name, - keywords: '*' + name, - perform: () => { - props.applySearchQuery(`subscription:\"${escapeQuotes(name)}\"`) - }, - } - }), - [sortedSubscriptions] - ) - - return ( - - {!collapsed ? ( - <> - - - - {(sortedSubscriptions ?? []).map((item) => { - switch (item.type) { - case SubscriptionType.NEWSLETTER: - return ( - - ) - case SubscriptionType.RSS: - return ( - - ) - } - })} - - - ) : ( - - )} - - ) -} - -function Labels( - props: LibraryFilterMenuProps & { labels: Label[] } -): JSX.Element { - const [collapsed, setCollapsed] = usePersistedState({ - key: `--labels-collapsed`, - initialValue: false, - }) - - const sortedLabels = useMemo(() => { - return props.labels.sort((left: Label, right: Label) => - left.name.localeCompare(right.name) - ) - }, [props.labels]) - - return ( - - {!collapsed && ( - <> - {sortedLabels.map((item) => { - return - })} - - - )} - - ) -} - -type MenuPanelProps = { - title: string - children: ReactNode - editFunc?: () => void - editTitle?: string - hideBottomBorder?: boolean - collapsed: boolean - setCollapsed: (collapsed: boolean) => void -} - -function MenuPanel(props: MenuPanelProps): JSX.Element { - return ( - - - - {props.title} - - - - - - {props.children} - - ) -} - type NavButtonProps = { text: string icon: ReactNode @@ -736,70 +853,6 @@ type NavButtonProps = { section: NavigationSection } -type NavButtonRedirectProps = { - text: string - icon: ReactNode - - redirectLocation: string -} - -function NavRedirectButton(props: NavButtonRedirectProps): JSX.Element { - const [selected, setSelected] = useState(false) - const router = useRouter() - - useEffect(() => { - setSelected(window.location.pathname.includes(props.redirectLocation)) - }, []) - - return ( - { - router.push(props.redirectLocation) - e.preventDefault() - }} - > - {props.icon} - {props.text} - - ) -} - function NavButton(props: NavButtonProps): JSX.Element { const router = useRouter() @@ -923,106 +976,6 @@ function FilterButton(props: FilterButtonProps): JSX.Element { ) } -type LabelButtonProps = { - label: Label - searchTerm: string | undefined - applySearchQuery: (searchTerm: string) => void -} - -function LabelButton(props: LabelButtonProps): JSX.Element { - const labelId = `checkbox-label-${props.label.id}` - const checkboxRef = useRef(null) - const state = useMemo(() => { - const term = props.searchTerm ?? '' - if (term.indexOf(`label:\"${escapeQuotes(props.label.name)}\"`) >= 0) { - return 'on' - } - return 'off' - }, [props.searchTerm, props.label]) - - return ( - - - - { - const escapedLabelName = escapeQuotes(props.label.name) - if (e.target.checked) { - props.applySearchQuery( - `${props.searchTerm ?? ''} label:\"${escapedLabelName}\"` - ) - } else { - const query = - props.searchTerm?.replace( - `label:\"${escapedLabelName}\"`, - '' - ) ?? '' - props.applySearchQuery(query) - } - }} - /> - - - ) -} - type EditButtonProps = { title: string destination: string diff --git a/packages/web/components/templates/navMenu/SettingsDropdown.tsx b/packages/web/components/templates/navMenu/SettingsDropdown.tsx index a54da2f9e..4bab09491 100644 --- a/packages/web/components/templates/navMenu/SettingsDropdown.tsx +++ b/packages/web/components/templates/navMenu/SettingsDropdown.tsx @@ -5,7 +5,7 @@ import { DropdownSeparator, } from '../../elements/DropdownElements' import { useRouter } from 'next/router' -import { List } from 'phosphor-react' +import { List } from '@phosphor-icons/react' export const SettingsDropdown = (): JSX.Element => { const router = useRouter() diff --git a/packages/web/components/templates/navMenu/SettingsMenu.tsx b/packages/web/components/templates/navMenu/SettingsMenu.tsx index 3a06506ce..e60c5008b 100644 --- a/packages/web/components/templates/navMenu/SettingsMenu.tsx +++ b/packages/web/components/templates/navMenu/SettingsMenu.tsx @@ -5,7 +5,7 @@ import { LogoBox } from '../../elements/LogoBox' import Link from 'next/link' import { styled, theme } from '../../tokens/stitches.config' import { Button } from '../../elements/Button' -import { ArrowSquareUpRight } from 'phosphor-react' +import { ArrowSquareUpRight } from '@phosphor-icons/react' import { useRouter } from 'next/router' import { NavMenuFooter } from './Footer' diff --git a/packages/web/components/templates/settings/SettingsTable.tsx b/packages/web/components/templates/settings/SettingsTable.tsx index 1db5872a6..5f81689fd 100644 --- a/packages/web/components/templates/settings/SettingsTable.tsx +++ b/packages/web/components/templates/settings/SettingsTable.tsx @@ -1,4 +1,4 @@ -import { Pencil, Trash } from 'phosphor-react' +import { Pencil, Trash } from '@phosphor-icons/react' import { Toaster } from 'react-hot-toast' import { Button } from '../../elements/Button' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' diff --git a/packages/web/lib/networking/networkHelpers.ts b/packages/web/lib/networking/networkHelpers.ts index d26733d6b..d8b290073 100644 --- a/packages/web/lib/networking/networkHelpers.ts +++ b/packages/web/lib/networking/networkHelpers.ts @@ -12,7 +12,7 @@ export type RequestContext = { } } -function requestHeaders(): Record { +export function requestHeaders(): Record { const authToken = window?.localStorage.getItem('authToken') || undefined const pendingAuthToken = window?.localStorage.getItem('pendingUserAuth') || undefined @@ -64,14 +64,19 @@ export function apiFetcher(path: string): Promise { credentials: 'include', mode: 'cors', }).then((result) => { + console.log('api fetcher result: ', result) return result.json() }) } -export function apiPoster(path: string, body: any): Promise { +export function apiPoster( + path: string, + body: any, + method = 'POST' +): Promise { const url = new URL(path, fetchEndpoint) return fetch(url.toString(), { - method: 'POST', + method: method, credentials: 'include', mode: 'cors', headers: { diff --git a/packages/web/lib/toastHelpers.tsx b/packages/web/lib/toastHelpers.tsx index 18c92a75c..5f0157cb2 100644 --- a/packages/web/lib/toastHelpers.tsx +++ b/packages/web/lib/toastHelpers.tsx @@ -1,5 +1,5 @@ import { toast, ToastOptions } from 'react-hot-toast' -import { CheckCircle, WarningCircle, X } from 'phosphor-react' +import { CheckCircle, WarningCircle, X } from '@phosphor-icons/react' import { Box, HStack } from '../components/elements/LayoutPrimitives' import { styled } from '@stitches/react' import { Button } from '../components/elements/Button' diff --git a/packages/web/package.json b/packages/web/package.json index cfdc9f0ae..bb1d23f49 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -18,6 +18,7 @@ "dependencies": { "@floating-ui/react": "^0.26.9", "@google-recaptcha/react": "^1.0.3", + "@phosphor-icons/react": "^2.1.5", "@radix-ui/react-avatar": "^0.1.1", "@radix-ui/react-checkbox": "^0.1.5", "@radix-ui/react-dialog": "1.0.5", @@ -43,32 +44,29 @@ "kbar": "^0.1.0-beta.35", "loadjs": "^4.3.0-rc1", "markdown-it": "^13.0.1", - "match-sorter": "^6.3.1", "nanoid": "^3.1.29", "next": "^13.5.6", "node-html-markdown": "^1.3.0", "papaparse": "^5.4.1", - "phosphor-react": "^1.4.0", "posthog-js": "^1.78.2", "pspdfkit": "^2023.4.6", "re-resizable": "^6.9.11", "react": "^18.2.0", + "react-arborist": "^3.4.0", "react-color": "^2.19.3", - "react-colorful": "^5.5.1", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-hot-toast": "^2.1.1", "react-input-autosize": "^3.0.0", "react-markdown": "^8.0.6", "react-markdown-editor-lite": "^1.3.4", - "react-masonry-css": "^1.0.16", "react-sliding-pane": "^7.3.0", "react-spinners": "^0.13.7", "react-super-responsive-table": "^5.2.1", "react-topbar-progress-indicator": "^4.1.1", "remark-gfm": "^3.0.1", "sharp": "^0.32.6", - "swr": "^1.0.1", + "swr": "^2.2.5", "uuid": "^8.3.2", "yet-another-react-lightbox": "^3.12.0" }, @@ -110,4 +108,4 @@ "volta": { "extends": "../../package.json" } -} +} \ No newline at end of file diff --git a/packages/web/pages/_app.tsx b/packages/web/pages/_app.tsx index 17c2f1364..c9bbbb95a 100644 --- a/packages/web/pages/_app.tsx +++ b/packages/web/pages/_app.tsx @@ -23,6 +23,7 @@ import { updateTheme } from '../lib/themeUpdater' import { ThemeId } from '../components/tokens/stitches.config' import { posthog } from 'posthog-js' import { GoogleReCaptchaProvider } from '@google-recaptcha/react' +import { SWRConfig } from 'swr' TopBarProgress.config({ barColors: { diff --git a/packages/web/pages/settings/discover-feeds/index.tsx b/packages/web/pages/settings/discover-feeds/index.tsx index e75bd76ac..c33f344df 100644 --- a/packages/web/pages/settings/discover-feeds/index.tsx +++ b/packages/web/pages/settings/discover-feeds/index.tsx @@ -1,5 +1,5 @@ import { useRouter } from 'next/router' -import { FloppyDisk, Pencil, XCircle } from 'phosphor-react' +import { FloppyDisk, Pencil, XCircle } from '@phosphor-icons/react' import { useMemo, useState } from 'react' import { FormInput } from '../../../components/elements/FormElements' import { HStack, SpanBox } from '../../../components/elements/LayoutPrimitives' diff --git a/packages/web/pages/settings/emails/index.tsx b/packages/web/pages/settings/emails/index.tsx index 28b98c05e..f33d6d7ee 100644 --- a/packages/web/pages/settings/emails/index.tsx +++ b/packages/web/pages/settings/emails/index.tsx @@ -2,7 +2,7 @@ import { Button } from '../../../components/elements/Button' import { useGetNewsletterEmailsQuery } from '../../../lib/networking/queries/useGetNewsletterEmailsQuery' import { createNewsletterEmailMutation } from '../../../lib/networking/mutations/createNewsletterEmailMutation' import { deleteNewsletterEmailMutation } from '../../../lib/networking/mutations/deleteNewsletterEmailMutation' -import { Copy } from 'phosphor-react' +import { Copy } from '@phosphor-icons/react' import { theme, styled } from '../../../components/tokens/stitches.config' import { Box, @@ -80,8 +80,9 @@ function CopyTextButton(props: CopyTextButtonProps): JSX.Element { export default function EmailsPage(): JSX.Element { const { emailAddresses, revalidate, isValidating } = useGetNewsletterEmailsQuery() - const [confirmDeleteEmailId, setConfirmDeleteEmailId] = - useState(undefined) + const [confirmDeleteEmailId, setConfirmDeleteEmailId] = useState< + undefined | string + >(undefined) applyStoredTheme() diff --git a/packages/web/pages/settings/features/beta.tsx b/packages/web/pages/settings/features/beta.tsx index c88fec98a..738d85f75 100644 --- a/packages/web/pages/settings/features/beta.tsx +++ b/packages/web/pages/settings/features/beta.tsx @@ -1,4 +1,4 @@ -import { Spinner } from 'phosphor-react' +import { Spinner } from '@phosphor-icons/react' import { useCallback, useMemo, useState } from 'react' import { Toaster } from 'react-hot-toast' import { Button } from '../../../components/elements/Button' diff --git a/packages/web/pages/settings/feeds/index.tsx b/packages/web/pages/settings/feeds/index.tsx index 6084ac145..0876d7631 100644 --- a/packages/web/pages/settings/feeds/index.tsx +++ b/packages/web/pages/settings/feeds/index.tsx @@ -1,5 +1,5 @@ import { useRouter } from 'next/router' -import { FloppyDisk, Pencil, XCircle } from 'phosphor-react' +import { FloppyDisk, Pencil, XCircle } from '@phosphor-icons/react' import { useMemo, useState } from 'react' import { FormInput } from '../../../components/elements/FormElements' import { diff --git a/packages/web/pages/settings/integrations.tsx b/packages/web/pages/settings/integrations.tsx index 77896cf47..071cd002f 100644 --- a/packages/web/pages/settings/integrations.tsx +++ b/packages/web/pages/settings/integrations.tsx @@ -1,19 +1,19 @@ import { styled } from '@stitches/react' import Image from 'next/image' import { useRouter } from 'next/router' -import { DownloadSimple, Link, Spinner } from 'phosphor-react' +import { DownloadSimple, Link, Spinner } from '@phosphor-icons/react' import { useCallback, useEffect, useState } from 'react' import { Toaster } from 'react-hot-toast' import { Button } from '../../components/elements/Button' import { Dropdown, - DropdownOption + DropdownOption, } from '../../components/elements/DropdownElements' import { Box, HStack, SpanBox, - VStack + VStack, } from '../../components/elements/LayoutPrimitives' import { SettingsLayout } from '../../components/templates/SettingsLayout' import { fetchEndpoint } from '../../lib/appConfig' diff --git a/packages/web/pages/settings/labels.tsx b/packages/web/pages/settings/labels.tsx index df780b045..4124a4b70 100644 --- a/packages/web/pages/settings/labels.tsx +++ b/packages/web/pages/settings/labels.tsx @@ -17,7 +17,12 @@ 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 } from 'phosphor-react' +import { + ArrowClockwise, + DotsThree, + PencilSimple, + Trash, +} from '@phosphor-icons/react' import { GenericTableCardProps } from '../../utils/settings-page/labels/types' import { labelColorObjects } from '../../utils/settings-page/labels/labelColorObjects' import { LabelColorDropdown } from '../../components/elements/LabelColorDropdown' diff --git a/packages/web/pages/settings/pinned-searches.tsx b/packages/web/pages/settings/pinned-searches.tsx index 784062d14..2ee3df74a 100644 --- a/packages/web/pages/settings/pinned-searches.tsx +++ b/packages/web/pages/settings/pinned-searches.tsx @@ -18,7 +18,7 @@ import { applyStoredTheme } from '../../lib/themeUpdater' import { useGetLabelsQuery } from '../../lib/networking/queries/useGetLabelsQuery' import { useGetSavedSearchQuery } from '../../lib/networking/queries/useGetSavedSearchQuery' import { Label } from '../../lib/networking/fragments/labelFragment' -import { CheckSquare, Circle, Square } from 'phosphor-react' +import { CheckSquare, Circle, Square } from '@phosphor-icons/react' import { SavedSearch } from '../../lib/networking/fragments/savedSearchFragment' import { usePersistedState } from '../../lib/hooks/usePersistedState' import { escapeQuotes } from '../../utils/helper' diff --git a/packages/web/pages/settings/saved-searches.tsx b/packages/web/pages/settings/saved-searches.tsx index 7c4de1e26..8c73fd1be 100644 --- a/packages/web/pages/settings/saved-searches.tsx +++ b/packages/web/pages/settings/saved-searches.tsx @@ -25,7 +25,7 @@ import { Trash, Plus, ArrowsDownUp, -} from 'phosphor-react' +} from '@phosphor-icons/react' import { Dropdown, DropdownOption, @@ -156,14 +156,14 @@ export default function SavedSearchesPage(): JSX.Element { const [editingId, setEditingId] = useState(null) const [isCreateMode, setIsCreateMode] = useState(false) const [windowWidth, setWindowWidth] = useState(0) - const [confirmRemoveSavedSearchId, setConfirmRemoveSavedSearchId] = - useState(null) + const [confirmRemoveSavedSearchId, setConfirmRemoveSavedSearchId] = useState< + string | null + >(null) const [draggedElementId, setDraggedElementId] = useState(null) - const [draggedElementPosition, setDraggedElementPosition] = - useState<{ - x: number - y: number - } | null>(null) + const [draggedElementPosition, setDraggedElementPosition] = useState<{ + x: number + y: number + } | null>(null) const [sortedSavedSearch, setSortedSavedSearch] = useState([]) // Some theming stuff here. @@ -579,15 +579,14 @@ function GenericTableCard( editingId === savedSearch?.id || (isCreateMode && !savedSearch) const iconColor = isDarkTheme() ? '#D8D7D5' : '#5F5E58' const DEFAULT_STYLE = { position: null } - const [style, setStyle] = - useState< - Partial<{ - position: string | null - top: string - left: string - maxWidth: string - }> - >(DEFAULT_STYLE) + const [style, setStyle] = useState< + Partial<{ + position: string | null + top: string + left: string + maxWidth: string + }> + >(DEFAULT_STYLE) const handleEdit = () => { editingId && updateSavedSearch(editingId) setEditingId(null) diff --git a/packages/web/pages/settings/shortcuts.tsx b/packages/web/pages/settings/shortcuts.tsx index f9fddfec2..debd72331 100644 --- a/packages/web/pages/settings/shortcuts.tsx +++ b/packages/web/pages/settings/shortcuts.tsx @@ -30,7 +30,7 @@ import { DragIcon } from '../../components/elements/icons/DragIcon' import { CoverImage } from '../../components/elements/CoverImage' import { Label } from '../../lib/networking/fragments/labelFragment' import { usePersistedState } from '../../lib/hooks/usePersistedState' -import { CheckSquare, Square } from 'phosphor-react' +import { CheckSquare, Square } from '@phosphor-icons/react' import { Button } from '../../components/elements/Button' import { styled } from '@stitches/react' import { SavedSearch } from '../../lib/networking/fragments/savedSearchFragment' @@ -465,17 +465,6 @@ const AvailableItemButton = (props: AvailableItemButtonProps): JSX.Element => { ) } -export type Shortcut = { - type: 'search' | 'label' | 'newsletter' | 'feed' - - id: string - name: string - filter: string - - icon?: string - label?: Label -} - const SelectedItems = (props: ListProps): JSX.Element => { return ( =3.1.1 <6": + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + memoizerific@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" @@ -25199,11 +25225,6 @@ pgvector@^0.1.5: resolved "https://registry.yarnpkg.com/pgvector/-/pgvector-0.1.8.tgz#50c44c7a26b1cb17cf0a001b0772fc0eb5c38762" integrity sha512-mD6aw+XYJrsuLl3Y8s8gHDDfOZQ9ERtfQPdhvjOrC7eOTM7b6sNkxeZxBhHwUdXMfHmyGWIbwU0QbmSnn7pPmg== -phosphor-react@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/phosphor-react/-/phosphor-react-1.4.1.tgz#97b0e034d9937db9b97fe53b186e9646464fd4e7" - integrity sha512-gO5j7U0xZrdglTAYDYPACU4xDOFBTJmptrrB/GeR+tHhCZF3nUMyGmV/0hnloKjuTrOmpSFlbfOY78H39rgjUQ== - picocolors@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" @@ -26672,6 +26693,17 @@ re-resizable@^6.9.11: resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.11.tgz#f356e27877f12d926d076ab9ad9ff0b95912b475" integrity sha512-a3hiLWck/NkmyLvGWUuvkAmN1VhwAz4yOhS6FdMTaxCUVN9joIWkT11wsO68coG/iEYuwn+p/7qAmfQzRhiPLQ== +react-arborist@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/react-arborist/-/react-arborist-3.4.0.tgz#8ef3de2c81d3b8cea0f4f4575c1971bd80c556c5" + integrity sha512-QI46oRGXJr0oaQfqqVobIiIoqPp5Y5gM69D2A2P7uHVif+X75XWnScR5drC7YDKgJ4CXVaDeFwnYKOWRRfncMg== + dependencies: + react-dnd "^14.0.3" + react-dnd-html5-backend "^14.0.3" + react-window "^1.8.10" + redux "^5.0.0" + use-sync-external-store "^1.2.0" + react-color@^2.19.3: version "2.19.3" resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d" @@ -26690,10 +26722,23 @@ react-colorful@^5.1.2: resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784" integrity sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg== -react-colorful@^5.5.1: - version "5.6.1" - resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b" - integrity sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw== +react-dnd-html5-backend@^14.0.3: + version "14.1.0" + resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-14.1.0.tgz#b35a3a0c16dd3a2bfb5eb7ec62cf0c2cace8b62f" + integrity sha512-6ONeqEC3XKVf4eVmMTe0oPds+c5B9Foyj8p/ZKLb7kL2qh9COYxiBHv3szd6gztqi/efkmriywLUVlPotqoJyw== + dependencies: + dnd-core "14.0.1" + +react-dnd@^14.0.3: + version "14.0.5" + resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-14.0.5.tgz#ecf264e220ae62e35634d9b941502f3fca0185ed" + integrity sha512-9i1jSgbyVw0ELlEVt/NkCUkxy1hmhJOkePoCH713u75vzHGyXhPDm28oLfc2NMSBjZRM1Y+wRjHXJT3sPrTy+A== + dependencies: + "@react-dnd/invariant" "^2.0.0" + "@react-dnd/shallowequal" "^2.0.0" + dnd-core "14.0.1" + fast-deep-equal "^3.1.3" + hoist-non-react-statics "^3.3.2" react-docgen-typescript@^2.0.0: version "2.2.2" @@ -26840,11 +26885,6 @@ react-markdown@^8.0.6: unist-util-visit "^4.0.0" vfile "^5.0.0" -react-masonry-css@^1.0.16: - version "1.0.16" - resolved "https://registry.yarnpkg.com/react-masonry-css/-/react-masonry-css-1.0.16.tgz#72b28b4ae3484e250534700860597553a10f1a2c" - integrity sha512-KSW0hR2VQmltt/qAa3eXOctQDyOu7+ZBevtKgpNDSzT7k5LA/0XntNa9z9HKCdz3QlxmJHglTZ18e4sX4V8zZQ== - react-modal@^3.14.3: version "3.16.1" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b" @@ -26982,6 +27022,14 @@ react-virtual@^2.8.2: dependencies: "@reach/observe-rect" "^1.1.0" +react-window@^1.8.10: + version "1.8.10" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.10.tgz#9e6b08548316814b443f7002b1cf8fd3a1bdde03" + integrity sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg== + dependencies: + "@babel/runtime" "^7.0.0" + memoize-one ">=3.1.1 <6" + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -27255,6 +27303,18 @@ redis@^4.6.13: "@redis/search" "1.1.6" "@redis/time-series" "1.0.5" +redux@^4.1.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" + integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== + dependencies: + "@babel/runtime" "^7.9.2" + +redux@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" + integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -27553,11 +27613,6 @@ remedial@^1.0.7: resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== -remove-accents@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" - integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA== - remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -29580,10 +29635,13 @@ swap-case@^2.0.2: dependencies: tslib "^2.0.3" -swr@^1.0.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/swr/-/swr-1.3.0.tgz#c6531866a35b4db37b38b72c45a63171faf9f4e8" - integrity sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw== +swr@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.5.tgz#063eea0e9939f947227d5ca760cc53696f46446b" + integrity sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg== + dependencies: + client-only "^0.0.1" + use-sync-external-store "^1.2.0" "sylvester@>= 0.0.8": version "0.0.21" @@ -31140,6 +31198,11 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" +use-sync-external-store@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" From 7cce65eda94e94221835b15a125fd12cc2e73cb7 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 10 Jun 2024 18:15:10 +0800 Subject: [PATCH 04/23] Update SWR usage, move highlights temporarily --- packages/web/lib/networking/networkHelpers.ts | 6 ++-- .../useGetArticleOriginalHtmlQuery.tsx | 3 +- .../networking/queries/useGetArticleQuery.tsx | 5 +-- .../queries/useGetArticleSavingStatus.tsx | 2 +- .../queries/useGetDiscoverFeeds.tsx | 31 ++++++++++--------- .../web/lib/networking/queries/useGetHome.tsx | 3 +- .../queries/useGetIntegrationQuery.tsx | 8 +++-- .../queries/useGetSubscriptionsQuery.tsx | 3 +- .../networking/queries/useGetWebhookQuery.tsx | 7 +++-- .../{highlights => highlightsbak}/index.tsx | 0 10 files changed, 41 insertions(+), 27 deletions(-) rename packages/web/pages/{highlights => highlightsbak}/index.tsx (100%) diff --git a/packages/web/lib/networking/networkHelpers.ts b/packages/web/lib/networking/networkHelpers.ts index d8b290073..461e4f4b7 100644 --- a/packages/web/lib/networking/networkHelpers.ts +++ b/packages/web/lib/networking/networkHelpers.ts @@ -54,6 +54,8 @@ export function gqlFetcher( credentials: 'include', mode: 'cors', }) + graphQLClient.request(query, variables, requestHeaders()).then((result) => {}) + return graphQLClient.request(query, variables, requestHeaders()) } @@ -64,7 +66,6 @@ export function apiFetcher(path: string): Promise { credentials: 'include', mode: 'cors', }).then((result) => { - console.log('api fetcher result: ', result) return result.json() }) } @@ -96,9 +97,10 @@ export function makePublicGqlFetcher( // Partially apply gql variables to the request // This avoids using an object for the swr cache key export function makeGqlFetcher( + gql: string, variables?: unknown ): (query: string) => Promise { - return (query: string) => gqlFetcher(query, variables, true) + return (query: string) => gqlFetcher(gql, variables, true) } export function ssrFetcher( diff --git a/packages/web/lib/networking/queries/useGetArticleOriginalHtmlQuery.tsx b/packages/web/lib/networking/queries/useGetArticleOriginalHtmlQuery.tsx index edee9e662..94870fd94 100644 --- a/packages/web/lib/networking/queries/useGetArticleOriginalHtmlQuery.tsx +++ b/packages/web/lib/networking/queries/useGetArticleOriginalHtmlQuery.tsx @@ -49,7 +49,8 @@ export function useGetArticleOriginalHtmlQuery({ const { data } = useSWRImmutable( slug ? [query, username, slug] : null, - makeGqlFetcher(variables) + makeGqlFetcher(query, variables), + {} ) const resultData: ArticleData | undefined = data as ArticleData diff --git a/packages/web/lib/networking/queries/useGetArticleQuery.tsx b/packages/web/lib/networking/queries/useGetArticleQuery.tsx index f62cc3530..282cff9e1 100644 --- a/packages/web/lib/networking/queries/useGetArticleQuery.tsx +++ b/packages/web/lib/networking/queries/useGetArticleQuery.tsx @@ -7,7 +7,7 @@ import { State, } from '../fragments/articleFragment' import { Highlight, highlightFragment } from '../fragments/highlightFragment' -import { ScopedMutator } from 'swr/dist/types' +import { ScopedMutator } from 'swr/dist/_internal' import { Label, labelFragment } from '../fragments/labelFragment' import { LibraryItems, @@ -116,7 +116,8 @@ export function useGetArticleQuery({ const { data, error, mutate } = useSWR( slug ? [query, username, slug, includeFriendsHighlights] : null, - makeGqlFetcher(variables) + makeGqlFetcher(query, variables), + {} ) let resultData: ArticleData | undefined = data as ArticleData diff --git a/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx b/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx index f75101914..8a87f10ac 100644 --- a/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx +++ b/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx @@ -87,7 +87,7 @@ export function useGetArticleSavingStatus({ ` const key = id ? [query, id] : [query, url] // poll twice a second - const { data, error } = useSWR(key, makeGqlFetcher({ id, url }), { + const { data, error } = useSWR(key, makeGqlFetcher(query, { id, url }), { refreshInterval: 500, }) diff --git a/packages/web/lib/networking/queries/useGetDiscoverFeeds.tsx b/packages/web/lib/networking/queries/useGetDiscoverFeeds.tsx index 517de97e6..3423847b3 100644 --- a/packages/web/lib/networking/queries/useGetDiscoverFeeds.tsx +++ b/packages/web/lib/networking/queries/useGetDiscoverFeeds.tsx @@ -1,6 +1,6 @@ -import { gql } from "graphql-request" -import useSWR from "swr" -import { makeGqlFetcher } from "../networkHelpers" +import { gql } from 'graphql-request' +import useSWR from 'swr' +import { makeGqlFetcher } from '../networkHelpers' type DiscoverFeedsQueryResponse = { error: any @@ -15,9 +15,9 @@ export type DiscoverFeed = { visibleName: string title: string link: string - description?: string, - image? : string, - type: "rss" | "atom" + description?: string + image?: string + type: 'rss' | 'atom' } export function useGetDiscoverFeeds(): DiscoverFeedsQueryResponse { @@ -26,16 +26,16 @@ export function useGetDiscoverFeeds(): DiscoverFeedsQueryResponse { discoverFeeds { ... on DiscoverFeedSuccess { feeds { - visibleName, - id, - title, - link, - description, - image, + visibleName + id + title + link + description + image type } } - ... on DiscoverFeedError{ + ... on DiscoverFeedError { errorCodes } } @@ -44,12 +44,13 @@ export function useGetDiscoverFeeds(): DiscoverFeedsQueryResponse { const { data, error, mutate, isValidating } = useSWR( [query], - makeGqlFetcher() + makeGqlFetcher(query), + {} ) try { if (data) { - const result = data as { discoverFeeds: { feeds: DiscoverFeed[] }} + const result = data as { discoverFeeds: { feeds: DiscoverFeed[] } } const feeds = result.discoverFeeds.feeds as DiscoverFeed[] return { error, diff --git a/packages/web/lib/networking/queries/useGetHome.tsx b/packages/web/lib/networking/queries/useGetHome.tsx index 05f0bf51e..1c4d32839 100644 --- a/packages/web/lib/networking/queries/useGetHome.tsx +++ b/packages/web/lib/networking/queries/useGetHome.tsx @@ -134,7 +134,8 @@ export function useGetHomeItems(): HomeItemResponse { const { data, error, isValidating, mutate } = useSWR( [query, variables.first, variables.after], - makeGqlFetcher(variables) + makeGqlFetcher(query, variables), + {} ) if (error) { diff --git a/packages/web/lib/networking/queries/useGetIntegrationQuery.tsx b/packages/web/lib/networking/queries/useGetIntegrationQuery.tsx index e1a8f2e7e..07cfc0c7c 100644 --- a/packages/web/lib/networking/queries/useGetIntegrationQuery.tsx +++ b/packages/web/lib/networking/queries/useGetIntegrationQuery.tsx @@ -40,7 +40,11 @@ export function useGetIntegrationQuery(name: string): IntegrationQueryResponse { } ` - const { data, mutate, isValidating } = useSWR(query, makeGqlFetcher({ name })) + const { data, mutate, isValidating } = useSWR( + query, + makeGqlFetcher(query, { name }), + {} + ) if (!data) { return { isValidating, @@ -50,7 +54,7 @@ export function useGetIntegrationQuery(name: string): IntegrationQueryResponse { }, } } - + const result = data as IntegrationQueryResponseData const error = result.integration.errorCodes?.find(() => true) if (error) { diff --git a/packages/web/lib/networking/queries/useGetSubscriptionsQuery.tsx b/packages/web/lib/networking/queries/useGetSubscriptionsQuery.tsx index 58ec302dd..d4afc71aa 100644 --- a/packages/web/lib/networking/queries/useGetSubscriptionsQuery.tsx +++ b/packages/web/lib/networking/queries/useGetSubscriptionsQuery.tsx @@ -93,7 +93,8 @@ export function useGetSubscriptionsQuery( } const { data, error, mutate, isValidating } = useSWR( [query, variables], - makeGqlFetcher(variables) + makeGqlFetcher(query, variables), + {} ) try { diff --git a/packages/web/lib/networking/queries/useGetWebhookQuery.tsx b/packages/web/lib/networking/queries/useGetWebhookQuery.tsx index e5e05a6d2..bdd29d4c6 100644 --- a/packages/web/lib/networking/queries/useGetWebhookQuery.tsx +++ b/packages/web/lib/networking/queries/useGetWebhookQuery.tsx @@ -40,8 +40,11 @@ export function useGetWebhookQuery(id: string): WebhookQueryResponse { } ` - const { data, mutate, isValidating } = useSWR(query, makeGqlFetcher({ id })) - console.log('webhook data', data) + const { data, mutate, isValidating } = useSWR( + query, + makeGqlFetcher(query, { id }), + {} + ) try { if (data) { diff --git a/packages/web/pages/highlights/index.tsx b/packages/web/pages/highlightsbak/index.tsx similarity index 100% rename from packages/web/pages/highlights/index.tsx rename to packages/web/pages/highlightsbak/index.tsx From dc6f5521dc1d1b0ce3d74f1a5b920f6ba81c5b44 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 10 Jun 2024 18:20:59 +0800 Subject: [PATCH 05/23] Add migrations --- .../0180.do.add_shortcuts_to_user_personalization.sql | 9 +++++++++ .../0180.undo.add_shortcuts_to_user_personalization.sql | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100755 packages/db/migrations/0180.do.add_shortcuts_to_user_personalization.sql create mode 100755 packages/db/migrations/0180.undo.add_shortcuts_to_user_personalization.sql diff --git a/packages/db/migrations/0180.do.add_shortcuts_to_user_personalization.sql b/packages/db/migrations/0180.do.add_shortcuts_to_user_personalization.sql new file mode 100755 index 000000000..222c5c44b --- /dev/null +++ b/packages/db/migrations/0180.do.add_shortcuts_to_user_personalization.sql @@ -0,0 +1,9 @@ +-- Type: DO +-- Name: add_shortcuts_to_user_personalization +-- Description: Add a new shortcuts column to the user personalization table + +BEGIN; + +ALTER TABLE omnivore.user_personalization ADD COLUMN shortcuts JSONB NULL; + +COMMIT; diff --git a/packages/db/migrations/0180.undo.add_shortcuts_to_user_personalization.sql b/packages/db/migrations/0180.undo.add_shortcuts_to_user_personalization.sql new file mode 100755 index 000000000..31b6a5911 --- /dev/null +++ b/packages/db/migrations/0180.undo.add_shortcuts_to_user_personalization.sql @@ -0,0 +1,9 @@ +-- Type: UNDO +-- Name: add_shortcuts_to_user_personalization +-- Description: Add a new shortcuts column to the user personalization table + +BEGIN; + +ALTER TABLE omnivore.user_personalization DROP COLUMN shortcuts ; + +COMMIT; From 371ed24371888e526733c58ec951806246eeacb2 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 10 Jun 2024 18:22:39 +0800 Subject: [PATCH 06/23] Remove debug --- packages/web/lib/networking/networkHelpers.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/web/lib/networking/networkHelpers.ts b/packages/web/lib/networking/networkHelpers.ts index 461e4f4b7..8c3cd65e7 100644 --- a/packages/web/lib/networking/networkHelpers.ts +++ b/packages/web/lib/networking/networkHelpers.ts @@ -54,7 +54,6 @@ export function gqlFetcher( credentials: 'include', mode: 'cors', }) - graphQLClient.request(query, variables, requestHeaders()).then((result) => {}) return graphQLClient.request(query, variables, requestHeaders()) } From 883d6619ea11a45998883ff006db44d53b16ad69 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 10 Jun 2024 18:56:12 +0800 Subject: [PATCH 07/23] Start to update navigation to use new sections --- .../components/templates/NavigationLayout.tsx | 1 - .../library/LibraryItemsContainer.tsx | 4 +- .../templates/navMenu/LibraryMenu.tsx | 2 +- .../templates/navMenu/NavigationMenu.tsx | 8 +- packages/web/lib/networking/networkHelpers.ts | 3 +- .../queries/useGetLibraryItemsQuery.tsx | 2 +- .../queries/useGetPublicArticleQuery.tsx | 134 ------- .../queries/useValidateUsernameQuery.tsx | 3 +- .../{highlights.tsx => highlights-old.tsx} | 0 packages/web/pages/highlights/index.tsx | 329 ++++++++++++++++++ packages/web/pages/highlightsbak/index.tsx | 29 -- packages/web/pages/{home.tsx => home-old.tsx} | 0 .../web/pages/{justread => home}/debug.tsx | 0 .../web/pages/{justread => home}/index.tsx | 2 +- packages/web/pages/settings/shortcuts.tsx | 4 + 15 files changed, 346 insertions(+), 175 deletions(-) delete mode 100644 packages/web/lib/networking/queries/useGetPublicArticleQuery.tsx rename packages/web/pages/{highlights.tsx => highlights-old.tsx} (100%) create mode 100644 packages/web/pages/highlights/index.tsx delete mode 100644 packages/web/pages/highlightsbak/index.tsx rename packages/web/pages/{home.tsx => home-old.tsx} (100%) rename packages/web/pages/{justread => home}/debug.tsx (100%) rename packages/web/pages/{justread => home}/index.tsx (99%) diff --git a/packages/web/components/templates/NavigationLayout.tsx b/packages/web/components/templates/NavigationLayout.tsx index 0d843d3fa..fcf01b605 100644 --- a/packages/web/components/templates/NavigationLayout.tsx +++ b/packages/web/components/templates/NavigationLayout.tsx @@ -23,7 +23,6 @@ import 'allotment/dist/style.css' import { LibrarySideBar } from './library/LibrarySideBar' export type NavigationSection = - | 'justread' | 'home' | 'library' | 'subscriptions' diff --git a/packages/web/components/templates/library/LibraryItemsContainer.tsx b/packages/web/components/templates/library/LibraryItemsContainer.tsx index 26b806515..5e9c3f771 100644 --- a/packages/web/components/templates/library/LibraryItemsContainer.tsx +++ b/packages/web/components/templates/library/LibraryItemsContainer.tsx @@ -15,9 +15,9 @@ export function LibraryItemsContainer(): JSX.Element { - + {/* - + */} ) } diff --git a/packages/web/components/templates/navMenu/LibraryMenu.tsx b/packages/web/components/templates/navMenu/LibraryMenu.tsx index 7832c220e..23bc2dde4 100644 --- a/packages/web/components/templates/navMenu/LibraryMenu.tsx +++ b/packages/web/components/templates/navMenu/LibraryMenu.tsx @@ -25,7 +25,7 @@ import { HomeIcon } from '../../elements/icons/HomeIcon' import { LibraryIcon } from '../../elements/icons/LibraryIcon' import { HighlightsIcon } from '../../elements/icons/HighlightsIcon' import { CoverImage } from '../../elements/CoverImage' -import { Shortcut } from '../../../pages/settings/shortcuts' +import { Shortcut } from './NavigationMenu' import { OutlinedLabelChip } from '../../elements/OutlinedLabelChip' import { NewsletterIcon } from '../../elements/icons/NewsletterIcon' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' diff --git a/packages/web/components/templates/navMenu/NavigationMenu.tsx b/packages/web/components/templates/navMenu/NavigationMenu.tsx index a3a3131c7..1154616df 100644 --- a/packages/web/components/templates/navMenu/NavigationMenu.tsx +++ b/packages/web/components/templates/navMenu/NavigationMenu.tsx @@ -241,8 +241,8 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { } /> { const url = new URL(path, fetchEndpoint) try { - const response = await fetch(url, { + const response = await fetch(url.toString(), { method: 'PUT', headers: { 'Content-Type': 'application/json', @@ -409,7 +409,7 @@ async function setShortcuts( async function resetShortcuts(path: string): Promise { const url = new URL(path, fetchEndpoint) try { - const response = await fetch(url, { + const response = await fetch(url.toString(), { method: 'DELETE', headers: { 'Content-Type': 'application/json', diff --git a/packages/web/lib/networking/networkHelpers.ts b/packages/web/lib/networking/networkHelpers.ts index 8c3cd65e7..2b810fcee 100644 --- a/packages/web/lib/networking/networkHelpers.ts +++ b/packages/web/lib/networking/networkHelpers.ts @@ -88,9 +88,10 @@ export function apiPoster( } export function makePublicGqlFetcher( + gql: string, variables?: unknown ): (query: string) => Promise { - return (query: string) => gqlFetcher(query, variables, false) + return (query: string) => gqlFetcher(gql, variables, false) } // Partially apply gql variables to the request diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx index a8699993d..325d57c39 100644 --- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx +++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx @@ -256,7 +256,7 @@ export function useGetLibraryItemsQuery({ pageIndex === 0 ? undefined : previousResult.search.pageInfo.endCursor, ] }, - (_query, _l, _s, _sq, cursor) => { + (_query: string, _l: string, _s: string, _sq: string, cursor: string) => { return gqlFetcher(query, { ...variables, after: cursor }, true) }, { revalidateFirstPage: false } diff --git a/packages/web/lib/networking/queries/useGetPublicArticleQuery.tsx b/packages/web/lib/networking/queries/useGetPublicArticleQuery.tsx deleted file mode 100644 index 81a0e60e8..000000000 --- a/packages/web/lib/networking/queries/useGetPublicArticleQuery.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { gql } from 'graphql-request' -import useSWR from 'swr' -import { makePublicGqlFetcher, RequestContext, ssrFetcher } from '../networkHelpers' -import { Highlight } from '../fragments/highlightFragment' - -type PublicArticleQueryInput = { - username: string - slug: string - selectedHighlightId?: string -} - -export type PublicArticleQueryOutput = { - publicArticle?: PublicArticleAttributes - fetchError: unknown - isLoading: boolean - isValidating: boolean -} - -type PublicArticleData = { - sharedArticle: NestedPublicArticleData -} - -type NestedPublicArticleData = { - article: PublicArticleAttributes -} - -export type PublicArticleAttributes = { - id: string - title: string - slug: string - url: string - author?: string - image?: string - description?: string - hasContent?: boolean - highlights: Highlight[] -} - -export const PublicArticleGQLFragment = gql` - fragment PublicArticle on Article { - id - title - slug - url - author - image - description - savedByViewer - postedByViewer - hasContent - highlights { - id - shortId - quote - prefix - suffix - patch - annotation - sharedAt - user { - id - name - profile { - id - username - pictureUrl - } - } - } - } -` - -const query = gql` - query GetPublicArticle( - $username: String! - $slug: String! - $selectedHighlightId: String - ) { - sharedArticle( - username: $username - slug: $slug - selectedHighlightId: $selectedHighlightId - ) { - ... on SharedArticleSuccess { - article { - ...PublicArticle - } - } - - ... on SharedArticleError { - errorCodes - } - } - } - ${PublicArticleGQLFragment} -` - -export function useGetPublicArticleQuery({ - username, - slug, - selectedHighlightId, -}: PublicArticleQueryInput): PublicArticleQueryOutput { - const variables = { - username, - slug, - selectedHighlightId, - } - - const { data, error, isValidating } = useSWR( - // Only make request if username is defined - !!username ? [query, username, slug, selectedHighlightId] : null, - makePublicGqlFetcher(variables) - ) - const publicArticle = (data as PublicArticleData)?.sharedArticle?.article - - return { - publicArticle, - fetchError: error as unknown, - isLoading: !error && !publicArticle, - isValidating, - } -} - -export async function publicArticleQuery( - context: RequestContext, - input: PublicArticleQueryInput -): Promise { - const result = (await ssrFetcher(context, query, input, false)) as PublicArticleData - if (result.sharedArticle.article) { - return result.sharedArticle.article - } - - return Promise.reject() -} diff --git a/packages/web/lib/networking/queries/useValidateUsernameQuery.tsx b/packages/web/lib/networking/queries/useValidateUsernameQuery.tsx index 467d737fc..12fda73f7 100644 --- a/packages/web/lib/networking/queries/useValidateUsernameQuery.tsx +++ b/packages/web/lib/networking/queries/useValidateUsernameQuery.tsx @@ -24,7 +24,8 @@ export function useValidateUsernameQuery({ // Don't fetch if username is empty const { data, error, isValidating } = useSWR( username ? [query, username] : null, - makePublicGqlFetcher({ username }) + makePublicGqlFetcher(query, { username }), + {} ) // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/web/pages/highlights.tsx b/packages/web/pages/highlights-old.tsx similarity index 100% rename from packages/web/pages/highlights.tsx rename to packages/web/pages/highlights-old.tsx diff --git a/packages/web/pages/highlights/index.tsx b/packages/web/pages/highlights/index.tsx new file mode 100644 index 000000000..abd6619f4 --- /dev/null +++ b/packages/web/pages/highlights/index.tsx @@ -0,0 +1,329 @@ +import { NavigationLayout } from '../../components/templates/NavigationLayout' +import { Box, HStack, VStack } from '../../components/elements/LayoutPrimitives' +import { useFetchMore } from '../../lib/hooks/useFetchMoreScroll' +import { useCallback, useMemo, useState } from 'react' +import { useGetHighlights } from '../../lib/networking/queries/useGetHighlights' +import { Highlight } from '../../lib/networking/fragments/highlightFragment' +import { NextRouter, useRouter } from 'next/router' +import { + UserBasicData, + useGetViewerQuery, +} from '../../lib/networking/queries/useGetViewerQuery' +import { SetHighlightLabelsModalPresenter } from '../../components/templates/article/SetLabelsModalPresenter' +import { TrashIcon } from '../../components/elements/icons/TrashIcon' +import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers' +import { ConfirmationModal } from '../../components/patterns/ConfirmationModal' +import { deleteHighlightMutation } from '../../lib/networking/mutations/deleteHighlightMutation' +import { LabelChip } from '../../components/elements/LabelChip' +import ReactMarkdown from 'react-markdown' +import remarkGfm from 'remark-gfm' +import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles' +import { HighlightHoverActions } from '../../components/patterns/HighlightHoverActions' +import { + autoUpdate, + offset, + size, + useFloating, + useHover, + useInteractions, +} from '@floating-ui/react' +import { highlightColor } from '../../lib/themeUpdater' + +import { HighlightViewNote } from '../../components/patterns/HighlightNotes' + +const PAGE_SIZE = 10 + +export default function Highlights(): JSX.Element { + const router = useRouter() + const viewer = useGetViewerQuery() + const [showFilterMenu, setShowFilterMenu] = useState(false) + const [_, setShowAddLinkModal] = useState(false) + + const { isLoading, setSize, size, data, mutate } = useGetHighlights({ + first: PAGE_SIZE, + }) + + const hasMore = useMemo(() => { + if (!data) { + return false + } + return data[data.length - 1].highlights.pageInfo.hasNextPage + }, [data]) + + const handleFetchMore = useCallback(() => { + if (isLoading || !hasMore) { + return + } + setSize(size + 1) + }, [isLoading, hasMore, setSize, size]) + + useFetchMore(handleFetchMore) + + const highlights = useMemo(() => { + if (!data) { + return [] + } + return data.flatMap((res) => res.highlights.edges.map((edge) => edge.node)) + }, [data]) + + return ( + + + {highlights.map((highlight) => { + return ( + viewer.viewerData?.me && ( + + ) + ) + })} + + + ) +} + +type HighlightCardProps = { + highlight: Highlight + viewer: UserBasicData + router: NextRouter + mutate: () => void +} + +type HighlightAnnotationProps = { + highlight: Highlight +} + +function HighlightAnnotation({ + highlight, +}: HighlightAnnotationProps): JSX.Element { + const [noteMode, setNoteMode] = useState<'edit' | 'preview'>('preview') + const [annotation, setAnnotation] = useState(highlight.annotation) + + return ( + { + setAnnotation(highlight.annotation) + }} + /> + ) +} + +function HighlightCard(props: HighlightCardProps): JSX.Element { + const [isOpen, setIsOpen] = useState(false) + const [showConfirmDeleteHighlightId, setShowConfirmDeleteHighlightId] = + useState(undefined) + const [labelsTarget, setLabelsTarget] = useState( + undefined + ) + + const viewInReader = useCallback( + (highlightId: string) => { + const router = props.router + const viewer = props.viewer + const item = props.highlight.libraryItem + + if (!router || !router.isReady || !viewer || !item) { + showErrorToast('Error navigating to highlight') + return + } + + router.push( + { + pathname: '/[username]/[slug]', + query: { + username: viewer.profile.username, + slug: item.slug, + }, + hash: highlightId, + }, + `${viewer.profile.username}/${item.slug}#${highlightId}`, + { + scroll: false, + } + ) + }, + [props.highlight.libraryItem, props.viewer, props.router] + ) + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + middleware: [ + offset({ + mainAxis: -25, + }), + size(), + ], + placement: 'top-end', + whileElementsMounted: autoUpdate, + }) + + const hover = useHover(context) + + const { getReferenceProps, getFloatingProps } = useInteractions([hover]) + + return ( + + + + + + + {timeAgo(props.highlight.updatedAt)} + + {props.highlight.quote && ( + + {props.highlight.quote} + + )} + + {props.highlight.labels && ( + + {props.highlight.labels.map((label) => { + return ( + + ) + })} + + )} + + {props.highlight.libraryItem?.title} + + + {props.highlight.libraryItem?.author} + + {showConfirmDeleteHighlightId && ( + { + ;(async () => { + const highlightId = showConfirmDeleteHighlightId + const success = await deleteHighlightMutation( + props.highlight.libraryItem?.id || '', + showConfirmDeleteHighlightId + ) + props.mutate() + if (success) { + showSuccessToast('Highlight deleted.', { + position: 'bottom-right', + }) + const event = new CustomEvent('deleteHighlightbyId', { + detail: highlightId, + }) + document.dispatchEvent(event) + } else { + showErrorToast('Error deleting highlight', { + position: 'bottom-right', + }) + } + })() + setShowConfirmDeleteHighlightId(undefined) + }} + onOpenChange={() => setShowConfirmDeleteHighlightId(undefined)} + icon={ + + } + /> + )} + {labelsTarget && ( + { + // Don't actually need to do something here + console.log('update highlight: ', highlight) + }} + onOpenChange={() => { + props.mutate() + setLabelsTarget(undefined) + }} + /> + )} + + ) +} diff --git a/packages/web/pages/highlightsbak/index.tsx b/packages/web/pages/highlightsbak/index.tsx deleted file mode 100644 index f9e38bd1d..000000000 --- a/packages/web/pages/highlightsbak/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { NavigationLayout } from '../../components/templates/NavigationLayout' -import { PrimaryLayout } from '../../components/templates/PrimaryLayout' -import { HomeFeedContainer } from '../../components/templates/homeFeed/HomeFeedContainer' -import { VStack } from '../../components/elements/LayoutPrimitives' - -export default function Highlights(): JSX.Element { - return ( - - -
Highlights will go here
-
-
- ) -} diff --git a/packages/web/pages/home.tsx b/packages/web/pages/home-old.tsx similarity index 100% rename from packages/web/pages/home.tsx rename to packages/web/pages/home-old.tsx diff --git a/packages/web/pages/justread/debug.tsx b/packages/web/pages/home/debug.tsx similarity index 100% rename from packages/web/pages/justread/debug.tsx rename to packages/web/pages/home/debug.tsx diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/home/index.tsx similarity index 99% rename from packages/web/pages/justread/index.tsx rename to packages/web/pages/home/index.tsx index a7edcb344..9a854acd3 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/home/index.tsx @@ -38,7 +38,7 @@ export default function Home(): JSX.Element { useApplyLocalTheme() return ( - + { id: search.id, name: search.name, type: 'label', + section: 'library', filter: search.filter, } props.dispatchList({ @@ -364,6 +366,7 @@ const AvailableItems = (props: ListProps): JSX.Element => { id: label.id, type: 'label', label: label, + section: 'library', name: label.name, filter: `label:\"${escapeQuotes(label.name)}\"`, } @@ -408,6 +411,7 @@ const AvailableItems = (props: ListProps): JSX.Element => { onClick={(event) => { const item: Shortcut = { id: subscription.id, + section: 'subscriptions', name: subscription.name, icon: subscription.icon, type: From 0b908c4bb1d1050a99848442d915de43f4d9f17a Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 11 Jun 2024 12:47:12 +0800 Subject: [PATCH 08/23] Dont crash reader if items have a webkit element --- packages/web/components/templates/article/Article.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/web/components/templates/article/Article.tsx b/packages/web/components/templates/article/Article.tsx index 5d58274de..25de5385c 100644 --- a/packages/web/components/templates/article/Article.tsx +++ b/packages/web/components/templates/article/Article.tsx @@ -67,8 +67,9 @@ export function Article(props: ArticleProps): JSX.Element { const [lightboxOpen, setLightboxOpen] = useState(false) const [imageSrcs, setImageSrcs] = useState([]) const [lightboxIndex, setlightBoxIndex] = useState(0) - const [linkHoverData, setlinkHoverData] = - useState() + const [linkHoverData, setlinkHoverData] = useState< + LinkHoverData | undefined + >() useEffect(() => { ;(async () => { @@ -97,7 +98,10 @@ export function Article(props: ArticleProps): JSX.Element { // Post message to webkit so apple app embeds get progress updates // TODO: verify if ios still needs this code...seeems to be duplicated useEffect(() => { - if (typeof window?.webkit != 'undefined') { + if ( + typeof window?.webkit != 'undefined' && + 'messageHandlers' in window.webkit + ) { window.webkit.messageHandlers.readingProgressUpdate?.postMessage({ progress: readingProgress, }) From 3a7698eb165e645614891db1f852426de2bbd3c8 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 11 Jun 2024 12:47:27 +0800 Subject: [PATCH 09/23] Show/hide the navigation menu --- .../components/templates/NavigationLayout.tsx | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/web/components/templates/NavigationLayout.tsx b/packages/web/components/templates/NavigationLayout.tsx index fcf01b605..e5cb17bd4 100644 --- a/packages/web/components/templates/NavigationLayout.tsx +++ b/packages/web/components/templates/NavigationLayout.tsx @@ -121,16 +121,18 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element { setShowNavMenu(!showNavMenu) }} /> - {}} - searchTerm="" - // eslint-disable-next-line @typescript-eslint/no-empty-function - applySearchQuery={() => {}} - showFilterMenu={showNavMenu} - setShowFilterMenu={setShowNavMenu} - /> + {showNavMenu && ( + {}} + searchTerm="" + // eslint-disable-next-line @typescript-eslint/no-empty-function + applySearchQuery={() => {}} + showFilterMenu={showNavMenu} + setShowFilterMenu={setShowNavMenu} + /> + )} {props.children} {showLogoutConfirmation ? ( Date: Tue, 11 Jun 2024 12:47:59 +0800 Subject: [PATCH 10/23] Move highlights into the main navigation container --- .../library/LibraryItemsContainer.tsx | 5 +- packages/web/pages/highlights/index.tsx | 80 ++++++++++++++----- packages/web/pages/library/index.tsx | 15 +--- packages/web/pages/subscriptions/index.tsx | 18 ++--- 4 files changed, 68 insertions(+), 50 deletions(-) diff --git a/packages/web/components/templates/library/LibraryItemsContainer.tsx b/packages/web/components/templates/library/LibraryItemsContainer.tsx index 5e9c3f771..7e391bfe4 100644 --- a/packages/web/components/templates/library/LibraryItemsContainer.tsx +++ b/packages/web/components/templates/library/LibraryItemsContainer.tsx @@ -6,6 +6,7 @@ import { useKBar } from 'kbar' import { useState } from 'react' import { LibraryContainer } from './LibraryContainer' import { LibrarySideBar } from './LibrarySideBar' +import { HighlightsList } from '../../../pages/highlights' export function LibraryItemsContainer(): JSX.Element { const router = useRouter() @@ -15,8 +16,8 @@ export function LibraryItemsContainer(): JSX.Element { - {/* - + {/* + */} ) diff --git a/packages/web/pages/highlights/index.tsx b/packages/web/pages/highlights/index.tsx index abd6619f4..2a8adf898 100644 --- a/packages/web/pages/highlights/index.tsx +++ b/packages/web/pages/highlights/index.tsx @@ -74,31 +74,69 @@ export default function Highlights(): JSX.Element { path: '/highlights', }} > - - {highlights.map((highlight) => { - return ( - viewer.viewerData?.me && ( - - ) - ) - })} - + ) } +export function HighlightsList(): JSX.Element { + const router = useRouter() + const viewer = useGetViewerQuery() + const [showFilterMenu, setShowFilterMenu] = useState(false) + const [_, setShowAddLinkModal] = useState(false) + + const { isLoading, setSize, size, data, mutate } = useGetHighlights({ + first: PAGE_SIZE, + }) + + const hasMore = useMemo(() => { + if (!data) { + return false + } + return data[data.length - 1].highlights.pageInfo.hasNextPage + }, [data]) + + const handleFetchMore = useCallback(() => { + if (isLoading || !hasMore) { + return + } + setSize(size + 1) + }, [isLoading, hasMore, setSize, size]) + + useFetchMore(handleFetchMore) + + const highlights = useMemo(() => { + if (!data) { + return [] + } + return data.flatMap((res) => res.highlights.edges.map((edge) => edge.node)) + }, [data]) + + return ( + + {highlights.map((highlight) => { + return ( + viewer.viewerData?.me && ( + + ) + ) + })} + + ) +} + type HighlightCardProps = { highlight: Highlight viewer: UserBasicData diff --git a/packages/web/pages/library/index.tsx b/packages/web/pages/library/index.tsx index 67d14c46b..f8aa17a2e 100644 --- a/packages/web/pages/library/index.tsx +++ b/packages/web/pages/library/index.tsx @@ -1,12 +1,6 @@ import { NavigationLayout } from '../../components/templates/NavigationLayout' -import { PrimaryLayout } from '../../components/templates/PrimaryLayout' -import { HomeFeedContainer } from '../../components/templates/homeFeed/HomeFeedContainer' -import { Box, VStack } from '../../components/elements/LayoutPrimitives' +import { Box } from '../../components/elements/LayoutPrimitives' import { LibraryContainer } from '../../components/templates/library/LibraryContainer' -import { LibraryItemsContainer } from '../../components/templates/library/LibraryItemsContainer' -import { LibrarySideBar } from '../../components/templates/library/LibrarySideBar' -import { Allotment, LayoutPriority } from 'allotment' -import 'allotment/dist/style.css' export default function Library(): JSX.Element { return ( @@ -17,16 +11,9 @@ export default function Library(): JSX.Element { path: '/library', }} > - {/* - */} - {/* - - - - */} ) } diff --git a/packages/web/pages/subscriptions/index.tsx b/packages/web/pages/subscriptions/index.tsx index 0a68a24cf..48d669fb8 100644 --- a/packages/web/pages/subscriptions/index.tsx +++ b/packages/web/pages/subscriptions/index.tsx @@ -1,7 +1,8 @@ import { NavigationLayout } from '../../components/templates/NavigationLayout' import { PrimaryLayout } from '../../components/templates/PrimaryLayout' import { HomeFeedContainer } from '../../components/templates/homeFeed/HomeFeedContainer' -import { VStack } from '../../components/elements/LayoutPrimitives' +import { Box, VStack } from '../../components/elements/LayoutPrimitives' +import { LibraryContainer } from '../../components/templates/library/LibraryContainer' export default function Subscriptions(): JSX.Element { return ( @@ -12,18 +13,9 @@ export default function Subscriptions(): JSX.Element { path: '/subscriptions', }} > - -
Subscriptions will go here
-
+ + + ) } From dad1fa049ab08c19a28f535e8c55ad15b470b78e Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 11 Jun 2024 13:36:42 +0800 Subject: [PATCH 11/23] Better scroll handling for the library containers --- packages/web/pages/library/index.tsx | 4 +--- packages/web/pages/subscriptions/index.tsx | 4 +--- packages/web/styles/globals.css | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/web/pages/library/index.tsx b/packages/web/pages/library/index.tsx index f8aa17a2e..796d7a4b5 100644 --- a/packages/web/pages/library/index.tsx +++ b/packages/web/pages/library/index.tsx @@ -11,9 +11,7 @@ export default function Library(): JSX.Element { path: '/library', }} > - - - + ) } diff --git a/packages/web/pages/subscriptions/index.tsx b/packages/web/pages/subscriptions/index.tsx index 48d669fb8..3b79509b0 100644 --- a/packages/web/pages/subscriptions/index.tsx +++ b/packages/web/pages/subscriptions/index.tsx @@ -13,9 +13,7 @@ export default function Subscriptions(): JSX.Element { path: '/subscriptions', }} > - - - + ) } diff --git a/packages/web/styles/globals.css b/packages/web/styles/globals.css index 49dd25852..8f5c4c620 100644 --- a/packages/web/styles/globals.css +++ b/packages/web/styles/globals.css @@ -6,7 +6,6 @@ html, body { padding: 0; margin: 0; - overflow: auto; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -webkit-font-smoothing: antialiased; From f0898de5d5ea222585620205813c6bf0fc599424 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 11 Jun 2024 14:31:50 +0800 Subject: [PATCH 12/23] SWR signature has changed for the fetcher --- .../web/lib/networking/queries/useGetLibraryItemsQuery.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx index 325d57c39..999bb0590 100644 --- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx +++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx @@ -244,7 +244,6 @@ export function useGetLibraryItemsQuery({ (pageIndex, previousPageData) => { const key = [query, limit, sortDescending, searchQuery, undefined] const previousResult = previousPageData as LibraryItemsData - if (pageIndex === 0) { return key } @@ -256,8 +255,9 @@ export function useGetLibraryItemsQuery({ pageIndex === 0 ? undefined : previousResult.search.pageInfo.endCursor, ] }, - (_query: string, _l: string, _s: string, _sq: string, cursor: string) => { - return gqlFetcher(query, { ...variables, after: cursor }, true) + (args: any[]) => { + const pageIndex = args[4] as number + return gqlFetcher(query, { ...variables, after: pageIndex }, true) }, { revalidateFirstPage: false } ) From 64ea02bb2c749fc354674a64dbc7273aa596b108 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 11 Jun 2024 16:00:25 +0800 Subject: [PATCH 13/23] Add trash and archive section, css fixups --- .../components/templates/NavigationLayout.tsx | 2 + .../templates/homeFeed/HomeFeedContainer.tsx | 2 +- .../templates/library/LibraryContainer.tsx | 38 ++---- .../library/LibraryItemsContainer.tsx | 10 +- .../templates/navMenu/LibraryLegacyMenu.tsx | 2 +- .../templates/navMenu/LibraryMenu.tsx | 49 -------- .../templates/navMenu/NavigationMenu.tsx | 111 +++++++++--------- .../queries/useGetLibraryItemsQuery.tsx | 17 ++- packages/web/pages/archive/index.tsx | 16 +++ packages/web/pages/library/index.tsx | 3 +- packages/web/pages/settings/account.tsx | 2 +- packages/web/pages/subscriptions/index.tsx | 5 +- packages/web/pages/tools/bulk.tsx | 2 +- packages/web/pages/trash/index.tsx | 16 +++ 14 files changed, 114 insertions(+), 161 deletions(-) create mode 100644 packages/web/pages/archive/index.tsx create mode 100644 packages/web/pages/trash/index.tsx diff --git a/packages/web/components/templates/NavigationLayout.tsx b/packages/web/components/templates/NavigationLayout.tsx index e5cb17bd4..fcf455f29 100644 --- a/packages/web/components/templates/NavigationLayout.tsx +++ b/packages/web/components/templates/NavigationLayout.tsx @@ -27,6 +27,8 @@ export type NavigationSection = | 'library' | 'subscriptions' | 'highlights' + | 'archive' + | 'trash' type NavigationLayoutProps = { children: ReactNode diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 0d221e34f..e1ad0702d 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -116,7 +116,7 @@ export function HomeFeedContainer(): JSX.Element { performActionOnItem, mutate, error: fetchItemsError, - } = useGetLibraryItemsQuery(queryInputs) + } = useGetLibraryItemsQuery('inbox', queryInputs) useEffect(() => { const handleRevalidate = () => { diff --git a/packages/web/components/templates/library/LibraryContainer.tsx b/packages/web/components/templates/library/LibraryContainer.tsx index 2b2f5c5ea..8bd7babab 100644 --- a/packages/web/components/templates/library/LibraryContainer.tsx +++ b/packages/web/components/templates/library/LibraryContainer.tsx @@ -73,7 +73,11 @@ const debouncedFetchSearchResults = debounce((query, cb) => { // the state as Failed. On refresh it will try again if the backend sends "PROCESSING" const TIMEOUT_DELAYS = [2000, 3500, 5000] -export function LibraryContainer(): JSX.Element { +type LibraryContainerProps = { + folder: string +} + +export function LibraryContainer(props: LibraryContainerProps): JSX.Element { const { viewerData } = useGetViewerQuery() const router = useRouter() const { queryValue } = useKBar((state) => ({ queryValue: state.searchQuery })) @@ -81,6 +85,7 @@ export function LibraryContainer(): JSX.Element { const defaultQuery = { limit: 10, + folder: props.folder, sortDescending: true, searchQuery: undefined, } @@ -111,7 +116,7 @@ export function LibraryContainer(): JSX.Element { performActionOnItem, mutate, error: fetchItemsError, - } = useGetLibraryItemsQuery(queryInputs) + } = useGetLibraryItemsQuery(props.folder, queryInputs) useEffect(() => { const handleRevalidate = () => { @@ -920,7 +925,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { height: '100%', px: '20px', py: '20px', - width: !showItems ? '100%' : 'unset', + width: '100%', }} distribution="start" alignment="start" @@ -1181,34 +1186,13 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element { paddingBottom: '0px', overflow: 'visible', px: '70px', + '@lgDown': { + px: '10px', + }, gridTemplateColumns: props.layout == 'LIST_LAYOUT' ? 'none' : `repeat( auto-fit, minmax(300px, 1fr) )`, - // '@media (max-width: 930px)': { - // gridGap: props.layout == 'LIST_LAYOUT' ? '0px' : '20px', - // }, - // '@xlgDown': { - // borderRadius: props.layout == 'LIST_LAYOUT' ? 0 : undefined, - // }, - // '@smDown': { - // border: 'unset', - // width: props.layout == 'LIST_LAYOUT' ? '100vw' : undefined, - // margin: props.layout == 'LIST_LAYOUT' ? '16px -16px' : undefined, - // borderRadius: props.layout == 'LIST_LAYOUT' ? 0 : undefined, - // }, - // '@media (min-width: 930px)': { - // gridTemplateColumns: - // props.layout == 'LIST_LAYOUT' ? 'none' : 'repeat(2, 1fr)', - // }, - // '@media (min-width: 1280px)': { - // gridTemplateColumns: - // props.layout == 'LIST_LAYOUT' ? 'none' : 'repeat(3, 1fr)', - // }, - // '@media (min-width: 1600px)': { - // gridTemplateColumns: - // props.layout == 'LIST_LAYOUT' ? 'none' : 'repeat(4, 1fr)', - // }, }} > {props.items.map((linkedItem) => ( diff --git a/packages/web/components/templates/library/LibraryItemsContainer.tsx b/packages/web/components/templates/library/LibraryItemsContainer.tsx index 7e391bfe4..a6b48ccbc 100644 --- a/packages/web/components/templates/library/LibraryItemsContainer.tsx +++ b/packages/web/components/templates/library/LibraryItemsContainer.tsx @@ -1,20 +1,12 @@ import { Allotment } from 'allotment' import 'allotment/dist/style.css' -import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery' -import { useRouter } from 'next/router' -import { useKBar } from 'kbar' -import { useState } from 'react' import { LibraryContainer } from './LibraryContainer' -import { LibrarySideBar } from './LibrarySideBar' -import { HighlightsList } from '../../../pages/highlights' export function LibraryItemsContainer(): JSX.Element { - const router = useRouter() - return ( - + {/* diff --git a/packages/web/components/templates/navMenu/LibraryLegacyMenu.tsx b/packages/web/components/templates/navMenu/LibraryLegacyMenu.tsx index 5b1dedfe7..631d8ba23 100644 --- a/packages/web/components/templates/navMenu/LibraryLegacyMenu.tsx +++ b/packages/web/components/templates/navMenu/LibraryLegacyMenu.tsx @@ -2,7 +2,7 @@ import { ReactNode, useEffect, useMemo, useRef } from 'react' import { StyledText } from '../../elements/StyledText' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { Button } from '../../elements/Button' -import { Circle, NewspaperClipping, X } from '@phosphor-icons/react' +import { Circle, X } from '@phosphor-icons/react' import { Subscription, SubscriptionType, diff --git a/packages/web/components/templates/navMenu/LibraryMenu.tsx b/packages/web/components/templates/navMenu/LibraryMenu.tsx index 23bc2dde4..6e4849bba 100644 --- a/packages/web/components/templates/navMenu/LibraryMenu.tsx +++ b/packages/web/components/templates/navMenu/LibraryMenu.tsx @@ -46,55 +46,6 @@ type LibraryFilterMenuProps = { } export function LibraryFilterMenu(props: LibraryFilterMenuProps): JSX.Element { - const [labels, setLabels] = usePersistedState({ - key: 'menu-labels', - isSessionStorage: false, - initialValue: [], - }) - const [savedSearches, setSavedSearches] = usePersistedState({ - key: 'menu-searches', - isSessionStorage: false, - initialValue: [], - }) - const [subscriptions, setSubscriptions] = usePersistedState({ - key: 'menu-subscriptions', - isSessionStorage: false, - initialValue: [], - }) - const labelsResponse = useGetLabelsQuery() - const searchesResponse = useGetSavedSearchQuery() - const subscriptionsResponse = useGetSubscriptionsQuery() - - useEffect(() => { - if ( - !labelsResponse.error && - !labelsResponse.isLoading && - labelsResponse.labels - ) { - setLabels(labelsResponse.labels) - } - }, [setLabels, labelsResponse]) - - useEffect(() => { - if ( - !subscriptionsResponse.error && - !subscriptionsResponse.isLoading && - subscriptionsResponse.subscriptions - ) { - setSubscriptions(subscriptionsResponse.subscriptions) - } - }, [setSubscriptions, subscriptionsResponse]) - - useEffect(() => { - if ( - !searchesResponse.error && - !searchesResponse.isLoading && - searchesResponse.savedSearches - ) { - setSavedSearches(searchesResponse.savedSearches) - } - }, [setSavedSearches, searchesResponse]) - return ( <> ({ - key: 'menu-labels', - isSessionStorage: false, - initialValue: [], - }) - const [savedSearches, setSavedSearches] = usePersistedState({ - key: 'menu-searches', - isSessionStorage: false, - initialValue: [], - }) - const [subscriptions, setSubscriptions] = usePersistedState({ - key: 'menu-subscriptions', - isSessionStorage: false, - initialValue: [], - }) - const labelsResponse = useGetLabelsQuery() - const searchesResponse = useGetSavedSearchQuery() - const subscriptionsResponse = useGetSubscriptionsQuery() - - useEffect(() => { - if ( - !labelsResponse.error && - !labelsResponse.isLoading && - labelsResponse.labels - ) { - setLabels(labelsResponse.labels) - } - }, [setLabels, labelsResponse]) - - useEffect(() => { - if ( - !subscriptionsResponse.error && - !subscriptionsResponse.isLoading && - subscriptionsResponse.subscriptions - ) { - setSubscriptions(subscriptionsResponse.subscriptions) - } - }, [setSubscriptions, subscriptionsResponse]) - - useEffect(() => { - if ( - !searchesResponse.error && - !searchesResponse.isLoading && - searchesResponse.savedSearches - ) { - setSavedSearches(searchesResponse.savedSearches) - } - }, [setSavedSearches, searchesResponse]) - return ( <> { + const [moreFoldersOpenState, setMoreFoldersOpenState] = + usePersistedState({ + key: 'nav-menu-more-folders-open', + isSessionStorage: false, + initialValue: false, + }) return ( { isSelected={props.section == 'highlights'} icon={} /> + + {moreFoldersOpenState && ( + + } + /> + } + /> + + )} ) } diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx index 999bb0590..f7e0c6fd5 100644 --- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx +++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx @@ -142,13 +142,11 @@ export const recommendationFragment = gql` } ` -export function useGetLibraryItemsQuery({ - limit, - sortDescending, - searchQuery, - cursor, - includeContent = false, -}: LibraryItemsQueryInput): LibraryItemsQueryResponse { +export function useGetLibraryItemsQuery( + folder: string, + { limit, searchQuery, cursor, includeContent = false }: LibraryItemsQueryInput +): LibraryItemsQueryResponse { + const fullQuery = (`in:${folder} use:folders ` + (searchQuery ?? '')).trim() const query = gql` query Search( $after: String @@ -236,13 +234,13 @@ export function useGetLibraryItemsQuery({ const variables = { after: cursor, first: limit, - query: searchQuery, + query: fullQuery, includeContent, } const { data, error, mutate, size, setSize, isValidating } = useSWRInfinite( (pageIndex, previousPageData) => { - const key = [query, limit, sortDescending, searchQuery, undefined] + const key = [query, variables.first, variables.query, undefined] const previousResult = previousPageData as LibraryItemsData if (pageIndex === 0) { return key @@ -250,7 +248,6 @@ export function useGetLibraryItemsQuery({ return [ query, limit, - sortDescending, searchQuery, pageIndex === 0 ? undefined : previousResult.search.pageInfo.endCursor, ] diff --git a/packages/web/pages/archive/index.tsx b/packages/web/pages/archive/index.tsx new file mode 100644 index 000000000..b11b73f5c --- /dev/null +++ b/packages/web/pages/archive/index.tsx @@ -0,0 +1,16 @@ +import { NavigationLayout } from '../../components/templates/NavigationLayout' +import { LibraryContainer } from '../../components/templates/library/LibraryContainer' + +export default function Archive(): JSX.Element { + return ( + + + + ) +} diff --git a/packages/web/pages/library/index.tsx b/packages/web/pages/library/index.tsx index 796d7a4b5..e21915ad2 100644 --- a/packages/web/pages/library/index.tsx +++ b/packages/web/pages/library/index.tsx @@ -1,5 +1,4 @@ import { NavigationLayout } from '../../components/templates/NavigationLayout' -import { Box } from '../../components/elements/LayoutPrimitives' import { LibraryContainer } from '../../components/templates/library/LibraryContainer' export default function Library(): JSX.Element { @@ -11,7 +10,7 @@ export default function Library(): JSX.Element { path: '/library', }} > - + ) } diff --git a/packages/web/pages/settings/account.tsx b/packages/web/pages/settings/account.tsx index 3cf0f6a84..e9ef3838b 100644 --- a/packages/web/pages/settings/account.tsx +++ b/packages/web/pages/settings/account.tsx @@ -99,7 +99,7 @@ export default function Account(): JSX.Element { isUsernameValidationLoading, ]) - const { itemsPages, isValidating } = useGetLibraryItemsQuery({ + const { itemsPages, isValidating } = useGetLibraryItemsQuery('', { limit: 0, searchQuery: 'in:all', sortDescending: false, diff --git a/packages/web/pages/subscriptions/index.tsx b/packages/web/pages/subscriptions/index.tsx index 3b79509b0..dae9f6acc 100644 --- a/packages/web/pages/subscriptions/index.tsx +++ b/packages/web/pages/subscriptions/index.tsx @@ -1,7 +1,4 @@ import { NavigationLayout } from '../../components/templates/NavigationLayout' -import { PrimaryLayout } from '../../components/templates/PrimaryLayout' -import { HomeFeedContainer } from '../../components/templates/homeFeed/HomeFeedContainer' -import { Box, VStack } from '../../components/elements/LayoutPrimitives' import { LibraryContainer } from '../../components/templates/library/LibraryContainer' export default function Subscriptions(): JSX.Element { @@ -13,7 +10,7 @@ export default function Subscriptions(): JSX.Element { path: '/subscriptions', }} > - + ) } diff --git a/packages/web/pages/tools/bulk.tsx b/packages/web/pages/tools/bulk.tsx index 1229aa4bd..24efa499f 100644 --- a/packages/web/pages/tools/bulk.tsx +++ b/packages/web/pages/tools/bulk.tsx @@ -33,7 +33,7 @@ export default function BulkPerformer(): JSX.Element { const [errorMessage, setErrorMessage] = useState() const [runningState, setRunningState] = useState('none') - const { itemsPages, isValidating } = useGetLibraryItemsQuery({ + const { itemsPages, isValidating } = useGetLibraryItemsQuery('', { searchQuery: query, limit: 1, sortDescending: false, diff --git a/packages/web/pages/trash/index.tsx b/packages/web/pages/trash/index.tsx new file mode 100644 index 000000000..1d3d29bc7 --- /dev/null +++ b/packages/web/pages/trash/index.tsx @@ -0,0 +1,16 @@ +import { NavigationLayout } from '../../components/templates/NavigationLayout' +import { LibraryContainer } from '../../components/templates/library/LibraryContainer' + +export default function Trash(): JSX.Element { + return ( + + + + ) +} From 5e7bd2fe0e674100b4cb79178d279d902f723524 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 11 Jun 2024 17:10:38 +0800 Subject: [PATCH 14/23] Theme clean up --- .../web/components/elements/SuggestionBox.tsx | 8 ++++-- .../templates/article/HighlightsLayer.tsx | 28 +++++++++++-------- .../templates/homeFeed/LibraryHeader.tsx | 1 - .../templates/library/LibraryHeader.tsx | 2 -- .../components/templates/navMenu/Footer.tsx | 2 +- .../templates/navMenu/NavigationMenu.tsx | 1 + .../web/components/tokens/stitches.config.ts | 24 +++++++++++++++- packages/web/pages/highlights/index.tsx | 3 -- 8 files changed, 46 insertions(+), 23 deletions(-) diff --git a/packages/web/components/elements/SuggestionBox.tsx b/packages/web/components/elements/SuggestionBox.tsx index 5118708af..5ffaeb822 100644 --- a/packages/web/components/elements/SuggestionBox.tsx +++ b/packages/web/components/elements/SuggestionBox.tsx @@ -40,14 +40,16 @@ const InternalOrExternalLink = (props: InternalOrExternalLinkProps) => { }} > {!isExternal ? ( - {props.children} + + {props.children} + ) : ( {props.children} )} - ); + ) } export const SuggestionBox = (props: SuggestionBoxProps) => { @@ -59,7 +61,7 @@ export const SuggestionBox = (props: SuggestionBoxProps) => { flexDirection: props.size == 'large' ? 'column' : 'row', width: 'fit-content', borderRadius: '5px', - background: props.background ?? '$thBackground3', + background: props.background ?? 'unset', fontSize: '15px', fontFamily: '$inter', fontWeight: '500', diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index a117e0ede..24e96ec9e 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -80,13 +80,15 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 }) const [currentHighlightIdx, setCurrentHighlightIdx] = useState(0) - const [focusedHighlight, setFocusedHighlight] = - useState(undefined) + const [focusedHighlight, setFocusedHighlight] = useState< + Highlight | undefined + >(undefined) const [selectionData, setSelectionData] = useSelection(highlightLocations) - const [labelsTarget, setLabelsTarget] = - useState(undefined) + const [labelsTarget, setLabelsTarget] = useState( + undefined + ) const [ confirmDeleteHighlightWithNoteId, @@ -363,13 +365,15 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { // highlight, so the app can display a native menu const rect = (target as Element).getBoundingClientRect() - window?.webkit?.messageHandlers.viewerAction?.postMessage({ - actionID: 'showMenu', - rectX: rect.x, - rectY: rect.y, - rectWidth: rect.width, - rectHeight: rect.height, - }) + if (window?.webkit?.messageHandlers) { + window?.webkit?.messageHandlers.viewerAction?.postMessage({ + actionID: 'showMenu', + rectX: rect.x, + rectY: rect.y, + rectWidth: rect.width, + rectHeight: rect.height, + }) + } window?.AndroidWebKitMessenger?.handleIdentifiableMessage( 'existingHighlightTap', @@ -394,7 +398,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { const highlight = highlights.find(($0) => $0.id === id) setFocusedHighlight(highlight) setLabelsTarget(highlight) - } else { + } else if (window?.webkit?.messageHandlers) { window?.webkit?.messageHandlers.viewerAction?.postMessage({ actionID: 'pageTapped', }) diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index b8636a80f..99412b1df 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -88,7 +88,6 @@ export function LegacyLibraryHeader(props: LibraryHeaderProps): JSX.Element { right: '0', zIndex: 5, px: '70px', - bg: '$thLibraryBackground', position: 'fixed', left: LIBRARY_LEFT_MENU_WIDTH, height: small ? '60px' : DEFAULT_HEADER_HEIGHT, diff --git a/packages/web/components/templates/library/LibraryHeader.tsx b/packages/web/components/templates/library/LibraryHeader.tsx index ecb4eef59..8995e7ebb 100644 --- a/packages/web/components/templates/library/LibraryHeader.tsx +++ b/packages/web/components/templates/library/LibraryHeader.tsx @@ -86,8 +86,6 @@ export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { css={{ width: '100%', px: '70px', - bg: '$thLibraryBackground', - // position: 'sticky', left: LIBRARY_LEFT_MENU_WIDTH, height: small ? '60px' : DEFAULT_HEADER_HEIGHT, transition: 'height 0.5s', diff --git a/packages/web/components/templates/navMenu/Footer.tsx b/packages/web/components/templates/navMenu/Footer.tsx index fa1e5b611..273514a6c 100644 --- a/packages/web/components/templates/navMenu/Footer.tsx +++ b/packages/web/components/templates/navMenu/Footer.tsx @@ -17,7 +17,7 @@ export const NavMenuFooter = (props: NavMenuFooterProps): JSX.Element => { position: 'fixed', bottom: '0%', alignItems: 'center', - backgroundColor: '$thBackground2', + backgroundColor: '$thNavMenuFooter', width: LIBRARY_LEFT_MENU_WIDTH, overflowY: 'auto', overflowX: 'hidden', diff --git a/packages/web/components/templates/navMenu/NavigationMenu.tsx b/packages/web/components/templates/navMenu/NavigationMenu.tsx index 64d921a39..b9ae4d1b9 100644 --- a/packages/web/components/templates/navMenu/NavigationMenu.tsx +++ b/packages/web/components/templates/navMenu/NavigationMenu.tsx @@ -750,6 +750,7 @@ const NodeItemContents = (props: NodeItemContentsProps): JSX.Element => { {props.node.isClosed ? ( ) : ( diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index 97edb8660..f69f0fd27 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -181,6 +181,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } = thBackgroundActive: '#FFEA9F', thBackgroundContrast: '#FFFFFF', thLeftMenuBackground: '#FCFCFC', + thNavMenuFooter: '#DFDFDF', thLibraryBackground: '#FFFFFF', thLibrarySearchbox: '#FCFCFC', thLibraryMenuPrimary: '#3D3D3D', @@ -324,6 +325,7 @@ const darkThemeSpec = { thBackgroundActive: '#3D3D3D', thBackgroundContrast: '#000000', thLeftMenuBackground: '#343434', + thNavMenuFooter: '#515151', thLibraryBackground: '#2A2A2A', thLibrarySearchbox: '#3D3D3D', thLibraryMenuPrimary: '#EBEBEB', @@ -397,11 +399,21 @@ const sepiaThemeSpec = { colorScheme: 'light', }, colors: { - readerBg: '#FBF0D9', + readerBg: '#FDF6E3', readerFont: '#5F4B32', readerMargin: '#F3F3F3', readerFontHighContrast: '#0A0806', readerTableHeader: '#FFFFFF', + + thLeftMenuBackground: '#EEE8D5', + thNavMenuFooter: '#DDD6C1', + + thLibrarySelectionColor: '#DDD6C1', + thBorderColor: '#DDD6C1', + + thBackground: '#FDF6E3', + + thLibraryMultiselectHover: '#EEE8D5', }, } @@ -412,6 +424,16 @@ const apolloThemeSpec = { readerMargin: '#474747', readerFontHighContrast: 'white', readerTableHeader: '#FFFFFF', + + thLeftMenuBackground: '#3D3D3D', + thNavMenuFooter: '#515151', + + thLibrarySelectionColor: '#515151', + thBorderColor: '#6A6968', + + thBackground: '#474747', + thBackground2: '#515151', + thLibraryMultiselectHover: '#EEE8D5', }, } diff --git a/packages/web/pages/highlights/index.tsx b/packages/web/pages/highlights/index.tsx index 2a8adf898..8b6f86e5d 100644 --- a/packages/web/pages/highlights/index.tsx +++ b/packages/web/pages/highlights/index.tsx @@ -235,9 +235,6 @@ function HighlightCard(props: HighlightCardProps): JSX.Element { bg: '$thBackground2', borderRadius: '8px', cursor: 'pointer', - '&:hover': { - backgroundColor: '$thBackground3', - }, }} > Date: Tue, 11 Jun 2024 17:49:14 +0800 Subject: [PATCH 15/23] More work on themes --- .../icons/home/AddToLibraryActionIcon.tsx | 1 - .../web/components/tokens/stitches.config.ts | 66 ++++++++++++------- packages/web/pages/highlights/index.tsx | 1 + packages/web/pages/home/index.tsx | 35 +++++----- 4 files changed, 64 insertions(+), 39 deletions(-) diff --git a/packages/web/components/elements/icons/home/AddToLibraryActionIcon.tsx b/packages/web/components/elements/icons/home/AddToLibraryActionIcon.tsx index b2db19fe2..ae852aac8 100644 --- a/packages/web/components/elements/icons/home/AddToLibraryActionIcon.tsx +++ b/packages/web/components/elements/icons/home/AddToLibraryActionIcon.tsx @@ -7,7 +7,6 @@ import React from 'react' export class AddToLibraryActionIcon extends React.Component { render() { const strokeColor = (this.props.color || '#D9D9D9').toString() - const backgroundColor = (this.props.color || '#3D3D3D').toString() return ( { fontFamily: '$inter', fontSize: '16px', fontWeight: '600', - color: '$readerText', + color: '$homeTextTitle', }} > {props.homeSection.title} - {props.homeSection.items.map((homeItem) => { return })} @@ -149,7 +148,7 @@ const TopPicksHomeSection = (props: HomeSectionProps): JSX.Element => { fontFamily: '$inter', fontSize: '16px', fontWeight: '600', - color: '$readerText', + color: '$homeTextTitle', }} > {props.homeSection.title} @@ -232,7 +231,7 @@ const HiddenHomeSection = (props: HomeSectionProps): JSX.Element => { fontFamily: '$inter', fontSize: '16px', fontWeight: '600', - color: '$readerText', + color: '$homeTextTitle', }} > {props.homeSection.title} @@ -299,7 +298,7 @@ const TimeAgo = (props: HomeItemViewProps): JSX.Element => { fontSize: '12px', fontWeight: 'medium', fontFamily: '$inter', - color: '$readerTextSubtle', + color: '$homeTextSubtle', }} > {timeAgo(props.homeItem.date)} @@ -317,7 +316,7 @@ const Title = (props: HomeItemViewProps): JSX.Element => { lineHeight: '20px', fontWeight: '600', fontFamily: '$inter', - color: '$readerText', + color: '$homeTextTitle', overflow: 'hidden', textOverflow: 'ellipsis', wordBreak: 'break-word', @@ -366,7 +365,7 @@ const JustAddedItemView = (props: HomeItemViewProps): JSX.Element => { padding: '5px', borderRadius: '5px', '&:hover': { - bg: '$thBackground', + bg: '$homeCardHover', borderRadius: '0px', }, }} @@ -403,7 +402,7 @@ const TopicPickHomeItemView = (props: HomeItemViewProps): JSX.Element => { borderRadius: '5px', '&:hover': { - bg: '#323232', + bg: '$homeCardHover', borderRadius: '0px', }, }} @@ -450,22 +449,26 @@ const TopicPickHomeItemView = (props: HomeItemViewProps): JSX.Element => { - +
) } @@ -537,7 +540,7 @@ const SourceInfo = (props: HomeItemViewProps) => ( fontFamily: '$inter', fontWeight: '500', fontSize: '13px', - color: '$readerFont', + color: '$homeTextSource', textDecoration: 'underline', }} > @@ -625,7 +628,7 @@ const SubscriptionSourceHoverContent = ( css={{ fontFamily: '$inter', fontSize: '13px', - color: '$thTextSubtle4', + color: '$homeTextBody', }} > {subscription ? <>{subscription.description} : <>} From 5f817bc336d23282e1824f446b268a6b200ff8b1 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 12 Jun 2024 14:00:45 +0800 Subject: [PATCH 16/23] Null handler as we know URL must be set for RSS subscriptions --- packages/api/src/services/user_personalization.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/services/user_personalization.ts b/packages/api/src/services/user_personalization.ts index d517215bb..5539ea462 100644 --- a/packages/api/src/services/user_personalization.ts +++ b/packages/api/src/services/user_personalization.ts @@ -146,7 +146,7 @@ const userDefaultShortcuts = async (userId: string): Promise => { filter: subscription.type == 'NEWSLETTER' ? `in:following subscription:"${subscription.name}"` - : `in:following rss:"${subscription.url}"`, + : `in:following rss:"${subscription.url ?? ''}"`, } }), }, From 5a495798ac1c394868b5a3745c0451e7ef082dbb Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 12 Jun 2024 14:15:27 +0800 Subject: [PATCH 17/23] Better hover colours for navigation menu items --- packages/web/components/templates/navMenu/NavigationMenu.tsx | 1 - packages/web/components/tokens/stitches.config.ts | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/web/components/templates/navMenu/NavigationMenu.tsx b/packages/web/components/templates/navMenu/NavigationMenu.tsx index b9ae4d1b9..64d921a39 100644 --- a/packages/web/components/templates/navMenu/NavigationMenu.tsx +++ b/packages/web/components/templates/navMenu/NavigationMenu.tsx @@ -750,7 +750,6 @@ const NodeItemContents = (props: NodeItemContentsProps): JSX.Element => { {props.node.isClosed ? ( ) : ( diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index bb27a1aae..6ed792011 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -339,7 +339,7 @@ const darkThemeSpec = { thLibraryMenuPrimary: '#EBEBEB', thLibraryMenuSecondary: '#EBEBEB', thLibraryMenuUnselected: 'white', - thLibrarySelectionColor: '#DFDFDF', + thLibrarySelectionColor: '#515151', thLibraryNavigationMenuFooter: '#3D3D3D', thLibraryMenuFooterHover: '#6A6968', thLibraryMultiselectHover: '#6A6968', @@ -422,6 +422,7 @@ const apolloThemeSpec = { thNavMenuFooter: '#515151', thLibrarySelectionColor: '#515151', + thBackground4: '#51515166', // used on hover of nav menu items thBorderColor: '#6A6968', homeCardHover: '#525252', @@ -448,6 +449,7 @@ const sepiaThemeSpec = { thNavMenuFooter: '#DDD6C1', thLibrarySelectionColor: '#DDD6C1', + thBackground4: '#DDD6C166', // used on hover of menu items thBorderColor: '#DDD6C1', thBackground: '#FDF6E3', From 05cf487b5a0f829951437a48c5a1f8168c3c60e2 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 12 Jun 2024 15:20:03 +0800 Subject: [PATCH 18/23] Cleanups for navigation menu icons --- .../elements/icons/ArchiveSectionIcon.tsx | 32 +++++++++++++ .../components/elements/icons/HomeIcon.tsx | 3 +- .../elements/icons/NavMoreButtonDown.tsx | 31 ++++++++++++ .../elements/icons/NavMoreButtonUp.tsx | 29 +++++++++++ .../elements/icons/ShortcutFolderClosed.tsx | 29 +++++++++++ .../components/templates/PrimaryDropdown.tsx | 5 +- .../components/templates/navMenu/Footer.tsx | 5 +- .../templates/navMenu/NavigationMenu.tsx | 48 +++++++++++++++---- .../templates/reader/ReaderHeader.tsx | 6 --- 9 files changed, 165 insertions(+), 23 deletions(-) create mode 100644 packages/web/components/elements/icons/ArchiveSectionIcon.tsx create mode 100644 packages/web/components/elements/icons/NavMoreButtonDown.tsx create mode 100644 packages/web/components/elements/icons/NavMoreButtonUp.tsx create mode 100644 packages/web/components/elements/icons/ShortcutFolderClosed.tsx diff --git a/packages/web/components/elements/icons/ArchiveSectionIcon.tsx b/packages/web/components/elements/icons/ArchiveSectionIcon.tsx new file mode 100644 index 000000000..b37c0447b --- /dev/null +++ b/packages/web/components/elements/icons/ArchiveSectionIcon.tsx @@ -0,0 +1,32 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class ArchiveSectionIcon extends React.Component { + render() { + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + + + + ) + } +} diff --git a/packages/web/components/elements/icons/HomeIcon.tsx b/packages/web/components/elements/icons/HomeIcon.tsx index 0f9e7ce0d..2bba94f9f 100644 --- a/packages/web/components/elements/icons/HomeIcon.tsx +++ b/packages/web/components/elements/icons/HomeIcon.tsx @@ -1,5 +1,6 @@ /* eslint-disable functional/no-class */ /* eslint-disable functional/no-this-expression */ +import { SpanBox } from '../LayoutPrimitives' import { IconProps } from './IconProps' import React from 'react' @@ -19,7 +20,7 @@ export class HomeIcon extends React.Component { > diff --git a/packages/web/components/elements/icons/NavMoreButtonDown.tsx b/packages/web/components/elements/icons/NavMoreButtonDown.tsx new file mode 100644 index 000000000..4d76c4c0a --- /dev/null +++ b/packages/web/components/elements/icons/NavMoreButtonDown.tsx @@ -0,0 +1,31 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class NavMoreButtonDownIcon extends React.Component { + render() { + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + + + ) + } +} diff --git a/packages/web/components/elements/icons/NavMoreButtonUp.tsx b/packages/web/components/elements/icons/NavMoreButtonUp.tsx new file mode 100644 index 000000000..4270cad1e --- /dev/null +++ b/packages/web/components/elements/icons/NavMoreButtonUp.tsx @@ -0,0 +1,29 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class NavMoreButtonUpIcon extends React.Component { + render() { + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + ) + } +} diff --git a/packages/web/components/elements/icons/ShortcutFolderClosed.tsx b/packages/web/components/elements/icons/ShortcutFolderClosed.tsx new file mode 100644 index 000000000..558cd8acf --- /dev/null +++ b/packages/web/components/elements/icons/ShortcutFolderClosed.tsx @@ -0,0 +1,29 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class ShortcutFolderClosed extends React.Component { + render() { + const size = (this.props.size || 26).toString() + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + + + ) + } +} diff --git a/packages/web/components/templates/PrimaryDropdown.tsx b/packages/web/components/templates/PrimaryDropdown.tsx index e2510ea7c..2785f8a57 100644 --- a/packages/web/components/templates/PrimaryDropdown.tsx +++ b/packages/web/components/templates/PrimaryDropdown.tsx @@ -20,8 +20,6 @@ import { ThemeSelector } from './article/ReaderSettingsControl' type PrimaryDropdownProps = { children?: ReactNode - showThemeSection: boolean - showFullThemeSection: boolean layout?: LayoutType updateLayout?: (layout: LayoutType) => void @@ -194,8 +192,7 @@ export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { - {props.showThemeSection && } - {props.showFullThemeSection && } + headerDropdownActionHandler('navigate-to-install')} title="Install" diff --git a/packages/web/components/templates/navMenu/Footer.tsx b/packages/web/components/templates/navMenu/Footer.tsx index 273514a6c..4a1c4f754 100644 --- a/packages/web/components/templates/navMenu/Footer.tsx +++ b/packages/web/components/templates/navMenu/Footer.tsx @@ -29,10 +29,7 @@ export const NavMenuFooter = (props: NavMenuFooterProps): JSX.Element => { }, }} > - + { style="articleActionIcon" css={{ display: 'flex', - ml: '15px', width: '100%', + + gap: '10px', + maxWidth: '100%', + height: '34px', + px: '15px', + + fontSize: '15px', + fontWeight: 'regular', + fontFamily: '$display', + + color: '$thLibraryMenuUnselected', + '&:hover': { + opacity: '1', + color: '$thLibraryMenuUnselected', backgroundColor: '$thBackground4', }, }} @@ -238,11 +257,19 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { event.preventDefault() }} > - + {moreFoldersOpenState ? ( - + ) : ( - + )} More @@ -254,14 +281,20 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { text="Archive" section="archive" isSelected={props.section == 'archive'} - icon={} + icon={ + + } /> } + icon={ + + } /> )} @@ -748,8 +781,7 @@ const NodeItemContents = (props: NodeItemContentsProps): JSX.Element => { }} > {props.node.isClosed ? ( - ) : ( diff --git a/packages/web/components/templates/reader/ReaderHeader.tsx b/packages/web/components/templates/reader/ReaderHeader.tsx index 75c776241..99a00d533 100644 --- a/packages/web/components/templates/reader/ReaderHeader.tsx +++ b/packages/web/components/templates/reader/ReaderHeader.tsx @@ -144,12 +144,6 @@ function ControlButtonBox(props: ReaderHeaderProps): JSX.Element { color={theme.colors.thHighContrast.toString()} /> - - - ) From 39bfe04ec1d143341592bf197f8ff52f45083ce0 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 12 Jun 2024 17:31:27 +0800 Subject: [PATCH 19/23] Update folder icons --- .../elements/icons/ShortcutFolderOpen.tsx | 31 ++++++++++++++++++ .../elements/icons/TrashSectionIcon.tsx | 32 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 packages/web/components/elements/icons/ShortcutFolderOpen.tsx create mode 100644 packages/web/components/elements/icons/TrashSectionIcon.tsx diff --git a/packages/web/components/elements/icons/ShortcutFolderOpen.tsx b/packages/web/components/elements/icons/ShortcutFolderOpen.tsx new file mode 100644 index 000000000..7c196bca9 --- /dev/null +++ b/packages/web/components/elements/icons/ShortcutFolderOpen.tsx @@ -0,0 +1,31 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class ShortcutFolderOpen extends React.Component { + render() { + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + + + ) + } +} diff --git a/packages/web/components/elements/icons/TrashSectionIcon.tsx b/packages/web/components/elements/icons/TrashSectionIcon.tsx new file mode 100644 index 000000000..a42cea548 --- /dev/null +++ b/packages/web/components/elements/icons/TrashSectionIcon.tsx @@ -0,0 +1,32 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class TrashSectionIcon extends React.Component { + render() { + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + + + + ) + } +} From cdf921c4b46011415adc00d230039a664ada8c16 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 12 Jun 2024 18:48:33 +0800 Subject: [PATCH 20/23] Clean up nav so there is no flicker due to component reloading --- .../nav-containers/highlights.tsx} | 68 +---- .../nav-containers/home.tsx} | 236 +++++------------- .../components/templates/NavigationLayout.tsx | 8 +- .../templates/navMenu/NavigationMenu.tsx | 180 ++----------- packages/web/pages/archive/index.tsx | 16 -- packages/web/pages/l/[section].tsx | 55 ++++ packages/web/pages/library/index.tsx | 16 -- packages/web/pages/trash/index.tsx | 16 -- 8 files changed, 159 insertions(+), 436 deletions(-) rename packages/web/{pages/highlights/index.tsx => components/nav-containers/highlights.tsx} (81%) rename packages/web/{pages/home/index.tsx => components/nav-containers/home.tsx} (72%) delete mode 100644 packages/web/pages/archive/index.tsx create mode 100644 packages/web/pages/l/[section].tsx delete mode 100644 packages/web/pages/library/index.tsx delete mode 100644 packages/web/pages/trash/index.tsx diff --git a/packages/web/pages/highlights/index.tsx b/packages/web/components/nav-containers/highlights.tsx similarity index 81% rename from packages/web/pages/highlights/index.tsx rename to packages/web/components/nav-containers/highlights.tsx index 12cf56ea4..ef1358249 100644 --- a/packages/web/pages/highlights/index.tsx +++ b/packages/web/components/nav-containers/highlights.tsx @@ -1,5 +1,5 @@ -import { NavigationLayout } from '../../components/templates/NavigationLayout' -import { Box, HStack, VStack } from '../../components/elements/LayoutPrimitives' +import { NavigationLayout } from '../templates/NavigationLayout' +import { Box, HStack, VStack } from '../elements/LayoutPrimitives' import { useFetchMore } from '../../lib/hooks/useFetchMoreScroll' import { useCallback, useMemo, useState } from 'react' import { useGetHighlights } from '../../lib/networking/queries/useGetHighlights' @@ -9,16 +9,16 @@ import { UserBasicData, useGetViewerQuery, } from '../../lib/networking/queries/useGetViewerQuery' -import { SetHighlightLabelsModalPresenter } from '../../components/templates/article/SetLabelsModalPresenter' -import { TrashIcon } from '../../components/elements/icons/TrashIcon' +import { SetHighlightLabelsModalPresenter } from '../templates/article/SetLabelsModalPresenter' +import { TrashIcon } from '../elements/icons/TrashIcon' import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers' -import { ConfirmationModal } from '../../components/patterns/ConfirmationModal' +import { ConfirmationModal } from '../patterns/ConfirmationModal' import { deleteHighlightMutation } from '../../lib/networking/mutations/deleteHighlightMutation' -import { LabelChip } from '../../components/elements/LabelChip' +import { LabelChip } from '../elements/LabelChip' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' -import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles' -import { HighlightHoverActions } from '../../components/patterns/HighlightHoverActions' +import { timeAgo } from '../patterns/LibraryCards/LibraryCardStyles' +import { HighlightHoverActions } from '../patterns/HighlightHoverActions' import { autoUpdate, offset, @@ -29,58 +29,12 @@ import { } from '@floating-ui/react' import { highlightColor } from '../../lib/themeUpdater' -import { HighlightViewNote } from '../../components/patterns/HighlightNotes' -import { theme } from '../../components/tokens/stitches.config' +import { HighlightViewNote } from '../patterns/HighlightNotes' +import { theme } from '../tokens/stitches.config' const PAGE_SIZE = 10 -export default function Highlights(): JSX.Element { - const router = useRouter() - const viewer = useGetViewerQuery() - const [showFilterMenu, setShowFilterMenu] = useState(false) - const [_, setShowAddLinkModal] = useState(false) - - const { isLoading, setSize, size, data, mutate } = useGetHighlights({ - first: PAGE_SIZE, - }) - - const hasMore = useMemo(() => { - if (!data) { - return false - } - return data[data.length - 1].highlights.pageInfo.hasNextPage - }, [data]) - - const handleFetchMore = useCallback(() => { - if (isLoading || !hasMore) { - return - } - setSize(size + 1) - }, [isLoading, hasMore, setSize, size]) - - useFetchMore(handleFetchMore) - - const highlights = useMemo(() => { - if (!data) { - return [] - } - return data.flatMap((res) => res.highlights.edges.map((edge) => edge.node)) - }, [data]) - - return ( - - - - ) -} - -export function HighlightsList(): JSX.Element { +export function HighlightsContainer(): JSX.Element { const router = useRouter() const viewer = useGetViewerQuery() const [showFilterMenu, setShowFilterMenu] = useState(false) diff --git a/packages/web/pages/home/index.tsx b/packages/web/components/nav-containers/home.tsx similarity index 72% rename from packages/web/pages/home/index.tsx rename to packages/web/components/nav-containers/home.tsx index 6d9dd8907..8ae31204b 100644 --- a/packages/web/pages/home/index.tsx +++ b/packages/web/components/nav-containers/home.tsx @@ -2,15 +2,15 @@ import * as HoverCard from '@radix-ui/react-hover-card' import { styled } from '@stitches/react' import { useRouter } from 'next/router' import { useMemo, useState } from 'react' -import { Button } from '../../components/elements/Button' -import { AddToLibraryActionIcon } from '../../components/elements/icons/home/AddToLibraryActionIcon' -import { ArchiveActionIcon } from '../../components/elements/icons/home/ArchiveActionIcon' -import { CommentActionIcon } from '../../components/elements/icons/home/CommentActionIcon' -import { RemoveActionIcon } from '../../components/elements/icons/home/RemoveActionIcon' -import { ShareActionIcon } from '../../components/elements/icons/home/ShareActionIcon' -import Pagination from '../../components/elements/Pagination' -import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles' -import { theme } from '../../components/tokens/stitches.config' +import { Button } from '../elements/Button' +import { AddToLibraryActionIcon } from '../elements/icons/home/AddToLibraryActionIcon' +import { ArchiveActionIcon } from '../elements/icons/home/ArchiveActionIcon' +import { CommentActionIcon } from '../elements/icons/home/CommentActionIcon' +import { RemoveActionIcon } from '../elements/icons/home/RemoveActionIcon' +import { ShareActionIcon } from '../elements/icons/home/ShareActionIcon' +import Pagination from '../elements/Pagination' +import { timeAgo } from '../patterns/LibraryCards/LibraryCardStyles' +import { theme } from '../tokens/stitches.config' import { useApplyLocalTheme } from '../../lib/hooks/useApplyLocalTheme' import { useGetHiddenHomeSection } from '../../lib/networking/queries/useGetHiddenHomeSection' import { @@ -24,83 +24,75 @@ import { SubscriptionType, useGetSubscriptionsQuery, } from '../../lib/networking/queries/useGetSubscriptionsQuery' -import { - Box, - HStack, - SpanBox, - VStack, -} from './../../components/elements/LayoutPrimitives' +import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives' import { Toaster } from 'react-hot-toast' -import { NavigationLayout } from '../../components/templates/NavigationLayout' -export default function Home(): JSX.Element { +export function HomeContainer(): JSX.Element { const homeData = useGetHomeItems() useApplyLocalTheme() return ( - + + - - - {homeData.sections?.map((homeSection, idx) => { - if (homeSection.items.length < 1) { + {homeData.sections?.map((homeSection, idx) => { + if (homeSection.items.length < 1) { + return <> + } + switch (homeSection.layout) { + case 'just_added': + return ( + + ) + case 'top_picks': + return ( + + ) + case 'quick_links': + return ( + + ) + case 'hidden': + return ( + + ) + default: return <> - } - switch (homeSection.layout) { - case 'just_added': - return ( - - ) - case 'top_picks': - return ( - - ) - case 'quick_links': - return ( - - ) - case 'hidden': - return ( - - ) - default: - return <> - } - })} - + } + })} - + ) } @@ -636,103 +628,3 @@ const SubscriptionSourceHoverContent = ( ) } - -// const SiteSourceHoverContent = ( -// props: SourceHoverContentProps -// ): JSX.Element => { -// const sendHomeFeedback = useCallback( -// async (feedbackType: SendHomeFeedbackType) => { -// const feedback: SendHomeFeedbackInput = { -// feedbackType, -// } -// feedback.site = props.source.name -// const result = await sendHomeFeedbackMutation(feedback) -// if (result) { -// showSuccessToast('Feedback sent') -// } else { -// showErrorToast('Error sending feedback') -// } -// }, -// [props] -// ) - -// return ( -// -// -// {props.source.icon && ( -// -// )} -// -// {props.source.name} -// -// -// {/* -// {subscription ? <>{subscription.description} : <>} -// */} -// -// -// ) -// } - -// type FeedbackViewProps = { -// sendFeedback: (type: SendHomeFeedbackType) => void -// } - -// const FeedbackView = (props: FeedbackViewProps): JSX.Element => { -// return ( -// -// -// -// -// ) -// } diff --git a/packages/web/components/templates/NavigationLayout.tsx b/packages/web/components/templates/NavigationLayout.tsx index fcf455f29..3fae76c18 100644 --- a/packages/web/components/templates/NavigationLayout.tsx +++ b/packages/web/components/templates/NavigationLayout.tsx @@ -128,11 +128,8 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element { section={props.section} // eslint-disable-next-line @typescript-eslint/no-empty-function setShowAddLinkModal={() => {}} - searchTerm="" - // eslint-disable-next-line @typescript-eslint/no-empty-function - applySearchQuery={() => {}} - showFilterMenu={showNavMenu} - setShowFilterMenu={setShowNavMenu} + showMenu={showNavMenu} + setShowMenu={setShowNavMenu} /> )} {props.children} @@ -185,6 +182,7 @@ const Header = (props: HeaderProps): JSX.Element => { props.toggleMenu() event.preventDefault() }} + css={{ height: 'unset' }} > diff --git a/packages/web/components/templates/navMenu/NavigationMenu.tsx b/packages/web/components/templates/navMenu/NavigationMenu.tsx index e686bf956..2f02eb8e0 100644 --- a/packages/web/components/templates/navMenu/NavigationMenu.tsx +++ b/packages/web/components/templates/navMenu/NavigationMenu.tsx @@ -2,7 +2,6 @@ import { CSSProperties, ReactNode, useCallback, - useEffect, useMemo, useRef, useState, @@ -10,22 +9,10 @@ import { import { StyledText } from '../../elements/StyledText' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { Button } from '../../elements/Button' -import { - DotsThree, - List, - X, - Folder, - FolderOpen, - Tag, - ArrowDown, - CaretDown, - CaretUp, - Archive, -} from '@phosphor-icons/react' +import { DotsThree, List, X, Tag } from '@phosphor-icons/react' import { Label } from '../../../lib/networking/fragments/labelFragment' import { theme } from '../../tokens/stitches.config' import { usePersistedState } from '../../../lib/hooks/usePersistedState' -import Link from 'next/link' import { NavMenuFooter } from './Footer' import { FollowingIcon } from '../../elements/icons/FollowingIcon' import { HomeIcon } from '../../elements/icons/HomeIcon' @@ -46,15 +33,12 @@ import { requestHeaders } from '../../../lib/networking/networkHelpers' import { v4 as uuidv4 } from 'uuid' import { showErrorToast } from '../../../lib/toastHelpers' import { OpenMap } from 'react-arborist/dist/module/state/open-slice' -import { ToggleCaretLeftIcon } from '../../elements/icons/ToggleCaretLeftIcon' -import { ToggleCaretDownIcon } from '../../elements/icons/ToggleCaretDownIcon' -import { TrashIcon } from '../../elements/icons/TrashIcon' -import { ArchiveActionIcon } from '../../elements/icons/home/ArchiveActionIcon' -import { ArchiveIcon } from '../../elements/icons/ArchiveIcon' import { ArchiveSectionIcon } from '../../elements/icons/ArchiveSectionIcon' import { NavMoreButtonDownIcon } from '../../elements/icons/NavMoreButtonDown' import { NavMoreButtonUpIcon } from '../../elements/icons/NavMoreButtonUp' import { ShortcutFolderClosed } from '../../elements/icons/ShortcutFolderClosed' +import { TrashSectionIcon } from '../../elements/icons/TrashSectionIcon' +import { ShortcutFolderOpen } from '../../elements/icons/ShortcutFolderOpen' export const LIBRARY_LEFT_MENU_WIDTH = '275px' @@ -74,19 +58,16 @@ export type Shortcut = { join?: string } -type LibraryFilterMenuProps = { +type NavigationMenuProps = { section: NavigationSection setShowAddLinkModal: (show: boolean) => void - searchTerm: string | undefined - applySearchQuery: (searchTerm: string) => void - - showFilterMenu: boolean - setShowFilterMenu: (show: boolean) => void + showMenu: boolean + setShowMenu: (show: boolean) => void } -export function NavigationMenu(props: LibraryFilterMenuProps): JSX.Element { +export function NavigationMenu(props: NavigationMenuProps): JSX.Element { return ( <> { - props.setShowFilterMenu(false) + props.setShowMenu(false) event.preventDefault() }} > @@ -152,7 +133,7 @@ export function NavigationMenu(props: LibraryFilterMenuProps): JSX.Element { }, }} onClick={(event) => { - props.setShowFilterMenu(false) + props.setShowMenu(false) event.preventDefault() }} > @@ -180,12 +161,12 @@ export function NavigationMenu(props: LibraryFilterMenuProps): JSX.Element { ) } -const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { - const [moreFoldersOpenState, setMoreFoldersOpenState] = +const LibraryNav = (props: NavigationMenuProps): JSX.Element => { + const [moreFolderSectionOpen, setMoreFolderSectionOpen] = usePersistedState({ - key: 'nav-menu-more-folders-open', + key: 'nav-more-folder-open', isSessionStorage: false, - initialValue: false, + initialValue: true, }) return ( { }, }} onClick={(event) => { - setMoreFoldersOpenState(!moreFoldersOpenState) + setMoreFolderSectionOpen(!moreFolderSectionOpen) event.preventDefault() }} > @@ -262,7 +243,7 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { alignment="center" distribution="start" > - {moreFoldersOpenState ? ( + {moreFolderSectionOpen ? ( @@ -274,7 +255,7 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { More - {moreFoldersOpenState && ( + {moreFolderSectionOpen && ( { section="trash" isSelected={props.section == 'trash'} icon={ - + } /> @@ -302,7 +285,7 @@ const LibraryNav = (props: LibraryFilterMenuProps): JSX.Element => { ) } -const Shortcuts = (props: LibraryFilterMenuProps): JSX.Element => { +const Shortcuts = (props: NavigationMenuProps): JSX.Element => { const treeRef = useRef | undefined>(undefined) const { trigger: resetShortcutsTrigger } = useSWRMutation( '/api/shortcuts', @@ -352,7 +335,7 @@ const Shortcuts = (props: LibraryFilterMenuProps): JSX.Element => { px: '15px', }} > - SHORTCUTS + Shortcuts { console.log('query: ', query) } } else if (node.data.section != null && node.data.filter != null) { - router.push(`/${node.data.section}?q=${node.data.filter}`) + router.push(`/l/${node.data.section}?q=${node.data.filter}`) } }, [tree, router] @@ -785,8 +768,7 @@ const NodeItemContents = (props: NodeItemContentsProps): JSX.Element => { color={theme.colors.thLibraryMenuPrimary.toString()} /> ) : ( - )} @@ -929,7 +911,7 @@ function NavButton(props: NavButtonProps): JSX.Element { }} title={props.text} onClick={(e) => { - router.push(`/` + props.section) + router.push(`/l/` + props.section) }} > {props.icon} @@ -937,113 +919,3 @@ function NavButton(props: NavButtonProps): JSX.Element { ) } - -type FilterButtonProps = { - text: string - - filterTerm: string - searchTerm: string | undefined - - applySearchQuery: (searchTerm: string) => void - - setShowFilterMenu: (show: boolean) => void -} - -function FilterButton(props: FilterButtonProps): JSX.Element { - const isInboxFilter = (filter: string) => { - return filter === '' || filter === 'in:inbox' - } - const selected = useMemo(() => { - if (isInboxFilter(props.filterTerm) && !props.searchTerm) { - return true - } - return props.searchTerm === props.filterTerm - }, [props.searchTerm, props.filterTerm]) - - return ( - { - props.applySearchQuery(props.filterTerm) - props.setShowFilterMenu(false) - e.preventDefault() - }} - > - {props.text} - - ) -} - -type EditButtonProps = { - title: string - destination: string -} - -function EditButton(props: EditButtonProps): JSX.Element { - return ( - - - {props.title} - - - ) -} diff --git a/packages/web/pages/archive/index.tsx b/packages/web/pages/archive/index.tsx deleted file mode 100644 index b11b73f5c..000000000 --- a/packages/web/pages/archive/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { NavigationLayout } from '../../components/templates/NavigationLayout' -import { LibraryContainer } from '../../components/templates/library/LibraryContainer' - -export default function Archive(): JSX.Element { - return ( - - - - ) -} diff --git a/packages/web/pages/l/[section].tsx b/packages/web/pages/l/[section].tsx new file mode 100644 index 000000000..fc943e05b --- /dev/null +++ b/packages/web/pages/l/[section].tsx @@ -0,0 +1,55 @@ +import { useRouter } from 'next/router' +import { useApplyLocalTheme } from '../../lib/hooks/useApplyLocalTheme' +import { + NavigationLayout, + NavigationSection, +} from '../../components/templates/NavigationLayout' +import { HomeContainer } from '../../components/nav-containers/home' +import { LibraryContainer } from '../../components/templates/library/LibraryContainer' +import { useMemo } from 'react' +import { HighlightsContainer } from '../../components/nav-containers/highlights' + +export default function Home(): JSX.Element { + const router = useRouter() + useApplyLocalTheme() + + const section: NavigationSection | undefined = useMemo(() => { + if (!router.isReady) { + return undefined + } + const res = router.query.section + if (typeof res !== 'string') { + return undefined + } + return res as NavigationSection + }, [router]) + + const sectionView = (name: string | string[] | undefined) => { + if (typeof name !== 'string') { + return <> + } + switch (name) { + case 'home': + return + case 'highlights': + return + case 'library': + return + case 'subscriptions': + return + case 'archive': + return + case 'trash': + return + + default: + return <> + } + } + + return ( + + {sectionView(section)} + + ) +} diff --git a/packages/web/pages/library/index.tsx b/packages/web/pages/library/index.tsx deleted file mode 100644 index e21915ad2..000000000 --- a/packages/web/pages/library/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { NavigationLayout } from '../../components/templates/NavigationLayout' -import { LibraryContainer } from '../../components/templates/library/LibraryContainer' - -export default function Library(): JSX.Element { - return ( - - - - ) -} diff --git a/packages/web/pages/trash/index.tsx b/packages/web/pages/trash/index.tsx deleted file mode 100644 index 1d3d29bc7..000000000 --- a/packages/web/pages/trash/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { NavigationLayout } from '../../components/templates/NavigationLayout' -import { LibraryContainer } from '../../components/templates/library/LibraryContainer' - -export default function Trash(): JSX.Element { - return ( - - - - ) -} From fec6e241b9640e5ddda2917ea87a59708321161d Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 12 Jun 2024 19:11:34 +0800 Subject: [PATCH 21/23] Allow old home widths to work --- .../patterns/LibraryCards/CardTypes.tsx | 2 ++ .../patterns/LibraryCards/LibraryListCard.tsx | 34 ++++++++++++------- .../templates/homeFeed/HomeFeedContainer.tsx | 1 + packages/web/pages/home.tsx | 32 +++++++++++++++++ 4 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 packages/web/pages/home.tsx diff --git a/packages/web/components/patterns/LibraryCards/CardTypes.tsx b/packages/web/components/patterns/LibraryCards/CardTypes.tsx index ab9d9dfcb..fbf713688 100644 --- a/packages/web/components/patterns/LibraryCards/CardTypes.tsx +++ b/packages/web/components/patterns/LibraryCards/CardTypes.tsx @@ -31,4 +31,6 @@ export type LinkedItemCardProps = { isHovered?: boolean isLoading?: boolean + + legacyLayout?: boolean } diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index e988e8525..12b23f437 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -54,6 +54,26 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { const { getReferenceProps, getFloatingProps } = useInteractions([hover]) + const layoutWidths = props.legacyLayout + ? { + width: '100vw', + '@media (min-width: 768px)': { + width: `calc(100vw - ${LIBRARY_LEFT_MENU_WIDTH})`, + }, + '@media (min-width: 930px)': { + width: '580px', + }, + '@media (min-width: 1280px)': { + width: '890px', + }, + '@media (min-width: 1600px)': { + width: '1200px', + }, + } + : { + width: '100%', + } + return ( {props.viewer && ( +} + +function LoadedContent(): JSX.Element { + return ( + + + + + + ) +} From 652e2cc5645c3d8f626028df18be668e717b8428 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 12 Jun 2024 19:16:39 +0800 Subject: [PATCH 22/23] More explicit default on migration --- .../0180.do.add_shortcuts_to_user_personalization.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/db/migrations/0180.do.add_shortcuts_to_user_personalization.sql b/packages/db/migrations/0180.do.add_shortcuts_to_user_personalization.sql index 222c5c44b..e5b6bfafe 100755 --- a/packages/db/migrations/0180.do.add_shortcuts_to_user_personalization.sql +++ b/packages/db/migrations/0180.do.add_shortcuts_to_user_personalization.sql @@ -4,6 +4,6 @@ BEGIN; -ALTER TABLE omnivore.user_personalization ADD COLUMN shortcuts JSONB NULL; +ALTER TABLE omnivore.user_personalization ADD COLUMN shortcuts JSONB DEFAULT NULL; COMMIT; From 51aff8342663f45e28d854f4b4de93efce92fbbb Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 12 Jun 2024 19:31:44 +0800 Subject: [PATCH 23/23] Clean up some issues with padding during responsive --- packages/web/components/templates/NavigationLayout.tsx | 6 +++++- packages/web/components/templates/library/LibraryHeader.tsx | 5 ----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/web/components/templates/NavigationLayout.tsx b/packages/web/components/templates/NavigationLayout.tsx index 3fae76c18..9d23ce1e5 100644 --- a/packages/web/components/templates/NavigationLayout.tsx +++ b/packages/web/components/templates/NavigationLayout.tsx @@ -114,7 +114,11 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element { }, [showLogout]) return ( - + {props.pageMetaDataProps ? ( ) : null} diff --git a/packages/web/components/templates/library/LibraryHeader.tsx b/packages/web/components/templates/library/LibraryHeader.tsx index 8995e7ebb..aead3f2fe 100644 --- a/packages/web/components/templates/library/LibraryHeader.tsx +++ b/packages/web/components/templates/library/LibraryHeader.tsx @@ -87,7 +87,6 @@ export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { width: '100%', px: '70px', left: LIBRARY_LEFT_MENU_WIDTH, - height: small ? '60px' : DEFAULT_HEADER_HEIGHT, transition: 'height 0.5s', '@lgDown': { px: '20px' }, '@mdDown': { @@ -99,10 +98,6 @@ export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { > - - {/* This spacer is put in to push library content down - below the fixed header height. */} - ) }