From 14bc964aa471610611f1944e44159ceeb4f231cd Mon Sep 17 00:00:00 2001 From: Kushagra Gour Date: Tue, 19 Mar 2024 15:53:02 +0530 Subject: [PATCH] add subs details in profile modal --- src/components/Footer.jsx | 43 +++++++++---- src/components/Panel.jsx | 31 +++++++++ src/components/Profile.jsx | 128 ++++++++++++++++++++++++++++--------- src/components/Text.jsx | 2 +- src/components/app.jsx | 9 ++- src/db.js | 28 ++++++++ src/style.css | 54 ++++++++++++++++ src/utils.js | 35 ++++++++++ 8 files changed, 284 insertions(+), 46 deletions(-) create mode 100644 src/components/Panel.jsx diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 4b8792c..634c25c 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -132,20 +132,35 @@ export default class Footer extends Component { - + {user?.isPro ? ( + + ) : ( + + )} {this.props.prefs.isJs13kModeOn ? ( diff --git a/src/components/Panel.jsx b/src/components/Panel.jsx new file mode 100644 index 0000000..0653837 --- /dev/null +++ b/src/components/Panel.jsx @@ -0,0 +1,31 @@ +import { forwardRef } from 'preact/compat'; + +export const Panel = forwardRef(function Panel( + { + classes = '', + padding = '2rem', + fullWidth = true, + fullHeight = false, + glowing = false, + topFocus = false, + onlyBorder = false, + children + }, + ref +) { + return ( +
+ {children} +
+ ); +}); diff --git a/src/components/Profile.jsx b/src/components/Profile.jsx index 7a1050b..ded7110 100644 --- a/src/components/Profile.jsx +++ b/src/components/Profile.jsx @@ -1,43 +1,113 @@ -import { h } from 'preact'; +import { useState, useEffect } from 'preact/hooks'; import { ProBadge } from './ProBadge'; import { HStack, Stack, VStack } from './Stack'; +import { Panel } from './Panel'; +import { Text } from './Text'; +import { getHumanReadableDate } from '../utils'; const DEFAULT_PROFILE_IMG = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23ccc' d='M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z'/%3E%3C/svg%3E"; -export function Profile({ user, logoutBtnHandler }) { +const Header = ({ user, logoutBtnHandler }) => { return ( -
- Profile image + + + Profile image - - -

+ +

{user && user.displayName ? user.displayName : 'Anonymous Creator'}

- {user.isPro && ( - - - - )} + {user.isPro && }
+ - - - - -

+ + + ); +}; + +export function Profile({ user, logoutBtnHandler }) { + const [currentSubscription, setCurrentSubscription] = useState(null); + useEffect(() => { + if (user?.isPro) { + window.db.getUserSubscriptionEvents(user.uid).then(events => { + console.log(events); + const creationEvent = events + .filter(event => event.type === 'subscription_created') + .sort((a, b) => b.timestamp.seconds - a.timestamp.seconds)[0]; + if (creationEvent) { + console.log(creationEvent); + creationEvent.attributes = creationEvent.data.data.attributes; + setCurrentSubscription(creationEvent); + } + }); + } + }, [user]); + return ( + +
+ {currentSubscription ? ( + + + + Plan: + + {' '} + {currentSubscription.attributes.product_name} + + + + Subscription Status:{' '} + {currentSubscription.attributes.status} + + + + Renews on:{' '} + + {getHumanReadableDate(currentSubscription.attributes.renews_at)} + + + + + Cancel subscription + + {/* + Link 2 + + + Link 3 + */} + + + ) : null} + ); } diff --git a/src/components/Text.jsx b/src/components/Text.jsx index c6db902..86e44a6 100644 --- a/src/components/Text.jsx +++ b/src/components/Text.jsx @@ -32,7 +32,7 @@ const sizes = { export const Text = forwardRef( ( { - size = 0, + size = 1, weight = 'normal', tag, style = 'normal', diff --git a/src/components/app.jsx b/src/components/app.jsx index a73dfed..5ff0fcc 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -1134,8 +1134,13 @@ export default class App extends Component { return false; } proBtnClickHandler() { - this.setState({ isProModalOpen: true }); - trackEvent('ui', 'proBtnClick'); + if (user?.isPro) { + this.setState({ isProfileModalOpen: true }); + trackEvent('ui', 'manageProBtnClick'); + } else { + this.setState({ isProModalOpen: true }); + trackEvent('ui', 'proBtnClick'); + } } codepenBtnClickHandler(e) { if (this.state.currentItem.cssMode === CssModes.ACSS) { diff --git a/src/db.js b/src/db.js index ff0c660..233d80e 100644 --- a/src/db.js +++ b/src/db.js @@ -5,6 +5,24 @@ import { deferred } from './deferred'; import { trackEvent } from './analytics'; import { log } from './utils'; +/** + * Converts a firestore query snapshot into native array + * @param {snapshot} querySnapshot Snapshot object returned by a firestore query + */ +function getArrayFromQuerySnapshot(querySnapshot) { + const arr = []; + querySnapshot.forEach(doc => { + // doc.data() has to be after doc.id because docs can have `id` key in them which + // should override the explicit `id` being set + arr.push({ + id: doc.id, + ...doc.data() + }); + // documentCache[doc.id] = doc.data() + }); + return arr; +} + (() => { const FAUX_DELAY = 1; @@ -179,6 +197,15 @@ import { log } from './utils'; }); } + async function getUserSubscriptionEvents(userId) { + const remoteDb = await getDb(); + return remoteDb + .collection('subscriptions') + .where('userId', '==', userId) + .get() + .then(getArrayFromQuerySnapshot); + } + window.db = { getDb, getUser, @@ -187,6 +214,7 @@ import { log } from './utils'; getSettings, fetchItem, getPublicItemCount, + getUserSubscriptionEvents, local: dbLocalAlias, sync: dbSyncAlias }; diff --git a/src/style.css b/src/style.css index 92896a5..6780108 100644 --- a/src/style.css +++ b/src/style.css @@ -2223,6 +2223,60 @@ while the theme CSS file is loading */ box-shadow: var(--shadow-elevation-low); } +.profile-modal__avatar-img.is-pro { + background: linear-gradient(45deg, var(--color-pro-1), var(--color-pro-2)); + padding: 0.2rem; + animation: avatar-rotate 2s forwards; +} +@keyframes avatar-rotate { + 0% { + transform: rotate(10deg); + } + 100% { + transform: rotate(0deg); + } +} +.profile-modal__name.is-pro { + background: linear-gradient(45deg, var(--color-pro-1), var(--color-pro-2)); + color: transparent; + background-clip: text; +} + +/* .PANEL */ +.panel { + --panel-bg: rgb(255 255 255 / 5%); + position: relative; + background: var(--panel-bg); + box-shadow: var(--panel-shadow); + border-radius: 1rem; + /* backdrop-filter: blur(20px); */ + overflow: hidden; +} + +.panelOnlyBorder { + background: none; + box-shadow: none; + backdrop-filter: none; + border: 2px solid var(--clr-border-1); +} + +.panelGlowing { + box-shadow: var(--glow-shadow); +} + +.panelTopFocus { + background: radial-gradient( + 82.25% 100% at 50% 0%, + rgba(var(--rgb-gray-1), 0.75) 37.28%, + rgba(var(--rgb-gray-0), 0) 100% + ); + + box-shadow: + 0 0 30px rgba(var(--rgb-brand), 0), + 0px 20px 50px rgba(0, 0, 0, 0.1), + inset 0px 1px 3px rgba(255, 255, 255, 0.1); +} + @media screen and (max-width: 600px) { body { font-size: 70%; diff --git a/src/utils.js b/src/utils.js index 7b18399..f903b04 100644 --- a/src/utils.js +++ b/src/utils.js @@ -205,6 +205,41 @@ export function getHumanDate(timestamp) { return retVal; } +/** + * Convert any date-ish string/obj to human readable form -> Jul 02, 2021 + * @param {string?object} date date to be formatted + * @returns string + */ +export function getHumanReadableDate( + date, + { showTime = true, utc = false } = {} +) { + if (!date) return ''; + let d = typeof date.toDate === 'function' ? date.toDate() : new Date(date); + if (utc) { + d = new Date( + Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours()) + ); + } + + let options = { + year: 'numeric', + month: 'short', + day: 'numeric' + }; + if (showTime) { + options = { + ...options, + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: true + }; + } + const dateTimeString = d.toLocaleString(false, options); + return dateTimeString; +} + // create a one-time event export function once(node, type, callback) { // create event