1- import React from "react" ;
2- import { useState } from 'react' ;
3- import { toast } from "@/components/ToastProvider" ; // Adjust the import path as needed
1+ import React , { useState } from "react" ;
2+ import { toast } from "@/components/ToastProvider" ;
43
5- type EvolutionImage = {
6- url : string ;
7- version : string ;
8- } ;
9- type EmojiData = {
10- title : string ;
11- slug : string ;
12- unicode ?: string ;
13- latestAppleImage : string ;
14- description ?: string ;
15- apple_vendor_description ?: string ;
16- appleEvolutionImages : EvolutionImage [ ] ;
17- } ;
4+ // ----------------------
5+ // Helpers
6+ // ----------------------
187
198const cleanDescription = ( text ?: string ) => {
209 if ( ! text ) return "" ;
@@ -32,129 +21,145 @@ const cleanDescription = (text?: string) => {
3221
3322const copyRasterImage = async ( url : string ) => {
3423 try {
35- const response = await fetch ( url ) ;
36- const blob = await response . blob ( ) ;
24+ const res = await fetch ( url ) ;
25+ const blob = await res . blob ( ) ;
3726 await navigator . clipboard . write ( [ new ClipboardItem ( { [ blob . type ] : blob } ) ] ) ;
3827 toast . success ( "Image copied to clipboard!" ) ;
39- } catch ( err ) {
40- console . error ( "Failed to copy image:" , err ) ;
28+ } catch {
4129 toast . error ( "Failed to copy image." ) ;
4230 }
4331} ;
4432
45- const MainEmojiBox : React . FC < { emoji : any } > = ( { emoji } ) => {
46- // Helpers for copy feedback
47- const [ copiedCode , setCopiedCode ] = useState ( false ) ;
48- const emojiChar = emoji . code || emoji . fluentui_metadata ?. glyph || ( emoji as any ) . glyph || '' ;
33+ // ----------------------
34+ // Main Emoji Box
35+ // ----------------------
4936
37+ const MainEmojiBox : React . FC < { emoji : any ; vendor : "apple" | "discord" } > = ( {
38+ emoji,
39+ vendor,
40+ } ) => {
41+ const [ copiedCode , setCopiedCode ] = useState ( false ) ;
5042
51- // Copy emoji character to clipboard
52- const copyToClipboard = async ( text : string , type : string ) => {
53- try {
54- await navigator . clipboard . writeText ( text ) ;
55- setCopiedCode ( true ) ;
43+ const emojiChar =
44+ emoji . code || emoji . fluentui_metadata ?. glyph || emoji . glyph || "" ;
5645
57- setTimeout ( ( ) => setCopiedCode ( false ) , 1200 ) ;
58- } catch ( err ) {
59- setCopiedCode ( false ) ;
60- }
61- } ;
46+ // Pick vendor-specific fields
47+ const vendorTitle = vendor === "apple" ? "Apple" : "Discord" ;
48+ const latestImage =
49+ vendor === "apple" ? emoji . latestAppleImage : emoji . latestDiscordImage ;
50+ const vendorDescription =
51+ vendor === "apple"
52+ ? emoji . apple_vendor_description
53+ : emoji . discord_vendor_description ;
6254
6355 return (
64- < div className = "bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-xl p-4 md:p-6 mb-8 shadow-sm flex flex-col md:flex-row items-center md:items-start gap-4 md:gap-6" >
65- < img
66- src = { emoji . latestAppleImage }
67- alt = { `${ emoji . title || emoji . slug || emoji . fluentui_metadata ?. cldr } Apple Emoji` }
68- className = "w-24 h-24 md:w-28 md:h-28 cursor-pointer"
69- onClick = { ( ) => copyRasterImage ( emoji . latestAppleImage ) }
70- />
71- < div className = "flex-1 text-center md:text-left" >
72- < h1 className = "text-2xl md:text-3xl font-semibold mb-2 flex flex-col md:flex-row items-center md:items-center justify-center md:justify-start gap-2" style = { { textTransform : 'capitalize' } } >
73- { emoji . title || emoji . slug || emoji . fluentui_metadata ?. cldr } (Apple)
74- < button
75- className = "p-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800 mt-1 md:mt-0"
76- onClick = { ( ) => navigator . clipboard . writeText ( emoji . latestAppleImage ) }
77- title = "Copy image URL"
78- >
79- { /* Copy Icon */ }
80- </ button >
81- </ h1 >
82- { /* Copy Buttons and Unicode Info */ }
83- < div className = "flex flex-wrap justify-center md:justify-start items-center gap-2 md:gap-4 mb-4" >
84- < button
85- onClick = { ( ) => copyToClipboard ( emojiChar , 'code' ) }
86- className = { `px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
87- copiedCode
88- ? 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400'
89- : 'bg-blue-100 text-blue-800 hover:bg-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:hover:bg-blue-900/30'
90- } `} >
91- { copiedCode ? '✓ Copied!' : `Copy ${ emojiChar } ` }
92-
93- </ button >
94- { emoji . codepointsHex && emoji . codepointsHex . length > 0 && (
95- < div className = "text-sm text-slate-500 dark:text-slate-400 whitespace-nowrap" >
96- Unicode: { emoji . codepointsHex . join ( ', ' ) }
56+ < div className = "bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-xl p-4 md:p-6 mb-8 shadow-sm flex flex-col md:flex-row items-center md:items-start gap-4 md:gap-6" >
57+ < img
58+ src = { latestImage }
59+ alt = { `${ emoji . title || emoji . slug } ${ vendorTitle } Emoji` }
60+ className = "w-24 h-24 md:w-28 md:h-28 cursor-pointer"
61+ onClick = { ( ) => copyRasterImage ( latestImage ) }
62+ />
63+
64+ < div className = "flex-1 text-center md:text-left" >
65+ < h1
66+ className = "text-2xl md:text-3xl font-semibold mb-2 flex flex-col md:flex-row items-center justify-center md:justify-start gap-2"
67+ style = { { textTransform : "capitalize" } }
68+ >
69+ { emoji . title || emoji . slug } ({ vendorTitle } )
70+ </ h1 >
71+
72+ { /* Copy character */ }
73+ < div className = "flex flex-wrap justify-center md:justify-start items-center gap-2 md:gap-4 mb-4" >
74+ < button
75+ onClick = { ( ) => {
76+ navigator . clipboard . writeText ( emojiChar ) ;
77+ setCopiedCode ( true ) ;
78+ setTimeout ( ( ) => setCopiedCode ( false ) , 1200 ) ;
79+ } }
80+ className = { `px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
81+ copiedCode
82+ ? "bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400"
83+ : "bg-blue-100 text-blue-800 hover:bg-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:hover:bg-blue-900/30"
84+ } `}
85+ >
86+ { copiedCode ? "✓ Copied!" : `Copy ${ emojiChar } ` }
87+ </ button >
88+
89+ { emoji . codepointsHex ?. length > 0 && (
90+ < div className = "text-sm text-slate-500 dark:text-slate-400 whitespace-nowrap" >
91+ Unicode: { emoji . codepointsHex . join ( ", " ) }
92+ </ div >
93+ ) }
9794 </ div >
98- ) }
99- </ div >
100-
101- { /* Description */ }
102- < p className = "text-slate-600 dark:text-slate-300 mt-2" > { cleanDescription ( emoji . apple_vendor_description || emoji . description ) } </ p >
103- </ div >
104- </ div >
105-
106- ) ;
107- } ;
108-
10995
96+ < p className = "text-slate-600 dark:text-slate-300 mt-2" >
97+ { cleanDescription ( vendorDescription || emoji . description ) }
98+ </ p >
99+ </ div >
100+ </ div >
101+ ) ;
102+ } ;
110103
111- const VendorEmojiPage : React . FC < { emoji : EmojiData } > = ( { emoji } ) => (
112- < div className = "max-w-6xl mx-auto px-4 md:px-6 mt-[74px]" >
113- { /* AdBanner can still be a client:only astro component, or use React if desired */ }
114- { /* <AdBanner /> */ }
104+ // ----------------------
105+ // Vendor Emoji Page
106+ // ----------------------
115107
116- < MainEmojiBox emoji = { emoji } />
108+ const VendorEmojiPage : React . FC < { emoji : any ; vendor : "apple" | "discord" } > = ( {
109+ emoji,
110+ vendor,
111+ } ) => {
112+ const evolution =
113+ vendor === "apple"
114+ ? emoji . appleEvolutionImages
115+ : emoji . discordEvolutionImages ;
117116
118- { /* Evolution Section */ }
119- { ! ! emoji . appleEvolutionImages ?. length && (
120- < div className = "mb-8" >
121- < h2 className = "text-xl font-semibold mb-4 text-center md:text-left" > Evolution</ h2 >
122-
123- < div className = "flex flex-wrap justify-center md:justify-start gap-4" >
124- { emoji . appleEvolutionImages . map ( ( item ) => (
125- < div
126- key = { item . url }
127- className = "flex flex-col items-center min-w-[80px] cursor-pointer"
128- onClick = { ( ) => copyRasterImage ( item . url ) }
129- >
130- < img
131- src = { item . url }
132- alt = { `Apple Emoji ${ item . version } ` }
133- className = "w-16 h-16 md:w-20 md:h-20 mb-1"
134- />
135- < span className = "text-sm text-slate-500 dark:text-slate-400" > { item . version } </ span >
117+ return (
118+ < div className = "max-w-6xl mx-auto px-4 md:px-6 mt-[74px]" >
119+ < MainEmojiBox emoji = { emoji } vendor = { vendor } />
120+
121+ { /* Evolution */ }
122+ { ! ! evolution ?. length && (
123+ < div className = "mb-8" >
124+ < h2 className = "text-xl font-semibold mb-4 text-center md:text-left" >
125+ Evolution
126+ </ h2 >
127+
128+ < div className = "flex flex-wrap justify-center md:justify-start gap-4" >
129+ { evolution . map ( ( item : any ) => (
130+ < div
131+ key = { item . url }
132+ className = "flex flex-col items-center min-w-[80px] cursor-pointer"
133+ onClick = { ( ) => copyRasterImage ( item . url ) }
134+ >
135+ < img
136+ src = { item . url }
137+ alt = { `${ vendor } Emoji ${ item . version } ` }
138+ className = "w-16 h-16 md:w-20 md:h-20 mb-1"
139+ />
140+ < span className = "text-sm text-slate-500 dark:text-slate-400" >
141+ { item . version }
142+ </ span >
143+ </ div >
144+ ) ) }
136145 </ div >
137- ) ) }
138- </ div >
139-
140- </ div >
146+ </ div >
147+ ) }
141148
142- ) }
143-
144- { /* Link Back to Original Emoji Button */ }
145- < div className = "mb-12 flex justify-center md:justify-start" >
146- < a
147- href = { `/freedevtools/emojis/${ emoji . slug } /` }
148- className = "inline-flex items-center gap-2 px-5 py-2.5 text-sm font-semibold text-blue-700 dark:text-blue-300
149- bg-blue-100 dark:bg-blue-900/30 rounded-xl shadow-sm
150- hover:bg-blue-200 dark:hover:bg-blue-900/50
151- hover:shadow-md transition-all duration-200"
152- >
153- < span > See Full Emoji Details</ span >
154- < span className = "text-base" > →</ span >
155- </ a >
149+ { /* Back Button */ }
150+ < div className = "mb-12 flex justify-center md:justify-start" >
151+ < a
152+ href = { `/freedevtools/emojis/${ emoji . slug } /` }
153+ className = "inline-flex items-center gap-2 px-5 py-2.5 text-sm font-semibold text-blue-700 dark:text-blue-300
154+ bg-blue-100 dark:bg-blue-900/30 rounded-xl shadow-sm
155+ hover:bg-blue-200 dark:hover:bg-blue-900/50 transition-all duration-200"
156+ >
157+ < span > See Full Emoji Details</ span >
158+ < span className = "text-base" > →</ span >
159+ </ a >
160+ </ div >
156161 </ div >
157- </ div >
158- ) ;
162+ ) ;
163+ } ;
159164
160165export default VendorEmojiPage ;
0 commit comments