mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #4055 from omnivore-app/feat/recently-added
This commit is contained in:
commit
833a83f235
10 changed files with 177 additions and 50 deletions
|
|
@ -628,7 +628,7 @@ export const functionResolvers = {
|
|||
|
||||
switch (section.layout) {
|
||||
case 'just_added':
|
||||
return 'Just Added'
|
||||
return 'Recently Added'
|
||||
case 'top_picks':
|
||||
return 'Top Picks'
|
||||
case 'quick_links':
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ export const Button = styled('button', {
|
|||
cursor: 'pointer',
|
||||
p: '5px',
|
||||
borderRadius: '5px',
|
||||
'&:hover': { bg: '$readerHoverBg', opacity: '1' },
|
||||
'&:hover': { bg: '$homeActionHoverBg', opacity: '1' },
|
||||
},
|
||||
menuAction: {
|
||||
display: 'flex',
|
||||
|
|
|
|||
|
|
@ -26,11 +26,18 @@ import {
|
|||
} from '../../lib/networking/queries/useGetSubscriptionsQuery'
|
||||
import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
|
||||
export function HomeContainer(): JSX.Element {
|
||||
const homeData = useGetHomeItems()
|
||||
const { viewerData } = useGetViewerQuery()
|
||||
|
||||
useApplyLocalTheme()
|
||||
|
||||
const viewerUsername = useMemo(() => {
|
||||
return viewerData?.me?.profile.username
|
||||
}, [viewerData])
|
||||
|
||||
return (
|
||||
<VStack
|
||||
distribution="start"
|
||||
|
|
@ -47,7 +54,7 @@ export function HomeContainer(): JSX.Element {
|
|||
distribution="start"
|
||||
css={{
|
||||
width: '646px',
|
||||
gap: '40px',
|
||||
gap: '50px',
|
||||
minHeight: '100vh',
|
||||
'@mdDown': {
|
||||
width: '100%',
|
||||
|
|
@ -64,6 +71,7 @@ export function HomeContainer(): JSX.Element {
|
|||
<JustAddedHomeSection
|
||||
key={`section-${idx}`}
|
||||
homeSection={homeSection}
|
||||
viewerUsername={viewerUsername}
|
||||
/>
|
||||
)
|
||||
case 'top_picks':
|
||||
|
|
@ -71,6 +79,7 @@ export function HomeContainer(): JSX.Element {
|
|||
<TopPicksHomeSection
|
||||
key={`section-${idx}`}
|
||||
homeSection={homeSection}
|
||||
viewerUsername={viewerUsername}
|
||||
/>
|
||||
)
|
||||
case 'quick_links':
|
||||
|
|
@ -78,6 +87,7 @@ export function HomeContainer(): JSX.Element {
|
|||
<QuickLinksHomeSection
|
||||
key={`section-${idx}`}
|
||||
homeSection={homeSection}
|
||||
viewerUsername={viewerUsername}
|
||||
/>
|
||||
)
|
||||
case 'hidden':
|
||||
|
|
@ -85,6 +95,7 @@ export function HomeContainer(): JSX.Element {
|
|||
<HiddenHomeSection
|
||||
key={`section-${idx}`}
|
||||
homeSection={homeSection}
|
||||
viewerUsername={viewerUsername}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
|
|
@ -98,9 +109,11 @@ export function HomeContainer(): JSX.Element {
|
|||
|
||||
type HomeSectionProps = {
|
||||
homeSection: HomeSection
|
||||
viewerUsername: string | undefined
|
||||
}
|
||||
|
||||
const JustAddedHomeSection = (props: HomeSectionProps): JSX.Element => {
|
||||
const router = useRouter()
|
||||
return (
|
||||
<VStack
|
||||
distribution="start"
|
||||
|
|
@ -109,19 +122,64 @@ const JustAddedHomeSection = (props: HomeSectionProps): JSX.Element => {
|
|||
gap: '20px',
|
||||
}}
|
||||
>
|
||||
<SpanBox
|
||||
css={{
|
||||
fontFamily: '$inter',
|
||||
fontSize: '16px',
|
||||
fontWeight: '600',
|
||||
color: '$homeTextTitle',
|
||||
}}
|
||||
<HStack
|
||||
css={{ width: '100%', lineHeight: '1' }}
|
||||
distribution="start"
|
||||
alignment="start"
|
||||
>
|
||||
{props.homeSection.title}
|
||||
</SpanBox>
|
||||
{props.homeSection.items.map((homeItem) => {
|
||||
return <JustAddedItemView key={homeItem.id} homeItem={homeItem} />
|
||||
})}
|
||||
<SpanBox
|
||||
css={{
|
||||
fontFamily: '$inter',
|
||||
fontSize: '16px',
|
||||
fontWeight: '600',
|
||||
color: '$homeTextTitle',
|
||||
}}
|
||||
>
|
||||
{props.homeSection.title}
|
||||
</SpanBox>
|
||||
<SpanBox
|
||||
css={{
|
||||
ml: 'auto',
|
||||
fontFamily: '$inter',
|
||||
fontSize: '13px',
|
||||
fontWeight: '400',
|
||||
color: '$homeTextTitle',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style="link"
|
||||
onClick={(event) => {
|
||||
router.push('/l/library')
|
||||
event.preventDefault()
|
||||
}}
|
||||
css={{
|
||||
'&:hover': {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
}}
|
||||
>
|
||||
View All
|
||||
</Button>
|
||||
</SpanBox>
|
||||
</HStack>
|
||||
<HStack
|
||||
css={{
|
||||
width: '100%',
|
||||
lineHeight: '1',
|
||||
overflow: 'scroll',
|
||||
gap: '25px',
|
||||
scrollbarWidth: 'none',
|
||||
'::-webkit-scrollbar': {
|
||||
display: 'none',
|
||||
},
|
||||
}}
|
||||
distribution="start"
|
||||
alignment="start"
|
||||
>
|
||||
{props.homeSection.items.map((homeItem) => {
|
||||
return <JustAddedItemView key={homeItem.id} homeItem={homeItem} />
|
||||
})}
|
||||
</HStack>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
|
@ -165,7 +223,7 @@ const QuickLinksHomeSection = (props: HomeSectionProps): JSX.Element => {
|
|||
css={{
|
||||
width: '100%',
|
||||
gap: '20px',
|
||||
bg: '#3D3D3D',
|
||||
bg: '$thNavMenuFooter',
|
||||
py: '30px',
|
||||
px: '20px',
|
||||
borderRadius: '5px',
|
||||
|
|
@ -279,6 +337,7 @@ const CoverImage = styled('img', {
|
|||
|
||||
type HomeItemViewProps = {
|
||||
homeItem: HomeItem
|
||||
viewerUsername?: string | undefined
|
||||
}
|
||||
|
||||
const TimeAgo = (props: HomeItemViewProps): JSX.Element => {
|
||||
|
|
@ -301,6 +360,7 @@ const TimeAgo = (props: HomeItemViewProps): JSX.Element => {
|
|||
const Title = (props: HomeItemViewProps): JSX.Element => {
|
||||
return (
|
||||
<HStack
|
||||
className="title-text"
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{
|
||||
|
|
@ -315,6 +375,34 @@ const Title = (props: HomeItemViewProps): JSX.Element => {
|
|||
display: '-webkit-box',
|
||||
'-webkit-line-clamp': '3',
|
||||
'-webkit-box-orient': 'vertical',
|
||||
'&:title-text': {
|
||||
transition: 'text-decoration 0.3s ease',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{props.homeItem.title}
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
||||
const TitleSmall = (props: HomeItemViewProps): JSX.Element => {
|
||||
return (
|
||||
<HStack
|
||||
className="title-text"
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{
|
||||
fontSize: '13px',
|
||||
lineHeight: '26px',
|
||||
fontWeight: '500',
|
||||
fontFamily: '$inter',
|
||||
color: '$homeTextTitle',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
wordBreak: 'break-word',
|
||||
display: '-webkit-box',
|
||||
'-webkit-line-clamp': '3',
|
||||
'-webkit-box-orient': 'vertical',
|
||||
}}
|
||||
>
|
||||
{props.homeItem.title}
|
||||
|
|
@ -353,31 +441,41 @@ const JustAddedItemView = (props: HomeItemViewProps): JSX.Element => {
|
|||
return (
|
||||
<VStack
|
||||
css={{
|
||||
width: '100%',
|
||||
padding: '5px',
|
||||
minWidth: '377px',
|
||||
gap: '5px',
|
||||
padding: '12px',
|
||||
cursor: 'pointer',
|
||||
bg: '$homeCardHover',
|
||||
borderRadius: '5px',
|
||||
'&:hover': {
|
||||
bg: '$homeCardHover',
|
||||
borderRadius: '0px',
|
||||
},
|
||||
'&:hover .title-text': {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
}}
|
||||
onClick={(event) => {
|
||||
const path = `/${props.viewerUsername ?? 'me'}/${props.homeItem.slug}`
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
window.open(props.homeItem.url, '_blank')
|
||||
window.open(path, '_blank')
|
||||
} else {
|
||||
router.push(props.homeItem.url)
|
||||
router.push(path)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{ gap: '5px', lineHeight: '1' }}
|
||||
css={{ width: '100%', gap: '5px', lineHeight: '1' }}
|
||||
>
|
||||
<SourceInfo homeItem={props.homeItem} />
|
||||
<TimeAgo homeItem={props.homeItem} />
|
||||
<SourceInfo homeItem={props.homeItem} subtle={true} />
|
||||
<SpanBox css={{ ml: 'auto' }}>
|
||||
<TimeAgo homeItem={props.homeItem} />
|
||||
</SpanBox>
|
||||
</HStack>
|
||||
<Title homeItem={props.homeItem} />
|
||||
|
||||
<TitleSmall homeItem={props.homeItem} />
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
|
@ -391,18 +489,22 @@ const TopicPickHomeItemView = (props: HomeItemViewProps): JSX.Element => {
|
|||
width: '100%',
|
||||
p: '0px',
|
||||
pt: '35px',
|
||||
|
||||
cursor: 'pointer',
|
||||
borderRadius: '5px',
|
||||
'&:hover': {
|
||||
bg: '$homeCardHover',
|
||||
borderRadius: '0px',
|
||||
},
|
||||
'&:hover .title-text': {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
}}
|
||||
onClick={(event) => {
|
||||
const path = `/${props.viewerUsername ?? 'me'}/${props.homeItem.slug}`
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
window.open(props.homeItem.url, '_blank')
|
||||
window.open(path, '_blank')
|
||||
} else {
|
||||
router.push(props.homeItem.url)
|
||||
router.push(path)
|
||||
}
|
||||
}}
|
||||
alignment="start"
|
||||
|
|
@ -482,10 +584,11 @@ const QuickLinkHomeItemView = (props: HomeItemViewProps): JSX.Element => {
|
|||
},
|
||||
}}
|
||||
onClick={(event) => {
|
||||
const path = `/${props.viewerUsername ?? 'me'}/${props.homeItem.slug}`
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
window.open(props.homeItem.url, '_blank')
|
||||
window.open(path, '_blank')
|
||||
} else {
|
||||
router.push(props.homeItem.url)
|
||||
router.push(path)
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
@ -511,13 +614,17 @@ const SiteIconLarge = styled('img', {
|
|||
borderRadius: '100px',
|
||||
})
|
||||
|
||||
const SourceInfo = (props: HomeItemViewProps) => (
|
||||
type SourceInfoProps = {
|
||||
subtle?: boolean
|
||||
}
|
||||
|
||||
const SourceInfo = (props: HomeItemViewProps & SourceInfoProps) => (
|
||||
<HoverCard.Root>
|
||||
<HoverCard.Trigger asChild>
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{ gap: '5px', cursor: 'pointer' }}
|
||||
css={{ gap: '8px', cursor: 'pointer' }}
|
||||
>
|
||||
{props.homeItem.source.icon && (
|
||||
<SiteIconSmall
|
||||
|
|
@ -528,11 +635,10 @@ const SourceInfo = (props: HomeItemViewProps) => (
|
|||
<HStack
|
||||
css={{
|
||||
lineHeight: '1',
|
||||
pb: '3px',
|
||||
fontFamily: '$inter',
|
||||
fontWeight: '500',
|
||||
fontSize: '13px',
|
||||
color: '$homeTextSource',
|
||||
fontSize: props.subtle ? '12px' : '13px',
|
||||
color: props.subtle ? '$homeTextSubtle' : '$homeTextSource',
|
||||
textDecoration: 'underline',
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { PageMetaData, PageMetaDataProps } from '../patterns/PageMetaData'
|
||||
import { Box, HStack, VStack } from '../elements/LayoutPrimitives'
|
||||
import { HStack, SpanBox, VStack } from '../elements/LayoutPrimitives'
|
||||
import { ReactNode, useEffect, useState, useCallback } from 'react'
|
||||
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { navigationCommands } from '../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { useKeyboardShortcuts } from '../../lib/keyboardShortcuts/useKeyboardShortcuts'
|
||||
import { NextRouter, useRouter } from 'next/router'
|
||||
import { useRouter } from 'next/router'
|
||||
import { ConfirmationModal } from '../patterns/ConfirmationModal'
|
||||
import { KeyboardShortcutListModal } from './KeyboardShortcutListModal'
|
||||
import { setupAnalytics } from '../../lib/analytics'
|
||||
|
|
@ -19,8 +19,6 @@ import { DEFAULT_HEADER_HEIGHT } from './homeFeed/HeaderSpacer'
|
|||
import { Button } from '../elements/Button'
|
||||
import { List } from '@phosphor-icons/react'
|
||||
import { usePersistedState } from '../../lib/hooks/usePersistedState'
|
||||
import 'allotment/dist/style.css'
|
||||
import { LibrarySideBar } from './library/LibrarySideBar'
|
||||
|
||||
export type NavigationSection =
|
||||
| 'home'
|
||||
|
|
@ -122,11 +120,19 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element {
|
|||
{props.pageMetaDataProps ? (
|
||||
<PageMetaData {...props.pageMetaDataProps} />
|
||||
) : null}
|
||||
<Header
|
||||
toggleMenu={() => {
|
||||
setShowNavMenu(!showNavMenu)
|
||||
<SpanBox
|
||||
css={{
|
||||
'@lgDown': {
|
||||
display: 'none',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Header
|
||||
toggleMenu={() => {
|
||||
setShowNavMenu(!showNavMenu)
|
||||
}}
|
||||
/>
|
||||
</SpanBox>
|
||||
{showNavMenu && (
|
||||
<NavigationMenu
|
||||
section={props.section}
|
||||
|
|
|
|||
|
|
@ -926,6 +926,9 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
px: '20px',
|
||||
py: '20px',
|
||||
width: '100%',
|
||||
'@mdDown': {
|
||||
px: '0px',
|
||||
},
|
||||
}}
|
||||
distribution="start"
|
||||
alignment="start"
|
||||
|
|
@ -1189,6 +1192,10 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element {
|
|||
'@lgDown': {
|
||||
px: '10px',
|
||||
},
|
||||
'@mdDown': {
|
||||
px: '0px',
|
||||
gap: '0px',
|
||||
},
|
||||
gridTemplateColumns:
|
||||
props.layout == 'LIST_LAYOUT'
|
||||
? 'none'
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ const HeaderControls = (props: LibraryHeaderProps): JSX.Element => {
|
|||
<SpanBox
|
||||
css={{
|
||||
display: 'none',
|
||||
'@mdDown': { display: 'flex' },
|
||||
'@lgDown': { display: 'flex' },
|
||||
}}
|
||||
>
|
||||
<MenuHeaderButton {...props} />
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import { NavMoreButtonUpIcon } from '../../elements/icons/NavMoreButtonUp'
|
|||
import { ShortcutFolderClosed } from '../../elements/icons/ShortcutFolderClosed'
|
||||
import { TrashSectionIcon } from '../../elements/icons/TrashSectionIcon'
|
||||
import { ShortcutFolderOpen } from '../../elements/icons/ShortcutFolderOpen'
|
||||
import useResizeObserver from 'use-resize-observer'
|
||||
|
||||
export const LIBRARY_LEFT_MENU_WIDTH = '275px'
|
||||
|
||||
|
|
@ -356,6 +357,8 @@ const Shortcuts = (props: NavigationMenuProps): JSX.Element => {
|
|||
</HStack>
|
||||
<Box
|
||||
css={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
'[role="treeitem"]': {
|
||||
outline: 'none',
|
||||
},
|
||||
|
|
@ -456,6 +459,7 @@ const cachedShortcutsData = (): Shortcut[] | undefined => {
|
|||
|
||||
const ShortcutsTree = (props: ShortcutsTreeProps): JSX.Element => {
|
||||
const router = useRouter()
|
||||
const { ref, width, height } = useResizeObserver()
|
||||
|
||||
const { isValidating, data } = useSWR('/api/shortcuts', getShortcuts, {
|
||||
fallbackData: cachedShortcutsData(),
|
||||
|
|
@ -562,7 +566,7 @@ const ShortcutsTree = (props: ShortcutsTreeProps): JSX.Element => {
|
|||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={ref}>
|
||||
{!isValidating && (
|
||||
<Tree
|
||||
ref={props.treeRef}
|
||||
|
|
@ -575,12 +579,13 @@ const ShortcutsTree = (props: ShortcutsTreeProps): JSX.Element => {
|
|||
onActivate={onActivate}
|
||||
rowHeight={36}
|
||||
initialOpenState={folderOpenState}
|
||||
width={275}
|
||||
width={width}
|
||||
height={640}
|
||||
>
|
||||
{NodeRenderer}
|
||||
</Tree>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
|||
readerTableHeader: '#FFFFFF',
|
||||
readerMargin: 'white',
|
||||
readerTextSubtle: '#898989',
|
||||
readerHoverBg: '#1E1E1E',
|
||||
|
||||
// Avatar Fallback color
|
||||
avatarBg: '#FFEA9F',
|
||||
|
|
@ -236,6 +235,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
|||
homeTextBody: '#3D3D3D',
|
||||
homeTextSubtle: '#898989',
|
||||
homeActionIcons: '#898989',
|
||||
homeActionHoverBg: '#DFDFDF',
|
||||
homeDivider: '#D9D9D9',
|
||||
|
||||
thLibraryAISummaryBorder: '#6A6968',
|
||||
|
|
@ -306,7 +306,6 @@ const darkThemeSpec = {
|
|||
readerFontHighContrast: 'white',
|
||||
readerTableHeader: '#FFFFFF',
|
||||
readerMargin: '#2A2A2A',
|
||||
readerHoverBg: '#1E1E1E',
|
||||
readerTextSubtle: '#EDEDED',
|
||||
|
||||
avatarBg: '#7B5C3E',
|
||||
|
|
@ -390,6 +389,7 @@ const darkThemeSpec = {
|
|||
homeTextBody: '#D9D9D9',
|
||||
homeTextSubtle: '#898989',
|
||||
homeActionIcons: '#898989',
|
||||
homeActionHoverBg: '#515151',
|
||||
homeDivider: '#3D3D3D',
|
||||
|
||||
thLibraryAISummaryBorder: '#6A6968',
|
||||
|
|
@ -427,6 +427,7 @@ const apolloThemeSpec = {
|
|||
|
||||
homeCardHover: '#525252',
|
||||
homeDivider: '#6A6968',
|
||||
homeActionHoverBg: '#515151',
|
||||
|
||||
thBackground: '#474747',
|
||||
thBackground2: '#515151',
|
||||
|
|
@ -456,6 +457,7 @@ const sepiaThemeSpec = {
|
|||
|
||||
homeCardHover: '#EEE8D5',
|
||||
homeDivider: '#DDD6C1',
|
||||
homeActionHoverBg: '#DDD6C1',
|
||||
|
||||
thLibraryMultiselectHover: '#EEE8D5',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "^0.32.6",
|
||||
"swr": "^2.2.5",
|
||||
"use-resize-observer": "^9.1.0",
|
||||
"uuid": "^8.3.2",
|
||||
"yet-another-react-lightbox": "^3.12.0"
|
||||
},
|
||||
|
|
@ -108,4 +109,4 @@
|
|||
"volta": {
|
||||
"extends": "../../package.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31183,7 +31183,7 @@ use-latest@^1.0.0:
|
|||
dependencies:
|
||||
use-isomorphic-layout-effect "^1.0.0"
|
||||
|
||||
use-resize-observer@^9.0.0:
|
||||
use-resize-observer@^9.0.0, use-resize-observer@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/use-resize-observer/-/use-resize-observer-9.1.0.tgz#14735235cf3268569c1ea468f8a90c5789fc5c6c"
|
||||
integrity sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==
|
||||
|
|
|
|||
Loading…
Reference in a new issue