Skip to content

Instantly share code, notes, and snippets.

@WebD00D
Created May 15, 2018 19:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WebD00D/a9043f77423578ed3800e0a55e1779c8 to your computer and use it in GitHub Desktop.
Save WebD00D/a9043f77423578ed3800e0a55e1779c8 to your computer and use it in GitHub Desktop.
paywall app
import React, { Component } from "react";
import "./App.css";
import fire from "./firebase";
import ReactGA from "react-ga";
import cx from "classnames";
import ReactJWPlayer from "react-jw-player";
import InitialView from "./components/InitialView";
import Signup from "./components/Signup";
import SignIn from "./components/SignIn";
import SignedInView from "./components/SignedInView";
import LearnMore from "./components/LearnMore";
class App extends Component {
constructor(props) {
super(props);
this._handleAccountCreation = this._handleAccountCreation.bind(this);
this._handleCustomerCardSave = this._handleCustomerCardSave.bind(this);
this._checkForExistingUser = this._checkForExistingUser.bind(this);
this._handleFreeCreditUsage = this._handleFreeCreditUsage.bind(this);
this._handleStoryCharge = this._handleStoryCharge.bind(this);
this._handleShowStory = this._handleShowStory.bind(this);
this._hackChecker = this._hackChecker.bind(this);
this._onAdPlay = this._onAdPlay.bind(this);
this._handleAdEnd = this._handleAdEnd.bind(this);
this._handleTabChange = this._handleTabChange.bind(this);
this._handleTabFocus = this._handleTabFocus.bind(this);
window.addEventListener("blur", this._handleTabChange);
window.addEventListener("focus", this._handleTabFocus);
this.state = {
// publisher ..
publicationHomePage: "",
publicationId: "",
publicationName: "",
publicationAdFileURL: "http://content.jwplatform.com/videos/cq9gJhoq-EcnFEWyJ.mp4",
contentId: "",
payWall: false,
videoAd: false,
payWallPrice: "",
protectedPath: "",
protectedArticleId: "",
protectedArticleTitle: "",
protectedArticleAudio: "",
paid: false,
// user authentication ..
authentication_userAuthenticated: false,
authentication_userId: "",
authentication_stripeCustomerId: "",
authentication_name: "",
authentication_accountEmail: "",
authentication_hasFreeStories: false,
authentication_freeStoriesRemaining: false,
authentication_creditsRemaining: 0,
authentication_purchased: [],
authentication_show_signup: false, // should be false.
authentication_show_signin: false, // should be false,
authentication_show_learnmore: false, // should be false
// display ..
display_zIndex: -1,
display_widgetWidth: "400px",
display_right: "0px",
display_error: "",
display_success: "",
// overall app state..
loading: false,
step: "payment", // should be "personal" by default
createAccountError: false
};
}
_handleTabChange () {
// console.log("tab changed..pause the video..");
// this.setState({
// videoAd:false
// })
}
_handleTabFocus () {
// console.log("window is focused..resume video play..")
// this.setState({
// videoAd:true
// })
}
_handleAdEnd() {
this.setState({
videoAd: false,
paid: true
});
var dateId = Date.now();
var updates = {};
updates[
`slugs/${this.state.publicationId}/${
this.state.protectedArticleId
}/watchedAdvertisement/${dateId}/purchaseDate`
] = dateId
if ( this.state.authentication_userId ) {
updates[
`slugs/${this.state.publicationId}/${
this.state.protectedArticleId
}/watchedAdvertisement/${dateId}/userId`
] = this.state.authentication_userId;
}
fire
.database()
.ref()
.update(updates);
this._handleShowStory();
}
_onAdPlay(e) {
}
_hackChecker() {
let qcPaid = document.body.getAttribute("qc-paid");
let qcAppDisplay = document.getElementById("quietcorner-app").style.display;
let qcPayWallBGDisplay = document.getElementById("paywall-bg").style
.display;
if (this.state.payWall && qcPaid && qcAppDisplay === "none") {
console.log(
"woah..why ya' trying to take away from our hard working writers.. not cool man.. not cool.."
);
document.getElementById("quietcorner-app").style.display = "block";
}
if (this.state.payWall && qcPaid && qcPayWallBGDisplay === "none") {
console.log(
"woah..why ya' trying to take away from our hard working writers.. not cool man.. not cool.."
);
document.getElementById("paywall-bg").style.display = "flex";
}
let qcPayWallContainerDisplay;
if (document.getElementById("paywall-container")) {
qcPayWallContainerDisplay = document.getElementById("paywall-container")
.style.display;
if (
this.state.payWall &&
qcPaid &&
qcPayWallContainerDisplay === "none"
) {
console.log(
"woah..why ya' trying to take away from our hard working writers.. not cool man.. not cool.."
);
document.getElementById("paywall-container").style.display = "flex";
}
}
}
componentWillMount() {
const publicationId = document
.getElementById("quietcorner-app")
.getAttribute("data-publication");
this.setState({
publicationId,
publicationHomePage: window.location.origin
});
ReactGA.initialize("UA-112700797-1", {
gaOptions: {
debug: true
}
});
ReactGA.pageview(window.location.pathname + window.location.search);
ReactGA.set({ publisherId: publicationId });
}
componentDidMount() {
document.body.setAttribute("qc-paid", false);
setInterval(this._hackChecker, 1000);
const currentPath = window.location.pathname.replace(/\/$/, "");
fire
.database()
.ref(`slugs/${this.state.publicationId}`)
.once("value")
.then(
function(snapshot) {
const protectedArticles = snapshot.val();
if (protectedArticles) {
Object.keys(protectedArticles).map(
function(key) {
const protectedPath = protectedArticles[key].slug.replace(
/\/$/,
""
);
if (
protectedArticles[key].active &&
protectedPath === currentPath
) {
document.getElementsByTagName("html")[0].style.overflow =
"hidden";
document.body.style.overflow = "hidden";
if (window.innerWidth <= 700) {
document.body.style.position = "fixed";
}
this._checkForExistingUser(); // check for exiting user
this.setState({
payWall: true, // enables the popup to be displayed..
display_zIndex: 100000, // ensures our software is shown over the top of the website
payWallPrice: protectedArticles[key].price,
protectedPath: protectedPath,
protectedArticleTitle: protectedArticles[key].title,
protectedArticleAudio: protectedArticles[key].audio,
protectedArticleId: key
});
}
}.bind(this)
);
}
}.bind(this)
);
fire
.database()
.ref(`publications/${this.state.publicationId}`)
.once("value")
.then(
function(snap) {
const publisherInfo = snap.val();
console.log("PUBLISHER INFO", publisherInfo)
let adFile = this.state.publicationAdFileURL;
if ( publisherInfo.jwfile ) {
adFile = publisherInfo.jwfile;
}
this.setState({
publicationName: publisherInfo.publication,
publicationAdFileURL: adFile
});
}.bind(this)
);
}
_handleShowStory() {
this.setState({
payWall: false,
display_zIndex: -1
});
document.getElementsByTagName("html")[0].style.overflow = "scroll";
document.body.style.overflow = "scroll";
document.body.style.position = "initial";
document.body.setAttribute("qc-paid", true);
}
_handleFreeCreditUsage() {
const currentFreeCredit = this.state.authentication_freeStoriesRemaining;
const currentFreeCreditAfterUsage = currentFreeCredit - 1;
var updates = {};
updates[
`readers/${this.state.authentication_userId}/freeStoriesRemaining`
] = currentFreeCreditAfterUsage;
updates[`readers/${this.state.authentication_userId}/hasFreeStories`] =
currentFreeCreditAfterUsage == 0 ? false : true;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/articleId`
] = this.state.protectedArticleId;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/pricePaid`
] = 0;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/dateAccessed`
] = Date.now();
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/title`
] = this.state.protectedArticleTitle;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/audio`
] = this.state.protectedArticleAudio;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/publicationName`
] = this.state.publicationName;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/publicationId`
] = this.state.publicationId;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/slug`
] =
this.state.publicationHomePage + "" + this.state.protectedPath;
updates[
`slugs/${this.state.publicationId}/${
this.state.protectedArticleId
}/purchased/${this.state.authentication_userId}/purhcaseDate`
] = Date.now();
updates[
`slugs/${this.state.publicationId}/${
this.state.protectedArticleId
}/purchased/${this.state.authentication_userId}/usedFreeCredit`
] = true;
fire
.database()
.ref()
.update(updates)
.then(
function() {
this._checkForExistingUser();
}.bind(this)
);
}
_handleStoryCharge(article_price) {
let credits_remaining = this.state.authentication_creditsRemaining;
credits_remaining = Number(parseFloat(credits_remaining).toFixed(2));
let newAccountTotal = (
Number(credits_remaining) - Number(article_price)
).toFixed(2);
ReactGA.event({
category: `${this.state.publicationName}`,
action: `Story Purchase`,
label: `${this.state.protectedArticleTitle}`
});
var updates = {};
updates[
`readers/${this.state.authentication_userId}/credits`
] = newAccountTotal;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/articleId`
] = this.state.protectedArticleId;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/pricePaid`
] = article_price;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/dateAccessed`
] = Date.now();
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/title`
] = this.state.protectedArticleTitle;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/audio`
] = this.state.protectedArticleAudio;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/publicationName`
] = this.state.publicationName;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/publicationId`
] = this.state.publicationId;
updates[
`readers/${this.state.authentication_userId}/stories/${
this.state.protectedArticleId
}/slug`
] =
this.state.publicationHomePage + "" + this.state.protectedPath;
updates[
`slugs/${this.state.publicationId}/${
this.state.protectedArticleId
}/purchased/${this.state.authentication_userId}/purhcaseDate`
] = Date.now();
fire
.database()
.ref()
.update(updates)
.then(
function() {
this._checkForExistingUser();
}.bind(this)
);
}
_checkForExistingUser() {
const user = localStorage.getItem("qc_user");
if (user) {
// make call to firebase to "sign" user in..
fire
.database()
.ref("readers/" + user)
.once("value")
.then(
function(snapshot) {
this.setState({
authentication_userAuthenticated: true,
authentication_userId: user,
authentication_stripeCustomerId: snapshot.val().stripeCustomerId,
authentication_name: snapshot.val().name,
authentication_accountEmail: snapshot.val().email,
authentication_hasFreeStories: snapshot.val().hasFreeStories,
authentication_freeStoriesRemaining: snapshot.val()
.freeStoriesRemaining,
authentication_creditsRemaining: snapshot.val().credits,
authentication_purchased: snapshot.val().stories
});
// have they paid?
if (snapshot.val().stories) {
Object.keys(snapshot.val().stories).map(
function(key) {
if (key === this.state.protectedArticleId) {
// user has already paid..
this.setState({
paid: true
});
}
}.bind(this)
);
}
localStorage.setItem("qc_user", user);
}.bind(this)
);
}
}
_handleCustomerCardSave(customerId) {
var updates = {};
updates[
`readers/${this.state.authentication_userId}/stripeCustomerId`
] = customerId;
fire
.database()
.ref()
.update(updates);
}
_handleCheckout(chargeId, amount) {
// update the user to have $5.00 in credit...
// store stripe customer id...
// store transaction history..
// authentication_userId
let credits_remaining = 0;
if (this.state.authentication_creditsRemaining) {
credits_remaining = this.state.authentication_creditsRemaining;
}
credits_remaining = Number(parseFloat(credits_remaining).toFixed(2));
let amountToAdd;
switch (amount) {
case "five":
amountToAdd = 5.0;
break;
case "ten":
amountToAdd = 10.0;
break;
case "fifteen":
amountToAdd = 15.0;
break;
case "twenty":
amountToAdd = 20.0;
break;
default:
}
let newAccountTotal = (
Number(amountToAdd) + Number(credits_remaining)
).toFixed(2);
var updates = {};
updates[
`readers/${this.state.authentication_userId}/credits`
] = newAccountTotal;
updates[
`readers/${this.state.authentication_userId}/charges/${chargeId}/date`
] = Date.now();
fire
.database()
.ref()
.update(updates);
this.setState({
loading: false,
display_error: false,
display_success: "payment succeeded!",
authentication_creditsRemaining: newAccountTotal,
display_error: false,
display_success: false,
authentication_show_signup: false,
authentication_show_signin: true
});
}
_handleAccountCreation(name, email, password) {
if (!name.trim() || !email.trim() || !password.trim()) {
this.setState({
display_error: "A valid name, email, and password is required.",
display_success: false
});
return;
}
this.setState({
loading: true
});
fire
.auth()
.createUserWithEmailAndPassword(email, password)
.then(
function(user) {
fire
.database()
.ref("readers/" + user.uid)
.set({
name: name,
userId: user.uid,
email: email,
signupDate: Date.now(),
hasFreeStories: true,
freeStoriesRemaining: 5,
credits: 0.0
});
this.setState({
loading: false,
display_error: false,
display_success: "Account created successfully!",
authentication_userId: user.uid,
authentication_name: name,
authentication_accountEmail: email,
step: "payment"
});
fetch(
`https://embeddable-api.herokuapp.com/send-welcome-email?email=${
email
}`
)
.then(function(response) {})
.catch(function(e) {}.bind(this));
}.bind(this)
)
.catch(
function(error) {
// handle errors.
const errorCode = error.code;
const errorMessage = error.message;
this.setState({
createAccountError: true,
accountErrorType: errorCode,
display_error: errorMessage,
display_success: false,
loading: false,
step: "personal"
});
}.bind(this)
);
}
_handleSignIn(email, password) {
this.setState({ loading: true });
fire
.auth()
.signInWithEmailAndPassword(email, password)
.then(
function(user) {
fire
.database()
.ref("readers/" + user.uid)
.once("value")
.then(
function(snapshot) {
this.setState({
authentication_userAuthenticated: true,
authentication_userId: user.uid,
authentication_stripeCustomerId: snapshot.val()
.stripeCustomerId,
authentication_name: "",
authentication_accountEmail: snapshot.val().email,
authentication_hasFreeStories: snapshot.val().hasFreeStories,
authentication_freeStoriesRemaining: snapshot.val()
.freeStoriesRemaining,
authentication_creditsRemaining: snapshot.val().credits,
authentication_purchased: snapshot.val().stories
});
if (snapshot.val().stories) {
Object.keys(snapshot.val().stories).map(
function(key) {
if (key === this.state.protectedArticleId) {
// user has already paid..
this.setState({
paid: true
});
}
}.bind(this)
);
}
localStorage.setItem("qc_user", user.uid);
}.bind(this)
);
this.setState({
loading: false,
hasError: false
});
}.bind(this)
)
.catch(
function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
this.setState({
errorMessage,
loading: false,
hasError: true
});
// ...
}.bind(this)
);
this.setState({
display_error: false,
display_success: false
});
}
render() {
return (
<div
id="paywall-bg"
style={{
height: "100vh",
width: "100%",
position: "fixed",
zIndex: this.state.display_zIndex
}}
>
{this.state.loading ? (
<div className="loader-wrap">
<div className="loader">Loading...</div>
</div>
) : (
""
)}
{this.state.payWall ? (
<div id="paywall-container">
<div
className={cx([
"paywall-form",
{
"paywall-form--pink": this.state.authentication_show_signup,
"paywall-form--purple": this.state
.authentication_userAuthenticated,
"d-none": this.state.videoAd
}
])}
>
{!this.state.authentication_userAuthenticated &&
!this.state.authentication_show_signup &&
!this.state.authentication_show_signin &&
!this.state.authentication_show_learnmore ? (
<InitialView
articlePrice={this.state.payWallPrice}
handleSignIn={() =>
this.setState({
authentication_show_signin: true,
authentication_show_signup: false,
authentication_show_learnmore: false
})
}
handleSignup={() =>
this.setState({
authentication_show_signup: true,
authentication_show_signin: false,
authentication_show_learnmore: false
})
}
handleLearnMore={() =>
this.setState({
authentication_show_signup: false,
authentication_show_signin: false,
authentication_show_learnmore: true
})
}
/>
) : (
""
)}{" "}
{/* end user NOT authenticated */}
{!this.state.authentication_userAuthenticated &&
this.state.authentication_show_signup ? (
<Signup
handleShowInitial={() =>
this.setState({
authentication_show_signin: false,
authentication_show_signup: false,
authentication_show_learnmore: false
})
}
accountErrorType={this.state.accountErrorType}
createAccountError={this.state.createAccountError}
showPaymentLoading={bool => this.setState({ loading: bool })}
readerName={this.state.authentication_name}
readerEmail={this.state.authentication_accountEmail}
step={this.state.step}
createUser={(name, email, password) =>
this._handleAccountCreation(name, email, password)
}
checkout={(chargeId, amount) =>
this._handleCheckout(chargeId, amount)
}
saveCustomerCard={customerId =>
this._handleCustomerCardSave(customerId)
}
showSignIn={() =>
this.setState({
authentication_show_signin: true,
authentication_show_signup: false
})
}
handleError={message =>
this.setState({
display_error: message,
display_success: false
})
}
/>
) : (
""
)}
{/* User wants to learn more... */}
{!this.state.authentication_userAuthenticated &&
this.state.authentication_show_learnmore ? (
<LearnMore
handleShowInitial={() =>
this.setState({
authentication_show_signup: false,
authentication_show_signin: false,
authentication_show_learnmore: false
})
}
handleSignIn={() =>
this.setState({
authentication_show_signin: true,
authentication_show_signup: false,
authentication_show_learnmore: false
})
}
handleSignup={() =>
this.setState({
authentication_show_signup: true,
authentication_show_signin: false,
authentication_show_learnmore: false
})
}
handleLearnMore={() =>
this.setState({
authentication_show_signup: false,
authentication_show_signin: false,
authentication_show_learnmore: true
})
}
/>
) : (
""
)}
{/* User wants to sign up... */}
{!this.state.authentication_userAuthenticated &&
this.state.authentication_show_signin ? (
<SignIn
handleError={message =>
this.setState({
display_error: message,
display_success: false
})
}
handleSuccess={message =>
this.setState({
display_success: message,
display_error: false
})
}
handleSignIn={(email, password) =>
this._handleSignIn(email, password)
}
handleSignup={() =>
this.setState({
authentication_show_signup: true,
authentication_show_signin: false,
authentication_show_learnmore: false
})
}
showError={this.state.hasError}
/>
) : (
""
)}
{this.state.authentication_userAuthenticated ? (
<SignedInView
showStory={() => this._handleShowStory()}
useFreeCredit={() => this._handleFreeCreditUsage()}
appState={this.state}
showPaymentLoading={bool => this.setState({ loading: bool })}
handleError={message =>
this.setState({
display_error: message,
display_success: false
})
}
checkout={(chargeId, amount) =>
this._handleCheckout(chargeId, amount)
}
saveCustomerCard={customerId =>
this._handleCustomerCardSave(customerId)
}
payForStory={article_price =>
this._handleStoryCharge(article_price)
}
/>
) : (
""
)}
{/* APP ERROR AND SUCCESS MESSAGES... */}
{this.state.display_error
? ""
: // <div className="billing-option-error">
// {this.state.display_error} !!
// </div>
""}
{this.state.display_success
? ""
: // <div className="billing-option-success">
// {this.state.display_success} !!
// </div>
""}
</div>{" "}
<div style={{ width: "95%" }}>
{this.state.videoAd ? (
<div>
<ReactJWPlayer
onAutoStart={this._onAdPlay}
onOneHundredPercent={this._handleAdEnd}
onVideoLoad={() => this.setState({ loading: false })}
licenseKey="ZjxiyGwbBr+eL4He4yNSIfQeYjo6XW8VK2MqoYVnHsmnzIxY"
playerId="QC_JWPLAYER"
isAutoPlay={true}
image="http://content.jwplatform.com/thumbs/cq9gJhoq-1920.jpg"
playerScript="https://content.jwplatform.com/libraries/R37fsXQ4.js"
file={this.state.publicationAdFileURL}
/>
</div>
) : (
""
)}
</div>
{!this.state.videoAd ? (
<div
onClick={() =>
this.setState({
videoAd: true,
loading: true
})
}
className="skip-and-watch"
>
OR WATCH A 60 SEC AD INSTEAD{" "}
</div>
) : (
""
)}
{/* end sidebar container */}
</div> // end paywall-container
) : (
""
)}
</div>
);
}
}
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment