diff --git a/src/index.html b/src/index.html index ba8a9e795..033f9a267 100644 --- a/src/index.html +++ b/src/index.html @@ -15,6 +15,7 @@
<%= htmlWebpackPlugin.tags.bodyTags %> + \ No newline at end of file diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 989e6febd..0613e1490 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -12,6 +12,8 @@ const { Button, Image, Checkbox } = require('stremio/components'); const CredentialsTextInput = require('./CredentialsTextInput'); const PasswordResetModal = require('./PasswordResetModal'); const useFacebookLogin = require('./useFacebookLogin'); +const { default: useAppleLogin } = require('./useAppleLogin'); + const styles = require('./styles'); const SIGNUP_FORM = 'signup'; @@ -22,6 +24,7 @@ const Intro = ({ queryParams }) => { const { t } = useTranslation(); const routeFocused = useRouteFocused(); const [startFacebookLogin, stopFacebookLogin] = useFacebookLogin(); + const [startAppleLogin, stopAppleLogin] = useAppleLogin(); const emailRef = React.useRef(null); const passwordRef = React.useRef(null); const confirmPasswordRef = React.useRef(null); @@ -106,6 +109,32 @@ const Intro = ({ queryParams }) => { stopFacebookLogin(); closeLoaderModal(); }, []); + const loginWithApple = React.useCallback(() => { + openLoaderModal(); + startAppleLogin() + .then(({ email, password }) => { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'Authenticate', + args: { + type: 'Login', + email, + password, + apple: true + } + } + }); + }) + .catch((error) => { + closeLoaderModal(); + dispatch({ type: 'error', error: error.message }); + }); + }, []); + const cancelLoginWithApple = React.useCallback(() => { + stopAppleLogin(); + closeLoaderModal(); + }, []); const loginWithEmail = React.useCallback(() => { if (typeof state.email !== 'string' || state.email.length === 0 || !emailRef.current.validity.valid) { dispatch({ type: 'error', error: 'Invalid email' }); @@ -336,7 +365,7 @@ const Intro = ({ queryParams }) => { } { - state.error.length > 0 ? + state.error && state.error.length > 0 ?
{state.error}
: null @@ -350,6 +379,10 @@ const Intro = ({ queryParams }) => {
Continue with Facebook
+ { state.form === SIGNUP_FORM ? diff --git a/src/routes/Intro/useAppleLogin.ts b/src/routes/Intro/useAppleLogin.ts new file mode 100644 index 000000000..695888bee --- /dev/null +++ b/src/routes/Intro/useAppleLogin.ts @@ -0,0 +1,80 @@ +import { useCallback, useRef } from 'react'; + +type AppleLoginResponse = { + email: string; + password: string; +}; + +type AppleSignInResponse = { + authorization: { + code: string; + id_token: string; + state: string; + }; + user: string; + email?: string; +}; + +const CLIENT_ID = 'com.stremio.one'; + +const useAppleLogin = (): [() => Promise, () => void] => { + const started = useRef(false); + + const start = useCallback((): Promise => { + return new Promise((resolve, reject) => { + if (typeof window.AppleID === 'undefined') { + reject(new Error('Apple Sign-In not loaded')); + return; + } + + if (started.current) { + reject(new Error('Apple login already in progress')); + return; + } + + started.current = true; + + window.AppleID.auth.init({ + clientId: CLIENT_ID, + scope: 'name email', + redirectURI: window.location.origin, + state: 'signin', + usePopup: true + }); + + window.AppleID.auth.signIn() + .then((response: AppleSignInResponse) => { + if (response.authorization) { + const userEmail = response.email || response.user; + + if (!userEmail) { + reject(new Error('No email received from Apple')); + return; + } + + resolve({ + email: userEmail as string, + password: response.authorization.id_token + }); + } else { + reject(new Error('No authorization received from Apple')); + } + }) + .catch((error: Error) => { + console.error('Error during Apple Sign-In:', error); + reject(error); + }) + .finally(() => { + started.current = false; + }); + }); + }, []); + + const stop = useCallback(() => { + started.current = false; + }, []); + + return [start, stop]; +}; + +export default useAppleLogin; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 3849b8914..4b46cb22d 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -26,6 +26,28 @@ interface Chrome { declare global { var qt: Qt | undefined; var chrome: Chrome | undefined; + interface Window { + AppleID: { + auth: { + init: (config: { + clientId: string; + scope: string; + redirectURI: string; + state: string; + usePopup: boolean; + }) => void; + signIn: () => Promise<{ + authorization: { + code: string; + id_token: string; + state: string; + }; + user: string; + email?: string; + }>; + }; + }; + } } export {};