Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Feed Page #30

Merged
merged 2 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@tailwindcss/typography": "^0.5.14",
"@tsconfig/svelte": "^5.0.4",
"@types/node": "^22.5.2",
"autoprefixer": "^10.4.20",
"svelte": "^4.2.18",
"svelte-check": "^3.8.5",
Expand All @@ -37,6 +38,7 @@
"svelte-sonner": "^0.3.27",
"tailwind-merge": "^2.5.2",
"tailwind-variants": "^0.2.1",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
}
}
41 changes: 40 additions & 1 deletion src/app.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,42 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind utilities;

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 45%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 72% 51%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5.9% 10%;
--radius: 0.5rem;
}
}

@layer base {
* {
@apply border-border;
}

body {
@apply bg-background text-foreground font-body;
}

h1, h2, h3, h4, h5, h6 {
@apply font-heading;
}
}
134 changes: 73 additions & 61 deletions src/lib/Feed.svelte
Original file line number Diff line number Diff line change
@@ -1,63 +1,75 @@
<!-- Feed.svelte -->
<script>
import { onMount } from "svelte";
import Tweet from "./Post.svelte";

let tweets = [
{
id: "1",
username: "johnDoe",
text: "Just learned Svelte! #SvelteLearning",
likes: 10,
retweets: 5,
},
{
id: "2",
username: "janeSmith",
text: "I'm loving this Svelte tutorial! #SvelteTutorials",
likes: 20,
retweets: 10,
},
{
id: "3",
username: "janeSmith",
text: "I'm loving this Svelte tutorial! #SvelteTutorials",
likes: 20,
retweets: 10,
},
];

onMount(() => {
// fetch tweets from API or database here
// for demonstration purposes, we'll just use the pre-defined array
console.log("tweets:", tweets);
<script lang="ts">
//@ts-nocheck
import { onMount } from 'svelte';
import Tweet from './Tweet.svelte';
import { fetchMemes, fetchMemesByIds } from './ao/mememaker';

let tweets = [];

onMount(async () => {
try {
const fetchedMemes = await fetchMemes("1", "100");
tweets = fetchedMemes.map(meme => ({
id: meme.Pool,
avatarSrc: meme.Profile?.Image ? `https://arweave.net/${meme.Profile.Image}` : '/default-avatar.png',
username: meme.Profile?.Name || 'Anonymous',
handle: `@${meme.Creator.slice(0, 12)}`,
time: formatTime(meme.createdAt),
content: meme.Post.Content,
likes: meme.Pumps,
retweets: meme.Replies,
price: meme.Analytics.Price,
marketCap: meme.Analytics.MarketCap
}));
} catch (error) {
console.error('Error fetching memes:', error);
}
});

// function handleTweetLike(tweetId) {
// const tweet = tweets.find((t) => t.id === tweetId);
// if (tweet) {
// tweet.likes++;
// }
// }

// function handleTweetRetweet(tweetId) {
// const tweet = tweets.find((t) => t.id === tweetId);
// if (tweet) {
// tweet.retweets++;
// }
// }
</script>

<div>
{#each tweets as tweet}
<Tweet
id={tweet.id}
username={tweet.username}
text={tweet.text}
likes={tweet.likes}
retweets={tweet.retweets}
onLike={() => {}}
onRetweet={() => {}}
/>

function formatTime(timestamp) {
const now = Date.now();
const diff = now - timestamp;
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);

if (days > 0) return `${days}d`;
if (hours > 0) return `${hours}h`;
if (minutes > 0) return `${minutes}m`;
return 'Just now';
}

function handleTweetLike(tweetId) {
const tweet = tweets.find((t) => t.id === tweetId);
if (tweet) {
tweet.likes++;
}
}

function handleTweetRetweet(tweetId) {
const tweet = tweets.find((t) => t.id === tweetId);
if (tweet) {
tweet.retweets++;
}
}
</script>

<div class="flex flex-col items-center max-w-2xl mx-auto px-4 py-8 space-y-6">
{#each tweets as tweet (tweet.id)}
<div class="w-full bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-300 ease-in-out">
<Tweet
avatarSrc={tweet.avatarSrc}
username={tweet.username}
handle={tweet.handle}
time={tweet.time}
content={tweet.content}
likes={tweet.likes}
retweets={tweet.retweets}
onLike={() => handleTweetLike(tweet.id)}
onRetweet={() => handleTweetRetweet(tweet.id)}
price={tweet.price}
marketCap={tweet.marketCap}
/>
</div>
{/each}
</div>
</div>
84 changes: 84 additions & 0 deletions src/lib/Tweet.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<script>
import { MessageCircleIcon, RepeatIcon, HeartIcon, ShareIcon, DollarSignIcon, TrendingUpIcon } from 'lucide-svelte';
import Card from '$lib/components/ui/ui/card/card.svelte';
import Avatar from '$lib/components/ui/ui/avatar/avatar.svelte';
import Button from './components/ui/ui/button/button.svelte';

export let avatarSrc = '/placeholder-user.jpg';
export let username = 'User';
export let handle = '@user';
export let time = '1h';
export let content = 'Tweet content here';
export let imageSrc = '';
export let showImage = false;
export let likes = 0;
export let retweets = 0;
export let price = 0;
export let marketCap = 0;
export let onLike = () => {};
export let onRetweet = () => {};

$: formattedPrice = price.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
$: formattedMarketCap = marketCap.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
</script>

<Card class="max-w-full md:max-w-lg">
<div class="p-4">
<div class="flex items-start gap-4">
<a href="/" class="flex-shrink-0">
<Avatar class="h-10 w-10 rounded-full">
<img src={avatarSrc} alt={`Avatar of ${username}`} />
<span>{username.charAt(0)}</span>
</Avatar>
</a>
<div class="flex-1 space-y-2">
<div class="flex items-center gap-2">
<a href="/" class="font-medium">{username}</a>
<span class="text-sm text-muted-foreground">{handle} · {time}</span>
</div>
<p class="text-sm">
{content}
</p>
{#if showImage}
<!-- svelte-ignore a11y-img-redundant-alt -->
<img
src={imageSrc}
alt="Tweet image"
class="rounded-md object-cover w-full"
style="aspect-ratio: 600/400; object-fit: cover;"
/>
{/if}
<div class="flex items-center gap-4 text-sm text-muted-foreground">
<div class="flex items-center gap-1">
<DollarSignIcon class="h-4 w-4" />
<span>Price: {formattedPrice}</span>
</div>
<div class="flex items-center gap-1">
<TrendingUpIcon class="h-4 w-4" />
<span>Market Cap: {formattedMarketCap}</span>
</div>
</div>
<div class="flex items-center gap-4">
<Button variant="ghost" size="icon" on:click={onLike}>
<HeartIcon class="h-5 w-5" />
<span class="sr-only">Like</span>
<span>{likes}</span>
</Button>
<Button variant="ghost" size="icon" on:click={onRetweet}>
<RepeatIcon class="h-5 w-5" />
<span class="sr-only">Retweet</span>
<span>{retweets}</span>
</Button>
<Button variant="ghost" size="icon">
<MessageCircleIcon class="h-5 w-5" />
<span class="sr-only"> Comment</span>
</Button>
<Button variant="ghost" size="icon">
<ShareIcon class="h-5 w-5" />
<span class="sr-only">Share</span>
</Button>
</div>
</div>
</div>
</div>
</Card>
Loading
Loading