mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
First pass at the just read home web ux
This commit is contained in:
parent
b4ff550df7
commit
5c4f8fc916
8 changed files with 731 additions and 0 deletions
|
|
@ -0,0 +1,33 @@
|
|||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from '../IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class AddToLibraryActionIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const strokeColor = (this.props.color || '#D9D9D9').toString()
|
||||
const backgroundColor = (this.props.color || '#3D3D3D').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="50"
|
||||
height="32"
|
||||
viewBox="0 0 50 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="50" height="32" rx="4" fill={backgroundColor} />
|
||||
<g>
|
||||
<path
|
||||
d="M30 11.8333V23.5L25 20.1667L20 23.5V11.8333C20 10.9493 20.3512 10.1014 20.9763 9.47631C21.6014 8.85119 22.4493 8.5 23.3333 8.5H26.6667C27.5507 8.5 28.3986 8.85119 29.0237 9.47631C29.6488 10.1014 30 10.9493 30 11.8333Z"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from '../IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class ArchiveActionIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const strokeColor = (this.props.color || '#D9D9D9').toString()
|
||||
const backgroundColor = (this.props.color || '#3D3D3D').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="50"
|
||||
height="32"
|
||||
viewBox="0 0 50 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="50" height="32" rx="4" fill={backgroundColor} />
|
||||
<g>
|
||||
<path
|
||||
d="M17.5 11.0002C17.5 10.5581 17.6756 10.1342 17.9882 9.82165C18.3007 9.50909 18.7246 9.3335 19.1667 9.3335H30.8333C31.2754 9.3335 31.6993 9.50909 32.0118 9.82165C32.3244 10.1342 32.5 10.5581 32.5 11.0002C32.5 11.4422 32.3244 11.8661 32.0118 12.1787C31.6993 12.4912 31.2754 12.6668 30.8333 12.6668H19.1667C18.7246 12.6668 18.3007 12.4912 17.9882 12.1787C17.6756 11.8661 17.5 11.4422 17.5 11.0002Z"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M19.1666 12.6665V20.9998C19.1666 21.4419 19.3422 21.8658 19.6548 22.1783C19.9673 22.4909 20.3913 22.6665 20.8333 22.6665H29.1666C29.6087 22.6665 30.0326 22.4909 30.3451 22.1783C30.6577 21.8658 30.8333 21.4419 30.8333 20.9998V12.6665"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M23.3334 16H26.6667"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from '../IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class CommentActionIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const strokeColor = (this.props.color || '#D9D9D9').toString()
|
||||
const backgroundColor = (this.props.color || '#3D3D3D').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="50"
|
||||
height="32"
|
||||
viewBox="0 0 50 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="50" height="32" rx="4" fill={backgroundColor} />
|
||||
<g>
|
||||
<path
|
||||
d="M17.5 22.6668L18.5833 19.4168C17.647 18.032 17.3083 16.3922 17.6302 14.8021C17.952 13.212 18.9126 11.7799 20.3331 10.772C21.7537 9.76421 23.5377 9.24923 25.3534 9.32285C27.1691 9.39647 28.8929 10.0537 30.2044 11.1723C31.5159 12.2909 32.3257 13.7948 32.4834 15.4043C32.6411 17.0138 32.1358 18.6195 31.0616 19.9228C29.9873 21.226 28.4172 22.1382 26.6432 22.4896C24.8691 22.8411 23.0118 22.6079 21.4167 21.8335L17.5 22.6668Z"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from '../IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class RemoveActionIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const strokeColor = (this.props.color || '#D9D9D9').toString()
|
||||
const backgroundColor = (this.props.color || '#3D3D3D').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="50"
|
||||
height="32"
|
||||
viewBox="0 0 50 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="50" height="32" rx="4" fill={backgroundColor} />
|
||||
<g>
|
||||
<path
|
||||
d="M18.3334 11.8335H31.6667"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M23.3334 15.1665V20.1665"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M26.6666 15.1665V20.1665"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M19.1666 11.8335L20 21.8335C20 22.2755 20.1756 22.6994 20.4881 23.012C20.8007 23.3246 21.2246 23.5002 21.6666 23.5002H28.3333C28.7753 23.5002 29.1992 23.3246 29.5118 23.012C29.8244 22.6994 30 22.2755 30 21.8335L30.8333 11.8335"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M22.5 11.8333V9.33333C22.5 9.11232 22.5878 8.90036 22.7441 8.74408C22.9004 8.5878 23.1123 8.5 23.3333 8.5H26.6667C26.8877 8.5 27.0996 8.5878 27.2559 8.74408C27.4122 8.90036 27.5 9.11232 27.5 9.33333V11.8333"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from '../IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class ShareActionIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const strokeColor = (this.props.color || '#D9D9D9').toString()
|
||||
const backgroundColor = (this.props.color || '#3D3D3D').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="49"
|
||||
height="32"
|
||||
viewBox="0 0 49 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect y="0.5" width="49" height="31" rx="4" fill={backgroundColor} />
|
||||
<g>
|
||||
<path
|
||||
d="M25.2917 9.6665V12.8332C20.0865 13.647 18.1509 18.207 17.375 22.3332C17.3457 22.4963 21.6374 17.6133 25.2917 17.5832V20.7498L31.625 15.2082L25.2917 9.6665Z"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +156,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
|||
readerFontHighContrast: 'black',
|
||||
readerTableHeader: '#FFFFFF',
|
||||
readerMargin: 'white',
|
||||
readerTextSubtle: '#898989',
|
||||
|
||||
// Avatar Fallback color
|
||||
avatarBg: '#FFEA9F',
|
||||
|
|
@ -216,6 +217,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
|||
thTextSubtle: '#1E1E1E',
|
||||
thTextSubtle2: '#6A6968',
|
||||
thTextSubtle3: '#ADADAD',
|
||||
thTextSubtle4: '#EDEDED',
|
||||
|
||||
thBorderColor: '#E1E1E1',
|
||||
thBorderSubtle: '#EEEEEE',
|
||||
|
|
|
|||
244
packages/web/lib/networking/queries/useGetHome.tsx
Normal file
244
packages/web/lib/networking/queries/useGetHome.tsx
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import useSWR from 'swr'
|
||||
import { gqlFetcher, makeGqlFetcher, publicGqlFetcher } from '../networkHelpers'
|
||||
|
||||
type HomeResult = {
|
||||
home: {
|
||||
edges?: HomeEdge[]
|
||||
errorCodes?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export type HomeItemResponse = {
|
||||
error: boolean
|
||||
isValidating: boolean
|
||||
errorMessage?: string
|
||||
sections?: HomeSection[]
|
||||
}
|
||||
|
||||
export type HomeItem = {
|
||||
id: string
|
||||
date: string
|
||||
title: string
|
||||
url: string
|
||||
source: HomeItemSource
|
||||
|
||||
previewContent?: string
|
||||
wordCount?: number
|
||||
|
||||
author?: string
|
||||
broadcastCount?: number
|
||||
canArchive?: boolean
|
||||
canComment?: boolean
|
||||
canDelete?: boolean
|
||||
canSave?: boolean
|
||||
canShare?: boolean
|
||||
dir?: string
|
||||
|
||||
likeCount?: number
|
||||
saveCount?: number
|
||||
seen_at?: string
|
||||
thumbnail?: string
|
||||
}
|
||||
|
||||
type HomeItemSourceType = 'LIBRARY' | 'NEWSLETTER' | 'RECOMMENDATION' | 'RSS'
|
||||
|
||||
export type HomeItemSource = {
|
||||
name: string
|
||||
type: HomeItemSourceType
|
||||
id?: string
|
||||
icon?: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
export type HomeSection = {
|
||||
title: string
|
||||
layout: string
|
||||
items: HomeItem[]
|
||||
thumbnail?: string
|
||||
}
|
||||
|
||||
type HomeEdge = {
|
||||
cursor: string
|
||||
node: HomeSection
|
||||
}
|
||||
|
||||
export function useGetHomeItems(): HomeItemResponse {
|
||||
const query = gql`
|
||||
query GetHomeItems($after: String, $first: Int) {
|
||||
home(first: $first, after: $after) {
|
||||
... on HomeSuccess {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
title
|
||||
layout
|
||||
thumbnail
|
||||
items {
|
||||
id
|
||||
title
|
||||
url
|
||||
thumbnail
|
||||
previewContent
|
||||
saveCount
|
||||
likeCount
|
||||
broadcastCount
|
||||
date
|
||||
author
|
||||
dir
|
||||
seen_at
|
||||
wordCount
|
||||
source {
|
||||
id
|
||||
name
|
||||
url
|
||||
icon
|
||||
type
|
||||
}
|
||||
canSave
|
||||
canComment
|
||||
canShare
|
||||
canArchive
|
||||
canDelete
|
||||
}
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
... on HomeError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const variables = {
|
||||
first: 3,
|
||||
after: null,
|
||||
}
|
||||
|
||||
const { data, error, isValidating } = useSWR(
|
||||
[query, variables.first, variables.after],
|
||||
makeGqlFetcher(variables)
|
||||
)
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
error: true,
|
||||
isValidating,
|
||||
errorMessage: error.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
const result = data as HomeResult
|
||||
console.log('result: ', result)
|
||||
|
||||
if (result && result.home.errorCodes) {
|
||||
const errorCodes = result.home.errorCodes
|
||||
return {
|
||||
error: true,
|
||||
isValidating,
|
||||
errorMessage: errorCodes.length > 0 ? errorCodes[0] : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
if (result && result.home && result.home.edges) {
|
||||
return {
|
||||
error: false,
|
||||
isValidating,
|
||||
sections: result.home.edges.map((edge) => {
|
||||
return edge.node
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValidating,
|
||||
error: !!error,
|
||||
}
|
||||
}
|
||||
|
||||
// Fake HomeSubscription data
|
||||
const source1: HomeItemSource = {
|
||||
id: 'sub1',
|
||||
name: 'Tech News',
|
||||
type: 'NEWSLETTER',
|
||||
icon: 'https://example.com/icon1.png',
|
||||
url: 'https://example.com/tech-news',
|
||||
}
|
||||
|
||||
const source2: HomeItemSource = {
|
||||
id: 'sub2',
|
||||
name: 'Daily Updates',
|
||||
type: 'RSS',
|
||||
icon: 'https://example.com/icon2.png',
|
||||
url: 'https://example.com/daily-updates',
|
||||
}
|
||||
|
||||
// Fake HomeItem data
|
||||
export const stubHomeItems: HomeItem[] = [
|
||||
{
|
||||
id: 'item1',
|
||||
date: '2024-05-01',
|
||||
title: 'Breaking Tech News',
|
||||
url: 'https://example.com/article1',
|
||||
source: source1,
|
||||
previewContent: 'A brief summary of the tech news...',
|
||||
wordCount: 500,
|
||||
author: 'John Doe',
|
||||
broadcastCount: 10,
|
||||
canArchive: true,
|
||||
canComment: true,
|
||||
canDelete: true,
|
||||
canSave: true,
|
||||
canShare: true,
|
||||
dir: 'ltr',
|
||||
likeCount: 100,
|
||||
saveCount: 50,
|
||||
seen_at: '2024-05-02',
|
||||
thumbnail: 'https://example.com/thumbnail1.png',
|
||||
},
|
||||
{
|
||||
id: 'item2',
|
||||
date: '2024-05-03',
|
||||
title: 'Daily Updates: May 3rd',
|
||||
url: 'https://example.com/article2',
|
||||
source: source2,
|
||||
previewContent: 'A brief summary of the daily updates...',
|
||||
wordCount: 300,
|
||||
author: 'Jane Smith',
|
||||
broadcastCount: 5,
|
||||
canArchive: false,
|
||||
canComment: true,
|
||||
canDelete: false,
|
||||
canSave: true,
|
||||
canShare: false,
|
||||
dir: 'ltr',
|
||||
likeCount: 75,
|
||||
saveCount: 30,
|
||||
seen_at: '2024-05-04',
|
||||
thumbnail: 'https://example.com/thumbnail2.png',
|
||||
},
|
||||
{
|
||||
id: 'item3',
|
||||
date: '2024-05-05',
|
||||
title: 'In-Depth Analysis',
|
||||
url: 'https://example.com/article3',
|
||||
source: source1,
|
||||
wordCount: 1500,
|
||||
canArchive: true,
|
||||
canComment: true,
|
||||
canDelete: true,
|
||||
canSave: true,
|
||||
canShare: true,
|
||||
likeCount: 200,
|
||||
saveCount: 120,
|
||||
seen_at: '2024-05-06',
|
||||
},
|
||||
]
|
||||
278
packages/web/pages/justread/index.tsx
Normal file
278
packages/web/pages/justread/index.tsx
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
import { styled } from '@stitches/react'
|
||||
import { AddToLibraryActionIcon } from '../../components/elements/icons/home/AddToLibraryActionIcon'
|
||||
import { ArchiveActionIcon } from '../../components/elements/icons/home/ArchiveActionIcon'
|
||||
import { CommentActionIcon } from '../../components/elements/icons/home/CommentActionIcon'
|
||||
import { RemoveActionIcon } from '../../components/elements/icons/home/RemoveActionIcon'
|
||||
import { ShareActionIcon } from '../../components/elements/icons/home/ShareActionIcon'
|
||||
import { useApplyLocalTheme } from '../../lib/hooks/useApplyLocalTheme'
|
||||
import {
|
||||
HStack,
|
||||
SpanBox,
|
||||
VStack,
|
||||
} from './../../components/elements/LayoutPrimitives'
|
||||
|
||||
import * as HoverCard from '@radix-ui/react-hover-card'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import {
|
||||
HomeItem,
|
||||
HomeSection,
|
||||
stubHomeItems,
|
||||
useGetHomeItems,
|
||||
} from '../../lib/networking/queries/useGetHome'
|
||||
import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles'
|
||||
import { theme } from '../../components/tokens/stitches.config'
|
||||
|
||||
export default function Home(): JSX.Element {
|
||||
const homeData = useGetHomeItems()
|
||||
console.log('home sections: ', homeData.sections)
|
||||
useApplyLocalTheme()
|
||||
|
||||
return (
|
||||
<VStack
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{
|
||||
width: '100%',
|
||||
bg: '$readerBg',
|
||||
pt: '45px',
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<VStack
|
||||
distribution="start"
|
||||
css={{
|
||||
width: '646px',
|
||||
gap: '40px',
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
{homeData.sections?.map((homeSection, idx) => {
|
||||
switch (homeSection.layout) {
|
||||
case 'long':
|
||||
return (
|
||||
<LongHomeSection
|
||||
key={`section-${idx}`}
|
||||
homeSection={homeSection}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</VStack>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
||||
type HomeSectionProps = {
|
||||
homeSection: HomeSection
|
||||
}
|
||||
|
||||
const LongHomeSection = (props: HomeSectionProps): JSX.Element => {
|
||||
return (
|
||||
<SpanBox css={{ width: '100%' }}>
|
||||
{props.homeSection.items.map((homeItem) => {
|
||||
return <HomeItemView key={homeItem.id} homeItem={homeItem} />
|
||||
})}
|
||||
</SpanBox>
|
||||
)
|
||||
}
|
||||
|
||||
const CoverImage = styled('img', {
|
||||
objectFit: 'cover',
|
||||
})
|
||||
|
||||
type HomeItemViewProps = {
|
||||
homeItem: HomeItem
|
||||
}
|
||||
|
||||
const TimeAgo = (props: HomeItemViewProps): JSX.Element => {
|
||||
return (
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{
|
||||
fontSize: '12px',
|
||||
fontWeight: 'normal',
|
||||
fontFamily: '$inter',
|
||||
color: '$readerContrast',
|
||||
}}
|
||||
>
|
||||
{timeAgo(props.homeItem.date)}
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
||||
const Title = (props: HomeItemViewProps): JSX.Element => {
|
||||
return (
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{
|
||||
fontSize: '16px',
|
||||
lineHeight: '20px',
|
||||
fontWeight: '600',
|
||||
fontFamily: '$inter',
|
||||
color: '$readerText',
|
||||
}}
|
||||
>
|
||||
{props.homeItem.title}
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
||||
const HomeItemView = (props: HomeItemViewProps): JSX.Element => {
|
||||
return (
|
||||
<VStack
|
||||
css={{
|
||||
width: '100%',
|
||||
padding: '20px',
|
||||
borderRadius: '5px',
|
||||
'&:hover': {
|
||||
bg: '$thBackground',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<HStack css={{ width: '100%', gap: '5px' }}>
|
||||
<VStack css={{ gap: '15px' }}>
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{ gap: '5px', lineHeight: '1' }}
|
||||
>
|
||||
<SourceInfo homeItem={props.homeItem} />
|
||||
<TimeAgo homeItem={props.homeItem} />
|
||||
</HStack>
|
||||
<Title homeItem={props.homeItem} />
|
||||
</VStack>
|
||||
<SpanBox css={{ ml: 'auto' }}>
|
||||
{props.homeItem.thumbnail && (
|
||||
<CoverImage
|
||||
css={{
|
||||
mt: '6px',
|
||||
width: '120px',
|
||||
height: '70px',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
src={props.homeItem.thumbnail}
|
||||
></CoverImage>
|
||||
)}
|
||||
</SpanBox>
|
||||
</HStack>
|
||||
<SpanBox
|
||||
css={{ fontFamily: '$inter', fontSize: '13px', lineHeight: '23px' }}
|
||||
>
|
||||
{props.homeItem.previewContent}
|
||||
</SpanBox>
|
||||
<HStack css={{ gap: '6px', mt: '20px' }}>
|
||||
<Button style="ghost">
|
||||
<AddToLibraryActionIcon />
|
||||
</Button>
|
||||
<Button style="ghost">
|
||||
<CommentActionIcon />
|
||||
</Button>
|
||||
<Button style="ghost">
|
||||
<ShareActionIcon />
|
||||
</Button>
|
||||
<Button style="ghost">
|
||||
<ArchiveActionIcon />
|
||||
</Button>
|
||||
<Button style="ghost">
|
||||
<RemoveActionIcon />
|
||||
</Button>
|
||||
</HStack>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
||||
const SiteIconSmall = styled('img', {
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '100px',
|
||||
})
|
||||
|
||||
const SiteIconLarge = styled('img', {
|
||||
width: '25px',
|
||||
height: '25px',
|
||||
borderRadius: '100px',
|
||||
})
|
||||
|
||||
const SourceInfo = (props: HomeItemViewProps) => (
|
||||
<HoverCard.Root>
|
||||
<HoverCard.Trigger asChild>
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{ gap: '5px', cursor: 'pointer' }}
|
||||
>
|
||||
<SiteIconSmall
|
||||
className="Image normal"
|
||||
src="https://pbs.twimg.com/profile_images/1337055608613253126/r_eiMp2H_400x400.png"
|
||||
alt={props.homeItem.source.name}
|
||||
/>
|
||||
<HStack
|
||||
css={{
|
||||
lineHeight: '1',
|
||||
pb: '3px',
|
||||
fontFamily: '$inter',
|
||||
fontWeight: '500',
|
||||
fontSize: '13px',
|
||||
color: '$readerFont',
|
||||
textDecoration: 'underline',
|
||||
}}
|
||||
>
|
||||
{props.homeItem.source.name}
|
||||
</HStack>
|
||||
</HStack>
|
||||
</HoverCard.Trigger>
|
||||
<HoverCard.Portal>
|
||||
<HoverCard.Content sideOffset={5}>
|
||||
<VStack
|
||||
alignment="start"
|
||||
distribution="start"
|
||||
css={{
|
||||
width: '380px',
|
||||
height: '200px',
|
||||
bg: '$thBackground2',
|
||||
borderRadius: '10px',
|
||||
padding: '15px',
|
||||
gap: '10px',
|
||||
boxShadow: theme.shadows.cardBoxShadow.toString(),
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
distribution="start"
|
||||
alignment="center"
|
||||
css={{ width: '100%', gap: '10px' }}
|
||||
>
|
||||
<SiteIconLarge
|
||||
className="Image normal"
|
||||
src="https://pbs.twimg.com/profile_images/1337055608613253126/r_eiMp2H_400x400.png"
|
||||
alt={props.homeItem.source.name}
|
||||
/>
|
||||
<SpanBox
|
||||
css={{
|
||||
fontFamily: '$inter',
|
||||
fontWeight: '500',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
{props.homeItem.source.name}
|
||||
</SpanBox>
|
||||
<SpanBox css={{ ml: 'auto' }}>
|
||||
<Button style="ctaBlue">+ Follow</Button>
|
||||
</SpanBox>
|
||||
</HStack>
|
||||
<SpanBox
|
||||
css={{
|
||||
fontFamily: '$inter',
|
||||
fontSize: '13px',
|
||||
color: '$thTextSubtle4',
|
||||
}}
|
||||
>
|
||||
The description of the newsletter or RSS feed, this would lazy load.
|
||||
</SpanBox>
|
||||
</VStack>
|
||||
<HoverCard.Arrow fill={theme.colors.thBackground2.toString()} />
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Portal>
|
||||
</HoverCard.Root>
|
||||
)
|
||||
Loading…
Reference in a new issue