mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-17 13:21:44 +01:00
add support modal
This commit is contained in:
parent
2ed579783d
commit
d0b11e9081
2
.github/workflows/deploy-next.yml
vendored
2
.github/workflows/deploy-next.yml
vendored
@ -34,6 +34,8 @@ jobs:
|
||||
- name: Build
|
||||
env:
|
||||
VITE_TENOR_API_KEY: ${{ secrets.VITE_TENOR_API_KEY }}
|
||||
VITE_PAYWALL_NIP05: "/.well-known/nostr.json"
|
||||
VITE_PAYWALL_MESSAGE: "This is the latest alpha build of noStrudel.\nIf your enjoying the new features consider supporting the project by donating some sats and adding your message on the support page."
|
||||
run: pnpm build
|
||||
|
||||
- name: Redirect 404 to Index for SPA
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ dist
|
||||
node_modules
|
||||
stats.html
|
||||
exe/bin
|
||||
.env
|
||||
|
46
src/components/layout/components/support-paywall.tsx
Normal file
46
src/components/layout/components/support-paywall.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { useObservable } from "applesauce-react/hooks";
|
||||
|
||||
import { paywall, hidePaywall } from "../../../services/paywall";
|
||||
import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from "@chakra-ui/react";
|
||||
import { unixNow } from "applesauce-core/helpers";
|
||||
import RouterLink from "../../router-link";
|
||||
import { useMatch } from "react-router-dom";
|
||||
import { LightningIcon } from "../../icons";
|
||||
import { PAYWALL_MESSAGE } from "../../../env";
|
||||
|
||||
export default function SupportPaywall() {
|
||||
const isSupportPage = useMatch("/support");
|
||||
const paid = useObservable(paywall);
|
||||
|
||||
const dismiss = () => {
|
||||
hidePaywall.next(unixNow() + 60 * 60 * 24);
|
||||
};
|
||||
|
||||
if (!paid && !isSupportPage)
|
||||
return (
|
||||
<Modal isOpen={!paid} onClose={dismiss} size="lg" closeOnOverlayClick={false}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Support the app</ModalHeader>
|
||||
<ModalBody>
|
||||
{PAYWALL_MESSAGE || "If your enjoying this app consider supporting the developer by donating some sats"}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter gap="2">
|
||||
<Button variant="link" px="4" py="2" onClick={dismiss}>
|
||||
Dismiss for a day
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="primary"
|
||||
as={RouterLink}
|
||||
to="/support"
|
||||
leftIcon={<LightningIcon color="yellow.400" boxSize={5} />}
|
||||
>
|
||||
Support
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
else return null;
|
||||
}
|
@ -4,10 +4,12 @@ import { Outlet } from "react-router-dom";
|
||||
|
||||
import DesktopSideNav from "./side-nav";
|
||||
import { ErrorBoundary } from "../../error-boundary";
|
||||
import SupportPaywall from "../components/support-paywall";
|
||||
|
||||
export default function DesktopLayout() {
|
||||
return (
|
||||
<>
|
||||
<SupportPaywall />
|
||||
<DesktopSideNav />
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { Suspense } from "react";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Spinner } from "@chakra-ui/react";
|
||||
|
||||
import MobileBottomNav from "./bottom-nav";
|
||||
import { ErrorBoundary } from "../../error-boundary";
|
||||
import { Suspense } from "react";
|
||||
import { Spinner } from "@chakra-ui/react";
|
||||
import SupportPaywall from "../components/support-paywall";
|
||||
|
||||
export default function MobileLayout() {
|
||||
return (
|
||||
<>
|
||||
<SupportPaywall />
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<ErrorBoundary>
|
||||
<Outlet />
|
||||
|
@ -5,3 +5,6 @@ export const CAP_IS_WEB = platform === "web";
|
||||
export const CAP_IS_NATIVE = platform === "ios" || platform === "android";
|
||||
export const CAP_IS_ANDROID = platform === "android";
|
||||
export const CAP_IS_IOS = platform === "ios";
|
||||
|
||||
export const PAYWALL_NIP05 = import.meta.env.VITE_PAYWALL_NIP05 as string | undefined;
|
||||
export const PAYWALL_MESSAGE = import.meta.env.VITE_PAYWALL_MESSAGE as string | undefined;
|
||||
|
68
src/services/paywall.ts
Normal file
68
src/services/paywall.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
filter,
|
||||
from,
|
||||
map,
|
||||
merge,
|
||||
Observable,
|
||||
of,
|
||||
shareReplay,
|
||||
startWith,
|
||||
Subject,
|
||||
switchMap,
|
||||
tap,
|
||||
} from "rxjs";
|
||||
import { DomainIdentityJson } from "applesauce-loaders/helpers/dns-identity";
|
||||
import { unixNow } from "applesauce-core/helpers";
|
||||
|
||||
import accounts from "./accounts";
|
||||
import { PAYWALL_NIP05 } from "../env";
|
||||
import { logger } from "../helpers/debug";
|
||||
|
||||
const log = logger.extend("paywall");
|
||||
|
||||
export const hidePaywall = new BehaviorSubject(
|
||||
localStorage.getItem("paywall-dismiss") ? parseInt(localStorage.getItem("paywall-dismiss")!) : null,
|
||||
);
|
||||
|
||||
hidePaywall.subscribe((ts) => {
|
||||
if (ts) localStorage.setItem("paywall-dismiss", String(ts));
|
||||
});
|
||||
|
||||
let paywall: Observable<boolean>;
|
||||
if (PAYWALL_NIP05) {
|
||||
const accountPaid = accounts.active$.pipe(
|
||||
// ignore empty accounts
|
||||
filter((a) => !!a),
|
||||
// fetch the identity document
|
||||
switchMap((account) => {
|
||||
log(`Fetching NIP-05 document`);
|
||||
const document = from(fetch(PAYWALL_NIP05!).then((res) => res.json() as Promise<DomainIdentityJson>));
|
||||
return combineLatest([of(account), document]);
|
||||
}),
|
||||
// check if account is in document
|
||||
map(([account, document]) => {
|
||||
log(`Found document`, document);
|
||||
return document.names ? Object.values(document.names).includes(account.pubkey) : false;
|
||||
}),
|
||||
// start with true until document is checked
|
||||
startWith(true),
|
||||
tap((paid) => log(`Account paid ${paid}`)),
|
||||
);
|
||||
|
||||
const dismiss = hidePaywall.pipe(
|
||||
map((hideUntil) => (hideUntil ? hideUntil > unixNow() : false)),
|
||||
tap((dismissed) => log(`Paywall dismissed ${dismissed}`)),
|
||||
);
|
||||
|
||||
paywall = combineLatest([dismiss, accountPaid]).pipe(
|
||||
map(([dismiss, account]) => dismiss || account),
|
||||
// share results for UI
|
||||
shareReplay(1),
|
||||
);
|
||||
} else {
|
||||
paywall = of(true);
|
||||
}
|
||||
|
||||
export { paywall };
|
Loading…
x
Reference in New Issue
Block a user