diff --git a/components/Parallax/index.tsx b/components/Parallax/index.tsx deleted file mode 100644 index 28a37d2..0000000 --- a/components/Parallax/index.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import { Parallax, Background } from 'react-parallax'; - -import styles from './styles.module.css'; - - -const _Parallax: React.FC = () => { - const parallaxBackgroundImage: string = 'https://images.unsplash.com/photo-1493612276216-ee3925520721?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1300&q=80'; - const sectionRef = useRef(null); - - useEffect(() => { - let observer: IntersectionObserver; - - if (process.browser && 'IntersectionObserver' in window && sectionRef.current) { - const observerOptions: IntersectionObserverInit = { - root: null, - rootMargin: '0px', - threshold: [0.5, 0.8] - }; - - observer = new IntersectionObserver(handleIntersection, observerOptions); - - observer.observe(sectionRef.current); - } - - return () => { - const sectionRefDuplicate = sectionRef; - - if (observer && sectionRefDuplicate.current) { - observer.unobserve(sectionRefDuplicate.current) - } - } - }, []); - - const handleIntersection: IntersectionObserverCallback = (entries: IntersectionObserverEntry[]) => { - const [parallaxContainer] = entries; - - if (parallaxContainer.boundingClientRect.top <= 200) { - console.log('about to scroll') - parallaxContainer.target.scrollTo({ - top: 0, - left: 0, - behavior: 'smooth' - }); - } - - console.log(parallaxContainer.boundingClientRect, parallaxContainer.target.scrollTo, parallaxContainer) - if (parallaxContainer.intersectionRatio > 0) { - console.log('Element is Intersecting'); - } else { - console.log('Element is NOT Intersecting'); - } - } - - return ( -
- - {/* -
-
*/} -
- Custom Text -
- -
- Custom Text -
- -
- Custom Text -
-
-
- ); -}; - -export default _Parallax; diff --git a/components/Parallax/styles.module.css b/components/Parallax/styles.module.css deleted file mode 100644 index 062240f..0000000 --- a/components/Parallax/styles.module.css +++ /dev/null @@ -1,30 +0,0 @@ -.parallaxContainer { - height: 100vh; - width: 100vw; - border: 5px solid red; -} - -.parallaxBackground { - width: 100% !important; - height: 100% !important; -} - -.page { - height: 100%; - width: 100%; -} - -.pageOne { - composes: page; - background-color: red; -} - -.pageTwo { - composes: page; - background-color: black; -} - -.pageThree { - composes: page; - background-color: beige; -} diff --git a/components/Playlist/SelectPlatform.tsx b/components/Playlist/SelectPlatform.tsx index 433ed52..6a8384c 100644 --- a/components/Playlist/SelectPlatform.tsx +++ b/components/Playlist/SelectPlatform.tsx @@ -1,9 +1,10 @@ -import React, { Fragment, useState } from 'react'; +import React, { FormEvent, useState } from 'react'; import { RadioGroup } from '@headlessui/react'; import cn from 'classnames'; +import { useRouter } from 'next/router'; import styles from './styles.module.css'; -import { SupportedPlatformsForLogin } from '../../utils/platforms'; +import { SupportedPlatformsForLogin, getPlatformLabelForLogin } from '../../utils/platforms'; import type { LoginPlatform, RadioGroupRenderProps @@ -12,12 +13,28 @@ import CheckIcon from '../icons/CheckIcon'; const SelectPlatform: React.FC = () => { + const router = useRouter(); const [userPlatform, setUserPlatform] = useState(null); const isUserPlatformSelected = userPlatform !== null; + const handlePlatformSelect = (e: FormEvent): void => { + e.preventDefault(); + + if (userPlatform) { + const platformLabel = getPlatformLabelForLogin(userPlatform.name); + const currentUrl = window.location.href; + + router.replace(`/auth/example/${platformLabel}?type=web&next=${currentUrl}`); + return; + } + + // TODO: log this to sentry + console.error('Invalid platform selection. You have to select a platfrom before proceeding.'); + }; + return ( -
+
Select a platform to clone the playlist. @@ -48,8 +65,14 @@ const SelectPlatform: React.FC = () => {
- -
+ + ); } diff --git a/index.d.ts b/index.d.ts index e3d5cca..71e43cc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,12 +3,6 @@ import { MenuId } from 'react-contexify'; import { ContextMenuChildName, - APPLE_MUSIC_TYPE, - SPOTIFY_TYPE, - DEEZER_TYPE -} from './utils/constants'; - -import { ContactFormReducerEnum, ANNIE_TYPE, SPOTIFY_TYPE, @@ -261,3 +255,34 @@ export interface ModalProps { title: string; description?: string; } + +export interface AuthExampleProps { + error?: string; + url: string; + type: string; + provider: string; + nextUrl: string; +} + +export interface AuthExampleServerSideProps { + props: AuthExampleProps; +} + +export interface AuthRedirectProps { + error: string | null; + url: string | null; +} + +export interface AuthRedirectServerSideProps { + props: AuthRedirectProps; +} + +export interface AuthVerifyProps { + error: string | null; + code: string | null; + provider: string | null; +} + +export interface AuthVerifyServerSideProps { + props: AuthVerifyProps; +} diff --git a/next.config.js b/next.config.js index b3d1970..be19c64 100644 --- a/next.config.js +++ b/next.config.js @@ -14,10 +14,10 @@ module.exports = { publicRuntimeConfig: { isDev, apiBaseUrl: process.env.ANNIE_API_BASE_URL, + NONCE: COMMIT_REF, + ENV: process.env.NODE_ENV || 'development', }, env: { - COMMIT_REF, - NONCE: COMMIT_REF, ENV: process.env.NODE_ENV || 'development', BASE_URL: process.env.ANNIE_CLIENT_BASE_URL, API_BASE_URL: process.env.ANNIE_API_BASE_URL diff --git a/package.json b/package.json index e5fa4c4..9de9d23 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "react-hot-toast": "1.0.2", "react-parallax": "3.0.3", "react-select": "4.1.0", + "react-use": "^17.3.1", "typescript": "4.3.5" }, "scripts": { diff --git a/pages/_document.tsx b/pages/_document.tsx index dde397f..163b79d 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -6,10 +6,14 @@ import Document, { Html, } from 'next/document'; import { ServerPortal } from '@jesstelford/react-portal-universal/server'; +import getConfig from 'next/config'; import { GA_TRACKING_ID } from '../utils/googleAnalytics'; import { Fragment } from 'react'; + +const { publicRuntimeConfig } = getConfig(); + class MyDocument extends Document { static async getInitialProps(ctx: DocumentContext) { const portals = new ServerPortal(); @@ -33,7 +37,7 @@ class MyDocument extends Document { render() { const analyticsUrl: string = `https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`; - const { NONCE, ENV } = process.env; + const { NONCE, ENV } = publicRuntimeConfig; const isDev: boolean = ENV === 'development'; // const referrer = 'strict-origin'; // const cspContent = `default-src 'self' *.dzcdn.net api.anniemusic.app googletagmanager.com; style-src 'strict-dynamic' 'nonce-w329sdada'; script-src 'strict-dynamic'; object-src 'none'; font-src 'self'; img-src 'self' res.cloudinary.com *.scdn.co *.dzcdn.net; frame-src airtable.com; base-uri 'self'; frame-ancestors airtable.com; report-uri http://localhost:5000/api/v1/complaint/csp`; diff --git a/pages/auth/[provider].tsx b/pages/auth/[provider].tsx new file mode 100644 index 0000000..098797d --- /dev/null +++ b/pages/auth/[provider].tsx @@ -0,0 +1,124 @@ +import React, { Fragment, useState } from 'react'; +import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next'; +import { useRouter } from 'next/router'; +import getConfig from 'next/config'; +import { useLocalStorage, useMount } from 'react-use'; + +import Header from '../../components/Header'; +import Footer from '../../components/Footer'; +import CircleSpinner from '../../components/Spinner/CircleSpinner'; +import styles from './styles.module.css'; +import type { + AuthRedirectProps, + AuthRedirectServerSideProps +} from '../..'; +import { isValidProvider } from '../../utils/provider'; +import { AUTH_NEXT_URL_KEY, FetchStatus } from '../../utils/constants'; + + +const { publicRuntimeConfig } = getConfig(); + +const AuthRedirectPage: NextPage = (props: AuthRedirectProps) => { + const { url, error } = props; + + const [status, setStatus] = useState(FetchStatus.IDLE); + const [isMobile, setIsMobile] = useState(false); + const router = useRouter(); + const [nextUrl] = useLocalStorage(AUTH_NEXT_URL_KEY, undefined, { raw: true }); + + const handleAuthVerification = () => { + setIsMobile(!nextUrl); + setStatus(FetchStatus.SUCCESS); + console.log('mounted', nextUrl); + }; + + useMount(handleAuthVerification); + + const renderContent = (): JSX.Element => { + if (error || status === FetchStatus.ERROR) { + const message = error || 'An error occurred while verifying your authentication credentials. Please try again.'; + + return ( + +

{message}

+
+ ); + } + + if (url && status === FetchStatus.SUCCESS) { + switch (isMobile) { + case true: + window.location.href = `annie://login${url}`; + break; + case false: + router.replace(`/auth/verify${url}`); + break; + default: + break; + } + + return
; + } + + return ; + } + + + return ( + +
+
+ {renderContent()} +
+