panku-app/src/pages/activity-share.jsx

1import { useContext, useMemo, useRef, useState } from "react"; 2import { useParams } from "react-router"; 3import { QRCodeCanvas } from "qrcode.react"; 4 5import PageTitle from "@/components/page-title"; 6import GoBack from "@/components/goback"; 7 8import { AppContext } from "@/ContextProvider"; 9import { formatDate, logger } from "@/utils"; 10 11const ActivityShare = () => { 12 const params = useParams(); 13 const whatsappLinkRef = useRef(null); 14 const qrCodeRef = useRef(null); 15 16 const [copyStatus, setCopyStatus] = useState(""); 17 18 const { state } = useContext(AppContext); 19 const upiId = state.upiSettings.id || ""; 20 const upiName = state.upiSettings.name || ""; 21 22 const activity = state.activities.find((a) => a.id === params.id); 23 24 let upiLink = `upi://pay?pa=${upiId}&am=${ 25 activity.splitPerPerson 26 }.00&cu=INR&tn=${encodeURIComponent(activity.title)}`; 27 if (upiName) { 28 upiLink += `&pn=${encodeURIComponent(upiName)}`; 29 } 30 31 const shareMessage = useMemo(() => { 32 const data = { 33 text: "", 34 encoded: "", 35 whatsappLink: "https://api.whatsapp.com/send?text=", 36 }; 37 38 if (!activity) { 39 return data; 40 } 41 42 data.text += `Hiyya,\n\n`; 43 data.text += `Split for ${activity.title} is Rs.${activity.splitPerPerson}\n\n`; 44 data.text += `Items\n`; 45 for (let i = 0; i < activity.itemSpends.length; i++) { 46 const item = activity.itemSpends[i]; 47 data.text += ` ${i + 1}. ${item["title"]} ${item["amount"]}\n`; 48 } 49 data.text += "\n"; 50 data.text += `Total: Rs.${activity.totalAmount}\n`; 51 data.text += "\n"; 52 53 for (let i = 0; i < activity.friends.length; i++) { 54 const friend = activity.friends[i]; 55 data.text += ` ${i + 1}. ${friend["name"]}\n`; 56 } 57 data.text += "\n"; 58 59 data.text += "To pay, please scan the below QR code.\n"; 60 data.text += "\n"; 61 62 // will be using QR code for now. 63 // links are makred insecure by google pay and phonepe 64 // data.text += `Pay via UPI:\n${upiLink}`; 65 66 data.encoded = encodeURIComponent(data.text) 67 .replace(/'/g, "%27") 68 .replace(/"/g, "%22"); 69 70 data.whatsappLink += data.encoded; 71 72 return data; 73 }, [ 74 activity, 75 // upiLink 76 ]); 77 78 const handleCopyQRCode = async () => { 79 if (!qrCodeRef.current) { 80 return; 81 } 82 83 const qrCodeCanvas = qrCodeRef.current; 84 try { 85 qrCodeCanvas.toBlob(async (blob) => { 86 if (!blob) { 87 logger("Failed to create blob."); 88 setCopyStatus("failed"); 89 return; 90 } 91 92 const item = new ClipboardItem({ [blob.type]: blob }); 93 await navigator.clipboard.write([item]); 94 setCopyStatus("copied!"); 95 setTimeout(() => setCopyStatus(""), 3000); 96 }, "image/png"); 97 } catch (error) { 98 logger("Error copying canvas:", error); 99 setCopyStatus("denied"); 100 } 101 }; 102 103 const handleWhatsAppShareClick = async () => { 104 if (!whatsappLinkRef.current) { 105 return; 106 } 107 whatsappLinkRef.current.click(); 108 return; 109 }; 110 111 if (!activity) { 112 return ( 113 <> 114 <GoBack /> 115 <PageTitle title="activity not found" /> 116 117 <h1>activity not found</h1> 118 </> 119 ); 120 } 121 122 return ( 123 <> 124 <GoBack /> 125 <PageTitle title={`share ${activity.title}`} /> 126 127 <h1 className="h6"> 128 <span className="muted small">share</span> {activity.title} 129 </h1> 130 131 <div style={{ marginTop: "10px" }}> 132 <p style={{ margin: "0px", padding: "0px", fontWeight: "bold" }}> 133 {activity.title} 134 </p> 135 <p style={{ margin: "0px", padding: "0px" }} className="muted"> 136 <small>{formatDate(activity.createdAt)}</small> 137 </p> 138 139 <table 140 className="table" 141 style={{ fontSize: "0.8rem", width: "100%", marginTop: "5px" }} 142 > 143 <thead> 144 <tr> 145 <th>#</th> 146 <th>item / spend</th> 147 <th>₹</th> 148 </tr> 149 </thead> 150 <tbody> 151 {activity.itemSpends.map((i, idx) => ( 152 <tr key={i.id}> 153 <td>{idx + 1}</td> 154 <td>{i.title}</td> 155 <td style={{ fontFamily: "monospace" }}>{i.amount}</td> 156 </tr> 157 ))} 158 159 <tr> 160 <td></td> 161 <td style={{ fontWeight: "bold" }}>Total</td> 162 <td style={{ fontFamily: "monospace" }}> 163 {activity.totalAmount} 164 </td> 165 </tr> 166 </tbody> 167 </table> 168 169 <br /> 170 171 <table 172 className="table" 173 style={{ fontSize: "0.8rem", width: "100%", marginTop: "5px" }} 174 > 175 <thead> 176 <tr> 177 <th>#</th> 178 <th>friend</th> 179 <th>₹</th> 180 </tr> 181 </thead> 182 183 <tbody> 184 {activity.friends.map((f, idx) => ( 185 <tr key={f.id}> 186 <td>{idx + 1}</td> 187 <td>{f.name}</td> 188 <td style={{ fontFamily: "monospace" }}> 189 {activity.splitPerPerson} 190 </td> 191 </tr> 192 ))} 193 <tr> 194 <td>{activity.friends.length + 1}</td> 195 <td>me</td> 196 <td style={{ fontFamily: "monospace" }}> 197 {activity.splitPerPerson} 198 </td> 199 </tr> 200 </tbody> 201 </table> 202 </div> 203 204 <div style={{ marginTop: "10px", fontSize: "0.8rem" }}> 205 <details> 206 <summary style={{ padding: "3px" }} className="cursor-pointer"> 207 message 208 </summary> 209 <div> 210 <textarea 211 value={shareMessage.text} 212 readOnly 213 rows={10} 214 style={{ fontSize: "0.8rem" }} 215 ></textarea> 216 </div> 217 </details> 218 </div> 219 220 <textarea className="d-none" value={upiLink} readOnly></textarea> 221 <div className="text-center" style={{ marginTop: "10px" }}> 222 <QRCodeCanvas 223 marginSize={4} 224 title={upiId} 225 value={upiLink} 226 ref={qrCodeRef} 227 /> 228 </div> 229 230 <div className="text-center" style={{ marginTop: "8px" }}> 231 <button className="btn btn-b" onClick={handleCopyQRCode}> 232 {copyStatus === "" ? "copy QR code" : copyStatus} 233 </button> 234 </div> 235 236 <a 237 href={shareMessage.whatsappLink} 238 className="d-none" 239 ref={whatsappLinkRef} 240 target="_blank" 241 rel="noopener noreferrer" 242 > 243 whatsapp link 244 </a> 245 246 <div className="text-center" style={{ marginTop: "18px" }}> 247 <button 248 className="btn btn-big" 249 type="button" 250 onClick={handleWhatsAppShareClick} 251 > 252 share via WhatsApp 253 </button> 254 </div> 255 </> 256 ); 257}; 258 259export default ActivityShare;