diff --git a/packages/web/components/elements/StyledText.tsx b/packages/web/components/elements/StyledText.tsx
index f5113ffb3..55ef94b5e 100644
--- a/packages/web/components/elements/StyledText.tsx
+++ b/packages/web/components/elements/StyledText.tsx
@@ -177,3 +177,7 @@ export const StyledImg = styled('img', {
export const StyledAnchor = styled('a', {
textDecoration: 'none'
})
+
+export const StyledMark = styled('mark', {
+
+})
\ No newline at end of file
diff --git a/packages/web/components/patterns/HighlightView.tsx b/packages/web/components/patterns/HighlightView.tsx
index 2534048ca..e6c5d631d 100644
--- a/packages/web/components/patterns/HighlightView.tsx
+++ b/packages/web/components/patterns/HighlightView.tsx
@@ -8,6 +8,7 @@ type HighlightViewProps = {
highlight: Highlight
author?: string
title?: string
+ scrollToHighlight?: (arg: string) => void;
}
export function HighlightView(props: HighlightViewProps): JSX.Element {
@@ -22,6 +23,7 @@ export function HighlightView(props: HighlightViewProps): JSX.Element {
fontSize: '18px',
lineHeight: '27px',
color: '$textDefault',
+ cursor: 'pointer',
})
return (
@@ -31,7 +33,11 @@ export function HighlightView(props: HighlightViewProps): JSX.Element {
{annotation}
)
}
-
+ {
+ if (props.scrollToHighlight) {
+ props.scrollToHighlight(props.highlight.id)
+ }
+ }}>
{props.highlight.prefix}
{lines.map((line: string, index: number) => (
diff --git a/packages/web/components/patterns/LibraryCards/HighlightItemCard.tsx b/packages/web/components/patterns/LibraryCards/HighlightItemCard.tsx
new file mode 100644
index 000000000..f3f685ab7
--- /dev/null
+++ b/packages/web/components/patterns/LibraryCards/HighlightItemCard.tsx
@@ -0,0 +1,89 @@
+import { styled } from '@stitches/react'
+import { VStack, HStack } from '../../elements/LayoutPrimitives'
+import { StyledMark, StyledText } from '../../elements/StyledText'
+import { LinkedItemCardAction, LinkedItemCardProps } from './CardTypes'
+
+export interface HighlightItemCardProps
+ extends Pick {
+ handleAction: (action: LinkedItemCardAction) => void
+}
+
+export const PreviewImage = styled('img', {
+ objectFit: 'cover',
+ cursor: 'pointer',
+})
+
+export function HighlightItemCard(props: HighlightItemCardProps): JSX.Element {
+ return (
+ {
+ props.handleAction('showDetail')
+ }}
+ >
+
+
+ {props.item.quote}
+
+
+
+ {props.item.image && (
+ {
+ ;(e.target as HTMLElement).style.display = 'none'
+ }}
+ />
+ )}
+
+ {props.item.title
+ .substring(0, 50)
+ .concat(props.item.title.length > 50 ? '...' : '')}
+
+
+
+ )
+}
diff --git a/packages/web/components/patterns/LibraryCards/LinkedItemCard.tsx b/packages/web/components/patterns/LibraryCards/LinkedItemCard.tsx
index 12a4a4be7..0b201184d 100644
--- a/packages/web/components/patterns/LibraryCards/LinkedItemCard.tsx
+++ b/packages/web/components/patterns/LibraryCards/LinkedItemCard.tsx
@@ -1,6 +1,8 @@
import { GridLinkedItemCard } from './GridLinkedItemCard'
import { ListLinkedItemCard } from './ListLinkedItemCard'
import type { LinkedItemCardProps } from './CardTypes'
+import { HighlightItemCard } from './HighlightItemCard'
+import { PageType } from '../../../lib/networking/fragments/articleFragment'
const siteName = (originalArticleUrl: string, itemUrl: string): string => {
try {
@@ -15,6 +17,9 @@ const siteName = (originalArticleUrl: string, itemUrl: string): string => {
export function LinkedItemCard(props: LinkedItemCardProps): JSX.Element {
const originText = siteName(props.item.originalArticleUrl, props.item.url)
+ if (props.item.pageType === PageType.HIGHLIGHTS) {
+ return
+ }
if (props.layout == 'LIST_LAYOUT') {
return
} else {
diff --git a/packages/web/components/templates/article/Article.tsx b/packages/web/components/templates/article/Article.tsx
index 9c6382857..d4f931565 100644
--- a/packages/web/components/templates/article/Article.tsx
+++ b/packages/web/components/templates/article/Article.tsx
@@ -21,8 +21,10 @@ import { ArticleMutations } from '../../../lib/articleActions'
export type ArticleProps = {
articleId: string
content: string
+ highlightReady: boolean
initialAnchorIndex: number
initialReadingProgress?: number
+ highlightHref: MutableRefObject
scrollElementRef: MutableRefObject
articleMutations: ArticleMutations
}
@@ -139,50 +141,53 @@ export function Article(props: ArticleProps): JSX.Element {
return
}
- if (!shouldScrollToInitialPosition) {
- return
- }
-
- setShouldScrollToInitialPosition(false)
-
- if (props.initialReadingProgress && props.initialReadingProgress >= 98) {
- return
- }
-
- const anchorElement = document.querySelector(
- `[data-omnivore-anchor-idx='${props.initialAnchorIndex.toString()}']`
- )
-
- if (anchorElement) {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const calculateOffset = (obj: any): number => {
- let offset = 0
- if (obj.offsetParent) {
- do {
- offset += obj.offsetTop
- } while ((obj = obj.offsetParent))
- return offset
- }
-
- return 0
+ if (props.highlightReady) {
+ if (!shouldScrollToInitialPosition) {
+ return
}
- if (props.scrollElementRef.current) {
- props.scrollElementRef.current?.scroll(
- 0,
- calculateOffset(anchorElement)
- )
- } else {
- window.document.documentElement.scroll(
- 0,
- calculateOffset(anchorElement)
- )
+ setShouldScrollToInitialPosition(false)
+
+ if (props.initialReadingProgress && props.initialReadingProgress >= 98) {
+ return
+ }
+
+ const anchorElement = props.highlightHref.current
+ ? document.querySelector(
+ `[omnivore-highlight-id="${props.highlightHref.current}"]`
+ )
+ : document.querySelector(
+ `[data-omnivore-anchor-idx='${props.initialAnchorIndex.toString()}']`
+ )
+
+ if (anchorElement) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const calculateOffset = (obj: any): number => {
+ let offset = 0
+ if (obj.offsetParent) {
+ do {
+ offset += obj.offsetTop
+ } while ((obj = obj.offsetParent))
+ return offset
+ }
+
+ return 0
+ }
+
+ const calculatedOffset = calculateOffset(anchorElement)
+
+ if (props.scrollElementRef.current) {
+ props.scrollElementRef.current?.scroll(0, calculatedOffset - 100)
+ } else {
+ window.document.documentElement.scroll(0, calculatedOffset - 100)
+ }
}
}
}, [
+ props.highlightReady,
+ props.scrollElementRef,
props.initialAnchorIndex,
props.initialReadingProgress,
- props.scrollElementRef,
shouldScrollToInitialPosition,
])
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index 1ef48a9f7..78c7371b4 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -6,7 +6,7 @@ import { ArticleSubtitle } from './../../patterns/ArticleSubtitle'
import { theme, ThemeId } from './../../tokens/stitches.config'
import { HighlightsLayer } from '../../templates/article/HighlightsLayer'
import { Button } from '../../elements/Button'
-import { MutableRefObject, useEffect, useState } from 'react'
+import { MutableRefObject, useEffect, useState, useRef } from 'react'
import { ReportIssuesModal } from './ReportIssuesModal'
import { reportIssueMutation } from '../../../lib/networking/mutations/reportIssueMutation'
import { ArticleHeaderToolbar } from './ArticleHeaderToolbar'
@@ -15,6 +15,7 @@ import { updateThemeLocally } from '../../../lib/themeUpdater'
import { ArticleMutations } from '../../../lib/articleActions'
import { LabelChip } from '../../elements/LabelChip'
import { Label } from '../../../lib/networking/fragments/labelFragment'
+import { HighlightLocation, makeHighlightStartEndOffset } from '../../../lib/highlights/highlightGenerator'
type ArticleContainerProps = {
article: ArticleAttributes
@@ -36,6 +37,11 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
const [showShareModal, setShowShareModal] = useState(false)
const [showReportIssuesModal, setShowReportIssuesModal] = useState(false)
const [fontSize, setFontSize] = useState(props.fontSize ?? 20)
+ const highlightHref = useRef(window.location.hash ? window.location.hash.split('#')[1] : null)
+ const [highlightReady, setHighlightReady] = useState(false)
+ const [highlightLocations, setHighlightLocations] = useState<
+ HighlightLocation[]
+ >([])
const updateFontSize = async (newFontSize: number) => {
if (fontSize !== newFontSize) {
@@ -48,6 +54,21 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
updateFontSize(props.fontSize ?? 20)
}, [props.fontSize])
+ // Load the highlights
+ useEffect(() => {
+ const res: HighlightLocation[] = []
+ props.article.highlights.forEach((highlight) => {
+ try {
+ const offset = makeHighlightStartEndOffset(highlight)
+ res.push(offset)
+ } catch (err) {
+ console.error(err)
+ }
+ })
+ setHighlightLocations(res)
+ setHighlightReady(true)
+ }, [props.article.highlights, setHighlightLocations])
+
// Listen for font size and color mode change events sent from host apps (ios, macos...)
useEffect(() => {
const increaseFontSize = async () => {
@@ -158,6 +179,8 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
)}
>
articleMutations: ArticleMutations
+ highlightLocations: HighlightLocation[]
}
type HighlightModalAction = 'none' | 'addComment' | 'share'
@@ -49,9 +50,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
const [highlightModalAction, setHighlightModalAction] =
useState({ highlightModalAction: 'none' })
- const [highlightLocations, setHighlightLocations] = useState<
- HighlightLocation[]
- >([])
const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 })
const [focusedHighlight, setFocusedHighlight] = useState<
@@ -59,26 +57,12 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
>(undefined)
const [selectionData, setSelectionData] = useSelection(
- highlightLocations,
+ props.highlightLocations,
false //noteModal.open,
)
const canShareNative = useCanShareNative()
- // Load the highlights
- useEffect(() => {
- const res: HighlightLocation[] = []
- highlights.forEach((highlight) => {
- try {
- const offset = makeHighlightStartEndOffset(highlight)
- res.push(offset)
- } catch (err) {
- console.error(err)
- }
- })
- setHighlightLocations(res)
- }, [highlights, setHighlightLocations])
-
const removeHighlightCallback = useCallback(
async (id?: string) => {
const highlightId = id || focusedHighlight?.id
@@ -89,7 +73,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
if (didDeleteHighlight) {
removeHighlights(
highlights.map(($0) => $0.id),
- highlightLocations
+ props.highlightLocations
)
setHighlights(highlights.filter(($0) => $0.id !== highlightId))
setFocusedHighlight(undefined)
@@ -97,16 +81,16 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
console.error('Failed to delete highlight')
}
},
- [focusedHighlight, highlights, highlightLocations]
+ [focusedHighlight, highlights, props.highlightLocations]
)
const updateHighlightsCallback = useCallback(
(highlight: Highlight) => {
- removeHighlights([highlight.id], highlightLocations)
+ removeHighlights([highlight.id], props.highlightLocations)
const keptHighlights = highlights.filter(($0) => $0.id !== highlight.id)
setHighlights([...keptHighlights, highlight])
},
- [highlights, highlightLocations]
+ [highlights, props.highlightLocations]
)
const handleNativeShare = useCallback(
@@ -159,7 +143,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
selection: selection,
articleId: props.articleId,
existingHighlights: highlights,
- highlightStartEndOffsets: highlightLocations,
+ highlightStartEndOffsets: props.highlightLocations,
annotation: note,
}, props.articleMutations)
@@ -214,10 +198,22 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
selectionData,
setSelectionData,
canShareNative,
- highlightLocations,
+ props.highlightLocations,
]
)
+ const scrollToHighlight = (id: string) => {
+ const foundElement = document.querySelector(`[omnivore-highlight-id="${id}"]`)
+ if(foundElement){
+ foundElement.scrollIntoView({
+ block: 'center',
+ behavior: 'smooth'
+ })
+ window.location.hash = `#${id}`
+ props.setShowHighlightsModal(false)
+ }
+ }
+
// Detect mouseclick on a highlight -- call `setFocusedHighlight` when highlight detected
const handleClickHighlight = useCallback(
(event: MouseEvent) => {
@@ -261,7 +257,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
})
} else setFocusedHighlight(undefined)
},
- [highlights, highlightLocations]
+ [highlights, props.highlightLocations]
)
useEffect(() => {
@@ -469,9 +465,10 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
if (props.showHighlightsModal) {
return (
props.setShowHighlightsModal(false)}
- deleteHighlightAction={(highlightId: string) => {
+ highlights={highlights}
+ onOpenChange={() => props.setShowHighlightsModal(false)}
+ scrollToHighlight={scrollToHighlight}
+ deleteHighlightAction={(highlightId: string) => {
removeHighlightCallback(highlightId)
}}
/>
diff --git a/packages/web/components/templates/article/HighlightsModal.tsx b/packages/web/components/templates/article/HighlightsModal.tsx
index d31557c4b..9f3ffde6c 100644
--- a/packages/web/components/templates/article/HighlightsModal.tsx
+++ b/packages/web/components/templates/article/HighlightsModal.tsx
@@ -19,6 +19,7 @@ import { Pen, Trash } from 'phosphor-react'
type HighlightsModalProps = {
highlights: Highlight[]
+ scrollToHighlight?: (arg: string) => void;
deleteHighlightAction?: (highlightId: string) => void
onOpenChange: (open: boolean) => void
}
@@ -60,6 +61,7 @@ export function HighlightsModal(props: HighlightsModalProps): JSX.Element {
key={highlight.id}
highlight={highlight}
showDelete={!!props.deleteHighlightAction}
+ scrollToHighlight={props.scrollToHighlight}
deleteHighlightAction={() => {
if (props.deleteHighlightAction) {
props.deleteHighlightAction(highlight.id)
@@ -82,6 +84,7 @@ export function HighlightsModal(props: HighlightsModalProps): JSX.Element {
type ModalHighlightViewProps = {
highlight: Highlight
showDelete: boolean
+ scrollToHighlight?: (arg: string) => void;
deleteHighlightAction: () => void
}
@@ -156,7 +159,7 @@ function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element {
return (
<>
-
+
{props.highlight.annotation && !isEditing ? (
{props.highlight.annotation}
) : null}
diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx
index 916f02d05..37c97210f 100644
--- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx
+++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx
@@ -40,7 +40,7 @@ import { Label } from '../../../lib/networking/fragments/labelFragment'
import { isVipUser } from '../../../lib/featureFlag'
import { EmptyLibrary } from './EmptyLibrary'
import TopBarProgress from 'react-topbar-progress-indicator'
-import { State } from '../../../lib/networking/fragments/articleFragment'
+import { State, PageType } from '../../../lib/networking/fragments/articleFragment'
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
@@ -53,7 +53,7 @@ const timeZoneHourDiff = -new Date().getTimezoneOffset() / 60
const SAVED_SEARCHES: Record = {
Inbox: `in:inbox`,
'Read Later': `in:inbox -label:Newsletter`,
- Highlighted: `in:inbox has:highlights`,
+ Highlights: `type:highlights`,
Today: `in:inbox saved:${
new Date(new Date().getTime() - 24 * 3600000).toISOString().split('T')[0]
}Z${timeZoneHourDiff.toLocaleString('en-US', {
@@ -112,23 +112,23 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setQueryInputs, router.isReady, router.query])
- const { articlesPages, size, setSize, isValidating, performActionOnItem } =
+ const { itemsPages, size, setSize, isValidating, performActionOnItem } =
useGetLibraryItemsQuery(queryInputs)
const hasMore = useMemo(() => {
- if (!articlesPages) {
+ if (!itemsPages) {
return false
}
- return articlesPages[articlesPages.length - 1].articles.pageInfo.hasNextPage
- }, [articlesPages])
+ return itemsPages[itemsPages.length - 1].search.pageInfo.hasNextPage
+ }, [itemsPages])
const libraryItems = useMemo(() => {
const items =
- articlesPages?.flatMap((ad) => {
- return ad.articles.edges
+ itemsPages?.flatMap((ad) => {
+ return ad.search.edges
}) || []
return items
- }, [articlesPages, performActionOnItem])
+ }, [itemsPages, performActionOnItem])
const handleFetchMore = useCallback(() => {
if (isValidating || !hasMore) {
@@ -262,7 +262,8 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element {
if (item.node.state === State.PROCESSING) {
router.push(`/${username}/links/${item.node.id}`)
} else {
- router.push(`/${username}/${item.node.slug}`)
+ const dl = item.node.pageType === PageType.HIGHLIGHTS ? `#${item.node.id}` : ''
+ router.push(`/${username}/${item.node.slug}` + dl)
}
}
break
@@ -440,8 +441,8 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element {
setSize(size + 1)
}}
hasMore={hasMore}
- hasData={!!articlesPages}
- totalItems={articlesPages?.[0].articles.pageInfo.totalCount || 0}
+ hasData={!!itemsPages}
+ totalItems={itemsPages?.[0].search.pageInfo.totalCount || 0}
isValidating={isValidating}
shareTarget={shareTarget}
setShareTarget={setShareTarget}
diff --git a/packages/web/components/templates/homeFeed/LibrarySearchBar.tsx b/packages/web/components/templates/homeFeed/LibrarySearchBar.tsx
index 6e4f84e3b..6e08f3f33 100644
--- a/packages/web/components/templates/homeFeed/LibrarySearchBar.tsx
+++ b/packages/web/components/templates/homeFeed/LibrarySearchBar.tsx
@@ -1,10 +1,9 @@
-import { ReactNode, useEffect } from 'react'
-import { useState, useRef } from 'react'
+import { ReactNode, useEffect, useRef, useState } from 'react'
import { StyledText } from '../../elements/StyledText'
import { Box, HStack, VStack } from '../../elements/LayoutPrimitives'
import { SearchIcon } from '../../elements/images/SearchIcon'
import { theme } from '../../tokens/stitches.config'
-import { DropdownOption, Dropdown } from '../../elements/DropdownElements'
+import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
import { FormInput } from '../../elements/FormElements'
import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
@@ -16,7 +15,19 @@ type LibrarySearchBarProps = {
applySearchQuery: (searchQuery: string) => void
}
-type LibraryFilter = 'in:inbox' | 'in:all' | 'in:archive' | 'type:file'
+type LibraryFilter =
+ | 'in:inbox'
+ | 'in:all'
+ | 'in:archive'
+ | 'type:file'
+ | 'type:highlights'
+ | `saved:${string}`
+ | `sort:updated`
+
+// 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)'
@@ -153,6 +164,21 @@ export function DropdownFilterMenu(
title="Files"
hideSeparator
/>
+ props.onFilterChange('type:highlights')}
+ title="Highlights"
+ hideSeparator
+ />
+ props.onFilterChange(`saved:${recentlySavedStartDate}`)}
+ title="Recently Saved"
+ hideSeparator
+ />
+ props.onFilterChange(`sort:updated`)}
+ title="Recently Read"
+ hideSeparator
+ />
)
}
diff --git a/packages/web/lib/networking/fragments/articleFragment.ts b/packages/web/lib/networking/fragments/articleFragment.ts
index 59794687b..a8cc4636a 100644
--- a/packages/web/lib/networking/fragments/articleFragment.ts
+++ b/packages/web/lib/networking/fragments/articleFragment.ts
@@ -30,6 +30,16 @@ export enum State {
FAILED = 'FAILED',
}
+export enum PageType {
+ ARTICLE = 'ARTICLE',
+ BOOK = 'BOOK',
+ FILE = 'FILE',
+ PROFILE = 'PROFILE',
+ WEBSITE = 'WEBSITE',
+ HIGHLIGHTS = 'HIGHLIGHTS',
+ UNKNOWN = 'UNKNOWN',
+}
+
export type ArticleFragmentData = {
id: string
title: string
diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
index d710179d5..3cc484de8 100644
--- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
@@ -1,7 +1,8 @@
import { gql } from 'graphql-request'
import useSWRInfinite from 'swr/infinite'
import { gqlFetcher } from '../networkHelpers'
-import type { ArticleFragmentData } from '../fragments/articleFragment'
+import type { ArticleFragmentData, PageType, State } from '../fragments/articleFragment'
+import { ContentReader } from '../fragments/articleFragment'
import { setLinkArchivedMutation } from '../mutations/setLinkArchivedMutation'
import { deleteLinkMutation } from '../mutations/deleteLinkMutation'
import { articleReadingProgressMutation } from '../mutations/articleReadingProgressMutation'
@@ -16,8 +17,8 @@ export type LibraryItemsQueryInput = {
}
type LibraryItemsQueryResponse = {
- articlesPages?: LibraryItemsData[]
- articlesDataError?: unknown
+ itemsPages?: LibraryItemsData[]
+ itemsDataError?: unknown
isLoading: boolean
isValidating: boolean
size: number
@@ -36,7 +37,7 @@ type LibraryItemAction =
| 'refresh'
export type LibraryItemsData = {
- articles: LibraryItems
+ search: LibraryItems
}
export type LibraryItems = {
@@ -50,12 +51,30 @@ export type LibraryItem = {
node: LibraryItemNode
}
-export type LibraryItemNode = ArticleFragmentData & {
- description?: string
- hasContent: boolean
+export type LibraryItemNode = {
+ id: string
+ title: string
+ url: string
+ author?: string
+ image?: string
+ createdAt: string
+ publishedAt?: string
+ contentReader?: ContentReader
originalArticleUrl: string
- sharedComment?: string
+ readingProgressPercent: number
+ readingProgressAnchorIndex: number
+ slug: string
+ isArchived: boolean
+ description: string
+ ownedByViewer: boolean
+ uploadFileId: string
labels?: Label[]
+ pageId: string
+ shortId: string
+ quote: string
+ annotation: string
+ state: State
+ pageType: PageType
}
export type PageInfo = {
@@ -66,31 +85,6 @@ export type PageInfo = {
totalCount: number
}
-const libraryItemFragment = gql`
- fragment ArticleFields on Article {
- id
- title
- url
- author
- image
- savedAt
- createdAt
- publishedAt
- contentReader
- originalArticleUrl
- readingProgressPercent
- readingProgressAnchorIndex
- slug
- isArchived
- description
- linkId
- state
- labels {
- ...LabelFields
- }
- }
-`
-
export function useGetLibraryItemsQuery({
limit,
sortDescending,
@@ -98,27 +92,38 @@ export function useGetLibraryItemsQuery({
cursor,
}: LibraryItemsQueryInput): LibraryItemsQueryResponse {
const query = gql`
- query GetArticles(
- $sharedOnly: Boolean
- $sort: SortParams
- $after: String
- $first: Int
- $query: String
- ) {
- articles(
- sharedOnly: $sharedOnly
- sort: $sort
- first: $first
- after: $after
- query: $query
- includePending: true
- ) {
- ... on ArticlesSuccess {
+ query Search($after: String, $first: Int, $query: String) {
+ search(first: $first, after: $after, query: $query) {
+ ... on SearchSuccess {
edges {
cursor
node {
- ...ArticleFields
+ id
+ title
+ slug
+ url
+ pageType
+ contentReader
+ createdAt
+ isArchived
+ readingProgressPercent
+ readingProgressAnchorIndex
+ author
+ image
+ description
+ publishedAt
+ ownedByViewer
originalArticleUrl
+ uploadFileId
+ labels {
+ id
+ name
+ color
+ }
+ pageId
+ shortId
+ quote
+ annotation
}
}
pageInfo {
@@ -129,21 +134,14 @@ export function useGetLibraryItemsQuery({
totalCount
}
}
- ... on ArticlesError {
+ ... on SearchError {
errorCodes
}
}
}
- ${libraryItemFragment}
- ${labelFragment}
`
const variables = {
- sharedOnly: false,
- sort: {
- order: sortDescending ? 'DESCENDING' : 'ASCENDING',
- by: 'UPDATED_TIME',
- },
after: cursor,
first: limit,
query: searchQuery,
@@ -162,12 +160,10 @@ export function useGetLibraryItemsQuery({
limit,
sortDescending,
searchQuery,
- pageIndex === 0
- ? undefined
- : previousResult.articles.pageInfo.endCursor,
+ pageIndex === 0 ? undefined : previousResult.search.pageInfo.endCursor,
]
},
- (_query, _l, _s, _sq, cursor: string) => {
+ (_query, _l, _s, _sq, cursor) => {
return gqlFetcher(query, { ...variables, after: cursor }, true)
},
{ revalidateFirstPage: false }
@@ -182,7 +178,7 @@ export function useGetLibraryItemsQuery({
// the response in the case of an error.
if (!error && responsePages) {
const errors = responsePages.filter(
- (d) => d.articles.errorCodes && d.articles.errorCodes.length > 0
+ (d) => d.search.errorCodes && d.search.errorCodes.length > 0
)
if (errors?.length > 0) {
responseError = errors
@@ -202,13 +198,13 @@ export function useGetLibraryItemsQuery({
if (!responsePages) {
return
}
- for (const articlesData of responsePages) {
- const itemIndex = articlesData.articles.edges.indexOf(item)
+ for (const searchResults of responsePages) {
+ const itemIndex = searchResults.search.edges.indexOf(item)
if (itemIndex !== -1) {
if (typeof mutatedItem === 'undefined') {
- articlesData.articles.edges.splice(itemIndex, 1)
+ searchResults.search.edges.splice(itemIndex, 1)
} else {
- articlesData.articles.edges[itemIndex] = mutatedItem
+ searchResults.search.edges[itemIndex] = mutatedItem
}
break
}
@@ -313,8 +309,8 @@ export function useGetLibraryItemsQuery({
return {
isValidating,
- articlesPages: responsePages || undefined,
- articlesDataError: responseError,
+ itemsPages: responsePages || undefined,
+ itemsDataError: responseError,
isLoading: !error && !data,
performActionOnItem,
size,
diff --git a/packages/web/stories/HighlightItemCard.stories.tsx b/packages/web/stories/HighlightItemCard.stories.tsx
new file mode 100644
index 000000000..260338eb4
--- /dev/null
+++ b/packages/web/stories/HighlightItemCard.stories.tsx
@@ -0,0 +1,71 @@
+import { ComponentStory, ComponentMeta } from '@storybook/react'
+import { HighlightItemCard, HighlightItemCardProps } from '../components/patterns/LibraryCards/HighlightItemCard'
+import { updateThemeLocally } from '../lib/themeUpdater'
+import { ThemeId } from '../components/tokens/stitches.config'
+import { PageType, State } from '../lib/networking/fragments/articleFragment'
+
+export default {
+ title: 'Components/HighlightItemCard',
+ component: HighlightItemCard,
+ argTypes: {
+ item: {
+ description: 'The highlight.',
+ },
+ handleAction: {
+ description: 'Action that fires on click.'
+ }
+ }
+} as ComponentMeta
+
+const highlight: HighlightItemCardProps = {
+ handleAction: () => console.log('Handling Action'),
+ item:{
+ id: "nnnnn",
+ shortId: "shortId",
+ quote: "children not only participate in herding work, but are also encouraged to act independently in most other areas of life. They have a say in deciding when to eat, when to sleep, and what to wear, even at temperatures of -30C (-22F).",
+ annotation: "Okay… this is wild! I love this independence. Wondering how I can reponsibly instill this type of indepence in my own kids…",
+ createdAt: '',
+ description: '',
+ isArchived: false,
+ originalArticleUrl: 'https://example.com',
+ ownedByViewer: true,
+ pageId: '1',
+ readingProgressAnchorIndex: 12,
+ readingProgressPercent: 50,
+ slug: 'slug',
+ title: "This is a title",
+ uploadFileId: '1',
+ url: 'https://example.com',
+ author: 'Author',
+ image: 'https://logos-world.net/wp-content/uploads/2021/11/Unity-New-Logo.png',
+ state: State.SUCCEEDED,
+ pageType: PageType.HIGHLIGHTS,
+ },
+}
+
+const Template = (props: HighlightItemCardProps) =>
+
+export const LightHighlightItemCard: ComponentStory<
+ typeof HighlightItemCard
+> = (args: any) => {
+ updateThemeLocally(ThemeId.Light)
+ return (
+
+ )
+}
+export const DarkHighlightItemCard: ComponentStory<
+ typeof HighlightItemCard
+> = (args: any) => {
+ updateThemeLocally(ThemeId.Dark)
+ return (
+
+ )
+}
+
+LightHighlightItemCard.args = {
+ ...highlight
+}
+
+DarkHighlightItemCard.args = {
+ ...highlight
+}