aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastien Castiel <sebastien@castiel.me>2024-01-14 11:42:32 -0500
committerSebastien Castiel <sebastien@castiel.me>2024-01-14 11:43:48 -0500
commitbeae3366667b54b83e20f7e8d532f694c81bc5d0 (patch)
treec40a3136b474978d048a40a6589e35d9c08dfceb
parent2dcb80f954dcc6a9c87dac1dc33668b4478993b9 (diff)
Add donation button (closes #40)
-rw-r--r--README.md2
-rw-r--r--package-lock.json38
-rw-r--r--package.json1
-rw-r--r--src/app/layout.tsx64
-rw-r--r--src/components/donation-button.tsx123
-rw-r--r--src/lib/env.ts1
-rw-r--r--tailwind.config.js2
7 files changed, 201 insertions, 30 deletions
diff --git a/README.md b/README.md
index de9e4f3..e804cb7 100644
--- a/README.md
+++ b/README.md
@@ -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')],
}