@microbitsclub/paywall-solid
Version:
Solid components for Microbits paywalls
128 lines (127 loc) • 5.2 kB
JavaScript
import { DEFAULTS } from '@microbitsclub/microbits-client';
import crossFetch from 'cross-fetch';
import Cookies from 'js-cookie';
import { createStore } from 'solid-js/store';
import { createUrlPath, stringifyUrl } from './url';
export const createPaywallPopoverStore = (config) => {
const fetch = config.fetchFunction ?? crossFetch;
const getServerUrl = config.getServerUrl ?? ((path, query) => stringifyUrl({ ...path, query }));
// We need the full URL for when we redirect back to the content
const getContentUrl = config.getContentUrl ??
((path, query) => stringifyUrl({
proto: window.location.protocol.replace(':', ''),
hostname: window.location.hostname,
...path,
query,
}));
const [state, setState] = createStore({
userSessionId: undefined,
responseCache: {
paywall: {},
contentAuthorized: {},
},
popover: undefined,
get hasUserSessionId() {
if (this.userSessionId === undefined) {
const cookie = Cookies.get()[DEFAULTS.USER_SESSION_COOKIE];
if (cookie) {
setState('userSessionId', cookie);
return true;
}
}
return this.userSessionId !== undefined;
},
async getPaywall(contentUrl) {
const cacheEntry = this.responseCache.paywall[contentUrl];
if (cacheEntry?.type === 'ok') {
return cacheEntry.ok ?? undefined;
}
const url = getServerUrl(createUrlPath(`${DEFAULTS.MERCHANT_ROUTE_PREFIX}/get-paywall`), { contentUrl });
return fetch(url)
.then(resp => resp.json())
.then((x) => {
setState('responseCache', 'paywall', contentUrl, x);
return x.type === 'ok' ? x.ok ?? undefined : undefined;
});
},
async isContentAuthorized(contentUrl) {
const cacheEntry = this.responseCache.contentAuthorized[contentUrl];
if (cacheEntry?.type === 'ok') {
return cacheEntry.ok;
}
const url = getServerUrl(createUrlPath(`${DEFAULTS.MERCHANT_ROUTE_PREFIX}/is-content-authorized`), { contentUrl });
return fetch(url)
.then(resp => resp.json())
.then((x) => {
setState('responseCache', 'contentAuthorized', contentUrl, x);
return x.type === 'ok';
});
},
async attempNavigateToContent(contentUrl) {
if (!this.hasUserSessionId) {
await this.showPopoverPaywall(contentUrl, 'no-session');
}
else if (await this.isContentAuthorized(contentUrl)) {
await this.showPopoverPaywall(contentUrl, 'not-authorized');
}
else {
this.navigateToContent(contentUrl);
}
},
async handleContentLinkClick(e, contentUrl) {
if (!this.hasUserSessionId) {
e.preventDefault();
await this.showPopoverPaywall(contentUrl, 'no-session');
}
else if (await this.isContentAuthorized(contentUrl)) {
e.preventDefault();
await this.showPopoverPaywall(contentUrl, 'not-authorized');
}
},
getContentLinkClickHandler(contentUrl) {
return e => state.handleContentLinkClick(e, contentUrl);
},
navigateToGetUserSession(contentUrl, shouldPurchase) {
const getUserSessionIdUrl = stringifyUrl({
...DEFAULTS.MICROBITS_URL,
path: ['a', 'merchant-user-session'],
query: {
merchantId: config.merchantId,
contentUrl: getContentUrl(createUrlPath(contentUrl)),
shouldPurchase: shouldPurchase ? '1' : undefined,
},
});
const signInUrl = stringifyUrl({
...DEFAULTS.MICROBITS_URL,
path: ['a', 'signin'],
query: {
redirect: getUserSessionIdUrl,
},
});
window.location.href = signInUrl;
},
navigateToPurchaseThenViewContent(contentUrl) {
const url = getContentUrl(createUrlPath(contentUrl), { shouldPurchase: '1' });
window.location.href = url;
},
navigateToContent(contentUrl) {
const url = getContentUrl(createUrlPath(contentUrl));
window.location.href = url;
},
async showPopoverPaywall(contentUrl, mode) {
return this.getPaywall(contentUrl).then(paywall => {
if (paywall !== undefined) {
setState('popover', { mode, contentUrl, paywall });
}
else {
// TODO(logging)
console.log(`Paywall not found: ${contentUrl}`);
}
});
},
hidePopoverPaywall() {
setState('popover', undefined);
},
});
return state;
};