Skip to content

Commit e764c7f

Browse files
committed
Tweet list
1 parent 68f12b3 commit e764c7f

File tree

2 files changed

+157
-1
lines changed

2 files changed

+157
-1
lines changed

app/routes/_index.tsx

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,19 @@ import {getAuth} from '@clerk/remix/ssr.server'
22
import {redirect, type LoaderFunctionArgs} from '@remix-run/node'
33
import {useLoaderData} from '@remix-run/react'
44
import {eq} from 'drizzle-orm'
5+
import {Badge} from '../components/ui/badge'
6+
import {Button} from '../components/ui/button'
7+
import {
8+
Card,
9+
CardContent,
10+
CardDescription,
11+
CardFooter,
12+
CardHeader,
13+
CardTitle,
14+
} from '../components/ui/card'
515
import {db} from '../db'
616
import {tweet} from '../db/schema'
17+
import {classNames} from '../utils/classnames'
718

819
export async function loader(args: LoaderFunctionArgs) {
920
const {userId} = await getAuth(args)
@@ -19,5 +30,136 @@ export async function loader(args: LoaderFunctionArgs) {
1930
export default function Index() {
2031
const {tweets} = useLoaderData<typeof loader>()
2132

22-
return <pre>{JSON.stringify(tweets, null, 2)}</pre>
33+
return (
34+
<div className="mx-auto w-full max-w-7xl">
35+
<ul className="mx-auto grid w-full justify-center gap-8 sm:grid-cols-1 md:grid-cols-3">
36+
{tweets.map(tweet => {
37+
return (
38+
<li key={tweet.id}>
39+
<Tweet />
40+
</li>
41+
)
42+
})}
43+
</ul>
44+
</div>
45+
)
46+
}
47+
48+
function Tweet() {
49+
return (
50+
<Card>
51+
<a href="https://x.com/pedrito">
52+
<CardHeader>
53+
<div className="flex items-center gap-2">
54+
<img
55+
alt="Pedrito"
56+
className="aspect-square rounded-full object-cover"
57+
height="40"
58+
width="40"
59+
src="https://tweet-archive.gonzalostoll.com/_next/image?url=https://pbs.twimg.com/profile_images/1718058631315222528/9w6iOXwt_normal.jpg&w=96&q=75"
60+
/>
61+
<div className="flex flex-col">
62+
<CardTitle className="mb-auto text-base">Pedro Cattori</CardTitle>
63+
<CardDescription>@pedrito</CardDescription>
64+
</div>
65+
</div>
66+
</CardHeader>
67+
</a>
68+
69+
<CardContent>
70+
<p className="mb-4">
71+
For anyone else migrating their @remix_run app to our @vite_js plugin,
72+
this is a great reference migration that touched all the edge cases
73+
🐨🏂🤘
74+
</p>
75+
76+
<p className="text-xs text-muted-foreground">
77+
January 18, 2024 at 12:01 AM
78+
</p>
79+
80+
<div
81+
role="none"
82+
data-orientation="horizontal"
83+
className="my-1 h-[1px] w-full shrink-0 bg-border"
84+
/>
85+
86+
<div className="flex gap-2">
87+
<TweetStats type="likes" count={543} />
88+
<TweetStats type="retweet" count={13} />
89+
</div>
90+
91+
<Button variant="secondary" className="mt-6 w-full">
92+
Go to tweet
93+
</Button>
94+
</CardContent>
95+
96+
<div
97+
role="none"
98+
data-orientation="horizontal"
99+
className="h-[1px] w-full shrink-0 bg-border"
100+
/>
101+
102+
<CardFooter className="block bg-secondary px-6 py-4">
103+
<Badge variant="default">Remix</Badge>
104+
<p className="mt-2 text-sm text-secondary-foreground">
105+
Migrating remix to vite plugin. Making the description a little bit
106+
longer. A little more. Moar moar moar.
107+
</p>
108+
109+
<div className="mt-2 flex items-center gap-2">
110+
<Button size="sm" variant="outline">
111+
Edit
112+
</Button>
113+
<Button size="sm" variant="destructive">
114+
Delete
115+
</Button>
116+
</div>
117+
</CardFooter>
118+
</Card>
119+
)
120+
}
121+
122+
function TweetStats({
123+
count,
124+
type = 'likes',
125+
}: {
126+
count: number
127+
type: 'likes' | 'retweet'
128+
}) {
129+
const likeClassNames = 'text-red-400 dark:text-red-600'
130+
const retweetClassNames = 'text-green-400 dark:text-green-600'
131+
132+
return (
133+
<div className="flex items-center">
134+
<svg
135+
className={classNames('h-3 w-3', {
136+
[likeClassNames]: type === 'likes',
137+
[retweetClassNames]: type === 'retweet',
138+
})}
139+
fill="currentColor"
140+
height="24"
141+
stroke="currentColor"
142+
strokeLinecap="round"
143+
strokeLinejoin="round"
144+
strokeWidth="2"
145+
viewBox="0 0 24 24"
146+
width="24"
147+
xmlns="http://www.w3.org/2000/svg"
148+
>
149+
{type === 'likes' ? (
150+
<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" />
151+
) : (
152+
<path d="m3 21 1.9-5.7a8.5 8.5 0 1 1 3.8 3.8z" />
153+
)}
154+
</svg>
155+
<span
156+
className={classNames('ml-1 text-xs', {
157+
[likeClassNames]: type === 'likes',
158+
[retweetClassNames]: type === 'retweet',
159+
})}
160+
>
161+
{count}
162+
</span>
163+
</div>
164+
)
23165
}

app/utils/classnames.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export function classNames(
2+
...classes: Array<string | Record<string, boolean>>
3+
) {
4+
const mappedClasses = classes.map(cl => {
5+
if (typeof cl === 'string') {
6+
return cl
7+
} else {
8+
return Object.keys(cl)
9+
.filter(key => cl[key])
10+
.join(' ')
11+
}
12+
})
13+
return mappedClasses.join(' ')
14+
}

0 commit comments

Comments
 (0)