From d6cd57b5757efb675d6912ec96f99f7c6b93caea Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 14 Feb 2024 14:33:28 +0800 Subject: [PATCH] Move menus, add UI for setting auto theme --- packages/web/components/elements/LogoBox.tsx | 2 +- .../web/components/elements/StyledText.tsx | 2 + .../patterns/LibraryCards/LibraryListCard.tsx | 2 +- .../components/templates/SettingsLayout.tsx | 2 +- .../web/components/templates/SettingsMenu.tsx | 257 -------- .../article/ReaderSettingsControl.tsx | 30 +- .../templates/homeFeed/EmptyLibrary.tsx | 2 +- .../templates/homeFeed/HomeFeedContainer.tsx | 12 +- .../templates/homeFeed/LibraryFilterMenu.tsx | 613 ------------------ .../templates/homeFeed/LibraryHeader.tsx | 2 +- .../components/templates/navMenu/Footer.tsx | 2 +- 11 files changed, 42 insertions(+), 884 deletions(-) delete mode 100644 packages/web/components/templates/SettingsMenu.tsx delete mode 100644 packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx diff --git a/packages/web/components/elements/LogoBox.tsx b/packages/web/components/elements/LogoBox.tsx index 276941f07..1105263bc 100644 --- a/packages/web/components/elements/LogoBox.tsx +++ b/packages/web/components/elements/LogoBox.tsx @@ -1,4 +1,4 @@ -import { LIBRARY_LEFT_MENU_WIDTH } from '../templates/homeFeed/LibraryFilterMenu' +import { LIBRARY_LEFT_MENU_WIDTH } from '../templates/navMenu/LibraryMenu' import { theme } from '../tokens/stitches.config' import { OmnivoreFullLogo } from './images/OmnivoreFullLogo' import { OmnivoreNameLogo } from './images/OmnivoreNameLogo' diff --git a/packages/web/components/elements/StyledText.tsx b/packages/web/components/elements/StyledText.tsx index 0a47e4747..c9460941e 100644 --- a/packages/web/components/elements/StyledText.tsx +++ b/packages/web/components/elements/StyledText.tsx @@ -192,6 +192,8 @@ const textVariants = { fontSize: '12px', lineHeight: '20px', color: '$thTextSubtle2', + marginBlockStart: '0', + marginTop: '10px', }, error: { color: '$error', diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index 524c76848..b930e9c68 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -14,7 +14,7 @@ import { FLAIR_ICON_NAMES, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' -import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/homeFeed/LibraryFilterMenu' +import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/navMenu/LibraryMenu' import { LibraryHoverActions } from './LibraryHoverActions' import { useHover, diff --git a/packages/web/components/templates/SettingsLayout.tsx b/packages/web/components/templates/SettingsLayout.tsx index a7df5dedd..dd734e772 100644 --- a/packages/web/components/templates/SettingsLayout.tsx +++ b/packages/web/components/templates/SettingsLayout.tsx @@ -11,7 +11,7 @@ import { KeyboardShortcutListModal } from './KeyboardShortcutListModal' import { PageMetaData } from '../patterns/PageMetaData' import { DEFAULT_HEADER_HEIGHT } from './homeFeed/HeaderSpacer' import { logout } from '../../lib/logout' -import { SettingsMenu } from './SettingsMenu' +import { SettingsMenu } from './navMenu/SettingsMenu' type SettingsLayoutProps = { title?: string diff --git a/packages/web/components/templates/SettingsMenu.tsx b/packages/web/components/templates/SettingsMenu.tsx deleted file mode 100644 index 72ee1f099..000000000 --- a/packages/web/components/templates/SettingsMenu.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import { useMemo } from 'react' -import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives' -import { LIBRARY_LEFT_MENU_WIDTH } from './homeFeed/LibraryFilterMenu' -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 { useRouter } from 'next/router' -import { NavMenuFooter } from './navMenu/Footer' - -const HorizontalDivider = styled(SpanBox, { - width: '100%', - height: '1px', - my: '25px', - background: `${theme.colors.grayLine.toString()}`, -}) - -const StyledLink = styled(SpanBox, { - pl: '25px', - ml: '10px', - mb: '10px', - display: 'flex', - alignItems: 'center', - gap: '2px', - '&:hover': { - textDecoration: 'underline', - }, - - width: 'calc(100% - 10px)', - maxWidth: '100%', - height: '32px', - - fontSize: '14px', - fontWeight: 'regular', - fontFamily: '$display', - color: '$thLibraryMenuUnselected', - verticalAlign: 'middle', - borderRadius: '3px', - cursor: 'pointer', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', -}) - -type ExternalLinkProps = { - title: string - destination: string -} - -function ExternalLink(props: ExternalLinkProps): JSX.Element { - return ( - a': { - backgroundColor: 'transparent', - textDecoration: 'none', - }, - }} - title={props.title} - > - - - {props.title} - - - - - ) -} - -export function SettingsMenu(): JSX.Element { - const section1 = [ - { name: 'Account', destination: '/settings/account' }, - { name: 'API Keys', destination: '/settings/api' }, - { name: 'Emails', destination: '/settings/emails' }, - { name: 'Feeds', destination: '/settings/feeds' }, - { name: 'Subscriptions', destination: '/settings/subscriptions' }, - { name: 'Labels', destination: '/settings/labels' }, - { name: 'Saved Searches', destination: '/settings/saved-searches' }, - { name: 'Pinned Searches', destination: '/settings/pinned-searches' }, - ] - - const section2 = [ - { name: 'Integrations', destination: '/settings/integrations' }, - { name: 'Install', destination: '/settings/installation' }, - ] - return ( - <> - - - - - - - {section1.map((item) => { - return - })} - - {section2.map((item) => { - return - })} - - - - - - - - - - {/* This spacer pushes library content to the right of - the fixed left side menu. */} - - - ) -} - -type SettingsButtonProps = { - name: string - destination: string -} - -function SettingsButton(props: SettingsButtonProps): JSX.Element { - const router = useRouter() - const selected = useMemo(() => { - if (router && router.isReady) { - return router.asPath.endsWith(props.destination) - } - return false - }, [props, router]) - - return ( - - - {props.name} - - - ) -} diff --git a/packages/web/components/templates/article/ReaderSettingsControl.tsx b/packages/web/components/templates/article/ReaderSettingsControl.tsx index 3c0c0665f..673ef4624 100644 --- a/packages/web/components/templates/article/ReaderSettingsControl.tsx +++ b/packages/web/components/templates/article/ReaderSettingsControl.tsx @@ -17,6 +17,7 @@ import { currentThemeName, updateTheme } from '../../../lib/themeUpdater' import { LineHeightIncreaseIcon } from '../../elements/images/LineHeightIncreaseIconProps' import { LineHeightDecreaseIcon } from '../../elements/images/LineHeightDecreaseIcon' import * as Switch from '@radix-ui/react-switch' +import { Checkbox } from '@radix-ui/react-checkbox' type ReaderSettingsProps = { readerSettings: ReaderSettings @@ -575,7 +576,34 @@ function ThemeSelector(): JSX.Element { height: '100%', }} > - Themes + + Themes + + + + + + (null) - const [labelsTarget, setLabelsTarget] = useState( - undefined - ) + const [labelsTarget, setLabelsTarget] = + useState(undefined) - const [notebookTarget, setNotebookTarget] = useState( - undefined - ) + const [notebookTarget, setNotebookTarget] = + useState(undefined) const [showAddLinkModal, setShowAddLinkModal] = useState(false) const [showEditTitleModal, setShowEditTitleModal] = useState(false) diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx deleted file mode 100644 index 5cfa16319..000000000 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ /dev/null @@ -1,613 +0,0 @@ -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 } from 'phosphor-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 { SplitButton } from '../../elements/SplitButton' -import { AvatarDropdown } from '../../elements/AvatarDropdown' -import { PrimaryDropdown } from '../PrimaryDropdown' -import { NavMenuFooter } from '../navMenu/Footer' - -export const LIBRARY_LEFT_MENU_WIDTH = '275px' - -type LibraryFilterMenuProps = { - setShowAddLinkModal: (show: boolean) => void - - searchTerm: string | undefined - applySearchQuery: (searchTerm: string) => void - - showFilterMenu: boolean - setShowFilterMenu: (show: boolean) => void -} - -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 ( - <> - - - - - - - - - - - {/* This spacer pushes library content to the right of - the fixed left side menu. */} - - - ) -} - -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:\"${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 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 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:\"${props.label.name}\"`) >= 0) { - return 'on' - } - return 'off' - }, [props.searchTerm, props.label]) - - return ( - - - - { - if (e.target.checked) { - props.applySearchQuery( - `${props.searchTerm ?? ''} label:\"${props.label.name}\"` - ) - } else { - const query = - props.searchTerm?.replace( - `label:\"${props.label.name}\"`, - '' - ) ?? '' - props.applySearchQuery(query) - } - }} - /> - - - ) -} - -type EditButtonProps = { - title: string - destination: string -} - -function EditButton(props: EditButtonProps): JSX.Element { - return ( - - - {props.title} - - - ) -} diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index c802e2bbd..a178bd4b7 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -16,7 +16,7 @@ import { import { LayoutType } from './HomeFeedContainer' import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo' import { DEFAULT_HEADER_HEIGHT, HeaderSpacer } from './HeaderSpacer' -import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/homeFeed/LibraryFilterMenu' +import { LIBRARY_LEFT_MENU_WIDTH } from '../navMenu/LibraryMenu' import { CardCheckbox } from '../../patterns/LibraryCards/LibraryCardStyles' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation' diff --git a/packages/web/components/templates/navMenu/Footer.tsx b/packages/web/components/templates/navMenu/Footer.tsx index 4ac9bcf67..f8b0a9804 100644 --- a/packages/web/components/templates/navMenu/Footer.tsx +++ b/packages/web/components/templates/navMenu/Footer.tsx @@ -1,7 +1,7 @@ import { HStack, SpanBox } from '../../elements/LayoutPrimitives' import { SplitButton } from '../../elements/SplitButton' import { PrimaryDropdown } from '../PrimaryDropdown' -import { LIBRARY_LEFT_MENU_WIDTH } from '../homeFeed/LibraryFilterMenu' +import { LIBRARY_LEFT_MENU_WIDTH } from './LibraryMenu' type NavMenuFooterProps = { setShowAddLinkModal?: (show: true) => void