Skip to content
Open
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
47 changes: 47 additions & 0 deletions apps/frontend/src/components/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Button } from '@components/ui/button';
import React from 'react';

interface CopyButtonProps {
text: string;
copyAction: () => void;
}

const CopyButton = ({ text, copyAction }: CopyButtonProps) => {
return (
<div>
<Button
variant="unstyled"
size="sm"
style={{
backgroundColor: '#007b64',
color: 'white',
padding: '1.5rem',
fontWeight: 'bold',
}}
className="w-[13rem] gap-3 rounded-[10px] min-h-[2.5rem] flex justify-center items-center text-center text-[1.3rem] hover:opacity-90 transition-opacity"
onClick={copyAction}
>
{text}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
stroke="#FFFFFF"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
style={{ marginLeft: '10px' }}
>
<path
fill="none"
d="M12 2v13m4-9l-4-4l-4 4m-4 6v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"
/>
</svg>
</Button>
</div>
);
};

export default CopyButton;
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ShareOptions from '@containers/donations/ShareOptions';
import React, { useCallback, useEffect, useMemo, useState } from 'react';

export interface CarouselSlide {
Expand Down Expand Up @@ -196,6 +197,7 @@ export const AutoRotatingTestimonialCarousel: React.FC<Props> = ({
/>
</div>
</div>
<ShareOptions activeSlideUrl={slides[activeIndex].image} />
</div>
);
};
Expand Down
91 changes: 91 additions & 0 deletions apps/frontend/src/containers/donations/ShareOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Button } from '@components/ui/button';
import React, { useState } from 'react';
import {
FacebookShareButton,
FacebookIcon,
XIcon,
TwitterShareButton,
LinkedinShareButton,
LinkedinIcon,
} from 'react-share';
import CopyButton from '../../components/CopyButton';

const ShareOptions = ({ activeSlideUrl }: { activeSlideUrl: string }) => {
const [isCopyingText, setIsCopyingText] = useState(false);
const [isCopyingImage, setIsCopyingImage] = useState(false);
const message = `Want to support your community? Join me in donating to the Fenway Community Center!\n\n${window.location.href}`;

const handleCopyTextClick = async () => {
try {
setIsCopyingText(true);
await navigator.clipboard.writeText(message);
setTimeout(() => setIsCopyingText(false), 1000);
} catch (err) {
console.error('Failed to copy message to clipboard');
alert('Failed to copy message to clipboard');
}
};

const handleCopyImageClick = async () => {
try {
const response = await fetch(activeSlideUrl);
if (!response.ok) {
throw new Error('Failed to fetch image');
}

const blob = await response.blob();
const imageType = blob.type || 'image/png';
const imageItem = new ClipboardItem({ [imageType]: blob });
await navigator.clipboard.write([imageItem]);

setIsCopyingImage(true);
setTimeout(() => setIsCopyingImage(false), 1000);
} catch (err) {
console.error('Failed to copy image to clipboard', err);
alert('Failed to copy image to clipboard');
}
};

return (
<div>
<div
className="flex flex-wrap justify-center gap-3"
style={{ marginTop: '1.5rem' }}
>
<CopyButton
text={isCopyingText ? 'Copied!' : 'Copy message'}
copyAction={handleCopyTextClick}
/>
<CopyButton
text={isCopyingImage ? 'Copied!' : 'Copy image'}
copyAction={handleCopyImageClick}
/>
</div>
<div
className="flex justify-center"
style={{ gap: '3rem', marginTop: '2rem' }}
>
<FacebookShareButton url={window.location.href}>
<FacebookIcon
className="rounded-full"
style={{ maxWidth: '2.5rem', height: 'auto' }}
/>
</FacebookShareButton>
<TwitterShareButton url={window.location.href}>
<XIcon
className="rounded-full"
style={{ maxWidth: '2.5rem', height: 'auto' }}
/>
</TwitterShareButton>
<LinkedinShareButton url={window.location.href}>
<LinkedinIcon
className="rounded-full"
style={{ maxWidth: '2.5rem', height: 'auto' }}
/>
</LinkedinShareButton>
</div>
</div>
);
};

export default ShareOptions;
1 change: 1 addition & 0 deletions apps/frontend/src/containers/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { DonationForm } from './donations/DonationForm';
import CarouselImage1 from '@components/testimonials/TestimonialImages/Carousel_image1.png';
import CarouselImage2 from '@components/testimonials/TestimonialImages/Carousel_image2.png';
import CarouselImage3 from '@components/testimonials/TestimonialImages/Carousel_image3.png';
import ShareOptions from './donations/ShareOptions';

const SAMPLE_DONATION: SampleDonation = {
name: 'C4C',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"react-dom": "^18.2.0",
"react-is": "^19.2.4",
"react-router-dom": "^6.15.0",
"react-share": "^5.2.2",
"recharts": "^3.7.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0",
Expand Down
Loading