From 807cf9f0573865aebe25d81e7a49851128f1e205 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Thu, 7 Nov 2019 16:19:53 +0200 Subject: [PATCH 01/11] intro linked with stremio-core (login, logout, register) --- src/common/NavBar/NavMenu/NavMenu.js | 20 ++++++-- src/common/useUser.js | 12 +++-- src/routes/Intro/Intro.js | 75 +++++++++++++++++++++++++--- 3 files changed, 90 insertions(+), 17 deletions(-) diff --git a/src/common/NavBar/NavMenu/NavMenu.js b/src/common/NavBar/NavMenu/NavMenu.js index b8126116a..329e93dfb 100644 --- a/src/common/NavBar/NavMenu/NavMenu.js +++ b/src/common/NavBar/NavMenu/NavMenu.js @@ -2,14 +2,16 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const Icon = require('stremio-icons/dom'); +const { useServices } = require('stremio/services'); const Button = require('stremio/common/Button'); const Popup = require('stremio/common/Popup'); const useBinaryState = require('stremio/common/useBinaryState'); const useFullscreen = require('stremio/common/useFullscreen'); -const useUser = require('./useUser'); +const useUser = require('stremio/common/useUser'); const styles = require('./styles'); const NavMenu = ({ className }) => { + const { core } = useServices(); const [menuOpen, openMenu, closeMenu, toggleMenu] = useBinaryState(false); const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen(); const user = useUser(); @@ -21,6 +23,14 @@ const NavMenu = ({ className }) => { const popupMenuOnClick = React.useCallback((event) => { event.nativeEvent.togglePopupPrevented = true; }, []); + const logoutButtonOnClick = React.useCallback(() => { + core.dispatch({ + action: 'UserOp', + args: { + userOp: 'Logout' + } + }); + }, []); return ( {
-
{user.anonymous ? 'Anonymous user' : user.email}
+
{!user ? 'Anonymous user' : user.email}
-
diff --git a/src/common/useUser.js b/src/common/useUser.js index 0c5391b26..fe685894f 100644 --- a/src/common/useUser.js +++ b/src/common/useUser.js @@ -3,14 +3,16 @@ const { useServices } = require('stremio/services'); const useUser = () => { const { core } = useServices(); - const [user, setUser] = React.useState(state.ctx.auth ? state.ctx.auth.user : null); + const state = core.getState(); + const [user, setUser] = React.useState(state.ctx.content.auth ? state.ctx.content.auth.user : null); React.useEffect(() => { - const onNewModel = () => { - setUser(state.ctx.auth ? state.ctx.auth.user : null); + const onNewState = () => { + setUser(state.ctx.content.auth ? state.ctx.content.auth.user : null); }; - core.on('NewModel', onNewModel); + core.on('NewModel', onNewState); + onNewState(); return () => { - core.off('NewModel', onNewModel); + core.off('NewModel', onNewState); }; }, []); return user; diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 9f1ad474f..396c6cee1 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -3,14 +3,16 @@ const classnames = require('classnames'); const Icon = require('stremio-icons/dom'); const { useRouteFocused } = require('stremio-router'); const { Button } = require('stremio/common'); +const { useServices } = require('stremio/services'); const CredentialsTextInput = require('./CredentialsTextInput'); const ConsentCheckbox = require('./ConsentCheckbox'); const styles = require('./styles'); const LOGIN_FORM = 'LOGIN_FORM'; const SIGNUP_FORM = 'SIGNUP_FORM'; - +// TODO queryparams for signup and login const Intro = () => { + const { core } = useServices(); const routeFocused = useRouteFocused(); const emailRef = React.useRef(); const passwordRef = React.useRef(); @@ -65,6 +67,23 @@ const Intro = () => { error: '' } ); + React.useEffect(() => { + const onEvent = ({ event, args }) => { + if (event === 'CtxActionErr') { + dispatch({ type: 'error', error: args[1].args.message }); + } + if (event === 'CtxChanged') { + const state = core.getState(); + if (state.ctx.content.auth !== null) { + window.location.replace('/'); + } + } + }; + core.on('Event', onEvent); + return () => { + core.off('Event', onEvent); + }; + }, []); const loginWithFacebook = React.useCallback(() => { alert('TODO: Facebook login'); }, []); @@ -73,19 +92,61 @@ const Intro = () => { dispatch({ type: 'error', error: 'Invalid email' }); return; } - - alert('TODO: Login'); + core.dispatch({ + action: 'UserOp', + args: { + userOp: 'Login', + args: { + email: state.email, + password: state.password + } + } + }); }, [state.email, state.password]); const loginAsGuest = React.useCallback(() => { if (!state.termsAccepted) { dispatch({ type: 'error', error: 'You must accept the Terms of Service' }); return; + } else { + core.dispatch({ + action: 'UserOp', + args: { + userOp: 'Logout' + } + }); + location = '#/'; } - - alert('TODO: Guest login'); - }, [state.termsAccepted, state.privacyPolicyAccepted, state.marketingAccepted]); + }, [state.termsAccepted]); const signup = React.useCallback(() => { - alert('TODO: Signup'); + if (!state.termsAccepted) { + dispatch({ type: 'error', error: 'You must accept the Terms of Service' }); + return; + } + if (!state.privacyPolicyAccepted) { + dispatch({ type: 'error', error: 'You must accept the Privacy Policy' }); + return; + } + if (state.password !== state.confirmPassword) { + dispatch({ type: 'error', error: 'Passwords do not match' }); + return; + } + core.dispatch({ + action: 'UserOp', + args: { + userOp: 'Register', + args: { + email: state.email, + password: state.password, + gdpr_consent: { + tos: state.termsAccepted, + privacy: state.privacyPolicyAccepted, + marketing: state.marketingAccepted, + time: new Date(), + from: 'web' + } + } + } + }); }, [state.email, state.password, state.confirmPassword, state.termsAccepted, state.privacyPolicyAccepted, state.marketingAccepted]); const emailOnChange = React.useCallback((event) => { dispatch({ From 62e6ddde968ca81cc45a2aa29a2e8bb5fbe9b942 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Thu, 7 Nov 2019 16:30:38 +0200 Subject: [PATCH 02/11] query params for signup and login used --- src/routes/Intro/Intro.js | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 396c6cee1..1989cf6ea 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -8,10 +8,9 @@ const CredentialsTextInput = require('./CredentialsTextInput'); const ConsentCheckbox = require('./ConsentCheckbox'); const styles = require('./styles'); -const LOGIN_FORM = 'LOGIN_FORM'; -const SIGNUP_FORM = 'SIGNUP_FORM'; -// TODO queryparams for signup and login -const Intro = () => { +const SIGNUP_FORM = 'signup'; + +const Intro = ({ queryParams }) => { const { core } = useServices(); const routeFocused = useRouteFocused(); const emailRef = React.useRef(); @@ -21,12 +20,12 @@ const Intro = () => { const privacyPolicyRef = React.useRef(); const marketingRef = React.useRef(); const errorRef = React.useRef(); + const [selectedForm, setSelectedForm] = React.useState(queryParams.get('form')); const [state, dispatch] = React.useReducer( (state, action) => { switch (action.type) { - case 'switch-form': + case 'reset-form': return { - form: state.form === SIGNUP_FORM ? LOGIN_FORM : SIGNUP_FORM, email: '', password: '', confirmPassword: '', @@ -57,7 +56,6 @@ const Intro = () => { } }, { - form: SIGNUP_FORM, email: '', password: '', confirmPassword: '', @@ -166,12 +164,12 @@ const Intro = () => { }); }, []); const passwordOnSubmit = React.useCallback(() => { - if (state.form === SIGNUP_FORM) { + if (selectedForm === SIGNUP_FORM) { confirmPasswordRef.current.focus(); } else { loginWithEmail(); } - }, [state.form, loginWithEmail]); + }, [selectedForm, loginWithEmail]); const confirmPasswordOnChange = React.useCallback((event) => { dispatch({ type: 'change-credentials', @@ -191,9 +189,10 @@ const Intro = () => { const toggleMarketingAccepted = React.useCallback(() => { dispatch({ type: 'toggle-checkbox', name: 'marketingAccepted' }); }, []); - const switchForm = React.useCallback(() => { - dispatch({ type: 'switch-form' }); - }, []); + React.useEffect(() => { + dispatch({ type: 'reset-form' }); + setSelectedForm(queryParams.get('form')); + }, [queryParams]); React.useEffect(() => { if (typeof state.error === 'string' && state.error.length > 0) { errorRef.current.scrollIntoView(); @@ -203,7 +202,7 @@ const Intro = () => { if (routeFocused) { emailRef.current.focus(); } - }, [state.form, routeFocused]); + }, [selectedForm, routeFocused]); return (
@@ -231,7 +230,7 @@ const Intro = () => { onSubmit={passwordOnSubmit} /> { - state.form === SIGNUP_FORM ? + selectedForm === SIGNUP_FORM ? { : null } - { - state.form === SIGNUP_FORM ? + selectedForm === SIGNUP_FORM ? : null } -
From 8718d868bfc991a549c5e6508a4bd3f7772a5c0c Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Fri, 8 Nov 2019 15:59:45 +0200 Subject: [PATCH 03/11] login with fb implemented --- src/index.html | 11 +++++++++++ src/routes/Intro/Intro.js | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/index.html b/src/index.html index 55a8694ad..919a94851 100755 --- a/src/index.html +++ b/src/index.html @@ -12,6 +12,17 @@
+ + diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 1989cf6ea..062198661 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -82,8 +82,39 @@ const Intro = ({ queryParams }) => { core.off('Event', onEvent); }; }, []); + const statusChangeCallback = React.useCallback((response) => { + if (response.status === 'connected') { + fetch(baseUrl + "/fb-login-with-token/" + encodeURIComponent(response.authResponse.accessToken), { timeout: 10 * 1000 }) + .then((resp) => { + if (resp.status < 200 || resp.status >= 300) { + throw new Error('Login failed at getting token from Stremio with status ' + resp.status); + } else { + return resp.json(); + } + }) + .then(function() { + core.dispatch({ + action: 'UserOp', + args: { + userOp: 'Login', + args: { + email: state.email, + password: response.authResponse.accessToken + } + } + }); + }) + .catch(function(err) { + console.log(err); + }); + } else { + console.log('Please log into this app.'); + } + }, [state.email, state.password]); const loginWithFacebook = React.useCallback(() => { - alert('TODO: Facebook login'); + FB.login(function(response) { + statusChangeCallback(response); + }); }, []); const loginWithEmail = React.useCallback(() => { if (typeof state.email !== 'string' || state.email.length === 0) { From 5c349c4fcaae5d785b317d1373ec483890247cae Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 11 Nov 2019 09:35:38 +0200 Subject: [PATCH 04/11] getState called inside onNewState --- src/common/useUser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/useUser.js b/src/common/useUser.js index fe685894f..f5681c122 100644 --- a/src/common/useUser.js +++ b/src/common/useUser.js @@ -7,6 +7,7 @@ const useUser = () => { const [user, setUser] = React.useState(state.ctx.content.auth ? state.ctx.content.auth.user : null); React.useEffect(() => { const onNewState = () => { + const state = core.getState(); setUser(state.ctx.content.auth ? state.ctx.content.auth.user : null); }; core.on('NewModel', onNewState); From 98bd2e354a992961a311de70c385fc4a7db000b8 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 11 Nov 2019 09:57:56 +0200 Subject: [PATCH 05/11] not need else removed --- src/routes/Intro/Intro.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 062198661..6c63579e3 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -136,15 +136,14 @@ const Intro = ({ queryParams }) => { if (!state.termsAccepted) { dispatch({ type: 'error', error: 'You must accept the Terms of Service' }); return; - } else { - core.dispatch({ - action: 'UserOp', - args: { - userOp: 'Logout' - } - }); - location = '#/'; } + core.dispatch({ + action: 'UserOp', + args: { + userOp: 'Logout' + } + }); + location = '#/'; }, [state.termsAccepted]); const signup = React.useCallback(() => { if (!state.termsAccepted) { From 7c512aa995af8eab93ddba29c1272048d58df781 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 11 Nov 2019 10:20:57 +0200 Subject: [PATCH 06/11] define baseUrl --- src/routes/Intro/Intro.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 6c63579e3..729c1eb1b 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -9,6 +9,7 @@ const ConsentCheckbox = require('./ConsentCheckbox'); const styles = require('./styles'); const SIGNUP_FORM = 'signup'; +const baseUrl = 'https://www.strem.io'; const Intro = ({ queryParams }) => { const { core } = useServices(); From d8e01779376814121e526d3e618ac8411f708fab Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 11 Nov 2019 10:34:47 +0200 Subject: [PATCH 07/11] check for password length --- src/routes/Intro/Intro.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 729c1eb1b..3e29206a5 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -122,6 +122,10 @@ const Intro = ({ queryParams }) => { dispatch({ type: 'error', error: 'Invalid email' }); return; } + if (state.password.length === 0) { + dispatch({ type: 'error', error: 'Invalid password' }); + return; + } core.dispatch({ action: 'UserOp', args: { From 823994f0969cb136ad62c59ed78e93764fc32205 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 11 Nov 2019 10:38:50 +0200 Subject: [PATCH 08/11] console.error used --- src/routes/Intro/Intro.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 3e29206a5..7da434434 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -106,10 +106,8 @@ const Intro = ({ queryParams }) => { }); }) .catch(function(err) { - console.log(err); + console.error(err); }); - } else { - console.log('Please log into this app.'); } }, [state.email, state.password]); const loginWithFacebook = React.useCallback(() => { From 2bcf42b601ff0e531d656950a6174c30ef0864f8 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 11 Nov 2019 10:55:38 +0200 Subject: [PATCH 09/11] inline statusChangeCallback func --- src/routes/Intro/Intro.js | 57 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 7da434434..39438e717 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -83,38 +83,35 @@ const Intro = ({ queryParams }) => { core.off('Event', onEvent); }; }, []); - const statusChangeCallback = React.useCallback((response) => { - if (response.status === 'connected') { - fetch(baseUrl + "/fb-login-with-token/" + encodeURIComponent(response.authResponse.accessToken), { timeout: 10 * 1000 }) - .then((resp) => { - if (resp.status < 200 || resp.status >= 300) { - throw new Error('Login failed at getting token from Stremio with status ' + resp.status); - } else { - return resp.json(); - } - }) - .then(function() { - core.dispatch({ - action: 'UserOp', - args: { - userOp: 'Login', - args: { - email: state.email, - password: response.authResponse.accessToken - } - } - }); - }) - .catch(function(err) { - console.error(err); - }); - } - }, [state.email, state.password]); const loginWithFacebook = React.useCallback(() => { - FB.login(function(response) { - statusChangeCallback(response); + FB.login((response) => { + if (response.status === 'connected') { + fetch(baseUrl + "/fb-login-with-token/" + encodeURIComponent(response.authResponse.accessToken), { timeout: 10 * 1000 }) + .then((resp) => { + if (resp.status < 200 || resp.status >= 300) { + throw new Error('Login failed at getting token from Stremio with status ' + resp.status); + } else { + return resp.json(); + } + }) + .then(() => { + core.dispatch({ + action: 'UserOp', + args: { + userOp: 'Login', + args: { + email: state.email, + password: response.authResponse.accessToken + } + } + }); + }) + .catch((err) => { + console.error(err); + }); + } }); - }, []); + }, [state.email, state.password]); const loginWithEmail = React.useCallback(() => { if (typeof state.email !== 'string' || state.email.length === 0) { dispatch({ type: 'error', error: 'Invalid email' }); From 013ad5bcde60be0bd0cf9c95b674f6798e1118c7 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 11 Nov 2019 11:49:49 +0200 Subject: [PATCH 10/11] prop form added --- src/common/useUser.js | 6 +++-- src/routes/Intro/Intro.js | 47 +++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/common/useUser.js b/src/common/useUser.js index f5681c122..dd7f97482 100644 --- a/src/common/useUser.js +++ b/src/common/useUser.js @@ -3,8 +3,10 @@ const { useServices } = require('stremio/services'); const useUser = () => { const { core } = useServices(); - const state = core.getState(); - const [user, setUser] = React.useState(state.ctx.content.auth ? state.ctx.content.auth.user : null); + const [user, setUser] = React.useState(() => { + const state = core.getState(); + return state.ctx.content.auth ? state.ctx.content.auth.user : null; + }); React.useEffect(() => { const onNewState = () => { const state = core.getState(); diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 39438e717..8b7e8983d 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -21,20 +21,23 @@ const Intro = ({ queryParams }) => { const privacyPolicyRef = React.useRef(); const marketingRef = React.useRef(); const errorRef = React.useRef(); - const [selectedForm, setSelectedForm] = React.useState(queryParams.get('form')); const [state, dispatch] = React.useReducer( (state, action) => { switch (action.type) { - case 'reset-form': - return { - email: '', - password: '', - confirmPassword: '', - termsAccepted: false, - privacyPolicyAccepted: false, - marketingAccepted: false, - error: '' - }; + case 'set-form': + if (state.form !== action.form) { + return { + form: action.form, + email: '', + password: '', + confirmPassword: '', + termsAccepted: false, + privacyPolicyAccepted: false, + marketingAccepted: false, + error: '' + }; + } + return state; case 'change-credentials': return { ...state, @@ -57,6 +60,7 @@ const Intro = ({ queryParams }) => { } }, { + form: queryParams.get('form'), email: '', password: '', confirmPassword: '', @@ -194,12 +198,12 @@ const Intro = ({ queryParams }) => { }); }, []); const passwordOnSubmit = React.useCallback(() => { - if (selectedForm === SIGNUP_FORM) { + if (state.form === SIGNUP_FORM) { confirmPasswordRef.current.focus(); } else { loginWithEmail(); } - }, [selectedForm, loginWithEmail]); + }, [state.form, loginWithEmail]); const confirmPasswordOnChange = React.useCallback((event) => { dispatch({ type: 'change-credentials', @@ -220,8 +224,7 @@ const Intro = ({ queryParams }) => { dispatch({ type: 'toggle-checkbox', name: 'marketingAccepted' }); }, []); React.useEffect(() => { - dispatch({ type: 'reset-form' }); - setSelectedForm(queryParams.get('form')); + dispatch({ type: 'set-form', form: queryParams.get('form') }); }, [queryParams]); React.useEffect(() => { if (typeof state.error === 'string' && state.error.length > 0) { @@ -232,7 +235,7 @@ const Intro = ({ queryParams }) => { if (routeFocused) { emailRef.current.focus(); } - }, [selectedForm, routeFocused]); + }, [state.form, routeFocused]); return (
@@ -260,7 +263,7 @@ const Intro = ({ queryParams }) => { onSubmit={passwordOnSubmit} /> { - selectedForm === SIGNUP_FORM ? + state.form === SIGNUP_FORM ? { : null } - { - selectedForm === SIGNUP_FORM ? + state.form === SIGNUP_FORM ? : null } -
From 204f7ead5272ec1435b4764ceba28fbc60857955 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 11 Nov 2019 12:40:21 +0200 Subject: [PATCH 11/11] inline baseUrl --- src/common/useUser.js | 1 - src/routes/Intro/Intro.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/common/useUser.js b/src/common/useUser.js index dd7f97482..cb92a36b6 100644 --- a/src/common/useUser.js +++ b/src/common/useUser.js @@ -13,7 +13,6 @@ const useUser = () => { setUser(state.ctx.content.auth ? state.ctx.content.auth.user : null); }; core.on('NewModel', onNewState); - onNewState(); return () => { core.off('NewModel', onNewState); }; diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 8b7e8983d..beb35e26d 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -9,7 +9,6 @@ const ConsentCheckbox = require('./ConsentCheckbox'); const styles = require('./styles'); const SIGNUP_FORM = 'signup'; -const baseUrl = 'https://www.strem.io'; const Intro = ({ queryParams }) => { const { core } = useServices(); @@ -90,7 +89,7 @@ const Intro = ({ queryParams }) => { const loginWithFacebook = React.useCallback(() => { FB.login((response) => { if (response.status === 'connected') { - fetch(baseUrl + "/fb-login-with-token/" + encodeURIComponent(response.authResponse.accessToken), { timeout: 10 * 1000 }) + fetch('https://www.strem.io/fb-login-with-token/' + encodeURIComponent(response.authResponse.accessToken), { timeout: 10 * 1000 }) .then((resp) => { if (resp.status < 200 || resp.status >= 300) { throw new Error('Login failed at getting token from Stremio with status ' + resp.status);