mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 17:15:48 +00:00
commit
387d72af61
4 changed files with 154 additions and 37 deletions
|
|
@ -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']}>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue