Skip to content

Commit

Permalink
feat: add logic for inplayer social login
Browse files Browse the repository at this point in the history
  • Loading branch information
naumovski-filip committed May 31, 2023
1 parent 0dd1204 commit 04c1d74
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 24 deletions.
2 changes: 2 additions & 0 deletions src/components/LoginForm/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Visibility from '#src/icons/Visibility';
import VisibilityOff from '#src/icons/VisibilityOff';
import FormFeedback from '#components/FormFeedback/FormFeedback';
import LoadingOverlay from '#components/LoadingOverlay/LoadingOverlay';
import SocialButtonsList from '#components/SocialButtonsList/SocialButtonsList';
import { testId } from '#src/utils/common';
import { addQueryParam } from '#src/utils/location';
import type { FormErrors } from '#types/form';
Expand All @@ -35,6 +36,7 @@ const LoginForm: React.FC<Props> = ({ onSubmit, onChange, values, errors, submit

return (
<form onSubmit={onSubmit} data-testid={testId('login-form')} noValidate>
<SocialButtonsList />
<h2 className={styles.title}>{t('login.sign_in')}</h2>
{errors.form ? <FormFeedback variant="error">{errors.form}</FormFeedback> : null}
<TextField
Expand Down
37 changes: 35 additions & 2 deletions src/components/Root/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { FC, useEffect, useMemo } from 'react';
import React, { FC, useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
import { useSearchParams } from 'react-router-dom';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import InPlayer from '@inplayer-org/inplayer.js';

import ErrorPage from '#components/ErrorPage/ErrorPage';
import AccountModal from '#src/containers/AccountModal/AccountModal';
Expand All @@ -14,6 +15,8 @@ import { loadAndValidateConfig } from '#src/utils/configLoad';
import { initSettings } from '#src/stores/SettingsController';
import AppRoutes from '#src/containers/AppRoutes/AppRoutes';
import registerCustomScreens from '#src/screenMapping';
import { getAccount } from '#src/stores/AccountController';
import { useAccountStore } from '#src/stores/AccountStore';

const Root: FC = () => {
const { t } = useTranslation('error');
Expand Down Expand Up @@ -45,6 +48,36 @@ const Root: FC = () => {
registerCustomScreens();
}, []);

const userLoading = useAccountStore((s) => s.loading);

const navigate = useNavigate();
const location = useLocation();

const getAuthParams = useCallback(() => {
const queryParams = new URLSearchParams(window.location.href.split('#')[1]);
const token = queryParams.get('token');
const refreshToken = queryParams.get('refresh_token');
const expires = queryParams.get('expires');
return { token, refreshToken, expires: parseInt(expires ?? '') };
}, []);

useEffect(() => {
const getUserInfo = async () => {
const auth = getAuthParams();
if (!auth.token || !auth.refreshToken || !auth.expires) {
return;
}
InPlayer.Account.setToken(auth.token, auth.refreshToken, auth.expires);
await getAccount({
jwt: auth.token,
refreshToken: auth.refreshToken,
customerToken: '',
});
navigate(location.pathname);
};
getUserInfo();
}, [getAuthParams, location.pathname, navigate, userLoading]);

const IS_DEMO_OR_PREVIEW = IS_DEMO_MODE || IS_PREVIEW_MODE;

// Show the spinner while loading except in demo mode (the demo config shows its own loading status)
Expand Down
49 changes: 45 additions & 4 deletions src/components/SocialButton/SocialButton.module.scss
Original file line number Diff line number Diff line change
@@ -1,16 +1,57 @@
@use 'src/styles/variables';
@use 'src/styles/theme';

.socialButtonContainer {
position: relative; /* Add relative positioning to the container */
display: flex;
justify-content: center;
justify-content: center; /* Center the contents horizontally and vertically */
align-items: center;
width: fit-content;
width: 100%;
height: fit-content;
padding: 0.5rem;
color: variables.$gray-darker;
text-decoration: none;
background-color: variables.$white;
border: 1px solid #979797;
border-radius: 0.25rem;
gap: 0.5rem;
transition: background-color 0.1s ease-in-out;
}

.socialButtonContainer:hover {
transform: scale(1.05);
background-color: variables.$gray-lighter;
cursor: pointer;
}

.socialButtonContainer:active {
transform: scale(0.95);
background-color: variables.$gray-light;
}

.socialButtonIconContainer {
position: absolute;
left: 0.5rem;
display: flex;
justify-content: center;
align-items: center;
width: 2.5rem;
height: 100%;
}

.socialButtonIcon {
width: 100%;
height: 100%;
}

.socialButtonTextContainer {
display: flex;
flex-grow: 1;
justify-content: center;
align-items: center;
height: 100%;
padding: 0.5rem;
font-weight: 600;
font-size: 1.25rem;
text-decoration: none;
border-radius: 0.5rem;
gap: 0.5rem;
}
13 changes: 8 additions & 5 deletions src/components/SocialButton/SocialButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ export type SocialButtonVariant = 'facebook' | 'google' | 'twitter';

interface SocialButtonProps {
variant: SocialButtonVariant;
onClick: () => void;
href: string;
}

const SocialButton = ({ variant, onClick }: SocialButtonProps) => {
const SocialButton = ({ variant, href }: SocialButtonProps) => {
const [icon, setIcon] = useState<string | null>(null);

useEffect(() => {
Expand All @@ -21,9 +21,12 @@ const SocialButton = ({ variant, onClick }: SocialButtonProps) => {
}, [variant]);

return (
<div onClick={onClick} className={styles.socialButtonContainer}>
<img src={icon ?? ''} alt={`${variant} icon`} />
</div>
<a href={href} className={styles.socialButtonContainer}>
<div className={styles.socialButtonIconContainer}>
<img className={styles.socialButtonIcon} src={icon ?? ''} alt={`${variant} icon`} />
</div>
<span className={styles.socialButtonTextContainer}>Sign in with {variant.charAt(0).toUpperCase() + variant.slice(1)}</span>
</a>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.socialButtonsListContainer {
display: flex;
flex-direction: row;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
padding: 1rem;
padding: 1rem 0;
gap: 1rem;
}
33 changes: 22 additions & 11 deletions src/components/SocialButtonsList/SocialButtonsList.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
import { useQuery } from 'react-query';

import SocialButton, { SocialButtonVariant } from '../SocialButton/SocialButton';

import styles from './SocialButtonsList.module.scss';

type SocialButtonsListProps = {
buttonProps: {
[key in SocialButtonVariant]: {
onClick: () => void;
};
};
};
import { getSocialLoginUrls } from '#src/stores/AccountController';

const SocialButtonsList = () => {
const urls = useQuery('socialUrls', getSocialLoginUrls);

if (urls.error || !urls.data) {
return null;
}

const formattedData = urls.data.reduce(
(acc, url) => ({
...acc,
...url,
}),
{} as {
[key in SocialButtonVariant]: string;
},
);

const SocialButtonsList = ({ buttonProps }: SocialButtonsListProps) => {
const variants: SocialButtonVariant[] = ['facebook', 'google', 'twitter'];
return (
<div className={styles.socialButtonsListContainer}>
{variants.map((variant) => (
<SocialButton key={variant} variant={variant} onClick={buttonProps[variant].onClick} />
{Object.entries(formattedData).map(([variant, url]) => (
<SocialButton key={variant} variant={variant as SocialButtonVariant} href={url} />
))}
</div>
);
Expand Down
2 changes: 2 additions & 0 deletions src/services/cleeng.account.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ export const updatePersonalShelves: UpdatePersonalShelves = async (payload, sand
};
export const exportAccountData = () => null;

export const getSocialUrls = () => null;

export const canUpdateEmail = true;

export const canSupportEmptyFullName = true;
Expand Down
17 changes: 17 additions & 0 deletions src/services/inplayer.account.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,23 @@ export const exportAccountData: ExportAccountData = async () => {
};
};

export const getSocialUrls = async (config: Config) => {
const socialState = window.btoa(
JSON.stringify({
client_id: config.integrations.jwp?.clientId || '',
redirect: window.location.href.split('u=')[0],
}),
);

const socialResponse = await InPlayer.Account.getSocialLoginUrls(socialState);

if (socialResponse.status !== 200) {
throw new Error('Failed to fetch social urls');
}

return socialResponse.data.social_urls;
};

const getCustomerExternalData = async (): Promise<ExternalData> => {
const [favoritesData, historyData] = await Promise.all([InPlayer.Account.getFavorites(), await InPlayer.Account.getWatchHistory({})]);

Expand Down
6 changes: 6 additions & 0 deletions src/stores/AccountController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,12 @@ export async function exportAccountData() {
});
}

export async function getSocialLoginUrls() {
return await useService(async ({ accountService, config }) => {
return await accountService.getSocialUrls(config);
});
}

/**
* Get multiple media items for the given IDs. This function uses watchlists to get several medias via just one request.
*
Expand Down

0 comments on commit 04c1d74

Please sign in to comment.