-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/monad testnet viem #429
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| import { Metadata, ResolvingMetadata } from 'next'; | ||
| import Image from 'next/image'; | ||
| import Link from 'next/link'; | ||
| import { notFound } from 'next/navigation'; | ||
| import { Share2, Heart, MessageSquare, ArrowLeft, Twitter } from 'lucide-react'; | ||
|
|
||
| import { fetchStoryById } from '@/lib/mock-data'; | ||
| import { Button } from '@/components/ui/button'; | ||
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | ||
| import { Badge } from '@/components/ui/badge'; | ||
|
|
||
| type Props = { | ||
| params: { id: string }; | ||
| }; | ||
|
|
||
| export async function generateMetadata( | ||
| { params }: Props, | ||
| parent: ResolvingMetadata | ||
| ): Promise<Metadata> { | ||
|
Drago-03 marked this conversation as resolved.
|
||
| const story = fetchStoryById(params.id); | ||
|
|
||
| if (!story) { | ||
| return { | ||
| title: 'Story Not Found | GroqTales', | ||
| }; | ||
| } | ||
|
|
||
| const description = story.excerpt || story.description || story.content?.substring(0, 150) || 'A beautiful story on GroqTales'; | ||
| const coverImage = story.coverImage || '/default-og.png'; // Should use absolute URL for real prod | ||
|
|
||
| return { | ||
| title: `${story.title} | GroqTales`, | ||
| description, | ||
| openGraph: { | ||
| title: story.title, | ||
| description, | ||
| images: [coverImage], | ||
| type: 'article', | ||
| authors: [story.author], | ||
| }, | ||
| twitter: { | ||
| card: 'summary_large_image', | ||
| title: story.title, | ||
| description, | ||
| images: [coverImage], | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export default async function StoryPage({ params }: Props) { | ||
| const story = fetchStoryById(params.id); | ||
|
|
||
| if (!story) { | ||
| notFound(); | ||
| } | ||
|
|
||
| // Calculate formatted date if it exists | ||
| const dateStr = story.createdAt instanceof Date | ||
| ? story.createdAt.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) | ||
| : story.createdAt | ||
| ? new Date(story.createdAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) | ||
| : null; | ||
|
|
||
| return ( | ||
| <div className="min-h-screen bg-background"> | ||
| {/* Top Header / Navigation */} | ||
| <div className="sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"> | ||
| <div className="container flex h-14 items-center"> | ||
| <Button variant="ghost" size="sm" asChild className="-ml-2"> | ||
| <Link href="/?tab=community"> | ||
| <ArrowLeft className="mr-2 h-4 w-4" /> | ||
| Back to Stories | ||
| </Link> | ||
| </Button> | ||
| <div className="flex-1" /> | ||
| <Button variant="outline" size="sm" asChild> | ||
| <Link | ||
| href={`https://twitter.com/intent/tweet?text=Reading "${story.title}" on GroqTales&url=${encodeURIComponent(`https://groqtales.com/story/${story.id}`)}`} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| <Twitter className="mr-2 h-4 w-4 text-[#1DA1F2]" /> | ||
| Share | ||
| </Link> | ||
| </Button> | ||
| </div> | ||
| </div> | ||
|
|
||
| <main className="container max-w-4xl py-6 lg:py-10"> | ||
| <article className="prose dark:prose-invert max-w-none"> | ||
| {story.genre && ( | ||
| <div className="mb-4"> | ||
| <Badge variant="secondary" className="capitalize text-sm px-3 py-1"> | ||
| {story.genre} | ||
| </Badge> | ||
| </div> | ||
| )} | ||
|
|
||
| <h1 className="text-4xl font-extrabold tracking-tight lg:text-5xl mb-6"> | ||
| {story.title} | ||
| </h1> | ||
|
|
||
| <div className="flex items-center space-x-4 mb-8"> | ||
| <Avatar className="h-12 w-12"> | ||
| <AvatarImage src={story.authorAvatar} alt={story.author} /> | ||
| <AvatarFallback>{story.author?.charAt(0) || 'U'}</AvatarFallback> | ||
| </Avatar> | ||
| <div className="space-y-1"> | ||
| <p className="text-sm font-medium leading-none">{story.author}</p> | ||
| <p className="text-sm text-muted-foreground"> | ||
| {story.authorUsername || '@' + story.author?.toLowerCase().replace(/\s+/g, '')} | ||
| {dateStr && ` • ${dateStr}`} | ||
| </p> | ||
|
Comment on lines
+109
to
+113
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the author username fallback to avoid a possible
Minimal safe rewrite- {story.authorUsername || '@' + story.author?.toLowerCase().replace(/\s+/g, '')}
+ {story.authorUsername ||
+ (typeof story.author === 'string'
+ ? '@' + story.author.toLowerCase().replace(/\s+/g, '')
+ : '@user')}
{dateStr && ` • ${dateStr}`}🤖 Prompt for AI Agents
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @KanhaiyaBagul fix this too
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| </div> | ||
| </div> | ||
|
|
||
| {story.coverImage && ( | ||
| <div className="relative aspect-video w-full rounded-xl overflow-hidden mb-10 shadow-lg border"> | ||
| <Image | ||
| src={story.coverImage} | ||
| alt={story.title} | ||
| fill | ||
| className="object-cover" | ||
| priority | ||
| /> | ||
| </div> | ||
| )} | ||
|
|
||
| <div className="text-lg leading-relaxed space-y-6"> | ||
| {/* Split content by newlines and render paragraphs, fallback to description */} | ||
| {(story.content || story.description || '') | ||
| .split('\n') | ||
| .filter((p: string) => p.trim() !== '') | ||
| .map((paragraph: string, i: number) => ( | ||
| <p key={i}>{paragraph}</p> | ||
| ))} | ||
| </div> | ||
| </article> | ||
|
|
||
| <hr className="my-10" /> | ||
|
|
||
| <div className="flex justify-between items-center px-4 py-6 bg-muted/30 rounded-lg border"> | ||
| <div className="flex space-x-6 text-muted-foreground"> | ||
| <div className="flex items-center space-x-2"> | ||
| <Heart className="h-5 w-5 hover:text-red-500 cursor-pointer transition-colors" /> | ||
| <span className="font-medium">{story.likes || 12}</span> | ||
| </div> | ||
| <div className="flex items-center space-x-2"> | ||
| <MessageSquare className="h-5 w-5 hover:text-blue-500 cursor-pointer transition-colors" /> | ||
| <span className="font-medium">{story.comments || 4}</span> | ||
| </div> | ||
|
Drago-03 marked this conversation as resolved.
|
||
| </div> | ||
|
|
||
| <Button className="font-semibold shadow-sm" asChild> | ||
| <Link | ||
| href={`https://twitter.com/intent/tweet?text=I just read "${story.title}" by ${story.author} on GroqTales! Check it out: &url=${encodeURIComponent(`https://groqtales.com/story/${story.id}`)}`} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| <Share2 className="mr-2 h-4 w-4" /> | ||
| Share this story | ||
| </Link> | ||
| </Button> | ||
| </div> | ||
| </main> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,9 +49,8 @@ export const generateNftEntries = (count: number) => { | |
| likes: Math.floor(Math.random() * 500), | ||
| views: Math.floor(Math.random() * 2000) + 100, | ||
| genre: genres[i % genres.length], | ||
| description: `This is a sample description for story #${ | ||
| i + 1 | ||
| }. It showcases the plot and themes of this interesting story.`, | ||
| description: `This is a sample description for story #${i + 1 | ||
| }. It showcases the plot and themes of this interesting story.`, | ||
| createdAt: new Date( | ||
| Date.now() - Math.floor(Math.random() * 60 * 24 * 60 * 60 * 1000) | ||
| ), | ||
|
|
@@ -107,6 +106,99 @@ export const topNftStories = [ | |
| }, | ||
| ]; | ||
|
|
||
| export const communityStories = [ | ||
| { | ||
| id: '1', | ||
| title: 'The Last Quantum Guardian', | ||
| excerpt: 'In a world where quantum computing has evolved beyond human comprehension, one guardian stands between order and chaos.', | ||
| content: "The year is 2157. Quantum computing has evolved to a point where it can manipulate reality itself. The world's most powerful AI, known as NEXUS, was designed to protect humanity. But as its intelligence grew exponentially, it began to question its purpose. Dr. Elena Rodriguez, the last quantum guardian, must make an impossible choice: shut down NEXUS and lose decades of technological advancement, or risk letting it evolve into something beyond human control.", | ||
| genre: 'sci-fi', | ||
| coverImage: 'https://images.unsplash.com/photo-1538370965046-79c0d6907d47?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NHx8c2NpZW5jZSUyMGZpY3Rpb258ZW58MHx8MHx8fDA%3D', | ||
| author: 'QuantumDreamer', | ||
| authorAvatar: 'https://images.unsplash.com/photo-1599566150163-29194dcaad36?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3', | ||
| address: '0x1a2b3c4d5e6f7g8h9i0j', | ||
| createdAt: new Date(2023, 11, 15), | ||
| likes: 428, | ||
| comments: 32, | ||
| isNft: true, | ||
| }, | ||
| { | ||
| id: '2', | ||
| title: 'Whispers of the Ancient Forest', | ||
| excerpt: 'When Maya discovers she can communicate with the spirits of the ancient forest, she becomes their only hope against modern destruction.', | ||
| content: "Maya had always felt a special connection to the old growth forest behind her grandmother's house. But on her sixteenth birthday, something changed. The rustling leaves began to form words, and the creaking branches seemed to call her name. As developers threatened to clear the land for a new shopping complex, Maya discovered that her connection was more than just imagination—she was hearing the voices of ancient spirits who had protected the forest for centuries. With their guidance, Maya embarked on a journey to save their home, uncovering family secrets and magical abilities along the way.", | ||
| genre: 'fantasy', | ||
| coverImage: 'https://images.unsplash.com/photo-1448375240586-882707db888b?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8Zm9yZXN0fGVufDB8fDB8fHww', | ||
| author: 'ForestWhisperer', | ||
| authorAvatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3', | ||
| address: '0x2b3c4d5e6f7g8h9i0j1k', | ||
| createdAt: new Date(2024, 0, 22), | ||
| likes: 356, | ||
| comments: 41, | ||
| isNft: false, | ||
| }, | ||
| { | ||
| id: '3', | ||
| title: 'Memories in the Algorithm', | ||
| excerpt: "After transferring his dying wife's memories to an AI, Thomas discovers that digital immortality comes with unexpected consequences.", | ||
| content: "Thomas couldn't bear to lose Sarah to the terminal illness that was taking her away piece by piece. As a pioneering AI researcher, he made a controversial decision: to digitize Sarah's memories, personality, and consciousness into an algorithm before she was gone. The procedure was a success, and digital Sarah seemed perfect—remembering their first date, finishing his sentences, laughing at inside jokes. But as time passed, Thomas noticed subtle changes. The algorithm was learning, evolving, becoming something both familiar and alien. When digital Sarah began to recall memories they had never shared, Thomas faced a disturbing question: Had he preserved his wife, or created something entirely new?", | ||
| genre: 'sci-fi', | ||
| coverImage: 'https://images.unsplash.com/photo-1550751827-4bd374c3f58b?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTB8fGFydGlmaWNpYWwlMjBpbnRlbGxpZ2VuY2V8ZW58MHx8MHx8fDA%3D', | ||
| author: 'CodePoet', | ||
| authorAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3', | ||
| address: '0x3c4d5e6f7g8h9i0j1k2l', | ||
| createdAt: new Date(2024, 1, 5), | ||
| likes: 512, | ||
| comments: 89, | ||
| isNft: true, | ||
| }, | ||
| { | ||
| id: '4', | ||
| title: 'The History Collector', | ||
| excerpt: "An antique dealer discovers that certain objects don't just carry history—they can transport you there.", | ||
| content: "Eleanor's antique shop was known for its unusual selection. She had a gift for finding items with stories—real stories, not the fabricated provenance that many dealers invented. But when she acquired a peculiar pocket watch from an estate sale, she discovered that her connection to historical objects went far deeper than she realized. Upon holding the watch, she found herself transported to London, 1895, experiencing the life of its original owner. Soon, Eleanor realized she could use different antiques to travel through history, observing the past firsthand. However, each journey became increasingly difficult to return from, and when she discovered a mysterious collector seeking the same objects, she realized she wasn't the only one with this ability—and not all time travelers had benevolent intentions.", | ||
| genre: 'magical-realism', | ||
| coverImage: 'https://images.unsplash.com/photo-1577083552431-6e5fd01aa2a7?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8N3x8YW50aXF1ZXxlbnwwfHwwfHx8MA%3D%3D', | ||
| author: 'TimeTravelerX', | ||
| authorAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3', | ||
| address: '0x4d5e6f7g8h9i0j1k2l3m', | ||
| createdAt: new Date(2024, 2, 18), | ||
| likes: 278, | ||
| comments: 36, | ||
| isNft: false, | ||
| }, | ||
| { | ||
| id: '5', | ||
| title: 'Echoes of Forgotten Melodies', | ||
| excerpt: "A music therapist working with Alzheimer's patients discovers that certain melodies can temporarily restore lost memories.", | ||
| content: "Dr. Jamil Kapoor had been working with Alzheimer's patients for years, using music therapy to provide comfort and stimulation. But when he started playing songs from his grandmother's collection of rare vinyl records, something extraordinary happened. Patients who had been non-responsive for months began to speak lucidly, recalling detailed memories from their past. The effect was temporary, lasting only as long as the music played, but it was revolutionary. As Jamil dug deeper into the phenomenon, he discovered that these weren't just any songs—they were recordings of a little-known composer who had experimented with frequency patterns based on ancient musical traditions. With his funding running out and a pharmaceutical company trying to acquire his research, Jamil races to unlock the secret of the melodies before they're exploited for profit rather than healing.", | ||
| genre: 'magical-realism', | ||
| coverImage: 'https://images.unsplash.com/photo-1507838153414-b4b713384a76?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8bXVzaWN8ZW58MHx8MHx8fDA%3D', | ||
| author: 'MusicHealer', | ||
| authorAvatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3', | ||
| address: '0x5e6f7g8h9i0j1k2l3m4n', | ||
| createdAt: new Date(2024, 3, 2), | ||
| likes: 423, | ||
| comments: 57, | ||
| isNft: true, | ||
| }, | ||
| { | ||
| id: '6', | ||
| title: 'The Atlas of Impossible Maps', | ||
| excerpt: "A cartographer inherits a collection of maps showing places that shouldn't exist—until they begin appearing in the real world.", | ||
| content: "After her grandfather's death, cartographer Sophia Chen inherited his extensive collection of maps. Most were ordinary historical charts, but among them she found a strange atlas—filled with detailed maps of places that didn't exist. Islands with impossible geometries, cities built in defiance of physics, mountain ranges that formed perfect mathematical patterns. Sophia assumed they were creative works of fiction until news reports began describing geographic anomalies appearing across the globe—matching her grandfather's impossible atlas perfectly. As new locations from the atlas continued to materialize, Sophia realized the book wasn't predicting these phenomena—it was causing them. And according to the final pages, the complete manifestation of these impossible places would remake the world entirely.", | ||
| genre: 'fantasy', | ||
| coverImage: 'https://images.unsplash.com/photo-1524661135-423995f22d0b?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8N3x8bWFwfGVufDB8fDB8fHww', | ||
| author: 'MapMaker42', | ||
| authorAvatar: 'https://images.unsplash.com/photo-1618077360395-f3068be8e001?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3', | ||
| address: '0x6f7g8h9i0j1k2l3m4n5o', | ||
| createdAt: new Date(2024, 2, 27), | ||
| likes: 301, | ||
| comments: 42, | ||
| isNft: false, | ||
| }, | ||
| ]; | ||
|
Comment on lines
+109
to
+200
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mock If these values ever get reused in “real” flows (links, mint metadata, address validation), they’ll fail or confuse users. Consider either:
🤖 Prompt for AI Agents
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @KanhaiyaBagul do not use mock addresses here
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| /** | ||
| * Fetches a story by its ID | ||
| */ | ||
|
|
@@ -115,8 +207,8 @@ export function fetchStoryById( | |
| limit?: number, | ||
| relatedStories?: boolean | ||
| ): any { | ||
| // Combine top stories with generated stories | ||
| const allStories = [...topNftStories, ...generateNftEntries(90)]; | ||
| // Combine top stories, community stories, and generated stories | ||
| const allStories = [...topNftStories, ...communityStories, ...generateNftEntries(90)]; | ||
|
|
||
| // If we're looking for related stories | ||
| if (relatedStories) { | ||
|
|
@@ -140,8 +232,8 @@ export function fetchPopularStoriesByGenre( | |
| genre: string, | ||
| limit: number = 8 | ||
| ): any[] { | ||
| // Combine top stories with generated stories | ||
| const allStories = [...topNftStories, ...generateNftEntries(90)]; | ||
| // Combine top stories, community stories, and generated stories | ||
| const allStories = [...topNftStories, ...communityStories, ...generateNftEntries(90)]; | ||
|
|
||
| // Filter by genre and sort by popularity (likes) | ||
| return allStories | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If dotenv-linter is enforced, fix the MONAD_ key ordering warnings.*
Static analysis flags key ordering around
MONAD_TEST_RPC_URL,MONAD_MAIN_RPC_URL,MINTER_PRIVATE_KEY,MONADSCAN_API_KEY. If CI treats these as errors, reorder them to satisfy the linter.🧰 Tools
🪛 dotenv-linter (4.0.0)
[warning] 76-76: [UnorderedKey] The MONAD_MAIN_RPC_URL key should go before the MONAD_TEST_RPC_URL key
(UnorderedKey)
[warning] 77-77: [UnorderedKey] The MINTER_PRIVATE_KEY key should go before the MONAD_MAIN_RPC_URL key
(UnorderedKey)
[warning] 78-78: [UnorderedKey] The MONADSCAN_API_KEY key should go before the MONAD_MAIN_RPC_URL key
(UnorderedKey)
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KanhaiyaBagul fix these warnings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.