2022-03-09 19:05:37 +01:00
/* eslint-disable no-console */
// @flow
import React from 'react' ;
import moment from 'moment' ;
import Page from 'component/page' ;
import Spinner from 'component/spinner' ;
import { Lbryio } from 'lbryinc' ;
import { getStripeEnvironment } from 'util/stripe' ;
2022-05-23 04:33:23 +02:00
import { ODYSEE _CHANNEL } from 'constants/channels' ;
2022-03-09 19:05:37 +01:00
import * as ICONS from 'constants/icons' ;
import * as PAGES from 'constants/pages' ;
import * as MODALS from 'constants/modal_types' ;
import Card from 'component/common/card' ;
import MembershipSplash from 'component/membershipSplash' ;
import Button from 'component/button' ;
import ChannelSelector from 'component/channelSelector' ;
2022-07-01 21:40:06 +02:00
import PremiumBadge from 'component/premiumBadge' ;
2022-03-15 03:38:45 +01:00
import I18nMessage from 'component/i18nMessage' ;
2022-03-09 19:05:37 +01:00
import useGetUserMemberships from 'effects/use-get-user-memberships' ;
import usePersistedState from 'effects/use-persisted-state' ;
2022-05-23 04:22:58 +02:00
const stripeEnvironment = getStripeEnvironment ( ) ;
2022-03-09 19:05:37 +01:00
const isDev = process . env . NODE _ENV !== 'production' ;
2022-05-23 04:22:58 +02:00
function log ( ... args ) {
// @if process.env.LOG_MEMBERSHIP='true'
console . log ( args ) ;
// @endif
}
2022-03-09 19:05:37 +01:00
type Props = {
history : { action : string , push : ( string ) => void , replace : ( string ) => void } ,
location : { search : string , pathname : string } ,
totalBalance : ? number ,
openModal : ( string , { } ) => void ,
activeChannelClaim : ? ChannelClaim ,
channels : ? Array < ChannelClaim > ,
claimsByUri : { [ string ] : any } ,
fetchUserMemberships : ( claimIdCsv : string ) => void ,
incognito : boolean ,
updateUserOdyseeMembershipStatus : ( ) => void ,
user : ? User ,
2022-03-17 07:12:16 +01:00
locale : ? LocaleInfo ,
2022-03-23 18:13:04 +01:00
preferredCurrency : ? string ,
2022-03-09 19:05:37 +01:00
} ;
const OdyseeMembershipPage = ( props : Props ) => {
const {
openModal ,
activeChannelClaim ,
channels ,
claimsByUri ,
fetchUserMemberships ,
updateUserOdyseeMembershipStatus ,
incognito ,
user ,
2022-03-17 07:12:16 +01:00
locale ,
2022-03-23 18:13:04 +01:00
preferredCurrency ,
2022-03-09 19:05:37 +01:00
} = props ;
const userChannelName = activeChannelClaim ? activeChannelClaim . name : '' ;
const userChannelClaimId = activeChannelClaim && activeChannelClaim . claim _id ;
const [ cardSaved , setCardSaved ] = React . useState ( ) ;
const [ membershipOptions , setMembershipOptions ] = React . useState ( ) ;
const [ userMemberships , setUserMemberships ] = React . useState ( ) ;
2022-03-15 03:38:45 +01:00
const [ currencyToUse , setCurrencyToUse ] = React . useState ( 'usd' ) ;
2022-03-09 19:05:37 +01:00
const [ canceledMemberships , setCanceledMemberships ] = React . useState ( ) ;
const [ activeMemberships , setActiveMemberships ] = React . useState ( ) ;
const [ purchasedMemberships , setPurchasedMemberships ] = React . useState ( [ ] ) ;
const [ hasShownModal , setHasShownModal ] = React . useState ( false ) ;
const [ shouldFetchUserMemberships , setFetchUserMemberships ] = React . useState ( true ) ;
2022-03-15 03:38:45 +01:00
const [ apiError , setApiError ] = React . useState ( false ) ;
2022-03-09 19:05:37 +01:00
const [ showHelp , setShowHelp ] = usePersistedState ( 'premium-help-seen' , true ) ;
const hasMembership = activeMemberships && activeMemberships . length > 0 ;
const channelUrls = channels && channels . map ( ( channel ) => channel . permanent _url ) ;
// check if membership data for user is already fetched, if it's needed then fetch it
useGetUserMemberships ( shouldFetchUserMemberships , channelUrls , claimsByUri , ( value ) => {
fetchUserMemberships ( value ) ;
setFetchUserMemberships ( false ) ;
} ) ;
async function populateMembershipData ( ) {
try {
// show the memberships the user is subscribed to
const response = await Lbryio . call (
'membership' ,
'mine' ,
{
environment : stripeEnvironment ,
} ,
'post'
) ;
2022-03-16 16:34:34 +01:00
log ( 'mine response' ) ;
log ( response ) ;
2022-03-09 19:05:37 +01:00
let activeMemberships = [ ] ;
let canceledMemberships = [ ] ;
let purchasedMemberships = [ ] ;
for ( const membership of response ) {
// if it's autorenewing it's considered 'active'
const isActive = membership . Membership . auto _renew ;
if ( isActive ) {
activeMemberships . push ( membership ) ;
} else {
canceledMemberships . push ( membership ) ;
}
purchasedMemberships . push ( membership . Membership . membership _id ) ;
}
// hide the other membership options if there's already a purchased membership
if ( activeMemberships . length > 0 ) {
setMembershipOptions ( false ) ;
}
setActiveMemberships ( activeMemberships ) ;
setCanceledMemberships ( canceledMemberships ) ;
setPurchasedMemberships ( purchasedMemberships ) ;
// update the state to show the badge
fetchUserMemberships ( userChannelClaimId || '' ) ;
setUserMemberships ( response ) ;
} catch ( err ) {
2022-03-15 03:38:45 +01:00
setApiError ( true ) ;
2022-03-09 19:05:37 +01:00
console . log ( err ) ;
}
setFetchUserMemberships ( false ) ;
}
React . useEffect ( ( ) => {
if ( ! shouldFetchUserMemberships ) setFetchUserMemberships ( true ) ;
} , [ shouldFetchUserMemberships ] ) ;
2022-03-15 03:38:45 +01:00
// make calls to backend and populate all the data for the frontend
2022-03-09 19:05:37 +01:00
React . useEffect ( function ( ) {
2022-03-15 03:38:45 +01:00
// TODO: this should be refactored to make these calls in parallel
2022-03-09 19:05:37 +01:00
( async function ( ) {
try {
// check if there is a payment method
const response = await Lbryio . call (
'customer' ,
'status' ,
{
environment : stripeEnvironment ,
} ,
'post'
) ;
2022-03-15 03:38:45 +01:00
2022-03-16 16:34:34 +01:00
log ( 'customer/status response' ) ;
log ( response ) ;
2022-03-09 19:05:37 +01:00
// hardcoded to first card
const hasAPaymentCard = Boolean ( response && response . PaymentMethods && response . PaymentMethods [ 0 ] ) ;
setCardSaved ( hasAPaymentCard ) ;
} catch ( err ) {
const customerDoesntExistError = 'user as customer is not setup yet' ;
if ( err . message === customerDoesntExistError ) {
setCardSaved ( false ) ;
} else {
2022-03-15 03:38:45 +01:00
setApiError ( true ) ;
2022-03-09 19:05:37 +01:00
console . log ( err ) ;
}
}
try {
// check the available membership for odysee.com
const response = await Lbryio . call (
'membership' ,
'list' ,
{
environment : stripeEnvironment ,
2022-05-23 04:33:23 +02:00
// Using @odysee's channel info as memberships are only for @odysee.
channel _id : ODYSEE _CHANNEL . ID ,
channel _name : ODYSEE _CHANNEL . NAME ,
2022-03-09 19:05:37 +01:00
} ,
'post'
) ;
2022-03-16 16:34:34 +01:00
log ( 'membership/list response' ) ;
log ( response ) ;
2022-03-09 19:05:37 +01:00
// hide other options if there's already a membership
if ( activeMemberships && activeMemberships . length > 0 ) {
setMembershipOptions ( false ) ;
} else {
setMembershipOptions ( response ) ;
}
2022-03-15 03:38:45 +01:00
} catch ( err ) {
setApiError ( true ) ;
console . log ( err ) ;
}
2022-03-23 18:13:04 +01:00
// use currency if set on client, otherwise use USD by default or EUR if in Europe
if ( preferredCurrency ) {
setCurrencyToUse ( preferredCurrency . toLowerCase ( ) ) ;
} else {
if ( locale ? . continent === 'EU' ) {
setCurrencyToUse ( 'eur' ) ;
}
2022-03-09 19:05:37 +01:00
}
populateMembershipData ( ) ;
} ) ( ) ;
// eslint-disable-next-line react-hooks/exhaustive-deps
} , [ ] ) ;
2022-03-15 03:38:45 +01:00
// we are still waiting from the backend if any of these are undefined
2022-03-09 19:05:37 +01:00
const stillWaitingFromBackend =
purchasedMemberships === undefined ||
cardSaved === undefined ||
membershipOptions === undefined ||
2022-03-15 03:38:45 +01:00
userMemberships === undefined ||
currencyToUse === undefined ;
2022-03-09 19:05:37 +01:00
const formatDate = function ( date ) {
return moment ( new Date ( date ) ) . format ( 'MMMM DD YYYY' ) ;
} ;
2022-03-15 03:38:45 +01:00
// clear membership data
2022-03-09 19:05:37 +01:00
const deleteData = async function ( ) {
2022-03-15 03:38:45 +01:00
await Lbryio . call (
'membership' ,
'clear' ,
{
environment : 'test' ,
} ,
'post'
) ;
2022-03-09 19:05:37 +01:00
// $FlowFixMe
location . reload ( ) ;
} ;
// dont pass channel name and id when calling purchase
const noChannelsOrIncognitoMode = incognito || ! channels ;
// TODO: can clean this up, some repeating text
function buildPurchaseString ( price , interval , plan ) {
let featureString = '' ;
2022-03-15 03:38:45 +01:00
2022-03-09 19:05:37 +01:00
// generate different strings depending on other conditions
if ( plan === 'Premium' && ! noChannelsOrIncognitoMode ) {
2022-03-15 03:38:45 +01:00
featureString = (
2022-03-16 07:40:33 +01:00
< I18nMessage tokens = { { channel _name : < b className = "membership-bolded" > { userChannelName } < / b > } } >
Your badge will be shown for your % channel _name % channel in all areas of the app , and can be added to two
2022-03-15 03:38:45 +01:00
additional channels in the future for free .
< / I18nMessage >
) ;
2022-03-09 19:05:37 +01:00
} else if ( plan === 'Premium+' && ! noChannelsOrIncognitoMode ) {
2022-03-15 03:38:45 +01:00
// user has channel selected
featureString = (
2022-03-16 07:40:33 +01:00
< I18nMessage tokens = { { channel _name : < b className = "membership-bolded" > { userChannelName } < / b > } } >
The no ads feature applies site - wide for all channels and your badge will be shown for your % channel _name %
channel in all areas of the app , and can be added to two additional channels in the future for free .
2022-03-15 03:38:45 +01:00
< / I18nMessage >
) ;
2022-03-09 19:05:37 +01:00
} else if ( plan === 'Premium' && ! channels ) {
2022-03-15 03:38:45 +01:00
// user has no channels
2022-03-16 07:40:33 +01:00
featureString = _ _ (
'You currently have no channels. To show your badge on a channel, please create a channel first. If you register a channel later you will be able to show a badge for up to three channels.'
) ;
2022-03-09 19:05:37 +01:00
} else if ( plan === 'Premium+' && ! channels ) {
2022-03-15 03:38:45 +01:00
// user has no channels
2022-03-16 07:40:33 +01:00
featureString = _ _ (
'The no ads feature applies site-wide. You currently have no channels. To show your badge on a channel, please create a channel first. If you register a channel later you will be able to show a badge for up to three channels.'
) ;
2022-03-09 19:05:37 +01:00
} else if ( plan === 'Premium' && incognito ) {
2022-03-15 03:38:45 +01:00
// user has incognito selected
2022-03-16 07:40:33 +01:00
featureString = _ _ (
'You currently have no channel selected and will not have a badge be visible, if you want to show a badge you can select a channel now, or you can show a badge for up to three channels in the future for free.'
) ;
2022-03-09 19:05:37 +01:00
} else if ( plan === 'Premium+' && incognito ) {
2022-03-15 03:38:45 +01:00
// user has incognito selected
2022-03-16 07:40:33 +01:00
featureString = _ _ (
'The no ads feature applies site-wide. You currently have no channel selected and will not have a badge be visible, if you want to show a badge you can select a channel now, or you can show a badge for up to three channels in the future for free.'
) ;
2022-03-09 19:05:37 +01:00
}
2022-03-28 14:20:37 +02:00
const priceDisplayString = (
< I18nMessage
tokens = { {
monthly _yearly _bolded : (
< b className = "membership-bolded" > { interval === 'month' ? _ _ ( 'monthly' ) : _ _ ( 'yearly' ) } < / b >
) ,
monthly _yearly : interval === 'month' ? _ _ ( 'monthly' ) : _ _ ( 'yearly' ) ,
price : (
< b className = "membership-bolded" > { ` ${ currencyToUse . toUpperCase ( ) } ${ currencyToUse === 'usd' ? '$' : '€' } ${
price / 100
} ` }</b>
) ,
} }
>
You are purchasing a % monthly _yearly _bolded % % plan % membership that is active immediately and will renew
% monthly _yearly % at a price of % price % .
< / I18nMessage >
2022-03-15 03:38:45 +01:00
) ;
2022-03-09 19:05:37 +01:00
2022-03-28 21:24:46 +02:00
const noRefund = _ _ (
'You can cancel Premium at any time (no refunds) and you can also close this window and choose a different membership option.'
2022-03-16 07:40:33 +01:00
) ;
return (
2022-03-15 03:38:45 +01:00
< >
2022-03-16 07:40:33 +01:00
{ priceDisplayString } { featureString } { noRefund }
2022-03-15 03:38:45 +01:00
< / >
) ;
2022-03-09 19:05:37 +01:00
}
const purchaseMembership = function ( e , membershipOption , price ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
const planName = membershipOption . Membership . name ;
const membershipId = e . currentTarget . getAttribute ( 'membership-id' ) ;
const priceId = e . currentTarget . getAttribute ( 'price-id' ) ;
const purchaseString = buildPurchaseString ( price . unit _amount , price . recurring . interval , planName ) ;
openModal ( MODALS . CONFIRM _ODYSEE _MEMBERSHIP , {
membershipId ,
userChannelClaimId : noChannelsOrIncognitoMode ? undefined : userChannelClaimId ,
userChannelName : noChannelsOrIncognitoMode ? undefined : userChannelName ,
priceId ,
purchaseString ,
plan : planName ,
populateMembershipData ,
setMembershipOptions ,
updateUserOdyseeMembershipStatus ,
user ,
} ) ;
} ;
const cancelMembership = async function ( e , membership ) {
const membershipId = e . currentTarget . getAttribute ( 'membership-id' ) ;
const cancellationString =
'You are cancelling your Odysee Premium. You will still have access to all the paid ' +
'features until the point of the expiration of your current membership, at which point you will not be charged ' +
'again and your membership will no longer be active. At this time, there is no way to subscribe to another membership if you cancel and there are no refunds.' ;
openModal ( MODALS . CONFIRM _ODYSEE _MEMBERSHIP , {
membershipId ,
hasMembership ,
2022-03-15 03:38:45 +01:00
purchaseString : _ _ ( cancellationString ) ,
2022-03-09 19:05:37 +01:00
populateMembershipData ,
} ) ;
} ;
2022-03-15 03:38:45 +01:00
function convertIntervalVariableToString ( price ) {
2022-03-09 19:05:37 +01:00
const interval = price . recurring . interval ;
if ( interval === 'year' ) {
2022-03-15 03:38:45 +01:00
return _ _ ( 'Yearly' ) ;
2022-03-09 19:05:37 +01:00
} else if ( interval === 'month' ) {
2022-03-15 03:38:45 +01:00
return _ _ ( 'Monthly' ) ;
2022-03-09 19:05:37 +01:00
}
}
2022-03-15 03:38:45 +01:00
function capitalizedInterval ( planInterval ) {
if ( planInterval === 'year' ) {
return _ _ ( 'Year' ) ;
} else {
return _ _ ( 'Month' ) ;
}
2022-03-09 19:05:37 +01:00
}
function buildCurrencyDisplay ( priceObject ) {
let currencySymbol ;
if ( priceObject . currency === 'eur' ) {
currencySymbol = '€' ;
} else if ( priceObject . currency === 'usd' ) {
currencySymbol = '$' ;
}
const currency = priceObject . currency . toUpperCase ( ) ;
return currency + ' ' + currencySymbol ;
}
const urlSearchParams = new URLSearchParams ( window . location . search ) ;
const params = Object . fromEntries ( urlSearchParams . entries ( ) ) ;
const { interval , plan } = params ;
const planValue = params . plan ;
// description to be shown under plan name
2022-03-15 03:38:45 +01:00
function getPlanDescription ( plan , active ? ) {
2022-03-09 19:05:37 +01:00
if ( plan === 'Premium' ) {
2022-03-31 07:06:30 +02:00
return 'Badge on profile, automatic rewards confirmation, and early access to new features' ;
2022-03-09 19:05:37 +01:00
// if there's more plans added this needs to be expanded
2022-03-15 03:38:45 +01:00
} else if ( active ) {
2022-03-09 19:05:37 +01:00
return 'All Premium features, and no ads' ;
2022-03-15 03:38:45 +01:00
} else {
2022-03-31 07:06:30 +02:00
return 'Badge on profile, automatic rewards confirmation, early access to new features, and no ads' ;
2022-03-09 19:05:37 +01:00
}
}
// add a bit of a delay otherwise it's a bit jarring
const timeoutValue = 300 ;
// if user already selected plan, wait a bit (so it's not jarring) and open modal
React . useEffect ( ( ) => {
if ( ! stillWaitingFromBackend && planValue && cardSaved ) {
2022-03-15 03:38:45 +01:00
const delayTimeout = setTimeout ( function ( ) {
2022-03-09 19:05:37 +01:00
// clear query params
window . history . replaceState ( null , null , window . location . pathname ) ;
setHasShownModal ( true ) ;
// open confirm purchase
// $FlowFixMe
document . querySelector ( '[plan="' + plan + '"][interval="' + interval + '"]' ) . click ( ) ;
} , timeoutValue ) ;
2022-03-15 03:38:45 +01:00
return ( ) => clearTimeout ( delayTimeout ) ;
2022-03-09 19:05:37 +01:00
}
} , [ stillWaitingFromBackend , planValue , cardSaved ] ) ;
const helpText = (
< div className = "section__subtitle" >
< p >
{ _ _ (
2022-03-16 07:40:33 +01:00
'First of all, thank you for considering or purchasing a membership, it means a ton to us! A few important details to know:'
2022-03-09 19:05:37 +01:00
) }
< / p >
< p >
< ul >
< li >
{ _ _ (
2022-03-31 07:06:30 +02:00
'Exclusive and early access features include: recommended content, homepage customization, and the ability to post Odysee hyperlinks + images in comments. Account is also automatically eligible for Rewards. More to come later.'
2022-03-09 19:05:37 +01:00
) }
< / li >
2022-03-15 03:38:45 +01:00
< li >
{ _ _ (
2022-03-16 07:40:33 +01:00
'The yearly Premium+ membership has a discount compared to monthly, and Premium is only available yearly.'
2022-03-15 03:38:45 +01:00
) }
< / li >
2022-03-16 07:40:33 +01:00
< li > { _ _ ( 'These are limited time rates, so get in early!' ) } < / li >
2022-03-09 19:05:37 +01:00
< li >
{ _ _ (
2022-03-16 07:40:33 +01:00
'There may be higher tiers available in the future for creators and anyone else who wants to support us.'
2022-03-09 19:05:37 +01:00
) }
< / li >
< li >
2022-03-16 07:40:33 +01:00
{ _ _ ( 'Badges will be displayed on a single channel to start, with an option to add on two more later on.' ) }
2022-03-09 19:05:37 +01:00
< / li >
< li >
2022-03-16 07:40:33 +01:00
{ _ _ ( 'Cannot upgrade or downgrade a membership at this time. Refunds are not available. Choose wisely.' ) }
2022-03-09 19:05:37 +01:00
< / li >
< / ul >
< / p >
< / div >
) ;
return (
< >
< Page className = "premium-wrapper" >
{ /** splash frontend **/ }
2022-03-15 03:38:45 +01:00
{ ! stillWaitingFromBackend && ! apiError && purchasedMemberships . length === 0 && ! planValue && ! hasShownModal ? (
2022-03-09 19:05:37 +01:00
< MembershipSplash pageLocation = { 'confirmPage' } currencyToUse = { currencyToUse } / >
) : (
/** odysee membership page **/
< div className = { 'card-stack' } >
{ ! stillWaitingFromBackend && cardSaved !== false && (
< >
< h1 style = { { fontSize : '23px' } } > { _ _ ( 'Odysee Premium' ) } < / h1 >
{ /* let user switch channel */ }
< div style = { { marginTop : '10px' } } >
< ChannelSelector uri = { activeChannelClaim && activeChannelClaim . permanent _url } / >
{ /* explainer help text */ }
< Card
titleActions = {
< Button
button = "close"
icon = { showHelp ? ICONS . UP : ICONS . DOWN }
onClick = { ( ) => setShowHelp ( ! showHelp ) }
/ >
}
title = { _ _ ( 'Get More Information' ) }
2022-03-16 07:40:33 +01:00
subtitle = { _ _ ( 'Expand to learn more about how Odysee Premium works' ) }
2022-03-09 19:05:37 +01:00
actions = { showHelp && helpText }
className = { 'premium-explanation-text' }
/ >
< / div >
< / >
) }
{ /** available memberships **/ }
{ /* if they have a card and don't have a membership yet */ }
{ ! stillWaitingFromBackend && membershipOptions && purchasedMemberships . length < 1 && cardSaved !== false && (
< >
< div className = "card__title-section" >
< h2 className = "card__title" > { _ _ ( 'Available Memberships' ) } < / h2 >
< / div >
< Card >
{ membershipOptions . map ( ( membershipOption , i ) => (
< >
< div key = { i } >
{ purchasedMemberships && ! purchasedMemberships . includes ( membershipOption . Membership . id ) && (
< >
< div className = "premium-option" >
2022-03-15 03:38:45 +01:00
{ /* plan title */ }
2022-03-09 19:05:37 +01:00
< h4 className = "membership_title" >
2022-03-31 07:07:55 +02:00
{ membershipOption . Membership . name }
2022-03-09 19:05:37 +01:00
< PremiumBadge membership = { membershipOption . Membership . name } / >
< / h4 >
{ /* plan description */ }
< h4 className = "membership_subtitle" >
2022-03-15 03:38:45 +01:00
{ _ _ ( getPlanDescription ( membershipOption . Membership . name ) ) }
2022-03-09 19:05:37 +01:00
< / h4 >
< >
2022-03-15 03:38:45 +01:00
{ /* display different plans */ }
2022-03-09 19:05:37 +01:00
{ membershipOption . Prices . map ( ( price ) => (
< >
2022-03-15 03:38:45 +01:00
{ /* dont show a monthly Premium membership option (yearly only) */ }
2022-03-09 19:05:37 +01:00
{ ! (
2022-07-12 17:54:15 +02:00
price . recurring . interval === 'month' &&
2022-03-09 19:05:37 +01:00
membershipOption . Membership . name === 'Premium'
) && (
< >
2022-07-12 17:54:15 +02:00
{ price . currency === currencyToUse && (
2022-03-09 19:05:37 +01:00
< div >
< h4 className = "membership_info" >
2022-07-12 17:54:15 +02:00
< b > { _ _ ( 'Interval' ) } : < / b > { convertIntervalVariableToString ( price ) }
2022-03-09 19:05:37 +01:00
< / h4 >
< h4 className = "membership_info" >
2022-07-12 17:54:15 +02:00
< b > { _ _ ( 'Price' ) } : < / b > { buildCurrencyDisplay ( price ) }
{ price . unit _amount / 100 } / { capitalizedInterval ( price . recurring . interval ) }
2022-03-09 19:05:37 +01:00
< / h4 >
< Button
button = "primary"
2022-07-12 17:54:15 +02:00
onClick = { ( e ) => purchaseMembership ( e , membershipOption , price ) }
2022-03-09 19:05:37 +01:00
membership - id = { membershipOption . Membership . id }
membership - subscription - period = { membershipOption . Membership . type }
2022-07-12 17:54:15 +02:00
price - id = { price . id }
2022-03-09 19:05:37 +01:00
className = "membership_button"
2022-03-16 07:40:33 +01:00
label = { _ _ ( 'Join via %interval% membership' , {
2022-07-12 17:54:15 +02:00
interval : price . recurring . interval ,
2022-03-15 03:38:45 +01:00
} ) }
2022-03-09 19:05:37 +01:00
icon = { ICONS . FINANCE }
2022-07-12 17:54:15 +02:00
interval = { price . recurring . interval }
2022-03-09 19:05:37 +01:00
plan = { membershipOption . Membership . name }
/ >
< / div >
) }
< / >
) }
< / >
) ) }
< / >
< / div >
< / >
) }
< / div >
< / >
) ) }
< / Card >
< / >
) }
{ ! stillWaitingFromBackend && cardSaved === true && (
< >
< div className = "card__title-section" >
< h2 className = "card__title" > { _ _ ( 'Your Active Memberships' ) } < / h2 >
< / div >
< Card >
{ /** * list of active memberships from user ***/ }
< div >
{ /* <h1 style={{ fontSize: '19px' }}>Active Memberships</h1> */ }
{ ! stillWaitingFromBackend && activeMemberships && activeMemberships . length === 0 && (
2022-03-16 07:40:33 +01:00
< h4 > { _ _ ( 'You currently have no active memberships' ) } < / h4 >
2022-03-09 19:05:37 +01:00
) }
{ /** active memberships **/ }
{ ! stillWaitingFromBackend &&
activeMemberships &&
activeMemberships . map ( ( membership ) => (
< >
< div className = "premium-option" >
{ /* membership name */ }
< h4 className = "membership_title" >
{ membership . MembershipDetails . name }
< PremiumBadge membership = { membership . MembershipDetails . name } / >
< / h4 >
{ /* description section */ }
< h4 className = "membership_subtitle" >
2022-03-15 03:38:45 +01:00
{ _ _ ( getPlanDescription ( membership . MembershipDetails . name , 'active' ) ) }
2022-03-09 19:05:37 +01:00
< / h4 >
2022-03-15 03:38:45 +01:00
{ /* registered on */ }
2022-03-09 19:05:37 +01:00
< h4 className = "membership_info" >
2022-03-16 07:40:33 +01:00
< b > { _ _ ( 'Registered On' ) } : < / b > { formatDate ( membership . Membership . created _at ) }
2022-03-09 19:05:37 +01:00
< / h4 >
2022-03-15 03:38:45 +01:00
{ /* autorenews at */ }
2022-03-09 19:05:37 +01:00
< h4 className = "membership_info" >
< b > { _ _ ( 'Auto-Renews On' ) } : < / b > { ' ' }
{ formatDate ( membership . Subscription . current _period _end * 1000 ) }
< / h4 >
2022-03-15 03:38:45 +01:00
{ /* cancel membership button */ }
2022-03-09 19:05:37 +01:00
< Button
button = "alt"
membership - id = { membership . Membership . membership _id }
onClick = { ( e ) => cancelMembership ( e , membership ) }
className = "cancel-membership-button"
label = { _ _ ( 'Cancel membership' ) }
icon = { ICONS . FINANCE }
/ >
< / div >
< / >
) ) }
< / div >
< / Card >
< >
{ /** canceled memberships **/ }
< div className = "card__title-section" >
< h2 className = "card__title" > { _ _ ( 'Canceled Memberships' ) } < / h2 >
< / div >
< Card >
{ canceledMemberships && canceledMemberships . length === 0 && (
2022-03-16 07:40:33 +01:00
< h4 > { _ _ ( 'You currently have no canceled memberships' ) } < / h4 >
2022-03-09 19:05:37 +01:00
) }
{ canceledMemberships &&
canceledMemberships . map ( ( membership ) => (
< >
< h4 className = "membership_title" >
{ membership . MembershipDetails . name }
< PremiumBadge membership = { membership . MembershipDetails . name } / >
< / h4 >
< div className = "premium-option" >
< h4 className = "membership_info" >
2022-03-15 03:38:45 +01:00
< b > { _ _ ( 'Registered On' ) } : < / b > { formatDate ( membership . Membership . created _at ) }
2022-03-09 19:05:37 +01:00
< / h4 >
< h4 className = "membership_info" >
2022-03-15 03:38:45 +01:00
< b > { _ _ ( 'Canceled On' ) } : < / b > { formatDate ( membership . Subscription . canceled _at * 1000 ) }
2022-03-09 19:05:37 +01:00
< / h4 >
< h4 className = "membership_info" >
2022-03-15 03:38:45 +01:00
< b > { _ _ ( 'Still Valid Until' ) } : < / b > { formatDate ( membership . Membership . expires ) }
2022-03-09 19:05:37 +01:00
< / h4 >
< / div >
< / >
) ) }
< / Card >
< / >
< / >
) }
{ /** send user to add card if they don't have one yet */ }
{ ! stillWaitingFromBackend && cardSaved === false && (
< div >
< br / >
< h2 className = { 'getPaymentCard' } >
2022-03-15 03:38:45 +01:00
{ _ _ ( 'Please save a card as a payment method so you can join Odysee Premium' ) }
2022-03-09 19:05:37 +01:00
< / h2 >
< Button
button = "primary"
2022-03-24 08:13:14 +01:00
label = { _ _ ( 'Add a Card' ) }
2022-03-09 19:05:37 +01:00
icon = { ICONS . SETTINGS }
2022-03-23 21:14:15 +01:00
navigate = { ` / $ / ${ PAGES . SETTINGS _STRIPE _CARD } ?returnTo=premium ` }
2022-03-09 19:05:37 +01:00
className = "membership_button"
2022-03-15 03:38:45 +01:00
style = { { maxWidth : '151px' } }
2022-03-09 19:05:37 +01:00
/ >
< / div >
) }
{ /** loading section **/ }
2022-03-15 03:38:45 +01:00
{ stillWaitingFromBackend && ! apiError && (
2022-03-09 19:05:37 +01:00
< div className = "main--empty" >
< Spinner / >
< / div >
) }
2022-03-15 03:38:45 +01:00
{ /** loading section **/ }
{ stillWaitingFromBackend && apiError && (
< div className = "main--empty" >
< h1 style = { { fontSize : '19px' } } >
{ _ _ ( 'Sorry, there was an error, please contact support or try again later' ) }
< / h1 >
< / div >
) }
2022-03-09 19:05:37 +01:00
{ /** clear membership data (only available on dev) **/ }
{ isDev && cardSaved && purchasedMemberships . length > 0 && (
< >
2022-03-16 07:40:33 +01:00
< h1 style = { { marginTop : '30px' , fontSize : '20px' } } > Clear Membership Data ( Only Available On Dev ) < / h1 >
2022-03-09 19:05:37 +01:00
< div >
< Button
button = "primary"
2022-03-16 07:40:33 +01:00
label = "Clear Membership Data"
2022-03-09 19:05:37 +01:00
icon = { ICONS . SETTINGS }
className = "membership_button"
onClick = { deleteData }
/ >
< / div >
< / >
) }
< / div >
) }
< / Page >
< / >
) ;
} ;
export default OdyseeMembershipPage ;