Skip to content

Commit

Permalink
feat: add various signin elements
Browse files Browse the repository at this point in the history
  • Loading branch information
dackmin committed Aug 8, 2022
1 parent 2791cdd commit 5785da5
Show file tree
Hide file tree
Showing 12 changed files with 285 additions and 3 deletions.
10 changes: 9 additions & 1 deletion examples/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { createRoot } from 'react-dom/client';
import { SubscribeContext, SigninButton } from '@poool/react-subscribe';

const App = () => null;
const App = () => (
<SubscribeContext
config={{ debug: true }}
appId="155PF-L7Q6Q-EB2GG-04TF8"
>
<SigninButton />
</SubscribeContext>
);

createRoot(document.getElementById('app')).render(<App />);
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poool/react-subscribe",
"version": "1.0.0",
"version": "1.0.0-alpha.0",
"description": "Subscribe elements for React",
"main": "dist/poool-react-subscribe.cjs.js",
"repository": "https://github.com/p3ol/react-subscribe.git",
Expand Down Expand Up @@ -72,6 +72,7 @@
"dependencies": {
"@babel/runtime-corejs3": "7.18.3",
"core-js": "3.23.3",
"@poool/junipero-utils": "2.0.0-rc.21",
"prop-types": "15.8.1"
}
}
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const formats = ['umd', 'cjs', 'esm'];
const defaultExternals = ['react', 'prop-types'];
const defaultGlobals = {
react: 'React',
propTypes: 'PropTypes',
'prop-types': 'PropTypes',
};

const defaultPlugins = [
Expand Down
73 changes: 73 additions & 0 deletions src/Element/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
useRef,
useMemo,
useEffect,
useImperativeHandle,
forwardRef,
} from 'react';
import PropTypes from 'prop-types';

import { useSubscribe } from '../hooks';
import { generateId } from '../utils';

const Element = forwardRef(({
styles,
type,
tag: Tag = 'div',
useGlobalFactory = true,
...rest
}, ref) => {
const containerRef = useRef();
const elementRef = useRef();
const id = useMemo(() => generateId(), []);
const { lib, globalFactory, createFactory } = useSubscribe();

useImperativeHandle(ref, () => ({
containerRef,
elementRef,
destroy,
}));

useEffect(() => {
const factory = useGlobalFactory ? globalFactory : createFactory();

if (!factory) {
return;
}

if (elementRef.current) {
elementRef.current.destroy();
}

elementRef.current = factory.createAuthElement(type, {
target: containerRef.current,
styles,
...rest,
});

return () => {
destroy();
};
}, [lib, globalFactory]);

const destroy = () => {
elementRef.current?.destroy?.();
};

return (
<Tag ref={containerRef} id={id} />
);
});

Element.displayName = 'Element';
Element.propTypes = {
styles: PropTypes.object,
type: PropTypes.string.isRequired,
tag: PropTypes.oneOfType([
PropTypes.string,
PropTypes.elementType,
]),
useGlobalFactory: PropTypes.bool,
};

export default Element;
11 changes: 11 additions & 0 deletions src/SigninButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { forwardRef } from 'react';

import Element from '../Element';

const SigninButton = forwardRef((props, ref) => (
<Element type="signin-button" {...props} ref={ref} />
));

SigninButton.displayName = 'SigninButton';

export default SigninButton;
15 changes: 15 additions & 0 deletions src/SigninWithSubscribeButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { forwardRef } from 'react';
import PropTypes from 'prop-types';

import Element from '../Element';

const SigninWithSubscribeButton = forwardRef((props, ref) => (
<Element type="signin-with-subscribe-button" {...props} ref={ref} />
));

SigninWithSubscribeButton.displayName = 'SigninWithSubscribeButton';
SigninWithSubscribeButton.propTypes = {
offer: PropTypes.string,
};

export default SigninWithSubscribeButton;
99 changes: 99 additions & 0 deletions src/SubscribeContext/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { useEffect, useCallback, useReducer } from 'react';
import PropTypes from 'prop-types';
import { mockState, mergeDeep } from '@poool/junipero-utils';

import { SubscribeContext as Ctx } from '../contexts';
import { loadScript } from '../utils';

const SubscribeContext = ({
appId,
config,
events,
scriptUrl = 'https://assets.poool-subscribe.fr/subscribe.js',
...rest
}) => {
const [state, dispatch] = useReducer(mockState, {
lib: null,
factory: null,
});

useEffect(() => {
init();
}, []);

const init = async () => {
if (
!globalThis.Subscribe ||
!globalThis.Subscribe.isPoool ||
!globalThis.PooolSubscribe ||
!globalThis.PooolSubscribe.isPoool
) {
await loadScript(scriptUrl, 'poool-react-subscribe-lib');
}

const lib = globalThis.Subscribe.noConflict();

const factory = lib
.init(appId)
.config(config);

Object
.entries(events || {})
.forEach(([event, callback]) => {
if (callback.once) {
factory.once(event, callback.callback);
} else {
factory.on(event, callback);
}
});

dispatch({ lib, factory });
};

const createFactory = (opts = {}) => {
if (!state.lib) {
return;
}

const factory = state.lib
.init(appId)
.config(mergeDeep({}, config, opts.config));

Object
.entries(events || {})
.concat(Object.entries(opts.events || {}))
.forEach(([event, callback]) => {
if (callback.once) {
factory.once(event, callback.callback);
} else {
factory.on(event, callback);
}
});

return factory;
};

const getContext = useCallback(() => ({
appId,
config,
events,
scriptUrl,
lib: state.lib,
globalFactory: state.factory,
createFactory,
}), [state.lib, state.factory]);

return (
<Ctx.Provider value={getContext()} { ...rest } />
);
};

SubscribeContext.displayName = 'SubscribeContext';
SubscribeContext.propTypes = {
appId: PropTypes.string.isRequired,
config: PropTypes.object,
events: PropTypes.object,
scriptUrl: PropTypes.string,
};

export default SubscribeContext;
3 changes: 3 additions & 0 deletions src/contexts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createContext } from 'react';

export const SubscribeContext = createContext({});
7 changes: 7 additions & 0 deletions src/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useContext } from 'react';

import { SubscribeContext } from './contexts';

export const useSubscribe = () => {
return useContext(SubscribeContext);
};
8 changes: 8 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { default as SubscribeContext } from './SubscribeContext';
export { default as Element } from './Element';
export { default as SigninButton } from './SigninButton';
export {
default as SigninWithSubscribeButton,
} from './SigninWithSubscribeButton';

export { useSubscribe } from './hooks';
36 changes: 36 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export const randomString = () =>
Math.random().toString(36).substring(2, 5);

export const generateId = () => {
let id;

while (!id) {
const temp = `poool-${randomString()}-${randomString()}`;

/* istanbul ignore else: virtually impossible to happen */
if (typeof document === 'undefined' || !document.getElementById?.(temp)) {
id = temp;
}
}

return id;
};

export const loadScript = (url, id) => new Promise((resolve, reject) => {
/* istanbul ignore else: tested inside puppeteer */
if (process.env.NODE_ENV === 'test') {
return resolve();
}

if (document.querySelector(`#${id}`)) {
return resolve();
}

const script = globalThis.document.createElement('script');
script.onload = resolve;
script.onerror = reject;
script.async = true;
script.src = url;
script.id = id;
globalThis.document.head.appendChild(script);
});
21 changes: 21 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,14 @@
"@babel/plugin-transform-react-jsx-development" "^7.16.7"
"@babel/plugin-transform-react-pure-annotations" "^7.16.7"

"@babel/runtime-corejs3@7.17.9":
version "7.17.9"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.9.tgz#3d02d0161f0fbf3ada8e88159375af97690f4055"
integrity sha512-WxYHHUWF2uZ7Hp1K+D1xQgbgkGUfA+5UPOegEXGt2Y5SMog/rYCVaifLZDbw8UkNXozEqqrZTy6bglL7xTaCOw==
dependencies:
core-js-pure "^3.20.2"
regenerator-runtime "^0.13.4"

"@babel/runtime-corejs3@7.18.3":
version "7.18.3"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.3.tgz#52f0241a31e0ec61a6187530af6227c2846bd60c"
Expand Down Expand Up @@ -1367,6 +1375,14 @@
dependencies:
eslint-rule-composer "0.3.0"

"@poool/junipero-utils@2.0.0-rc.21":
version "2.0.0-rc.21"
resolved "https://registry.yarnpkg.com/@poool/junipero-utils/-/junipero-utils-2.0.0-rc.21.tgz#483924ccb1fad4aac2b80e03e226951ce66884f1"
integrity sha512-lN2YEEEtbOPm6eP7OjNIalChhynwbG4IU11hg5przagyKjENbZ+wqxpytCvTlPO2aKVKXnbacyw9uvyOlZrOgw==
dependencies:
"@babel/runtime-corejs3" "7.17.9"
core-js "3.22.0"

"@rollup/plugin-babel@5.3.1":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
Expand Down Expand Up @@ -2889,6 +2905,11 @@ core-js-pure@^3.20.2:
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3"
integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg==

core-js@3.22.0:
version "3.22.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.0.tgz#b52007870c5e091517352e833b77f0b2d2b259f3"
integrity sha512-8h9jBweRjMiY+ORO7bdWSeWfHhLPO7whobj7Z2Bl0IDo00C228EdGgH7FE4jGumbEjzcFfkfW8bXgdkEDhnwHQ==

core-js@3.23.3:
version "3.23.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.3.tgz#3b977612b15da6da0c9cc4aec487e8d24f371112"
Expand Down

0 comments on commit 5785da5

Please sign in to comment.