From 09328edfe5e4fb8204d77452dfa6242779229c08 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 2 Jan 2023 16:12:08 +0800 Subject: [PATCH 001/179] Updated library cards --- .../patterns/LibraryCards/LibraryGridCard.tsx | 492 ++++++++++++------ .../templates/library/LibraryList.tsx | 183 +++---- .../queries/useGetLibraryItemsQuery.tsx | 4 + packages/web/package.json | 1 + yarn.lock | 5 + 5 files changed, 411 insertions(+), 274 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 1d7962527..8ead0dfd4 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -1,13 +1,14 @@ import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives' import { StyledText } from '../../elements/StyledText' -import { removeHTMLTags } from '../ArticleSubtitle' -import { MoreOptionsIcon } from '../../elements/images/MoreOptionsIcon' -import { theme } from '../../tokens/stitches.config' -import { CardMenu } from '../CardMenu' import { LabelChip } from '../../elements/LabelChip' -import { ProgressBar } from '../../elements/ProgressBar' import type { LinkedItemCardProps } from './CardTypes' -import { ProgressBarVertical } from '../../elements/ProgressBarVertical' +import { CoverImage } from '../../elements/CoverImage' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' +import { useState } from 'react' +import { DotsThree } from 'phosphor-react' + +dayjs.extend(relativeTime) //Styles const ellipsisText = { @@ -16,12 +17,12 @@ const ellipsisText = { WebkitLineClamp: 1, WebkitBoxOrient: 'vertical', margin: 'auto 0', - pr:'10px', + pr: '10px', } const cardTitleStyle = { ...ellipsisText, - width:'100%', + width: '100%', fontSize: '14px', fontWeight: '600', textAlign: 'left', @@ -33,9 +34,7 @@ type CardTitleProps = { } // Functions -function CardTitle( - props: CardTitleProps, -): JSX.Element { +function CardTitle(props: CardTitleProps): JSX.Element { return ( {props.title} @@ -43,187 +42,344 @@ function CardTitle( ) } +type ProgressBarProps = { + fillPercentage: number + fillColor: string + backgroundColor: string + borderRadius: string +} + +export function ProgressBar(props: ProgressBarProps): JSX.Element { + return ( + + + + ) +} + +const timeAgo = (date: string | undefined): string => { + if (!date) { + return '' + } + return dayjs(date).fromNow() +} + +const shouldHideUrl = (url: string): boolean => { + try { + const origin = new URL(url).origin + const hideHosts = ['https://storage.googleapis.com', 'https://omnivore.app'] + if (hideHosts.indexOf(origin) != -1) { + return true + } + } catch { + console.log('invalid url item', url) + } + return false +} + +const siteName = (originalArticleUrl: string, itemUrl: string): string => { + if (shouldHideUrl(originalArticleUrl)) { + return '' + } + try { + return new URL(originalArticleUrl).hostname.replace(/^www\./, '') + } catch {} + try { + return new URL(itemUrl).hostname.replace(/^www\./, '') + } catch {} + return '' +} + // Component export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { + const [isHovered, setIsHovered] = useState(false) + const originText = + props.item.siteName || + siteName(props.item.originalArticleUrl, props.item.url) + return ( - <> - {props.layout === 'GRID_LAYOUT' ? ( - { - props.handleAction('showDetail') - }} - > - { + setIsHovered(true) + }} + onMouseLeave={() => { + setIsHovered(false) + }} + > + + {timeAgo(props.item.savedAt)} + {isHovered ? ( + + + + ) : ( + - - - { - // This is here to prevent menu click events from bubbling - // up and causing us to "click" on the link item. - e.stopPropagation() - }} - > - - } - actionHandler={props.handleAction} - /> - - - - - {props.item.author && ( - {removeHTMLTags(props.item.author)} - )} - {props.originText && ( - <> - - - {props.originText} - - - )} - - - + 15 min read + + )} + + + + + {props.item.title} + + + {props.item.description} + - - {props.item.description} - + {props.item.author} + {props.item.author && originText && ' | '} + {originText} + - + + + + + {props.item.labels?.map(({ name, color }, index) => ( ))} - - - ) : ( - // ELSE display List Layout - - - - { + ;(e.target as HTMLElement).style.display = 'none' }} - data-testid="listDesc" - > - {props.item.labels?.map(({ name, color }, index) => ( - - ))} - {props.item.description} - - - - {props.item.author && ( - {removeHTMLTags(props.item.author)} - )} - - + /> + )} - )} - + + ) } + +// // Component +// export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { +// return ( +// { +// props.handleAction('showDetail') +// }} +// > +// +// +// +// { +// // This is here to prevent menu click events from bubbling +// // up and causing us to "click" on the link item. +// e.stopPropagation() +// }} +// > +// +// } +// actionHandler={props.handleAction} +// /> +// +// +// +// +// {props.item.author && ( +// {removeHTMLTags(props.item.author)} +// )} +// {props.originText && ( +// <> +// +// +// {props.originText} +// +// +// )} +// +// +// +// +// +// {props.item.description} +// +// +// +// {props.item.labels?.map(({ name, color }, index) => ( +// +// ))} +// +// +// +// +// ) +// } diff --git a/packages/web/components/templates/library/LibraryList.tsx b/packages/web/components/templates/library/LibraryList.tsx index ebea7d71a..faba3b7ac 100644 --- a/packages/web/components/templates/library/LibraryList.tsx +++ b/packages/web/components/templates/library/LibraryList.tsx @@ -8,7 +8,7 @@ import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes' import { LibraryGridCard } from '../../patterns/LibraryCards/LibraryGridCard' import { LayoutCoordinator } from './LibraryContainer' import { EmptyLibrary } from '../homeFeed/EmptyLibrary' -import Masonry from 'react-masonry-css' +// import Masonry from 'react-masonry-css' export type LibraryListProps = { layoutCoordinator: LayoutCoordinator @@ -54,118 +54,89 @@ export function LibraryList(props: LibraryListProps): JSX.Element { /> ) } - console.log(fileNames) + + // return ( - - {inDragOperation && uploadingFiles.length < 1 && ( - - - Drag n drop files here - - - )} - { - setInDragOperation(true) + + { - setInDragOperation(false) - }} - noClick={true} - noDragEventsBubbling={true} > - {({ getRootProps, getInputProps, acceptedFiles, fileRejections }) => ( + {libraryItems.map((linkedItem) => ( div': { + // bg: '$libraryBackground', + // }, + // '&:focus': { + // '> div': { + // bg: '$grayBgActive', + // }, + // }, + // '&:hover': { + // '> div': { + // bg: '$grayBgActive', + // }, + // }, + // }} > - - - {libraryItems.map((linkedItem) => ( - div': { - bg: '$libraryBackground', - }, - '&:focus': { - '> div': { - bg: '$grayBgActive', - }, - }, - '&:hover': { - '> div': { - bg: '$grayBgActive', - }, - }, - }} - > - {viewerData?.me && ( - { - console.log('card clicked') - }} - /> - )} - - ))} - + {viewerData?.me && ( + { + console.log('card clicked') + }} + /> + )} - )} - - {/* Temporary code */} -
- Files: -
    - {fileNames.map((fileName) => ( -
  • {fileName}
  • - ))} -
-
{' '} - {/* Temporary code */} - {/* Extra padding at bottom to give space for scrolling */} + ))} +
) diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx index 57d1635e9..bd8ec2051 100644 --- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx +++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx @@ -82,6 +82,7 @@ export type LibraryItemNode = { siteName?: string subscription?: string readAt?: string + savedAt?: string recommendations?: Recommendation[] } @@ -166,6 +167,7 @@ export function useGetLibraryItemsQuery({ siteName subscription readAt + savedAt recommendations { id name @@ -390,6 +392,8 @@ export function useGetLibraryItemsQuery({ } } + console.log('responsePages', responsePages) + return { isValidating, itemsPages: responsePages || undefined, diff --git a/packages/web/package.json b/packages/web/package.json index 4b821c258..c48beb30a 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -34,6 +34,7 @@ "axios": "^1.2.0", "color2k": "^2.0.0", "cookie": "^0.5.0", + "dayjs": "^1.11.7", "diff-match-patch": "^1.0.5", "downshift": "^6.1.9", "graphql-request": "^3.6.1", diff --git a/yarn.lock b/yarn.lock index 33ab030a3..160854925 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12650,6 +12650,11 @@ dayjs@^1.10.4: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805" integrity sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug== +dayjs@^1.11.7: + version "1.11.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" + integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== + debounce@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" From ea734db683eedf57c5b9d8a67b6caf020a94bd4b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 24 Feb 2023 14:54:37 +0800 Subject: [PATCH 002/179] Update the library layout to be closer to the design spec --- .../elements/images/GridSelectorIcon.tsx | 48 ++ .../elements/images/ListSelectorIcon.tsx | 44 + .../elements/images/OmnivoreFullLogo.tsx | 61 ++ .../elements/images/OmnivoreNameLogo.tsx | 18 +- .../components/templates/PrimaryLayout.tsx | 41 +- .../templates/homeFeed/HomeFeedContainer.tsx | 769 +++++++++--------- .../templates/homeFeed/LibraryFilterMenu.tsx | 219 +++++ .../templates/homeFeed/LibraryHeader.tsx | 281 +++++++ 8 files changed, 1088 insertions(+), 393 deletions(-) create mode 100644 packages/web/components/elements/images/GridSelectorIcon.tsx create mode 100644 packages/web/components/elements/images/ListSelectorIcon.tsx create mode 100644 packages/web/components/elements/images/OmnivoreFullLogo.tsx create mode 100644 packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx create mode 100644 packages/web/components/templates/homeFeed/LibraryHeader.tsx diff --git a/packages/web/components/elements/images/GridSelectorIcon.tsx b/packages/web/components/elements/images/GridSelectorIcon.tsx new file mode 100644 index 000000000..553e26fd9 --- /dev/null +++ b/packages/web/components/elements/images/GridSelectorIcon.tsx @@ -0,0 +1,48 @@ +import { config } from '../../tokens/stitches.config' + +export type GridSelectorIconProps = { + color?: string +} + +export function GridSelectorIcon(props: GridSelectorIconProps): JSX.Element { + const fillColor = props.color || config.theme.colors.graySolid + + return ( + + + + + + + + + + + + + + ) +} diff --git a/packages/web/components/elements/images/ListSelectorIcon.tsx b/packages/web/components/elements/images/ListSelectorIcon.tsx new file mode 100644 index 000000000..130e20309 --- /dev/null +++ b/packages/web/components/elements/images/ListSelectorIcon.tsx @@ -0,0 +1,44 @@ +import { config } from '../../tokens/stitches.config' + +export type ListSelectorIconProps = { + color?: string +} + +export function ListSelectorIcon(props: ListSelectorIconProps): JSX.Element { + const fillColor = props.color || config.theme.colors.graySolid + + return ( + + + + + + + + + + + + + ) +} diff --git a/packages/web/components/elements/images/OmnivoreFullLogo.tsx b/packages/web/components/elements/images/OmnivoreFullLogo.tsx new file mode 100644 index 000000000..89d9ded0b --- /dev/null +++ b/packages/web/components/elements/images/OmnivoreFullLogo.tsx @@ -0,0 +1,61 @@ +import { config } from '../../tokens/stitches.config' + +export type OmnivoreFullLogoProps = { + color?: string + href?: string + showTitle?: boolean +} + +export function OmnivoreFullLogo(props: OmnivoreFullLogoProps): JSX.Element { + const fillColor = props.color || config.theme.colors.graySolid + const href = props.href || '/home' + + return ( + + + + + + + + + + + + ) +} diff --git a/packages/web/components/elements/images/OmnivoreNameLogo.tsx b/packages/web/components/elements/images/OmnivoreNameLogo.tsx index 0f3e25328..f32804b9b 100644 --- a/packages/web/components/elements/images/OmnivoreNameLogo.tsx +++ b/packages/web/components/elements/images/OmnivoreNameLogo.tsx @@ -42,6 +42,7 @@ export function OmnivoreLogoIcon(props: OmnivoreLogoProps): JSX.Element { export type OmnivoreNameLogoProps = { color?: string href?: string + showTitle?: boolean } export function OmnivoreNameLogo(props: OmnivoreNameLogoProps): JSX.Element { @@ -50,9 +51,22 @@ export function OmnivoreNameLogo(props: OmnivoreNameLogoProps): JSX.Element { return ( - + - {/* Omnivore */} + {props.showTitle && ( + + Omnivore + + )} ) diff --git a/packages/web/components/templates/PrimaryLayout.tsx b/packages/web/components/templates/PrimaryLayout.tsx index 30b652ea3..32fab35df 100644 --- a/packages/web/components/templates/PrimaryLayout.tsx +++ b/packages/web/components/templates/PrimaryLayout.tsx @@ -1,11 +1,6 @@ import { PageMetaData, PageMetaDataProps } from '../patterns/PageMetaData' import { Box } from '../elements/LayoutPrimitives' -import { - ReactNode, - MutableRefObject, - useEffect, - useState, -} from 'react' +import { ReactNode, MutableRefObject, useEffect, useState } from 'react' import { PrimaryHeader } from './../patterns/PrimaryHeader' import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery' import { navigationCommands } from '../../lib/keyboardShortcuts/navigationShortcuts' @@ -61,15 +56,17 @@ export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element { {props.pageMetaDataProps ? ( ) : null} - - + {/* + /> */} - + {/* */} {props.children} {showLogoutConfirmation ? ( - + + - + > + + + - {props.isValidating && props.items.length == 0 && } - + + + + {props.isValidating && props.items.length == 0 && } + {/* + /> */} - {viewerData?.me && ( - + {Object.keys(SAVED_SEARCHES).map((key) => { + const isInboxTerm = (term: string) => { + return !term || term === 'in:inbox' + } + + const searchQuery = SAVED_SEARCHES[key] + const style = + searchQuery === props.searchTerm || + (!props.searchTerm && isInboxTerm(searchQuery)) + ? 'ctaDarkYellow' + : 'ctaLightGray' + return ( + + ) + })} + + )} */} + { + setInDragOperation(true) + }} + onDragLeave={() => { + setInDragOperation(false) + }} + preventDropOnDocument={true} + noClick={true} + accept={{ + 'application/pdf': ['.pdf'], }} > - {Object.keys(SAVED_SEARCHES).map((key) => { - const isInboxTerm = (term: string) => { - return !term || term === 'in:inbox' - } - - const searchQuery = SAVED_SEARCHES[key] - const style = - searchQuery === props.searchTerm || - (!props.searchTerm && isInboxTerm(searchQuery)) - ? 'ctaDarkYellow' - : 'ctaLightGray' - return ( - - ) - })} - - )} - { - setInDragOperation(true) - }} - onDragLeave={() => { - setInDragOperation(false) - }} - preventDropOnDocument={true} - noClick={true} - accept={{ - 'application/pdf': ['.pdf'], - }} - > - {({ getRootProps, getInputProps, acceptedFiles, fileRejections }) => ( -
- {inDragOperation && uploadingFiles.length < 1 && ( - - - - Drop PDF document to to upload and add to your library - - - - )} - {uploadingFiles.length > 0 && ( - - - - - - - ( +
+ {inDragOperation && uploadingFiles.length < 1 && ( + + + - Uploading file - - - - - )} - - {!props.isValidating && props.items.length == 0 ? ( - { - props.setShowAddLinkModal(true) - }} - /> - ) : ( - - {props.items.map((linkedItem) => ( - div': { - bg: '$grayBg', - }, - '&:focus': { - '> div': { - bg: '$grayBgActive', - }, - }, - '&:hover': { - '> div': { - bg: '$grayBgActive', - }, - }, - }} - > - {viewerData?.me && ( - { - if (action === 'delete') { - setShowRemoveLinkConfirmation(true) - props.setLinkToRemove(linkedItem) - } else if (action === 'editTitle') { - props.setShowEditTitleModal(true) - props.setLinkToEdit(linkedItem) - } else if (action == 'unsubscribe') { - setShowUnsubscribeConfirmation(true) - props.setLinkToUnsubscribe(linkedItem) - } else { - props.actionHandler(action, linkedItem) - } - }} - /> - )} - - ))} - - )} - - {props.hasMore ? ( - - ) : ( - + Drop PDF document to to upload and add to your library + + + )} - -
- )} - - - {/* Temporary code */} - {/*
+ {uploadingFiles.length > 0 && ( + + + + + + + + Uploading file + + + + + )} + + {!props.isValidating && props.items.length == 0 ? ( + { + props.setShowAddLinkModal(true) + }} + /> + ) : ( + + {props.items.map((linkedItem) => ( + div': { + bg: '$grayBg', + }, + '&:focus': { + '> div': { + bg: '$grayBgActive', + }, + }, + '&:hover': { + '> div': { + bg: '$grayBgActive', + }, + }, + }} + > + {viewerData?.me && ( + { + if (action === 'delete') { + setShowRemoveLinkConfirmation(true) + props.setLinkToRemove(linkedItem) + } else if (action === 'editTitle') { + props.setShowEditTitleModal(true) + props.setLinkToEdit(linkedItem) + } else if (action == 'unsubscribe') { + setShowUnsubscribeConfirmation(true) + props.setLinkToUnsubscribe(linkedItem) + } else { + props.actionHandler(action, linkedItem) + } + }} + /> + )} + + ))} + + )} + + {props.hasMore ? ( + + ) : ( + + )} + +
+ )} + + + {/* Temporary code */} + {/*
Files:
    {uploadingFiles.map((fileName) => ( @@ -1093,142 +1123,143 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { ))}
*/} - {/* Temporary code */} - {props.showAddLinkModal && ( - props.setShowAddLinkModal(false)} /> - )} - {props.showEditTitleModal && ( - - props.actionHandler('update-item', item) - } - onOpenChange={() => props.setShowEditTitleModal(false)} - item={props.linkToEdit as LibraryItem} - /> - )} - {props.shareTarget && viewerData?.me?.profile.username && ( - { - if (props.shareTarget) { - const item = document.getElementById(props.shareTarget.node.id) - if (item) { - item.focus() + {/* Temporary code */} + {props.showAddLinkModal && ( + props.setShowAddLinkModal(false)} /> + )} + {props.showEditTitleModal && ( + + props.actionHandler('update-item', item) + } + onOpenChange={() => props.setShowEditTitleModal(false)} + item={props.linkToEdit as LibraryItem} + /> + )} + {props.shareTarget && viewerData?.me?.profile.username && ( + { + if (props.shareTarget) { + const item = document.getElementById(props.shareTarget.node.id) + if (item) { + item.focus() + } + props.setShareTarget(undefined) } - props.setShareTarget(undefined) - } - }} - /> - )} - {props.snoozeTarget && ( - { - if (!props.snoozeTarget) return - createReminderMutation( - props.snoozeTarget?.node.id, - ReminderType.Tonight, - true, - sendReminder - ) - .then(() => { - return props.actionHandler('archive', props.snoozeTarget) - }) - .then(() => { - showSuccessToast(msg, { position: 'bottom-right' }) - }) - .catch((error) => { - showErrorToast('There was an error snoozing your link.', { - position: 'bottom-right', - }) - }) - }} - onOpenChange={() => { - if (props.snoozeTarget) { - const item = document.getElementById(props.snoozeTarget.node.id) - if (item) { - item.focus() - } - props.setSnoozeTarget(undefined) - } - }} - /> - )} - {showRemoveLinkConfirmation && ( - - - Are you sure you want to delete this item? All associated notes - and highlights will be deleted. - - {props.linkToRemove?.node && viewerData?.me && ( - - {}} - /> - - )} - - } - onAccept={removeItem} - acceptButtonLabel="Delete Item" - onOpenChange={() => setShowRemoveLinkConfirmation(false)} - /> - )} - {showUnsubscribeConfirmation && ( - setShowUnsubscribeConfirmation(false)} - /> - )} - {props.labelsTarget?.node.id && ( - { - if (props.labelsTarget) { - props.labelsTarget.node.labels = labels - updateState({}) - } - }} - save={(labels: Label[]) => { - if (props.labelsTarget?.node.id) { - return setLabelsMutation( - props.labelsTarget.node.id, - labels.map((label) => label.id) + }} + /> + )} + {props.snoozeTarget && ( + { + if (!props.snoozeTarget) return + createReminderMutation( + props.snoozeTarget?.node.id, + ReminderType.Tonight, + true, + sendReminder ) + .then(() => { + return props.actionHandler('archive', props.snoozeTarget) + }) + .then(() => { + showSuccessToast(msg, { position: 'bottom-right' }) + }) + .catch((error) => { + showErrorToast('There was an error snoozing your link.', { + position: 'bottom-right', + }) + }) + }} + onOpenChange={() => { + if (props.snoozeTarget) { + const item = document.getElementById(props.snoozeTarget.node.id) + if (item) { + item.focus() + } + props.setSnoozeTarget(undefined) + } + }} + /> + )} + {showRemoveLinkConfirmation && ( + + + Are you sure you want to delete this item? All associated + notes and highlights will be deleted. + + {props.linkToRemove?.node && viewerData?.me && ( + + {}} + /> + + )} + } - return Promise.resolve(undefined) - }} - onOpenChange={() => { - if (props.labelsTarget) { - const activate = props.labelsTarget - props.setActiveItem(activate) - props.setLabelsTarget(undefined) - } - }} - /> - )} - + onAccept={removeItem} + acceptButtonLabel="Delete Item" + onOpenChange={() => setShowRemoveLinkConfirmation(false)} + /> + )} + {showUnsubscribeConfirmation && ( + setShowUnsubscribeConfirmation(false)} + /> + )} + {props.labelsTarget?.node.id && ( + { + if (props.labelsTarget) { + props.labelsTarget.node.labels = labels + updateState({}) + } + }} + save={(labels: Label[]) => { + if (props.labelsTarget?.node.id) { + return setLabelsMutation( + props.labelsTarget.node.id, + labels.map((label) => label.id) + ) + } + return Promise.resolve(undefined) + }} + onOpenChange={() => { + if (props.labelsTarget) { + const activate = props.labelsTarget + props.setActiveItem(activate) + props.setLabelsTarget(undefined) + } + }} + /> + )} + + ) } diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx new file mode 100644 index 000000000..6e9e10d35 --- /dev/null +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -0,0 +1,219 @@ +import { + InputHTMLAttributes, + ReactNode, + useEffect, + useRef, + useState, +} from 'react' +import { StyledText } from '../../elements/StyledText' +import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' +import { SearchIcon } from '../../elements/images/SearchIcon' +import { theme } from '../../tokens/stitches.config' +import { Dropdown, DropdownOption } from '../../elements/DropdownElements' +import { FormInput } from '../../elements/FormElements' +import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' +import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts' +import { Button, IconButton } from '../../elements/Button' +import { Circle, MagnifyingGlass, Plus, Textbox, X } from 'phosphor-react' +import { OmnivoreNameLogo } from '../../elements/images/OmnivoreNameLogo' +import { OmnivoreFullLogo } from '../../elements/images/OmnivoreFullLogo' +import { AvatarDropdown } from '../../elements/AvatarDropdown' +import { ListSelectorIcon } from '../../elements/images/ListSelectorIcon' +import { GridSelectorIcon } from '../../elements/images/GridSelectorIcon' +import { useGetSubscriptionsQuery } from '../../../lib/networking/queries/useGetSubscriptionsQuery' +import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery' +import { Label } from '../../../lib/networking/fragments/labelFragment' +import { Checkbox } from '@radix-ui/react-checkbox' + +export function LibraryFilterMenu(): JSX.Element { + return ( + + + + + + + + ) +} + +function SavedSearches(): JSX.Element { + return ( + + + + + + + + ) +} + +function Subscriptions(): JSX.Element { + const { subscriptions } = useGetSubscriptionsQuery() + console.log('subscriptions: ', subscriptions) + + return ( + + {subscriptions.slice(0, 4).map((item) => { + return + })} + + View All + + + ) +} + +function Labels(): JSX.Element { + const { labels } = useGetLabelsQuery() + console.log('labels: ', labels) + + return ( + + {labels.slice(0, 4).map((item) => { + return + })} + + View All + + + ) +} + +type MenuPanelProps = { + title: string + children: ReactNode +} + +function MenuPanel(props: MenuPanelProps): JSX.Element { + return ( + + + {props.title} + + {props.children} + + ) +} + +type FilterButtonProps = { + text: string + spaced?: boolean + selected: boolean +} + +function FilterButton(props: FilterButtonProps): JSX.Element { + return ( + + {props.text} + + ) +} + +type LabelButtonProps = { + label: Label + state: 'on' | 'off' | 'unset' +} + +function LabelButton(props: LabelButtonProps): JSX.Element { + return ( + + + {props.label.name} + + + + + ) +} + +function AddLinkButton(): JSX.Element { + return ( + + + + ) +} diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx new file mode 100644 index 000000000..6bf65a93c --- /dev/null +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -0,0 +1,281 @@ +import { + InputHTMLAttributes, + ReactNode, + useEffect, + useRef, + useState, +} from 'react' +import { StyledText } from '../../elements/StyledText' +import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' +import { SearchIcon } from '../../elements/images/SearchIcon' +import { theme } from '../../tokens/stitches.config' +import { Dropdown, DropdownOption } from '../../elements/DropdownElements' +import { FormInput } from '../../elements/FormElements' +import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' +import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts' +import { Button, IconButton } from '../../elements/Button' +import { MagnifyingGlass, Textbox, X } from 'phosphor-react' +import { OmnivoreNameLogo } from '../../elements/images/OmnivoreNameLogo' +import { OmnivoreFullLogo } from '../../elements/images/OmnivoreFullLogo' +import { AvatarDropdown } from '../../elements/AvatarDropdown' +import { ListSelectorIcon } from '../../elements/images/ListSelectorIcon' +import { GridSelectorIcon } from '../../elements/images/GridSelectorIcon' + +type LibrarySearchBarProps = { + searchTerm?: string + applySearchQuery: (searchQuery: string) => void +} + +type LibraryFilter = + | 'in:inbox' + | 'in:all' + | 'in:archive' + | 'type:file' + | 'type:highlights' + | `saved:${string}` + | `sort:read` + +// get last week's date +const recentlySavedStartDate = new Date( + new Date().getTime() - 7 * 24 * 60 * 60 * 1000 +).toLocaleDateString('en-US') + +const FOCUSED_BOXSHADOW = '0px 0px 2px 2px rgba(255, 234, 159, 0.56)' + +export function LibraryHeader(props: LibrarySearchBarProps): JSX.Element { + const [focused, setFocused] = useState(false) + 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() + } + }) + ) + + return ( + + + + + + + + ) +} + +function SearchBox(props: LibrarySearchBarProps): JSX.Element { + const inputRef = useRef(null) + const [focused, setFocused] = useState(false) + const [searchTerm, setSearchTerm] = useState('') + + return ( + + + + + +
{ + event.preventDefault() + props.applySearchQuery(searchTerm || '') + inputRef.current?.blur() + }} + > + { + event.target.select() + setFocused(true) + }} + onBlur={() => { + setFocused(false) + }} + onChange={(event) => { + setSearchTerm(event.target.value) + }} + /> + + {searchTerm ? ( + + ) : ( + + requestAnimationFrame(() => inputRef.current.focus())} + // we can make it unreachable via keyboard as we have the same message for the SR label + tabIndex={-1} + > + / + + + )} +
+
+ ) +} + +// Displays the full logo on larger screens, small logo on mobile +function LogoBox(): JSX.Element { + return ( + <> + + + + + + + + ) +} + +function ControlButtonBox(): JSX.Element { + return ( + <> + + + + + + + + + + + ) +} From 4dfee8d7f71785d520b9e385421edf4606165a68 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 24 Feb 2023 15:12:05 +0800 Subject: [PATCH 003/179] Fix SVG imports --- packages/web/components/elements/images/GridSelectorIcon.tsx | 2 +- packages/web/components/elements/images/ListSelectorIcon.tsx | 2 +- packages/web/components/elements/images/OmnivoreFullLogo.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/components/elements/images/GridSelectorIcon.tsx b/packages/web/components/elements/images/GridSelectorIcon.tsx index 553e26fd9..c1fad8ab2 100644 --- a/packages/web/components/elements/images/GridSelectorIcon.tsx +++ b/packages/web/components/elements/images/GridSelectorIcon.tsx @@ -15,7 +15,7 @@ export function GridSelectorIcon(props: GridSelectorIconProps): JSX.Element { fill="none" xmlns="http://www.w3.org/2000/svg" > - + - + ) From b8a42439d70daa12586225f94c057624b9cd3b81 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 24 Feb 2023 17:38:40 +0800 Subject: [PATCH 004/179] Add some legacy support for the mobile view items, we will slowly remove these --- .../templates/homeFeed/HomeFeedContainer.tsx | 292 ++++++++++-------- .../templates/homeFeed/LibraryFilterMenu.tsx | 168 +++++++--- .../templates/homeFeed/LibraryHeader.tsx | 68 ++-- 3 files changed, 332 insertions(+), 196 deletions(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 15076cc65..ca8a66a3a 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -1,4 +1,4 @@ -import { Box, HStack, VStack } from './../../elements/LayoutPrimitives' +import { Box, HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives' import Dropzone from 'react-dropzone' import * as Progress from '@radix-ui/react-progress' import type { @@ -6,7 +6,10 @@ import type { LibraryItemsQueryInput, } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery' -import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery' +import { + useGetViewerQuery, + UserBasicData, +} from '../../../lib/networking/queries/useGetViewerQuery' import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes' import { LinkedItemCard } from '../../patterns/LibraryCards/LinkedItemCard' import { useRouter } from 'next/router' @@ -679,25 +682,6 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { const [, updateState] = useState({}) - const StyledToggleButton = styled('button', { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - p: '0px', - backgroundColor: 'transparent', - border: 'none', - cursor: 'pointer', - width: '32px', - height: '32px', - borderRadius: '4px', - '&:hover': { - opacity: 0.8, - }, - '&[data-state="on"]': { - bg: 'rgb(43, 43, 43)', - }, - }) - const DragnDropContainer = styled('div', { width: '100%', height: '80%', @@ -800,22 +784,16 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { searchTerm={props.searchTerm} applySearchQuery={props.applySearchQuery} /> - - - + > + + {props.isValidating && props.items.length == 0 && } - {/* - - Library - - - { - updateLayout('GRID_LAYOUT') - }} - > - - - { - updateLayout('LIST_LAYOUT') - }} - > - - - - - - */} + + - {/* {viewerData?.me && ( - - {Object.keys(SAVED_SEARCHES).map((key) => { - const isInboxTerm = (term: string) => { - return !term || term === 'in:inbox' - } - - const searchQuery = SAVED_SEARCHES[key] - const style = - searchQuery === props.searchTerm || - (!props.searchTerm && isInboxTerm(searchQuery)) - ? 'ctaDarkYellow' - : 'ctaLightGray' - return ( - - ) - })} - - )} */} { @@ -1263,3 +1162,142 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { ) } + +type LegacyMobileTopNavProps = { + viewer?: UserBasicData + searchTerm?: string + applySearchQuery: (searchQuery: string) => void + + layout: LayoutType + updateLayout: (layout: LayoutType) => void + + setShowAddLinkModal: (show: boolean) => void +} + +function LegacyMobileTopNav(props: LegacyMobileTopNavProps): JSX.Element { + const StyledToggleButton = styled('button', { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + p: '0px', + backgroundColor: 'transparent', + border: 'none', + cursor: 'pointer', + width: '32px', + height: '32px', + borderRadius: '4px', + '&:hover': { + opacity: 0.8, + }, + '&[data-state="on"]': { + bg: 'rgb(43, 43, 43)', + }, + }) + + return ( + <> + + + Library + + + { + props.updateLayout('GRID_LAYOUT') + }} + > + + + { + props.updateLayout('LIST_LAYOUT') + }} + > + + + + + + + + {props.viewer && ( + + {Object.keys(SAVED_SEARCHES).map((key) => { + const isInboxTerm = (term: string) => { + return !term || term === 'in:inbox' + } + + const searchQuery = SAVED_SEARCHES[key] + const style = + searchQuery === props.searchTerm || + (!props.searchTerm && isInboxTerm(searchQuery)) + ? 'ctaDarkYellow' + : 'ctaLightGray' + return ( + + ) + })} + + )} + + ) +} diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx index 6e9e10d35..40dbf4ae6 100644 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -14,7 +14,14 @@ import { FormInput } from '../../elements/FormElements' import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts' import { Button, IconButton } from '../../elements/Button' -import { Circle, MagnifyingGlass, Plus, Textbox, X } from 'phosphor-react' +import { + Circle, + DotsThree, + MagnifyingGlass, + Plus, + Textbox, + X, +} from 'phosphor-react' import { OmnivoreNameLogo } from '../../elements/images/OmnivoreNameLogo' import { OmnivoreFullLogo } from '../../elements/images/OmnivoreFullLogo' import { AvatarDropdown } from '../../elements/AvatarDropdown' @@ -27,24 +34,36 @@ import { Checkbox } from '@radix-ui/react-checkbox' export function LibraryFilterMenu(): JSX.Element { return ( - - - - + <> + + + + - - + + + {/* This spacer pushes library content to the right of + the fixed left side menu. */} + + ) } @@ -62,32 +81,40 @@ function SavedSearches(): JSX.Element { function Subscriptions(): JSX.Element { const { subscriptions } = useGetSubscriptionsQuery() - console.log('subscriptions: ', subscriptions) + const [viewAll, setViewAll] = useState(false) return ( - - {subscriptions.slice(0, 4).map((item) => { + { + window.location.href = '/settings/subscriptions' + }} + > + {subscriptions.slice(0, viewAll ? undefined : 4).map((item) => { return })} - - View All - + ) } function Labels(): JSX.Element { const { labels } = useGetLabelsQuery() - console.log('labels: ', labels) + const [viewAll, setViewAll] = useState(false) return ( - - {labels.slice(0, 4).map((item) => { + { + window.location.href = '/settings/labels' + }} + > + {labels.slice(0, viewAll ? undefined : 4).map((item) => { return })} - - View All - + ) } @@ -95,6 +122,8 @@ function Labels(): JSX.Element { type MenuPanelProps = { title: string children: ReactNode + editFunc?: () => void + editTitle?: string } function MenuPanel(props: MenuPanelProps): JSX.Element { @@ -108,19 +137,46 @@ function MenuPanel(props: MenuPanelProps): JSX.Element { alignment="start" distribution="start" > - - {props.title} - + + + {props.title} + + + {props.editTitle && props.editFunc && ( + + } + > + { + if (props.editFunc) { + props.editFunc() + } + }} + /> + + )} + + {props.children} ) @@ -217,3 +273,29 @@ function AddLinkButton(): JSX.Element { ) } + +type ViewAllButtonProps = { + state: boolean + setState: (state: boolean) => void +} + +function ViewAllButton(props: ViewAllButtonProps): JSX.Element { + return ( + + ) +} diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 6bf65a93c..2fd2d6793 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -42,6 +42,9 @@ const recentlySavedStartDate = new Date( const FOCUSED_BOXSHADOW = '0px 0px 2px 2px rgba(255, 234, 159, 0.56)' +const HEADER_HEIGHT = '105px' +const MOBILE_HEIGHT = '44px' + export function LibraryHeader(props: LibrarySearchBarProps): JSX.Element { const [focused, setFocused] = useState(false) const inputRef = useRef(null) @@ -60,38 +63,51 @@ export function LibraryHeader(props: LibrarySearchBarProps): JSX.Element { ) return ( - - + - - - - - + + + + + + + {/* This spacer is put in to push library content down + below the fixed header height. */} + + ) } From 5237c35a813a3875628f9520b2abcc9c409e5d3e Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 24 Feb 2023 19:26:32 +0800 Subject: [PATCH 005/179] More work on the new design --- .../elements/images/GridSelectorIcon.tsx | 20 +-- .../elements/images/ListSelectorIcon.tsx | 18 +- .../templates/homeFeed/HomeFeedContainer.tsx | 17 +- .../templates/homeFeed/LibraryFilterMenu.tsx | 144 +++++++++++++-- .../templates/homeFeed/LibraryHeader.tsx | 169 +++++++++++++----- 5 files changed, 272 insertions(+), 96 deletions(-) diff --git a/packages/web/components/elements/images/GridSelectorIcon.tsx b/packages/web/components/elements/images/GridSelectorIcon.tsx index c1fad8ab2..65eb8a1d1 100644 --- a/packages/web/components/elements/images/GridSelectorIcon.tsx +++ b/packages/web/components/elements/images/GridSelectorIcon.tsx @@ -15,34 +15,24 @@ export function GridSelectorIcon(props: GridSelectorIconProps): JSX.Element { fill="none" xmlns="http://www.w3.org/2000/svg" > - + - - - - - ) } diff --git a/packages/web/components/elements/images/ListSelectorIcon.tsx b/packages/web/components/elements/images/ListSelectorIcon.tsx index fafcc14fa..5702b29f2 100644 --- a/packages/web/components/elements/images/ListSelectorIcon.tsx +++ b/packages/web/components/elements/images/ListSelectorIcon.tsx @@ -15,30 +15,20 @@ export function ListSelectorIcon(props: ListSelectorIconProps): JSX.Element { fill="none" xmlns="http://www.w3.org/2000/svg" > - + - - - - - ) } diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index ca8a66a3a..bbfd0520b 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -578,6 +578,7 @@ export function HomeFeedContainer(): JSX.Element { searchTerm={queryInputs.searchQuery} gridContainerRef={gridContainerRef} applySearchQuery={(searchQuery: string) => { + console.log('TOP LEVEL SETTING QUERY INPUTS: ', searchQuery) setQueryInputs({ ...queryInputs, searchQuery, @@ -781,8 +782,13 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { return ( { + console.log('searching with searchQuery: ', searchQuery) + props.applySearchQuery(searchQuery) + }} /> - + { + console.log('searching with searchQuery: ', searchQuery) + props.applySearchQuery(searchQuery) + }} + /> void + + searchTerm: string | undefined + applySearchQuery: (searchTerm: string) => void +} + +export function LibraryFilterMenu(props: LibraryFilterMenuProps): JSX.Element { return ( <> - - - + + + - + props.setShowAddLinkModal(true)} + /> {/* This spacer pushes library content to the right of the fixed left side menu. */} @@ -67,19 +78,45 @@ export function LibraryFilterMenu(): JSX.Element { ) } -function SavedSearches(): JSX.Element { +function SavedSearches(props: LibraryFilterMenuProps): JSX.Element { return ( - - - - + + + + + ) } -function Subscriptions(): JSX.Element { +function Subscriptions(props: LibraryFilterMenuProps): JSX.Element { const { subscriptions } = useGetSubscriptionsQuery() const [viewAll, setViewAll] = useState(false) @@ -92,14 +129,21 @@ function Subscriptions(): JSX.Element { }} > {subscriptions.slice(0, viewAll ? undefined : 4).map((item) => { - return + return ( + + ) })} ) } -function Labels(): JSX.Element { +function Labels(props: LibraryFilterMenuProps): JSX.Element { const { labels } = useGetLabelsQuery() const [viewAll, setViewAll] = useState(false) @@ -112,7 +156,7 @@ function Labels(): JSX.Element { }} > {labels.slice(0, viewAll ? undefined : 4).map((item) => { - return + return })} @@ -185,10 +229,20 @@ function MenuPanel(props: MenuPanelProps): JSX.Element { type FilterButtonProps = { text: string spaced?: boolean - selected: boolean + + filterTerm: string + searchTerm: string | undefined + applySearchQuery: (searchTerm: string) => void } function FilterButton(props: FilterButtonProps): JSX.Element { + const selected = useMemo(() => { + if (props.filterTerm === '' && !props.searchTerm) { + return true + } + return props.searchTerm === props.filterTerm + }, [props.searchTerm, props.filterTerm]) + return ( { + props.applySearchQuery(props.filterTerm) + e.preventDefault() }} > {props.text} @@ -212,10 +277,21 @@ function FilterButton(props: FilterButtonProps): JSX.Element { type LabelButtonProps = { label: Label - state: 'on' | 'off' | 'unset' + searchTerm: string | undefined + applySearchQuery: (searchTerm: string) => void } function LabelButton(props: LabelButtonProps): JSX.Element { + const state = useMemo(() => { + const term = props.searchTerm ?? '' + if (term.indexOf(`label:\"${props.label.name}\"`) >= 0) { + console.log('returning true for: ', term) + return 'on' + } + console.log('returning off for: ', term) + return 'off' + }, [props.searchTerm, props.label]) + return ( {props.label.name} - + { + console.log('changing check state') + if (e.target.checked) { + props.applySearchQuery + props.applySearchQuery( + `${props.searchTerm} label:\"${props.label.name}\"` + ) + } else { + const query = + props.searchTerm?.replace( + `label:\"${props.label.name}\"`, + '' + ) ?? '' + props.applySearchQuery(query) + } + }} + /> ) } -function AddLinkButton(): JSX.Element { +type AddLinkButtonProps = { + showAddLinkModal: () => void +} + +function AddLinkButton(props: AddLinkButtonProps): JSX.Element { return ( { + props.showAddLinkModal() + e.preventDefault() + }} > Add Link diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 2fd2d6793..9911fbb1d 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -1,6 +1,7 @@ import { InputHTMLAttributes, ReactNode, + useCallback, useEffect, useRef, useState, @@ -8,7 +9,7 @@ import { import { StyledText } from '../../elements/StyledText' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { SearchIcon } from '../../elements/images/SearchIcon' -import { theme } from '../../tokens/stitches.config' +import { theme, ThemeId } from '../../tokens/stitches.config' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' import { FormInput } from '../../elements/FormElements' import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' @@ -20,48 +21,25 @@ import { OmnivoreFullLogo } from '../../elements/images/OmnivoreFullLogo' import { AvatarDropdown } from '../../elements/AvatarDropdown' import { ListSelectorIcon } from '../../elements/images/ListSelectorIcon' import { GridSelectorIcon } from '../../elements/images/GridSelectorIcon' +import { LayoutType } from './HomeFeedContainer' +import { DropdownMenu, HeaderDropdownAction } from '../../patterns/DropdownMenu' +import { updateTheme } from '../../../lib/themeUpdater' +import { useRouter } from 'next/router' -type LibrarySearchBarProps = { - searchTerm?: string +type LibraryHeaderProps = { + layout: LayoutType + updateLayout: (layout: LayoutType) => void + + searchTerm: string | undefined applySearchQuery: (searchQuery: string) => void } -type LibraryFilter = - | 'in:inbox' - | 'in:all' - | 'in:archive' - | 'type:file' - | 'type:highlights' - | `saved:${string}` - | `sort:read` - -// get last week's date -const recentlySavedStartDate = new Date( - new Date().getTime() - 7 * 24 * 60 * 60 * 1000 -).toLocaleDateString('en-US') - const FOCUSED_BOXSHADOW = '0px 0px 2px 2px rgba(255, 234, 159, 0.56)' const HEADER_HEIGHT = '105px' -const MOBILE_HEIGHT = '44px' - -export function LibraryHeader(props: LibrarySearchBarProps): JSX.Element { - const [focused, setFocused] = useState(false) - 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() - } - }) - ) +const MOBILE_HEIGHT = '48px' +export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { return ( <> - + {/* This spacer is put in to push library content down @@ -111,10 +92,23 @@ export function LibraryHeader(props: LibrarySearchBarProps): JSX.Element { ) } -function SearchBox(props: LibrarySearchBarProps): JSX.Element { +type SearchBoxProps = { + searchTerm: string | undefined + applySearchQuery: (searchQuery: string) => void +} + +function SearchBox(props: SearchBoxProps): JSX.Element { const inputRef = useRef(null) const [focused, setFocused] = useState(false) - const [searchTerm, setSearchTerm] = useState('') + const [searchTerm, setSearchTerm] = useState(props.searchTerm ?? '') + + useKeyboardShortcuts( + searchBarCommands((action) => { + if (action === 'focusSearchBar' && inputRef.current) { + inputRef.current.select() + } + }) + ) return ( { + inputRef.current?.focus() + e.preventDefault() + }} > - {searchTerm ? ( + {props.searchTerm ? ( + + + } + actionHandler={headerDropdownActionHandler} + /> Date: Fri, 24 Feb 2023 19:43:33 +0800 Subject: [PATCH 006/179] Allow the left bar to scroll when expanded --- .../templates/homeFeed/HomeFeedContainer.tsx | 1 - .../templates/homeFeed/LibraryFilterMenu.tsx | 60 ++++++++++--------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index bbfd0520b..2c5a765d6 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -807,7 +807,6 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { }} /> - @@ -346,37 +347,40 @@ type AddLinkButtonProps = { function AddLinkButton(props: AddLinkButtonProps): JSX.Element { return ( - - - + + + + ) } From a57dad444dfd993bdfdb5df6f2caa2546c38988f Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sat, 25 Feb 2023 08:51:25 +0800 Subject: [PATCH 007/179] Move keyboard commands from header into layout --- .../web/components/patterns/PrimaryHeader.tsx | 59 ++++---------- .../components/templates/PrimaryLayout.tsx | 31 ++++---- .../templates/homeFeed/HomeFeedContainer.tsx | 10 --- .../templates/homeFeed/LibraryHeader.tsx | 2 +- packages/web/pages/_document.tsx | 79 ++++++++++++++----- 5 files changed, 92 insertions(+), 89 deletions(-) diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx index cb8f65a81..ac413f622 100644 --- a/packages/web/components/patterns/PrimaryHeader.tsx +++ b/packages/web/components/patterns/PrimaryHeader.tsx @@ -27,34 +27,6 @@ export function PrimaryHeader(props: HeaderProps): JSX.Element { const router = useRouter() const [isScrolled, setIsScrolled] = useState(false) - useKeyboardShortcuts( - primaryCommands((action) => { - switch (action) { - // case 'themeDarker': - // darkenTheme() - // break - // case 'themeLighter': - // lightenTheme() - // break - case 'toggleShortcutHelpModalDisplay': - props.setShowKeyboardCommandsModal(true) - break - } - }) - ) - - const initAnalytics = useCallback(() => { - setupAnalytics(props.user) - }, [props.user]) - - useEffect(() => { - initAnalytics() - window.addEventListener('load', initAnalytics) - return () => { - window.removeEventListener('load', initAnalytics) - } - }, [initAnalytics]) - function headerDropdownActionHandler(action: HeaderDropdownAction): void { switch (action) { case 'apply-darker-theme': @@ -108,12 +80,14 @@ export function PrimaryHeader(props: HeaderProps): JSX.Element { return ( <> - + - + @@ -256,7 +232,7 @@ function FloatingNavHeader(props: NavHeaderProps): JSX.Element { right: '18px', position: 'fixed', display: 'flex', - alignItems: 'center' + alignItems: 'center', }} > ) } - diff --git a/packages/web/components/templates/PrimaryLayout.tsx b/packages/web/components/templates/PrimaryLayout.tsx index 32fab35df..090f23b94 100644 --- a/packages/web/components/templates/PrimaryLayout.tsx +++ b/packages/web/components/templates/PrimaryLayout.tsx @@ -10,6 +10,8 @@ import { Analytics } from '@segment/analytics-next' import { ConfirmationModal } from '../patterns/ConfirmationModal' import { KeyboardShortcutListModal } from './KeyboardShortcutListModal' import { logoutMutation } from '../../lib/networking/mutations/logoutMutation' +import { setupAnalytics } from '../../lib/analytics' +import { primaryCommands } from '../../lib/keyboardShortcuts/navigationShortcuts' type PrimaryLayoutProps = { children: ReactNode @@ -29,8 +31,20 @@ export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element { useKeyboardShortcuts(navigationCommands(router)) + useKeyboardShortcuts( + primaryCommands((action) => { + switch (action) { + case 'toggleShortcutHelpModalDisplay': + setShowKeyboardCommandsModal(true) + break + } + }) + ) + // Attempt to identify the user if they are logged in. useEffect(() => { + setupAnalytics(viewerData?.me) + const user = window.analytics?.user().id() if (!user && viewerData?.me?.id) { window.analytics?.identify({ userId: viewerData?.me?.id }) @@ -66,17 +80,6 @@ export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element { }, }} > - {/* */} - {/* */} {props.children} {showLogoutConfirmation ? ( - {/* Temporary code */} - {/*
- Files: -
    - {uploadingFiles.map((fileName) => ( -
  • {fileName}
  • - ))} -
-
*/} - {/* Temporary code */} {props.showAddLinkModal && ( props.setShowAddLinkModal(false)} /> )} diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 9911fbb1d..0741f5da7 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -48,7 +48,7 @@ export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { css={{ top: '0', left: '0', - zIndex: 100, + zIndex: 5, position: 'fixed', width: '100%', height: HEADER_HEIGHT, diff --git a/packages/web/pages/_document.tsx b/packages/web/pages/_document.tsx index 927d240dc..c6fb0cf50 100644 --- a/packages/web/pages/_document.tsx +++ b/packages/web/pages/_document.tsx @@ -1,7 +1,9 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable functional/no-class */ import NextDocument, { Html, Head, Main, NextScript } from 'next/document' +import { useCallback } from 'react' import { getCssText, globalStyles } from '../components/tokens/stitches.config' +import { setupAnalytics } from '../lib/analytics' export default class Document extends NextDocument { render() { @@ -53,25 +55,61 @@ export default class Document extends NextDocument {