diff options
| author | Sebastien Castiel <sebastien@castiel.me> | 2024-01-14 11:42:32 -0500 |
|---|---|---|
| committer | Sebastien Castiel <sebastien@castiel.me> | 2024-01-14 11:43:48 -0500 |
| commit | beae3366667b54b83e20f7e8d532f694c81bc5d0 (patch) | |
| tree | c40a3136b474978d048a40a6589e35d9c08dfceb | |
| parent | 2dcb80f954dcc6a9c87dac1dc33668b4478993b9 (diff) | |
Add donation button (closes #40)
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | package-lock.json | 38 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | src/app/layout.tsx | 64 | ||||
| -rw-r--r-- | src/components/donation-button.tsx | 123 | ||||
| -rw-r--r-- | src/lib/env.ts | 1 | ||||
| -rw-r--r-- | tailwind.config.js | 2 |
7 files changed, 201 insertions, 30 deletions
@@ -32,6 +32,8 @@ Spliit is a free and open source alternative to Splitwise. I created it back in The project is open to contributions. Feel free to open an issue or even a pull-request! +If you want to contribute financially and help us keep the application free and without ads, you can also [make a small one-time donation](https://donate.stripe.com/28o3eh96G7hH8k89Ba) ❤️. + ## Run locally 1. Clone the repository (or fork it if you intend to contribute) diff --git a/package-lock.json b/package-lock.json index c1f908a..94af5ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "@react-email/html": "^0.0.7", "@react-email/preview": "^0.0.8", "@react-email/text": "^0.0.7", + "@tailwindcss/typography": "^0.5.10", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "lucide-react": "^0.290.0", @@ -1628,6 +1629,32 @@ "tslib": "^2.4.0" } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", + "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@total-typescript/ts-reset": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.5.1.tgz", @@ -4692,11 +4719,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, "license": "MIT" }, "node_modules/loose-envify": { diff --git a/package.json b/package.json index fb15f06..e98e6fc 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@react-email/html": "^0.0.7", "@react-email/preview": "^0.0.8", "@react-email/text": "^0.0.7", + "@tailwindcss/typography": "^0.5.10", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "lucide-react": "^0.290.0", diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3e8dade..2223a84 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,3 +1,4 @@ +import { DonationButton } from '@/components/donation-button' import { ProgressBar } from '@/components/progress-bar' import { ThemeProvider } from '@/components/theme-provider' import { ThemeToggle } from '@/components/theme-toggle' @@ -111,35 +112,42 @@ export default function RootLayout({ {children} - <footer className="sm:p-8 md:p-16 sm:mt-16 sm:text-sm md:text-base md:mt-32 bg-slate-50 dark:bg-card border-t p-6 mt-8 flex flex-col space-y-2 text-xs [&_a]:underline"> - <div className="sm:text-lg font-semibold text-base flex space-x-2 items-center"> - <Link className="flex items-center gap-2" href="/"> - <Image - src="/logo-with-text.png" - className="m-1 h-auto" - width={(35 * 522) / 180} - height={35} - alt="Spliit" - /> - </Link> - </div> - <div className="flex flex-col space-y a--no-underline-text-white"> - <span>Made in Montréal, Québec 🇨🇦</span> - <span> - Built by{' '} - <a href="https://scastiel.dev" target="_blank" rel="noopener"> - Sebastien Castiel - </a>{' '} - and{' '} - <a - href="https://github.com/scastiel/spliit2/graphs/contributors" - target="_blank" - rel="noopener" - > - contributors - </a> - </span> + <footer className="sm:p-8 md:p-16 sm:mt-16 sm:text-sm md:text-base md:mt-32 bg-slate-50 dark:bg-card border-t p-6 mt-8 flex flex-col sm:flex-row sm:justify-between gap-4 text-xs [&_a]:underline"> + <div className="flex flex-col space-y-2"> + <div className="sm:text-lg font-semibold text-base flex space-x-2 items-center"> + <Link className="flex items-center gap-2" href="/"> + <Image + src="/logo-with-text.png" + className="m-1 h-auto" + width={(35 * 522) / 180} + height={35} + alt="Spliit" + /> + </Link> + </div> + <div className="flex flex-col space-y a--no-underline-text-white"> + <span>Made in Montréal, Québec 🇨🇦</span> + <span> + Built by{' '} + <a href="https://scastiel.dev" target="_blank" rel="noopener"> + Sebastien Castiel + </a>{' '} + and{' '} + <a + href="https://github.com/scastiel/spliit2/graphs/contributors" + target="_blank" + rel="noopener" + > + contributors + </a> + </span> + </div> </div> + {env.STRIPE_DONATION_LINK && ( + <div> + <DonationButton donationUrl={env.STRIPE_DONATION_LINK} /> + </div> + )} </footer> <Toaster /> </ThemeProvider> diff --git a/src/components/donation-button.tsx b/src/components/donation-button.tsx new file mode 100644 index 0000000..34fcfb1 --- /dev/null +++ b/src/components/donation-button.tsx @@ -0,0 +1,123 @@ +'use client' +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog' +import { + Drawer, + DrawerContent, + DrawerDescription, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from '@/components/ui/drawer' +import { useMediaQuery } from '@/lib/hooks' +import { Heart } from 'lucide-react' +import { useState } from 'react' + +type Props = { + donationUrl: string +} + +export function DonationButton({ donationUrl }: Props) { + const isDesktop = useMediaQuery('(min-width: 768px)') + return isDesktop ? ( + <DonationDialog donationUrl={donationUrl} /> + ) : ( + <DonationDrawer donationUrl={donationUrl} /> + ) +} + +function DonationDrawer({ donationUrl }: Props) { + const [open, setOpen] = useState(false) + + return ( + <Drawer open={open} onOpenChange={setOpen}> + <DrawerTrigger asChild> + <Button className="bg-pink-700 hover:bg-pink-600"> + <Heart className="w-4 h-4 mr-2" /> Support us + </Button> + </DrawerTrigger> + <DrawerContent> + <DrawerHeader> + <DrawerTitle>Support us</DrawerTitle> + <DrawerDescription> + Help keep <strong>Spliit</strong> free and without ads! + </DrawerDescription> + </DrawerHeader> + <div className="px-4 pb-4"> + <DonationForm donationUrl={donationUrl} /> + </div> + </DrawerContent> + </Drawer> + ) +} + +function DonationDialog({ donationUrl }: Props) { + const [open, setOpen] = useState(false) + + return ( + <Dialog open={open} onOpenChange={setOpen}> + <DialogTrigger asChild> + <Button className="bg-pink-700 hover:bg-pink-600"> + <Heart className="w-4 h-4 mr-2" /> Support us + </Button> + </DialogTrigger> + <DialogContent> + <DialogHeader> + <DialogTitle>Support us</DialogTitle> + <DialogDescription> + Help keep <strong>Spliit</strong> free and without ads! + </DialogDescription> + </DialogHeader> + <DonationForm donationUrl={donationUrl} /> + </DialogContent> + </Dialog> + ) +} + +function DonationForm({ donationUrl }: Props) { + return ( + <> + <div className="prose prose-sm"> + <p> + Spliit is offered for free, but costs money and energy. If you like + the app, you can choose to support it by buying me (Sebastien) a + coffee with a one-time small donation. + </p> + <p>By supporting Spliit:</p> + <ul> + <li> + You contribute to the <strong>hosting costs</strong> for the app + (currently ~$150/year). + </li> + <li> + You help us keeping the application{' '} + <strong>free and without ads</strong>. + </li> + <li> + You give me energy to build <strong>new features</strong> and + improve the application. + </li> + </ul> + <p> + You will be redirected to <strong>Stripe</strong>, our payment + provider, where you can choose an amount to donate and complete the + payment. + </p> + </div> + <div className="mt-4 text-center"> + <Button asChild className="bg-pink-700 hover:bg-pink-600"> + <a href={donationUrl} target="_blank"> + <Heart className="w-4 h-4 mr-2" /> Support us + </a> + </Button> + </div> + </> + ) +} diff --git a/src/lib/env.ts b/src/lib/env.ts index ddaa553..4bf76ca 100644 --- a/src/lib/env.ts +++ b/src/lib/env.ts @@ -8,6 +8,7 @@ const envSchema = z.object({ FEEDBACK_EMAIL_FROM: z.string().email().optional(), FEEDBACK_EMAIL_TO: z.string().email().optional(), RESEND_API_KEY: z.string().optional(), + STRIPE_DONATION_LINK: z.string().optional(), }) export const env = envSchema.parse(process.env) diff --git a/tailwind.config.js b/tailwind.config.js index 03965fe..f417ca4 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -89,5 +89,5 @@ module.exports = { }, }, }, - plugins: [require('tailwindcss-animate')], + plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')], } |
