Skip to content

Commit

Permalink
implement link display drawer for mobile and UX
Browse files Browse the repository at this point in the history
  • Loading branch information
rohannair committed Aug 3, 2024
1 parent 1c53f44 commit fad68a7
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 32 deletions.
1 change: 1 addition & 0 deletions apps/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"tailwindcss-animate": "^1.0.7",
"ts-pattern": "^5.2.0",
"turndown": "^7.2.0",
"vaul": "^0.9.1",
"zod": "^3.23.8"
},
"devDependencies": {
Expand Down
11 changes: 2 additions & 9 deletions apps/ui/src/app/(app)/bookmarks/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { formatRelative } from 'date-fns'
import { ArrowBigLeft } from 'lucide-react'
import Link from 'next/link'
import { Markdown } from '@/components/Markdown'
import { LinkDisplay } from '@/components/LinkDisplay/LinkDisplay'

export default async function BookmarkDetails({
params: { id },
Expand Down Expand Up @@ -52,15 +53,7 @@ export default async function BookmarkDetails({
</div>
</section>

<section className="grid grid-cols-12 relative">
<Markdown>{cleaned}</Markdown>

<div className="col-span-4 flex flex-col gap-4 relative px-2 pt-6">
<div className="pr-4">
{summary ? <Summary summary={summary} /> : <Skeleton />}
</div>
</div>
</section>
<LinkDisplay summary={summary} cleaned={cleaned} />
</div>
)
}
42 changes: 42 additions & 0 deletions apps/ui/src/components/LinkDisplay/LinkDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Markdown } from '@/components/Markdown'
import { Skeleton } from '@/components/ui/skeleton'
import { cn } from '@/lib/utils'

interface LinkDisplayProps {
summary: string
cleaned: string
}

export function LinkDisplay({ summary, cleaned }: LinkDisplayProps) {
return (
<section className="flex flex-col relative lg:grid lg:grid-cols-12 ">
<Markdown>{cleaned}</Markdown>

<div className="order-first flex flex-col gap-4 relative lg:order-last lg:col-span-4 lg:px-2 lg:pt-6">
<div className="lg:pr-4">
{summary ? <Summary summary={summary} /> : <Skeleton />}
</div>
</div>
</section>
)
}

const Summary = ({ summary }: { summary: string }) => {
const lines = summary.split('\n\n')
return (
<div className="rounded-sm border border-muted bg-muted/80 text-foreground/80 p-2 text-sm">
<h4 className="text-sm font-semibold mb-1">Summary:</h4>
{lines.map((line: string, index: number) => (
<p
key={line}
className={cn(
'text-sm leading-5 subpixel-antialiased',
index < lines.length - 1 ? 'mb-2' : '',
)}
>
{line}
</p>
))}
</div>
)
}
1 change: 1 addition & 0 deletions apps/ui/src/components/LinkDisplay/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './LinkDisplay'
107 changes: 107 additions & 0 deletions apps/ui/src/components/LinkList/LinkDrawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'use client'

import {
DrawerClose,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
} from '@/components/ui/drawer'
import { Button } from '@/components/ui/button'
import { client } from '@/lib/api/client'
import { useEffect, useState } from 'react'
import { LinkDisplay } from '@/components/LinkDisplay/LinkDisplay'
import { formatRelative } from 'date-fns'
import Link from 'next/link'
import { Expand, X } from 'lucide-react'

interface LinkDrawerProps {
id: string | null
close: () => void
}

type TLink = {
id: string
summary: string
cleaned: string
title: string
url: string
updatedAt: string
}

export function LinkDrawer({ id, close }: LinkDrawerProps) {
if (!id) return null

const [link, setLink] = useState<TLink | null>(null)
useEffect(() => {
async function run() {
if (!id) return

const res = await client.api.v1.links[':id'].$get({ param: { id: id } })
const { link } = (await res.json()) as {
link: TLink
}

setLink(link)
}

run().catch(console.error)
}, [id])

return link ? (
<>
<DrawerHeader>
<DrawerTitle>
<div className="flex lg:flex-row">
<h3 className="text-left">{link?.title}</h3>
<div className="ml-auto flex flex-row gap-2">
<Button size="icon" variant="outline" asChild className="size-7">
<Link href={`/bookmarks/${link.id}`}>
<Expand className="h-4 w-4" />
</Link>
</Button>
<DrawerClose
onClick={() => {
close()
}}
asChild
>
<Button size="icon" variant="outline" className="size-7">
<X className="h-4 w-4" />
</Button>
</DrawerClose>
</div>
</div>
</DrawerTitle>
<DrawerDescription className="text-left">
<a
href={link?.url}
target="_blank"
rel="noreferrer"
className="text-muted-foreground text-xs"
>
{link?.url}
</a>
{link?.updatedAt && (
<p className="text-xs mt-1 text-foreground/50">
Last updated{' '}
{formatRelative(new Date(link?.updatedAt), new Date())}.
</p>
)}
</DrawerDescription>
</DrawerHeader>
<div className="py-4 px-10 max-h-96 overflow-y-scroll">
<LinkDisplay summary={link.summary} cleaned={link.cleaned} />
</div>
<DrawerFooter>
<DrawerClose
onClick={() => {
close()
}}
>
<Button variant="outline">Close</Button>
</DrawerClose>
</DrawerFooter>
</>
) : null
}
114 changes: 114 additions & 0 deletions apps/ui/src/components/LinkList/LinkItems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
'use client'

import { LinkDrawer } from '@/components/LinkList/LinkDrawer'
import { StatusIcon, type LinkStatus } from '@/components/StatusIcon'
import { AspectRatio } from '@/components/ui/aspect-ratio'
import { DrawerContent, DrawerTrigger } from '@/components/ui/drawer'
import { Drawer } from '@/components/ui/drawer'
import { cn } from '@/lib/utils'
import { formatRelative } from 'date-fns'
import Image from 'next/image'
import { useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'

interface LinkItemsProps {
links: {
id: string
href: string
prefetch?: boolean
title: string
url: string
imageUrl?: string
summary: string
status: LinkStatus
tags?: { key: string; label: string }[]
createdAt: string
}[]
}

export function LinkItems({ links }: LinkItemsProps) {
const [selectedLink, setLink] = useState<string | null>(null)
const searchParams = useSearchParams()
const router = useRouter()

const page = searchParams.get('page')
const pageSize = searchParams.get('pageSize')
const sort = searchParams.get('sort')
const direction = searchParams.get('direction')
const search = searchParams.get('search')

return (
<Drawer>
{links.map((link) => {
return (
<DrawerTrigger
key={link.id}
className={cn(
'grid grid-cols-12 w-full text-left gap-5 p-2 hover:shadow-md transition-shadow rounded-lg',
'dark:hover:bg-primary/2- dark:transition-colors',
'ease-in-out duration-75 group',
)}
onClick={() => {
setLink(link.id)
router.replace(
`/bookmarks?${new URLSearchParams({
page: page || '1',
pageSize: pageSize || '10',
sort: sort || 'createdAt',
direction: direction || 'desc',
search: search || '',
selected: link?.id || '',
}).toString()}`,
)
}}
>
<section className="col-span-3 flex rounded-md overflow-hidden shadow-md">
<AspectRatio ratio={16 / 9} className="w-full">
<Image
src={
link.imageUrl ??
'https://placehold.co/250x140/png?text=No+Image'
}
fill
className="object-cover"
alt={link.title ?? link.url}
/>
</AspectRatio>
</section>
<section className="col-span-8">
<div className="flex flex-row gap-1 items-baseline">
<h3 className="font-semibold text-lg truncate">
{link.title ?? link.url}
</h3>
<StatusIcon status={link.status} />
</div>
<div className="text-sm text-muted-foreground">
{link.title ? link.url : null}
</div>
<div className="text-sm text-foreground">
Added {formatRelative(link.createdAt, new Date())}
</div>
</section>
</DrawerTrigger>
)
})}

<DrawerContent>
<LinkDrawer
id={selectedLink}
close={() => {
router.replace(
`/bookmarks?${new URLSearchParams({
page: page || '1',
pageSize: pageSize || '10',
sort: sort || 'createdAt',
direction: direction || 'desc',
search: search || '',
}).toString()}`,
)
}}
/>
</DrawerContent>
</Drawer>
)
}
22 changes: 5 additions & 17 deletions apps/ui/src/components/LinkList/LinkList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {
PaginationPrevious,
} from '@/components/ui/pagination'
import { LinkListActions } from '@/components/LinkList/LinkListActions'
import { LinkListItem } from '@/components/LinkList/LinkListItem'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { SearchIcon } from 'lucide-react'
import { cn, createPaginationQueryString } from '@/lib/utils'
import type { LinkStatus } from '@/components/StatusIcon/StatusIcon'
import { LinkItems } from '@/components/LinkList/LinkItems'

interface Link {
interface ILink {
id: string
title: string
url: string
Expand All @@ -33,7 +33,7 @@ interface IPagination {
}

interface LinkListProps {
links: Link[]
links: ILink[]
pagination: IPagination
}

Expand Down Expand Up @@ -62,23 +62,11 @@ export async function LinkList({ links, pagination }: LinkListProps) {
</div>
<div className="grid gap-4">
<div className="space-y-4">
{links.map((link) => (
<LinkListItem
key={link.id}
href={`/bookmarks/${link.id}`}
title={link.title}
url={link.url}
imageUrl={link.imageUrl}
summary={link.summary}
tags={link.tags}
createdAt={link.createdAt}
status={link.status}
/>
))}
<LinkItems links={links} />
</div>
</div>
</main>

{/* <LinkDrawer /> */}
<aside className="flex my-4 mx-auto w-full justify-center">
<Pagination>
<PaginationContent>
Expand Down
8 changes: 4 additions & 4 deletions apps/ui/src/components/LinkList/LinkListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ interface LinkListItemProps {

export const LinkListItem = (props: LinkListItemProps) => {
return (
<Link
href={props.href}
<div
// href={props.href}
className="grid group grid-cols-12 gap-5 p-2 hover:shadow-md transition-shadow rounded-lg dark:hover:bg-primary/20 dark:transition-colors ease-in-out duration-75"
prefetch={props.prefetch}
// prefetch={props.prefetch}
>
<section className="col-span-3 flex rounded-md overflow-hidden shadow-md">
<AspectRatio ratio={16 / 9} className="w-full">
Expand Down Expand Up @@ -51,6 +51,6 @@ export const LinkListItem = (props: LinkListItemProps) => {
Added {formatRelative(props.createdAt, new Date())}
</div>
</section>
</Link>
</div>
)
}
4 changes: 2 additions & 2 deletions apps/ui/src/components/Markdown/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export function Markdown({
return (
<BaseMarkdown
className={cn(
'col-span-8 prose dark:prose-invert prose-purple max-w-none',
'border-r border-muted pl-4 pt-6 pr-6 pb-10',
'prose dark:prose-invert prose-purple max-w-none',
'lg:col-span-8 lg:border-r border-muted lg:pl-4 lg:pt-6 lg:pr-6 lg:pb-10',
'zwj-hide',
)}
// components={{ code }}
Expand Down
Loading

0 comments on commit fad68a7

Please sign in to comment.