|
| 1 | +"use client"; |
| 2 | +import { Share2 } from "lucide-react"; |
| 3 | +import React, { useState } from "react"; |
| 4 | + |
| 5 | +interface PoolData { |
| 6 | + amountRaised: string; |
| 7 | + remainingAmount: string; |
| 8 | + targetAmount: string; |
| 9 | + fundraiserId: string; |
| 10 | +} |
| 11 | + |
| 12 | +export const XShareButtonWithPoolImage = ({ |
| 13 | + poolData, |
| 14 | +}: { |
| 15 | + poolData: PoolData; |
| 16 | +}) => { |
| 17 | + const [showModal, setShowModal] = useState(false); |
| 18 | + |
| 19 | + const loadImage = (src: string): Promise<HTMLImageElement> => { |
| 20 | + return new Promise((resolve, reject) => { |
| 21 | + const img = new Image(); |
| 22 | + img.crossOrigin = "anonymous"; |
| 23 | + img.onload = () => resolve(img); |
| 24 | + img.onerror = () => reject(new Error(`Failed to load image at ${src}`)); |
| 25 | + img.src = src; |
| 26 | + }); |
| 27 | + }; |
| 28 | + |
| 29 | + const generateAndDownload = async () => { |
| 30 | + const canvas = document.createElement("canvas"); |
| 31 | + canvas.width = 1200; |
| 32 | + canvas.height = 1200; |
| 33 | + const ctx = canvas.getContext("2d")!; |
| 34 | + |
| 35 | + // 1. Background |
| 36 | + ctx.fillStyle = "#070A11"; |
| 37 | + ctx.fillRect(0, 0, 1200, 1200); |
| 38 | + |
| 39 | + // 2. Background Coin |
| 40 | + try { |
| 41 | + const coinImg = await loadImage("/coin.png"); |
| 42 | + ctx.globalAlpha = 0.6; |
| 43 | + ctx.drawImage( |
| 44 | + coinImg, |
| 45 | + 100, |
| 46 | + 380, |
| 47 | + 1000, |
| 48 | + 1000 * (coinImg.height / coinImg.width) |
| 49 | + ); |
| 50 | + ctx.globalAlpha = 1.0; |
| 51 | + } catch (e) { |
| 52 | + console.error("Background coin failed to load", e); |
| 53 | + } |
| 54 | + |
| 55 | + // 3. Logo Badge & Logo Image |
| 56 | + const badgeX = 80, |
| 57 | + badgeY = 420, |
| 58 | + badgeW = 280, |
| 59 | + badgeH = 75; |
| 60 | + |
| 61 | + // Draw the pill background |
| 62 | + ctx.fillStyle = "rgba(255, 255, 255, 0.05)"; |
| 63 | + ctx.beginPath(); |
| 64 | + ctx.roundRect(badgeX, badgeY, badgeW, badgeH, 40); |
| 65 | + ctx.fill(); |
| 66 | + ctx.strokeStyle = "rgba(255, 255, 255, 0.2)"; |
| 67 | + ctx.stroke(); |
| 68 | + |
| 69 | + try { |
| 70 | + // FIX: Load and draw the logo image inside the badge |
| 71 | + const logoImg = await loadImage("/logo.png"); |
| 72 | + // Adjust size (50x50) and position within the badge |
| 73 | + ctx.drawImage(logoImg, badgeX + 15, badgeY + 12, 50, 50); |
| 74 | + } catch (e) { |
| 75 | + console.error("Logo image failed to load", e); |
| 76 | + } |
| 77 | + |
| 78 | + // Draw "PAYMESH" Text |
| 79 | + ctx.fillStyle = "white"; |
| 80 | + ctx.font = "bold 32px Arial"; |
| 81 | + ctx.textAlign = "left"; // Ensure alignment |
| 82 | + ctx.fillText("PAYMESH", badgeX + 75, badgeY + 48); |
| 83 | + |
| 84 | + // 4. Stats Rendering |
| 85 | + const drawStat = ( |
| 86 | + x: number, |
| 87 | + label: string, |
| 88 | + value: string, |
| 89 | + color: string |
| 90 | + ) => { |
| 91 | + ctx.textAlign = "left"; |
| 92 | + ctx.fillStyle = "#8398AD"; |
| 93 | + ctx.font = "bold 24px Arial"; |
| 94 | + ctx.fillText(label, x, 650); |
| 95 | + ctx.fillStyle = color; |
| 96 | + ctx.font = "bold 30px Arial"; |
| 97 | + ctx.fillText(value, x, 700); |
| 98 | + }; |
| 99 | + |
| 100 | + drawStat(80, "Amount Raised", poolData.amountRaised, "#BCC0C5"); |
| 101 | + drawStat(480, "Remaining Amount", poolData.remainingAmount, "#BCC0C5"); |
| 102 | + drawStat(880, "Target Amount", poolData.targetAmount, "#92FFB0"); |
| 103 | + |
| 104 | + // 5. Bottom Text |
| 105 | + ctx.fillStyle = "white"; |
| 106 | + ctx.font = "900 100px Impact, Arial Black, sans-serif"; |
| 107 | + ctx.textAlign = "center"; |
| 108 | + ctx.fillText("A LITTLE SUPPORT CAN", 600, 950); |
| 109 | + ctx.fillText("MAKE A BIG DIFFERENCE.", 600, 1070); |
| 110 | + |
| 111 | + // 6. Download Trigger |
| 112 | + canvas.toBlob((blob) => { |
| 113 | + if (blob) { |
| 114 | + const url = URL.createObjectURL(blob); |
| 115 | + const a = document.createElement("a"); |
| 116 | + a.href = url; |
| 117 | + a.download = `paymesh-share.png`; |
| 118 | + a.click(); |
| 119 | + URL.revokeObjectURL(url); |
| 120 | + setShowModal(true); |
| 121 | + } |
| 122 | + }); |
| 123 | + }; |
| 124 | + |
| 125 | + const handlePostToX = () => { |
| 126 | + const fundraiserUrl = `https://paymesh.app/fundraiser/${poolData.fundraiserId}`; |
| 127 | + const defaultText = encodeURIComponent( |
| 128 | + `Every contribution counts! 🚀\n\n` + |
| 129 | + `I just joined the pool on @paymesh_ to support this initiative on @StarknetFndn. \n\n` + |
| 130 | + `Current progress: ${poolData.amountRaised} / ${poolData.targetAmount}\n\n` + |
| 131 | + `Join me and make an impact here: ${fundraiserUrl}` |
| 132 | + ); |
| 133 | + const xUrl = `https://twitter.com/intent/tweet?text=${defaultText}`; |
| 134 | + window.open(xUrl, "_blank"); |
| 135 | + setShowModal(false); |
| 136 | + }; |
| 137 | + |
| 138 | + return ( |
| 139 | + <> |
| 140 | + <button |
| 141 | + className="flex items-center gap-2 bg-white rounded-full px-3 py-1.5 cursor-pointer hover:bg-gray-100 transition-colors shrink-0" |
| 142 | + onClick={generateAndDownload} |
| 143 | + > |
| 144 | + <span className="text-[#030407] text-sm font-medium">Share</span> |
| 145 | + <Share2 className="w-4 h-4 text-[#030407]" /> |
| 146 | + </button> |
| 147 | + |
| 148 | + {showModal && ( |
| 149 | + <div className="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4"> |
| 150 | + <div className="bg-[#070A11] border border-white/20 p-8 rounded-3xl max-w-md w-full text-center shadow-2xl"> |
| 151 | + <h2 className="text-white text-2xl font-bold mb-4"> |
| 152 | + Image Downloaded |
| 153 | + </h2> |
| 154 | + <p className="text-[#8398AD] mb-6 leading-relaxed"> |
| 155 | + To share on X, simply click the button below. When the window |
| 156 | + opens, |
| 157 | + <strong> attach the image you just downloaded</strong> from your |
| 158 | + files. |
| 159 | + </p> |
| 160 | + <div className="flex flex-col gap-3"> |
| 161 | + <button |
| 162 | + onClick={handlePostToX} |
| 163 | + className="bg-[#575EB7] text-[#DDDDDD] font-bold py-3 rounded-full hover:brightness-110 transition-all" |
| 164 | + > |
| 165 | + Open X & Post |
| 166 | + </button> |
| 167 | + <button |
| 168 | + onClick={() => setShowModal(false)} |
| 169 | + className="text-white/50 text-sm hover:text-white transition-all" |
| 170 | + > |
| 171 | + Cancel |
| 172 | + </button> |
| 173 | + </div> |
| 174 | + </div> |
| 175 | + </div> |
| 176 | + )} |
| 177 | + </> |
| 178 | + ); |
| 179 | +}; |
0 commit comments