Merge pull request #4180 from omnivore-app/feat/web-home-fallback

Add a fallback to library if Home is not created for user
This commit is contained in:
Jackson Harper
2024-07-09 17:18:39 +08:00
committed by GitHub
6 changed files with 184 additions and 66 deletions

View File

@ -1,4 +1,4 @@
import { styled } from '../tokens/stitches.config'
import { styled, theme } from '../tokens/stitches.config'
export const Button = styled('button', {
fontFamily: 'inter',
@ -436,7 +436,12 @@ export const Button = styled('button', {
cursor: 'pointer',
p: '5px',
borderRadius: '5px',
'&:hover': { bg: '$homeActionHoverBg', opacity: '1' },
'&:hover': {
opacity: '1',
bg: '$homeActionHoverBg',
'--stroke': theme.colors.homeTextTitle.toString(),
},
'--stroke': theme.colors.homeActionIcons.toString(),
},
menuAction: {
display: 'flex',

View File

@ -6,8 +6,6 @@ import React from 'react'
export class AddToLibraryActionIcon extends React.Component<IconProps> {
render() {
const strokeColor = (this.props.color || '#D9D9D9').toString()
return (
<svg
width="19"
@ -19,7 +17,7 @@ export class AddToLibraryActionIcon extends React.Component<IconProps> {
<g>
<path
d="M14.25 5.54167V16.625L9.5 13.4583L4.75 16.625V5.54167C4.75 4.70181 5.08363 3.89636 5.6775 3.3025C6.27136 2.70863 7.07681 2.375 7.91667 2.375H11.0833C11.9232 2.375 12.7286 2.70863 13.3225 3.3025C13.9164 3.89636 14.25 4.70181 14.25 5.54167Z"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"

View File

@ -6,8 +6,6 @@ import React from 'react'
export class ArchiveActionIcon extends React.Component<IconProps> {
render() {
const strokeColor = (this.props.color || '#D9D9D9').toString()
return (
<svg
width="19"
@ -19,21 +17,21 @@ export class ArchiveActionIcon extends React.Component<IconProps> {
<g>
<path
d="M2.375 4.74984C2.375 4.32991 2.54181 3.92718 2.83875 3.63025C3.13568 3.33332 3.53841 3.1665 3.95833 3.1665H15.0417C15.4616 3.1665 15.8643 3.33332 16.1613 3.63025C16.4582 3.92718 16.625 4.32991 16.625 4.74984C16.625 5.16976 16.4582 5.57249 16.1613 5.86942C15.8643 6.16636 15.4616 6.33317 15.0417 6.33317H3.95833C3.53841 6.33317 3.13568 6.16636 2.83875 5.86942C2.54181 5.57249 2.375 5.16976 2.375 4.74984Z"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.95801 6.3335V14.2502C3.95801 14.6701 4.12482 15.0728 4.42176 15.3697C4.71869 15.6667 5.12141 15.8335 5.54134 15.8335H13.458C13.8779 15.8335 14.2807 15.6667 14.5776 15.3697C14.8745 15.0728 15.0413 14.6701 15.0413 14.2502V6.3335"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.91699 9.5H11.0837"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"

View File

@ -6,8 +6,6 @@ import React from 'react'
export class RemoveActionIcon extends React.Component<IconProps> {
render() {
const strokeColor = (this.props.color || '#D9D9D9').toString()
return (
<svg
width="19"
@ -19,35 +17,35 @@ export class RemoveActionIcon extends React.Component<IconProps> {
<g>
<path
d="M3.16699 5.5415H15.8337"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.91699 8.7085V13.4585"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M11.083 8.7085V13.4585"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.95801 5.5415L4.74967 15.0415C4.74967 15.4614 4.91649 15.8642 5.21342 16.1611C5.51035 16.458 5.91308 16.6248 6.33301 16.6248H12.6663C13.0863 16.6248 13.489 16.458 13.7859 16.1611C14.0829 15.8642 14.2497 15.4614 14.2497 15.0415L15.0413 5.5415"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.125 5.54167V3.16667C7.125 2.9567 7.20841 2.75534 7.35687 2.60687C7.50534 2.45841 7.7067 2.375 7.91667 2.375H11.0833C11.2933 2.375 11.4947 2.45841 11.6431 2.60687C11.7916 2.75534 11.875 2.9567 11.875 3.16667V5.54167"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"

View File

@ -6,8 +6,6 @@ import React from 'react'
export class ShareActionIcon extends React.Component<IconProps> {
render() {
const strokeColor = (this.props.color || '#D9D9D9').toString()
return (
<svg
width="19"
@ -19,7 +17,7 @@ export class ShareActionIcon extends React.Component<IconProps> {
<g>
<path
d="M10.2918 3.1665V6.33317C5.08661 7.147 3.15098 11.707 2.37515 15.8332C2.34586 15.9963 6.63748 11.1133 10.2918 11.0832V14.2498L16.6251 8.70817L10.2918 3.1665Z"
stroke={strokeColor}
stroke="var(--stroke)"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"

View File

@ -5,7 +5,6 @@ import { useCallback, useEffect, useMemo, useReducer, useState } from 'react'
import { Button } from '../elements/Button'
import { AddToLibraryActionIcon } from '../elements/icons/home/AddToLibraryActionIcon'
import { ArchiveActionIcon } from '../elements/icons/home/ArchiveActionIcon'
import { CommentActionIcon } from '../elements/icons/home/CommentActionIcon'
import { RemoveActionIcon } from '../elements/icons/home/RemoveActionIcon'
import { ShareActionIcon } from '../elements/icons/home/ShareActionIcon'
import Pagination from '../elements/Pagination'
@ -29,6 +28,7 @@ import { Toaster } from 'react-hot-toast'
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
import useLibraryItemActions from '../../lib/hooks/useLibraryItemActions'
import { SyncLoader } from 'react-spinners'
import { useGetLibraryItemsQuery } from '../../lib/networking/queries/useGetLibraryItemsQuery'
export function HomeContainer(): JSX.Element {
const router = useRouter()
@ -105,6 +105,9 @@ export function HomeContainer(): JSX.Element {
/>
)
case 'top_picks':
if (homeSection.items.length < 1) {
return <FromYourLibraryHomeSection />
}
return (
<TopPicksHomeSection
key={`section-${idx}`}
@ -113,6 +116,9 @@ export function HomeContainer(): JSX.Element {
/>
)
case 'quick_links':
if (homeSection.items.length < 1) {
return <SpanBox key={`section-${idx}`}></SpanBox>
}
return (
<QuickLinksHomeSection
key={`section-${idx}`}
@ -329,6 +335,118 @@ const TopPicksHomeSection = (props: HomeSectionProps): JSX.Element => {
)
}
const FromYourLibraryHomeSection = (): JSX.Element => {
const { itemsPages } = useGetLibraryItemsQuery('all', {
limit: 10,
includeContent: false,
sortDescending: true,
})
const searchItems = useMemo(() => {
return (
itemsPages?.flatMap((ad) => {
return ad.search.edges.map((it) => ({
...it,
isLoading: it.node.state === 'PROCESSING',
}))
}) || []
).map((item) => {
return {
id: item.node.id,
date: item.node.savedAt,
title: item.node.title,
url: item.node.url,
slug: item.node.slug,
score: 1.0,
thumbnail: item.node.image,
source: {
name:
item.node.folder == 'following'
? item.node.subscription
: item.node.siteName,
icon: item.node.siteIcon,
type: 'LIBRARY',
},
canArchive: true,
canDelete: true,
canShare: true,
canMove: item.node.folder == 'following',
} as HomeItem
})
}, [itemsPages])
const listReducer = (
state: HomeItem[],
action: {
type: string
itemId?: string
items?: HomeItem[]
}
) => {
switch (action.type) {
case 'RESET':
return action.items ?? []
case 'REMOVE_ITEM':
return state.filter((item) => item.id !== action.itemId)
default:
throw new Error()
}
}
const [items, dispatchList] = useReducer(listReducer, [])
useEffect(() => {
dispatchList({
type: 'RESET',
items: searchItems,
})
}, [searchItems])
console.log('items: ', items)
return (
<VStack
distribution="start"
css={{
width: '100%',
gap: '20px',
'@mdDown': {
gap: '10px',
},
}}
>
{items.length > 0 && (
<SpanBox
css={{
fontFamily: '$inter',
fontSize: '16px',
fontWeight: '600',
color: '$homeTextTitle',
'@mdDown': {
px: '20px',
},
}}
>
From your library
</SpanBox>
)}
<Pagination
items={items}
itemsPerPage={10}
loadMoreButtonText="Load more Top Picks"
render={(homeItem) => (
<TopPicksItemView
key={homeItem.id}
homeItem={homeItem}
dispatchList={dispatchList}
/>
)}
/>
</VStack>
)
}
const QuickLinksHomeSection = (props: HomeSectionProps): JSX.Element => {
return (
<VStack
@ -701,9 +819,7 @@ const TopPicksItemView = (
}
}}
>
<AddToLibraryActionIcon
color={theme.colors.homeActionIcons.toString()}
/>
<AddToLibraryActionIcon />
</Button>
)}
{props.homeItem.canArchive && (
@ -726,9 +842,7 @@ const TopPicksItemView = (
}
}}
>
<ArchiveActionIcon
color={theme.colors.homeActionIcons.toString()}
/>
<ArchiveActionIcon />
</Button>
)}
{props.homeItem.canDelete && (
@ -757,7 +871,7 @@ const TopPicksItemView = (
}
}}
>
<RemoveActionIcon color={theme.colors.homeActionIcons.toString()} />
<RemoveActionIcon />
</Button>
)}
{props.homeItem.canShare && (
@ -767,11 +881,10 @@ const TopPicksItemView = (
onClick={async (event) => {
event.preventDefault()
event.stopPropagation()
await shareItem(props.homeItem.title, props.homeItem.url)
}}
>
<ShareActionIcon color={theme.colors.homeActionIcons.toString()} />
<ShareActionIcon />
</Button>
)}
</HStack>
@ -847,47 +960,55 @@ type SourceInfoProps = {
subtle?: boolean
}
const SourceInfo = (props: HomeItemViewProps & SourceInfoProps) => (
<HoverCard.Root>
<HoverCard.Trigger asChild>
<HStack
distribution="start"
alignment="center"
css={{
gap: '8px',
height: '16px',
cursor: 'pointer',
flex: '1',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}}
>
{props.homeItem.source.icon && (
<SiteIconSmall src={props.homeItem.source.icon} />
)}
const SourceInfo = (props: HomeItemViewProps & SourceInfoProps) => {
const hasHover = useMemo(() => {
const type = props.homeItem.source.type
return type == 'RSS' || type == 'NEWSLETTER'
}, [props])
return (
<HoverCard.Root>
<HoverCard.Trigger asChild>
<HStack
distribution="start"
alignment="center"
css={{
lineHeight: '1',
fontFamily: '$inter',
fontWeight: '500',
fontSize: props.subtle ? '12px' : '13px',
color: props.subtle ? '$homeTextSubtle' : '$homeTextSource',
textDecoration: 'underline',
gap: '8px',
height: '16px',
cursor: 'pointer',
flex: '1',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}}
>
{props.homeItem.source.name}
{props.homeItem.source.icon && (
<SiteIconSmall src={props.homeItem.source.icon} />
)}
<HStack
css={{
lineHeight: '1',
fontFamily: '$inter',
fontWeight: '500',
fontSize: props.subtle ? '12px' : '13px',
color: props.subtle ? '$homeTextSubtle' : '$homeTextSource',
textDecoration: hasHover ? 'underline' : 'unset',
}}
>
{props.homeItem.source.name}
</HStack>
</HStack>
</HStack>
</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCard.Content sideOffset={5} style={{ zIndex: 5 }}>
<SubscriptionSourceHoverContent source={props.homeItem.source} />
<HoverCard.Arrow fill={theme.colors.thBackground2.toString()} />
</HoverCard.Content>
</HoverCard.Portal>
</HoverCard.Root>
)
</HoverCard.Trigger>
{hasHover && (
<HoverCard.Portal>
<HoverCard.Content sideOffset={5} style={{ zIndex: 5 }}>
<SubscriptionSourceHoverContent source={props.homeItem.source} />
<HoverCard.Arrow fill={theme.colors.thBackground2.toString()} />
</HoverCard.Content>
</HoverCard.Portal>
)}
</HoverCard.Root>
)
}
type SourceHoverContentProps = {
source: HomeItemSource