From 5c4f8fc9160418128ccd092c80e14952cd6b21d5 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 28 May 2024 17:24:34 +0800 Subject: [PATCH 01/15] First pass at the just read home web ux --- .../icons/home/AddToLibraryActionIcon.tsx | 33 +++ .../elements/icons/home/ArchiveActionIcon.tsx | 47 +++ .../elements/icons/home/CommentActionIcon.tsx | 33 +++ .../elements/icons/home/RemoveActionIcon.tsx | 61 ++++ .../elements/icons/home/ShareActionIcon.tsx | 33 +++ .../web/components/tokens/stitches.config.ts | 2 + .../web/lib/networking/queries/useGetHome.tsx | 244 +++++++++++++++ packages/web/pages/justread/index.tsx | 278 ++++++++++++++++++ 8 files changed, 731 insertions(+) create mode 100644 packages/web/components/elements/icons/home/AddToLibraryActionIcon.tsx create mode 100644 packages/web/components/elements/icons/home/ArchiveActionIcon.tsx create mode 100644 packages/web/components/elements/icons/home/CommentActionIcon.tsx create mode 100644 packages/web/components/elements/icons/home/RemoveActionIcon.tsx create mode 100644 packages/web/components/elements/icons/home/ShareActionIcon.tsx create mode 100644 packages/web/lib/networking/queries/useGetHome.tsx create mode 100644 packages/web/pages/justread/index.tsx diff --git a/packages/web/components/elements/icons/home/AddToLibraryActionIcon.tsx b/packages/web/components/elements/icons/home/AddToLibraryActionIcon.tsx new file mode 100644 index 000000000..e42a1bb8a --- /dev/null +++ b/packages/web/components/elements/icons/home/AddToLibraryActionIcon.tsx @@ -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 { + render() { + const strokeColor = (this.props.color || '#D9D9D9').toString() + const backgroundColor = (this.props.color || '#3D3D3D').toString() + + return ( + + + + + + + ) + } +} diff --git a/packages/web/components/elements/icons/home/ArchiveActionIcon.tsx b/packages/web/components/elements/icons/home/ArchiveActionIcon.tsx new file mode 100644 index 000000000..d97f49e5d --- /dev/null +++ b/packages/web/components/elements/icons/home/ArchiveActionIcon.tsx @@ -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 { + render() { + const strokeColor = (this.props.color || '#D9D9D9').toString() + const backgroundColor = (this.props.color || '#3D3D3D').toString() + + return ( + + + + + + + + + ) + } +} diff --git a/packages/web/components/elements/icons/home/CommentActionIcon.tsx b/packages/web/components/elements/icons/home/CommentActionIcon.tsx new file mode 100644 index 000000000..d0d704821 --- /dev/null +++ b/packages/web/components/elements/icons/home/CommentActionIcon.tsx @@ -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 { + render() { + const strokeColor = (this.props.color || '#D9D9D9').toString() + const backgroundColor = (this.props.color || '#3D3D3D').toString() + + return ( + + + + + + + ) + } +} diff --git a/packages/web/components/elements/icons/home/RemoveActionIcon.tsx b/packages/web/components/elements/icons/home/RemoveActionIcon.tsx new file mode 100644 index 000000000..ab55125b1 --- /dev/null +++ b/packages/web/components/elements/icons/home/RemoveActionIcon.tsx @@ -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 { + render() { + const strokeColor = (this.props.color || '#D9D9D9').toString() + const backgroundColor = (this.props.color || '#3D3D3D').toString() + + return ( + + + + + + + + + + + ) + } +} diff --git a/packages/web/components/elements/icons/home/ShareActionIcon.tsx b/packages/web/components/elements/icons/home/ShareActionIcon.tsx new file mode 100644 index 000000000..75648581d --- /dev/null +++ b/packages/web/components/elements/icons/home/ShareActionIcon.tsx @@ -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 { + render() { + const strokeColor = (this.props.color || '#D9D9D9').toString() + const backgroundColor = (this.props.color || '#3D3D3D').toString() + + return ( + + + + + + + ) + } +} diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index db11183dc..5a1908fbf 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -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', diff --git a/packages/web/lib/networking/queries/useGetHome.tsx b/packages/web/lib/networking/queries/useGetHome.tsx new file mode 100644 index 000000000..6d6b14dca --- /dev/null +++ b/packages/web/lib/networking/queries/useGetHome.tsx @@ -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', + }, +] diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx new file mode 100644 index 000000000..4d32c16d7 --- /dev/null +++ b/packages/web/pages/justread/index.tsx @@ -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 ( + + + {homeData.sections?.map((homeSection, idx) => { + switch (homeSection.layout) { + case 'long': + return ( + + ) + } + })} + + + ) +} + +type HomeSectionProps = { + homeSection: HomeSection +} + +const LongHomeSection = (props: HomeSectionProps): JSX.Element => { + return ( + + {props.homeSection.items.map((homeItem) => { + return + })} + + ) +} + +const CoverImage = styled('img', { + objectFit: 'cover', +}) + +type HomeItemViewProps = { + homeItem: HomeItem +} + +const TimeAgo = (props: HomeItemViewProps): JSX.Element => { + return ( + + {timeAgo(props.homeItem.date)} + + ) +} + +const Title = (props: HomeItemViewProps): JSX.Element => { + return ( + + {props.homeItem.title} + + ) +} + +const HomeItemView = (props: HomeItemViewProps): JSX.Element => { + return ( + + + + + + + + + </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> +) From 4fc57dd6f14d83602b065bd96005933b92cf72b4 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 17:31:49 +0800 Subject: [PATCH 02/15] Add hover cards --- packages/web/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/package.json b/packages/web/package.json index a067e1b51..2afe9b389 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -22,6 +22,7 @@ "@radix-ui/react-checkbox": "^0.1.5", "@radix-ui/react-dialog": "1.0.5", "@radix-ui/react-dropdown-menu": "2.0.6", + "@radix-ui/react-hover-card": "^1.0.7", "@radix-ui/react-id": "^0.1.1", "@radix-ui/react-popover": "1.0.7", "@radix-ui/react-progress": "^1.0.1", From e6b804ef35ff8475620e30b7337528fa3ba7df79 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 18:18:29 +0800 Subject: [PATCH 03/15] Increase number of home items --- packages/web/lib/networking/queries/useGetHome.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/lib/networking/queries/useGetHome.tsx b/packages/web/lib/networking/queries/useGetHome.tsx index 6d6b14dca..6e21fdcc4 100644 --- a/packages/web/lib/networking/queries/useGetHome.tsx +++ b/packages/web/lib/networking/queries/useGetHome.tsx @@ -119,7 +119,7 @@ export function useGetHomeItems(): HomeItemResponse { ` const variables = { - first: 3, + first: 20, after: null, } From e512fcd9f35324bc5ab3ea3f3b957fe047a80b61 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 20:00:43 +0800 Subject: [PATCH 04/15] Improve mobile layout --- packages/web/pages/justread/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index 4d32c16d7..a295874ac 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -44,6 +44,9 @@ export default function Home(): JSX.Element { width: '646px', gap: '40px', minHeight: '100vh', + '@mdDown': { + width: '100%', + }, }} > {homeData.sections?.map((homeSection, idx) => { From fdf25c3ed28a8d76c5d60aaf24fd200959a56311 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 21:15:43 +0800 Subject: [PATCH 05/15] Better site icon handling, start to separate out subscription hover cards --- .../web/lib/networking/queries/useGetHome.tsx | 6 +- packages/web/pages/justread/index.tsx | 148 ++++++++++++------ 2 files changed, 102 insertions(+), 52 deletions(-) diff --git a/packages/web/lib/networking/queries/useGetHome.tsx b/packages/web/lib/networking/queries/useGetHome.tsx index 6e21fdcc4..495ea73da 100644 --- a/packages/web/lib/networking/queries/useGetHome.tsx +++ b/packages/web/lib/networking/queries/useGetHome.tsx @@ -41,7 +41,11 @@ export type HomeItem = { thumbnail?: string } -type HomeItemSourceType = 'LIBRARY' | 'NEWSLETTER' | 'RECOMMENDATION' | 'RSS' +export type HomeItemSourceType = + | 'LIBRARY' + | 'NEWSLETTER' + | 'RECOMMENDATION' + | 'RSS' export type HomeItemSource = { name: string diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index a295874ac..e58613a33 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -15,12 +15,17 @@ import * as HoverCard from '@radix-ui/react-hover-card' import { Button } from '../../components/elements/Button' import { HomeItem, + HomeItemSource, + HomeItemSourceType, HomeSection, stubHomeItems, useGetHomeItems, } from '../../lib/networking/queries/useGetHome' import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles' import { theme } from '../../components/tokens/stitches.config' +import { useRouter } from 'next/router' +import { useGetSubscriptionsQuery } from '../../lib/networking/queries/useGetSubscriptionsQuery' +import { useMemo } from 'react' export default function Home(): JSX.Element { const homeData = useGetHomeItems() @@ -123,6 +128,8 @@ const Title = (props: HomeItemViewProps): JSX.Element => { } const HomeItemView = (props: HomeItemViewProps): JSX.Element => { + const router = useRouter() + return ( <VStack css={{ @@ -133,6 +140,13 @@ const HomeItemView = (props: HomeItemViewProps): JSX.Element => { bg: '$thBackground', }, }} + onClick={(event) => { + if (event.metaKey || event.ctrlKey) { + window.open(props.homeItem.url, '_blank') + } else { + router.push(props.homeItem.url) + } + }} > <HStack css={{ width: '100%', gap: '5px' }}> <VStack css={{ gap: '15px' }}> @@ -206,11 +220,12 @@ const SourceInfo = (props: HomeItemViewProps) => ( 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} - /> + {props.homeItem.source.icon && ( + <SiteIconSmall + src={props.homeItem.source.icon} + alt={props.homeItem.source.name} + /> + )} <HStack css={{ lineHeight: '1', @@ -228,54 +243,85 @@ const SourceInfo = (props: HomeItemViewProps) => ( </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> + <SubscriptionSourceHoverContent source={props.homeItem.source} /> <HoverCard.Arrow fill={theme.colors.thBackground2.toString()} /> </HoverCard.Content> </HoverCard.Portal> </HoverCard.Root> ) + +type SourceHoverContentProps = { + source: HomeItemSource +} + +const SubscriptionSourceHoverContent = ( + props: SourceHoverContentProps +): JSX.Element => { + const mapSourceType = ( + sourceType: HomeItemSourceType + ): SubscriptionType | undefined => { + switch (sourceType) { + case 'RSS': + case 'NEWSLETTER': + return sourceType + default: + return undefined + } + } + const { subscriptions, isValidating } = useGetSubscriptionsQuery( + mapSourceType(props.source.type) + ) + const subscription = useMemo(() => { + if (props.source.id && subscriptions) { + return subscriptions.find((sub) => sub.id == props.source.id) + } + return undefined + }, [subscriptions]) + + return ( + <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' }} + > + {props.source.icon && ( + <SiteIconLarge src={props.source.icon} alt={props.source.name} /> + )} + <SpanBox + css={{ + fontFamily: '$inter', + fontWeight: '500', + fontSize: '14px', + }} + > + {props.source.name} + </SpanBox> + <SpanBox css={{ ml: 'auto' }}> + <Button style="ctaBlue">+ Follow</Button> + </SpanBox> + </HStack> + <SpanBox + css={{ + fontFamily: '$inter', + fontSize: '13px', + color: '$thTextSubtle4', + }} + > + {subscription ? <>{subscription.description}</> : <></>} + </SpanBox> + </VStack> + ) +} From 45234b94761abb9cf6712855e9927cc37a17d907 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 21:22:52 +0800 Subject: [PATCH 06/15] Add unsubscribe button --- packages/web/components/elements/Button.tsx | 15 +++++++++++++++ packages/web/pages/justread/index.tsx | 8 ++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index d682ea3fd..fe3691e0f 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -49,6 +49,21 @@ export const Button = styled('button', { background: `#0056D2`, }, }, + ctaGray: { + borderRadius: '5px', + px: '10px', + py: '8px', + fontSize: '14px', + fontWeight: '500', + cursor: 'pointer', + border: '0px solid $yellow3', + bg: 'gray', + color: 'white', + '&:hover': { + border: '0px solid #0056D2', + background: `#0056D2`, + }, + }, tldr: { gap: '10px', display: 'flex', diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index e58613a33..51b943b22 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -309,8 +309,12 @@ const SubscriptionSourceHoverContent = ( > {props.source.name} </SpanBox> - <SpanBox css={{ ml: 'auto' }}> - <Button style="ctaBlue">+ Follow</Button> + <SpanBox css={{ ml: 'auto', minWidth: '100px' }}> + {subscription && subscription.status == 'ACTIVE' && ( + <Button style="ctaGray" css={{ fontSize: '12px' }}> + + Unsubscribe + </Button> + )} </SpanBox> </HStack> <SpanBox From 3f1e1688a8a72216f1d2783788ceecf0b2a72a81 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 21:29:51 +0800 Subject: [PATCH 07/15] Go edge to edge on mobile --- packages/web/pages/justread/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index 51b943b22..ddd9b31aa 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -138,6 +138,7 @@ const HomeItemView = (props: HomeItemViewProps): JSX.Element => { borderRadius: '5px', '&:hover': { bg: '$thBackground', + borderRadius: '0px', }, }} onClick={(event) => { From 0fa37c301aa8ce26e7af15e31ca055162054cb45 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 21:30:08 +0800 Subject: [PATCH 08/15] Request more home items --- packages/web/lib/networking/queries/useGetHome.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/web/lib/networking/queries/useGetHome.tsx b/packages/web/lib/networking/queries/useGetHome.tsx index 495ea73da..639cd08e5 100644 --- a/packages/web/lib/networking/queries/useGetHome.tsx +++ b/packages/web/lib/networking/queries/useGetHome.tsx @@ -123,7 +123,7 @@ export function useGetHomeItems(): HomeItemResponse { ` const variables = { - first: 20, + first: 100, after: null, } @@ -153,6 +153,7 @@ export function useGetHomeItems(): HomeItemResponse { } if (result && result.home && result.home.edges) { + console.log('data', result.home) return { error: false, isValidating, From 8dc2ac8f9d3c05d2c7b5ea10f997c76a5f8be4eb Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 22:10:06 +0800 Subject: [PATCH 09/15] Add CTA subtle --- packages/web/components/elements/Button.tsx | 2 +- packages/web/pages/justread/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index fe3691e0f..69eb8ac72 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -49,7 +49,7 @@ export const Button = styled('button', { background: `#0056D2`, }, }, - ctaGray: { + ctaSubtle: { borderRadius: '5px', px: '10px', py: '8px', diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index ddd9b31aa..c81abe53b 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -312,7 +312,7 @@ const SubscriptionSourceHoverContent = ( </SpanBox> <SpanBox css={{ ml: 'auto', minWidth: '100px' }}> {subscription && subscription.status == 'ACTIVE' && ( - <Button style="ctaGray" css={{ fontSize: '12px' }}> + <Button style="ctaSubtle" css={{ fontSize: '12px' }}> + Unsubscribe </Button> )} From 86ff1eddaecb24f457cbf77999aa06db20f8e03d Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 22:25:36 +0800 Subject: [PATCH 10/15] Add subscription type import --- packages/web/pages/justread/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index c81abe53b..85e786868 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -24,7 +24,10 @@ import { import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles' import { theme } from '../../components/tokens/stitches.config' import { useRouter } from 'next/router' -import { useGetSubscriptionsQuery } from '../../lib/networking/queries/useGetSubscriptionsQuery' +import { + SubscriptionType, + useGetSubscriptionsQuery, +} from '../../lib/networking/queries/useGetSubscriptionsQuery' import { useMemo } from 'react' export default function Home(): JSX.Element { From e4bc3c72fa223ac647b6c82d554d42488d441d48 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Tue, 28 May 2024 22:31:09 +0800 Subject: [PATCH 11/15] Cast to help typescript --- packages/web/pages/justread/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index 85e786868..563abffb8 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -267,7 +267,7 @@ const SubscriptionSourceHoverContent = ( switch (sourceType) { case 'RSS': case 'NEWSLETTER': - return sourceType + return sourceType as SubscriptionType default: return undefined } From 7466501c95cb4a9370e50e05f813ef52f638fcdf Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Wed, 29 May 2024 16:40:44 +0800 Subject: [PATCH 12/15] Parse the single score from the digest-score service --- packages/api/src/jobs/score_library_item.ts | 2 +- packages/api/src/jobs/update_home.ts | 2 +- packages/api/src/services/score.ts | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/api/src/jobs/score_library_item.ts b/packages/api/src/jobs/score_library_item.ts index 47279ce50..7080d0ea0 100644 --- a/packages/api/src/jobs/score_library_item.ts +++ b/packages/api/src/jobs/score_library_item.ts @@ -65,7 +65,7 @@ export const scoreLibraryItem = async ( }) logger.info('Scores', scores) - const score = scores[libraryItem.id] + const score = scores[libraryItem.id]['score'] if (!score) { logger.error('Failed to score library item', data) throw new Error('Failed to score library item') diff --git a/packages/api/src/jobs/update_home.ts b/packages/api/src/jobs/update_home.ts index cd3afdb40..56a6879fa 100644 --- a/packages/api/src/jobs/update_home.ts +++ b/packages/api/src/jobs/update_home.ts @@ -187,7 +187,7 @@ const rankCandidates = async ( const newScores = await getScores(data) // update scores for candidates candidates.forEach((item) => { - item.score = newScores[item.id] || 0 + item.score = newScores[item.id]['score'] || 0 }) // rank candidates by score in ascending order diff --git a/packages/api/src/services/score.ts b/packages/api/src/services/score.ts index 2aaf1cf9b..981eb6f08 100644 --- a/packages/api/src/services/score.ts +++ b/packages/api/src/services/score.ts @@ -20,7 +20,11 @@ export interface ScoreApiRequestBody { items: Record<string, Feature> // item_id -> feature } -export type ScoreApiResponse = Record<string, number> // item_id -> score +export type ScoreBody = { + score: number +} + +export type ScoreApiResponse = Record<string, ScoreBody> // item_id -> score export const getScores = async ( data: ScoreApiRequestBody From 67b38fe928a0e829b5595250399d04ea9c817c34 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Wed, 29 May 2024 16:41:16 +0800 Subject: [PATCH 13/15] Debug page to see scores --- .../web/lib/networking/queries/useGetHome.tsx | 2 + packages/web/pages/justread/debug.tsx | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 packages/web/pages/justread/debug.tsx diff --git a/packages/web/lib/networking/queries/useGetHome.tsx b/packages/web/lib/networking/queries/useGetHome.tsx index 639cd08e5..86a8e521a 100644 --- a/packages/web/lib/networking/queries/useGetHome.tsx +++ b/packages/web/lib/networking/queries/useGetHome.tsx @@ -21,6 +21,7 @@ export type HomeItem = { date: string title: string url: string + score: number source: HomeItemSource previewContent?: string @@ -82,6 +83,7 @@ export function useGetHomeItems(): HomeItemResponse { id title url + score thumbnail previewContent saveCount diff --git a/packages/web/pages/justread/debug.tsx b/packages/web/pages/justread/debug.tsx new file mode 100644 index 000000000..bba6aae1f --- /dev/null +++ b/packages/web/pages/justread/debug.tsx @@ -0,0 +1,89 @@ +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, + HomeItemSource, + HomeItemSourceType, + HomeSection, + stubHomeItems, + useGetHomeItems, +} from '../../lib/networking/queries/useGetHome' +import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles' +import { theme } from '../../components/tokens/stitches.config' +import { useRouter } from 'next/router' +import { + SubscriptionType, + useGetSubscriptionsQuery, +} from '../../lib/networking/queries/useGetSubscriptionsQuery' +import { useMemo } from 'react' + +export default function DebugHome(): 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', + '@mdDown': { + width: '100%', + }, + }} + > + {homeData.sections?.map((homeSection, idx) => { + return ( + <VStack key={`homeSection-${idx}`} css={{ width: '100%' }}> + <SpanBox>Section {idx}</SpanBox> + <SpanBox>Title: {homeSection.title}</SpanBox> + <SpanBox>Layout: {homeSection.layout}</SpanBox> + <SpanBox>Layout: {homeSection.thumbnail}</SpanBox> + + {homeSection.items.map((homeItem) => { + return ( + <VStack key={homeItem.id}> + <SpanBox> + {' '} + - Title:{' '} + <a href={'/me/${homeItem.slug}'}>{homeItem.title}</a> + </SpanBox> + <SpanBox> - Score: {homeItem.score}</SpanBox> + <SpanBox> - Word count: {homeItem.wordCount}</SpanBox> + <SpanBox> - Date: {homeItem.date}</SpanBox> + <SpanBox> - </SpanBox> + </VStack> + ) + })} + </VStack> + ) + })} + </VStack> + </VStack> + ) +} From c1305ab1e76991d72a847ad7eeb490fbfa85fae2 Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Wed, 29 May 2024 16:51:52 +0800 Subject: [PATCH 14/15] Remove stubs --- .../web/lib/networking/queries/useGetHome.tsx | 79 ------------------- 1 file changed, 79 deletions(-) diff --git a/packages/web/lib/networking/queries/useGetHome.tsx b/packages/web/lib/networking/queries/useGetHome.tsx index 86a8e521a..b9e68f820 100644 --- a/packages/web/lib/networking/queries/useGetHome.tsx +++ b/packages/web/lib/networking/queries/useGetHome.tsx @@ -170,82 +170,3 @@ export function useGetHomeItems(): HomeItemResponse { 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', - }, -] From d4700cc3936abc99ab6a1ec4e7a54d4c2940c90c Mon Sep 17 00:00:00 2001 From: Jackson Harper <jacksonh@gmail.com> Date: Wed, 29 May 2024 16:56:19 +0800 Subject: [PATCH 15/15] Fix stub import --- packages/web/pages/justread/debug.tsx | 1 - packages/web/pages/justread/index.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/web/pages/justread/debug.tsx b/packages/web/pages/justread/debug.tsx index bba6aae1f..2e31bb29a 100644 --- a/packages/web/pages/justread/debug.tsx +++ b/packages/web/pages/justread/debug.tsx @@ -18,7 +18,6 @@ import { HomeItemSource, HomeItemSourceType, HomeSection, - stubHomeItems, useGetHomeItems, } from '../../lib/networking/queries/useGetHome' import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles' diff --git a/packages/web/pages/justread/index.tsx b/packages/web/pages/justread/index.tsx index 563abffb8..62355e78c 100644 --- a/packages/web/pages/justread/index.tsx +++ b/packages/web/pages/justread/index.tsx @@ -18,7 +18,6 @@ import { HomeItemSource, HomeItemSourceType, HomeSection, - stubHomeItems, useGetHomeItems, } from '../../lib/networking/queries/useGetHome' import { timeAgo } from '../../components/patterns/LibraryCards/LibraryCardStyles'