Merge pull request #76 from Stremio/intro

Intro
This commit is contained in:
Nikola Hristov 2019-11-11 12:46:55 +02:00 committed by GitHub
commit 387d72af61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 154 additions and 37 deletions

View file

@ -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 (
<Popup
open={menuOpen}
@ -38,17 +48,17 @@ const NavMenu = ({ className }) => {
<div
className={styles['avatar-container']}
style={{
backgroundImage: user.anonymous ?
backgroundImage: !user ?
`url('/images/anonymous.png')`
:
`url('${user.avatar}'), url('/images/default_avatar.png')`
}}
/>
<div className={styles['email-container']}>
<div className={styles['email-label']}>{user.anonymous ? 'Anonymous user' : user.email}</div>
<div className={styles['email-label']}>{!user ? 'Anonymous user' : user.email}</div>
</div>
<Button className={styles['logout-button-container']} title={user.anonymous ? 'Log in / Sign up' : 'Log out'} href={'#/intro'} onClick={user.logout}>
<div className={styles['logout-label']}>{user.anonymous ? 'Log in / Sign up' : 'Log out'}</div>
<Button className={styles['logout-button-container']} title={!user ? 'Log in / Sign up' : 'Log out'} href={'#/intro'} onClick={logoutButtonOnClick}>
<div className={styles['logout-label']}>{!user ? 'Log in / Sign up' : 'Log out'}</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>

View file

@ -3,14 +3,18 @@ const { useServices } = require('stremio/services');
const useUser = () => {
const { core } = useServices();
const [user, setUser] = React.useState(state.ctx.auth ? state.ctx.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 onNewModel = () => {
setUser(state.ctx.auth ? state.ctx.auth.user : null);
const onNewState = () => {
const state = core.getState();
setUser(state.ctx.content.auth ? state.ctx.content.auth.user : null);
};
core.on('NewModel', onNewModel);
core.on('NewModel', onNewState);
return () => {
core.off('NewModel', onNewModel);
core.off('NewModel', onNewState);
};
}, []);
return user;

View file

@ -12,6 +12,17 @@
<body>
<div id="app"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '1537119779906825',
autoLogAppEvents : false,
xfbml : false,
version : 'v2.5'
});
};
</script>
<script async defer src="https://connect.facebook.net/en_US/sdk.js"></script>
<script type="text/javascript">
<%= compilation.assets['main.js'].source() %>
</script>

View file

@ -3,14 +3,15 @@ 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';
const SIGNUP_FORM = 'signup';
const Intro = () => {
const Intro = ({ queryParams }) => {
const { core } = useServices();
const routeFocused = useRouteFocused();
const emailRef = React.useRef();
const passwordRef = React.useRef();
@ -22,17 +23,20 @@ const Intro = () => {
const [state, dispatch] = React.useReducer(
(state, action) => {
switch (action.type) {
case 'switch-form':
return {
form: state.form === SIGNUP_FORM ? LOGIN_FORM : SIGNUP_FORM,
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,
@ -55,7 +59,7 @@ const Intro = () => {
}
},
{
form: SIGNUP_FORM,
form: queryParams.get('form'),
email: '',
password: '',
confirmPassword: '',
@ -65,27 +69,115 @@ const Intro = () => {
error: ''
}
);
const loginWithFacebook = React.useCallback(() => {
alert('TODO: Facebook login');
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(() => {
FB.login((response) => {
if (response.status === 'connected') {
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);
} 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' });
return;
}
alert('TODO: Login');
if (state.password.length === 0) {
dispatch({ type: 'error', error: 'Invalid password' });
return;
}
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;
}
alert('TODO: Guest login');
}, [state.termsAccepted, state.privacyPolicyAccepted, state.marketingAccepted]);
core.dispatch({
action: 'UserOp',
args: {
userOp: 'Logout'
}
});
location = '#/';
}, [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({
@ -130,9 +222,9 @@ const Intro = () => {
const toggleMarketingAccepted = React.useCallback(() => {
dispatch({ type: 'toggle-checkbox', name: 'marketingAccepted' });
}, []);
const switchForm = React.useCallback(() => {
dispatch({ type: 'switch-form' });
}, []);
React.useEffect(() => {
dispatch({ type: 'set-form', form: queryParams.get('form') });
}, [queryParams]);
React.useEffect(() => {
if (typeof state.error === 'string' && state.error.length > 0) {
errorRef.current.scrollIntoView();
@ -229,7 +321,7 @@ const Intro = () => {
:
null
}
<Button className={classnames(styles['form-button'], styles['switch-form-button'])} onClick={switchForm}>
<Button className={classnames(styles['form-button'], styles['switch-form-button'])} href={state.form === SIGNUP_FORM ? '#/intro?form=login' : '#/intro?form=signup'}>
<div className={styles['label']}>{state.form === SIGNUP_FORM ? 'LOG IN' : 'SING UP WITH EMAIL'}</div>
</Button>
</div>