Skip to content

Commit

Permalink
feat(videodetail): implement conditional startwatching button + label
Browse files Browse the repository at this point in the history
  • Loading branch information
royschut committed Jul 30, 2021
1 parent bc9dae1 commit 9915acb
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 96 deletions.
4 changes: 3 additions & 1 deletion src/components/Video/Video.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ describe('<Video>', () => {
title="Test video"
play
item={item}
startPlay={jest.fn()}
allowedToWatch={true}
startWatchingLabel="Start watching"
onStartWatchingClick={jest.fn()}
goBack={jest.fn()}
poster="fading"
hasShared={false}
Expand Down
14 changes: 9 additions & 5 deletions src/components/Video/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ type Props = {
feedId?: string;
trailerItem?: PlaylistItem;
play: boolean;
allowedToWatch: boolean;
startWatchingLabel: string;
progress?: number;
startPlay: () => void;
onStartWatchingClick: () => void;
goBack: () => void;
onComplete?: () => void;
isFavorited: boolean;
Expand All @@ -54,7 +56,9 @@ const Video: React.FC<Props> = ({
feedId,
trailerItem,
play,
startPlay,
allowedToWatch,
startWatchingLabel,
onStartWatchingClick,
progress,
goBack,
onComplete,
Expand Down Expand Up @@ -128,9 +132,9 @@ const Video: React.FC<Props> = ({
color="primary"
variant="contained"
size="large"
label={typeof progress === 'number' ? t('video:continue_watching') : t('video:start_watching')}
startIcon={<Play />}
onClick={startPlay}
label={startWatchingLabel}
startIcon={allowedToWatch ? <Play /> : undefined}
onClick={onStartWatchingClick}
active={play}
fullWidth={breakpoint < Breakpoint.md}
>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Video/__snapshots__/Video.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ exports[`<Video> renders and matches snapshot 1`] = `
</svg>
</div>
<span>
video:start_watching
Start watching
</span>
</button>
<button
Expand Down
11 changes: 9 additions & 2 deletions src/components/Welcome/Welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@ type Props = {

const Welcome: React.FC<Props> = ({ onCloseButtonClick, onCountdownCompleted }) => {
const { t } = useTranslation('account');
const config = ConfigStore.useState(s => s.config);
const config = ConfigStore.useState((s) => s.config);
const countdown = useCountdown(10, 1, onCountdownCompleted);

return (
<div className={styles.welcome}>
<h2>{t('checkout.welcome_title', { siteName: config.siteName })}</h2>
<p>{t('checkout.welcome_description', { siteName: config.siteName })}</p>
<Button variant="contained" color="primary" label={t('checkout.start_watching', { countdown })} onClick={onCloseButtonClick} size="large" fullWidth />
<Button
variant="contained"
color="primary"
label={t('checkout.start_watching', { countdown })}
onClick={onCloseButtonClick}
size="large"
fullWidth
/>
</div>
);
};
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/en_US/video.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
{
"add_to_favorites": "Add to favorites",
"all_seasons": "All seasons",
"complete_your_subscription": "Complete your subscription",
"continue_watching": "Continue watching",
"copied_url": "Copied url",
"current_episode": "Current episode",
"currently_playing": "Current video",
"episodes": "Episodes",
"favorite": "Favorite",
"remove_from_favorites": "Remove from favorites",
"renew_your_subscription": "Renew your subscription",
"season_prefix": "Season ",
"share": "Share",
"sign_up_to_start_watching": "Sign up to start watching!",
"start_watching": "Start watching",
"total_episodes": "{{ count }} episode",
"total_episodes_plural": "{{ count }} episodes",
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/nl_NL/video.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
{
"add_to_favorites": "",
"all_seasons": "",
"complete_your_subscription": "",
"continue_watching": "",
"copied_url": "",
"current_episode": "",
"currently_playing": "",
"episodes": "",
"favorite": "",
"remove_from_favorites": "",
"renew_your_subscription": "",
"season_prefix": "",
"share": "",
"sign_up_to_start_watching": "",
"start_watching": "",
"total_episodes": "",
"total_episodes_plural": "",
Expand Down
118 changes: 80 additions & 38 deletions src/screens/Movie/Movie.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import type { RouteComponentProps } from 'react-router-dom';
import { useHistory } from 'react-router';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';

import { useFavorites } from '../../stores/FavoritesStore';
import { ConfigContext } from '../../providers/ConfigProvider';
import useBlurImageUpdater from '../../hooks/useBlurImageUpdater';
import { cardUrl, movieURL, videoUrl } from '../../utils/formatting';
import type { PlaylistItem } from '../../../types/playlist';
Expand All @@ -19,51 +19,76 @@ import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
import useRecommendedPlaylist from '../../hooks/useRecommendationsPlaylist';
import { watchHistoryStore } from '../../stores/WatchHistoryStore';
import { VideoProgressMinMax } from '../../config';
import { ConfigStore } from '../../stores/ConfigStore';
import { AccountStore } from '../../stores/AccountStore';
import { configHasCleengOffer } from '../../utils/cleeng';
import type { Subscription } from '../../../types/subscription';
import { getSubscriptions } from '../../services/subscription.service';

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

type MovieRouteParams = {
id: string;
};

const Movie = ({
match: {
params: { id },
},
location,
}: RouteComponentProps<MovieRouteParams>): JSX.Element => {
const config = useContext(ConfigContext);
const Movie = ({ match, location }: RouteComponentProps<MovieRouteParams>): JSX.Element => {
// Routing
const history = useHistory();
const { t } = useTranslation('video');
const searchParams = useMemo(() => new URLSearchParams(location.search), [location.search]);
const id = match?.params.id;
const play = searchParams.get('play') === '1';
const feedId = searchParams.get('l');

// Config
const config = ConfigStore.useState((s) => s.config);
const { cleengId, options, recommendationsPlaylist, siteName, cleengSandbox } = config;
const configHasOffer = configHasCleengOffer(config);
const posterFading: boolean = options?.posterFading === true;
const enableSharing: boolean = options?.enableSharing === true;

const { t } = useTranslation('video');

// Media
const { isLoading, error, data: item } = useMedia(id);
const requiresSubscription = item?.requiresSubscription !== 'false';
useBlurImageUpdater(item);
const { data: trailerItem } = useMedia(item?.trailerId || '');
const { data: playlist } = useRecommendedPlaylist(recommendationsPlaylist || '', item);

const { hasItem, saveItem, removeItem } = useFavorites();
const play = searchParams.get('play') === '1';
const feedId = searchParams.get('l');
const posterFading: boolean = config ? config.options.posterFading === true : false;

const [hasShared, setHasShared] = useState<boolean>(false);
const [playTrailer, setPlayTrailer] = useState<boolean>(false);
const enableSharing: boolean = config.options.enableSharing === true;

const watchHistory = watchHistoryStore.useState((s) => s.watchHistory);
const watchHistoryItem =
item &&
watchHistory.find(({ mediaid, progress }) => {
return mediaid === item.mediaid && progress > VideoProgressMinMax.Min && progress < VideoProgressMinMax.Max;
});
const progress = watchHistoryItem?.progress;

useBlurImageUpdater(item);

// General state
const isFavorited = !!item && hasItem(item);
const [hasShared, setHasShared] = useState<boolean>(false);
const [playTrailer, setPlayTrailer] = useState<boolean>(false);

const startPlay = () => item && history.push(videoUrl(item, searchParams.get('r'), true));
const goBack = () => item && history.push(videoUrl(item, searchParams.get('r'), false));
// User
const user = AccountStore.useState((state) => state.user);
const auth = AccountStore.useState((state) => state.auth);
const jwt = auth?.jwt || '';
const customerId = user?.id || -1; // id must be number
const getSubscriptionsQuery = useQuery(['subscriptions', customerId], () => getSubscriptions({ customerId }, cleengSandbox, jwt), {
enabled: !!user,
});
const { data: subscriptionsResult, isLoading: isSubscriptionsLoading } = getSubscriptionsQuery;
const subscriptions = subscriptionsResult?.responseData?.items;
const hasActiveSubscription = subscriptions?.find((subscription: Subscription) => subscription.status === 'active');
const allowedToWatch = useMemo<boolean>(
() => !requiresSubscription || !cleengId || (!!user && (!configHasOffer || !hasActiveSubscription)),
[requiresSubscription, cleengId, user, configHasOffer, hasActiveSubscription],
);

// Handlers
const goBack = () => item && history.push(videoUrl(item, searchParams.get('r'), false));
const onCardClick = (item: PlaylistItem) => history.push(cardUrl(item));

const onShareClick = (): void => {
if (!item) return;

Expand All @@ -76,7 +101,32 @@ const Movie = ({
setHasShared(true);
setTimeout(() => setHasShared(false), 2000);
};
const handleComplete = useCallback(() => {
if (!id || !playlist) return;

const index = playlist.playlist.findIndex(({ mediaid }) => mediaid === id);
const nextItem = playlist.playlist[index + 1];

return nextItem && history.push(videoUrl(nextItem, searchParams.get('r'), true));
}, [history, id, playlist, searchParams]);

const formatStartWatchingLabel = (): string => {
if (allowedToWatch) return typeof progress === 'number' ? t('continue_watching') : t('start_watching');
if (!user) return t('sign_up_to_start_watching');
if (subscriptions?.find((subscription: Subscription) => subscription.status === 'cancelled')) return t('complete_your_subscription');
if (subscriptions?.find((subscription: Subscription) => subscription.status === 'expired')) return t('renew_your_subscription');
if (subscriptions?.find((subscription: Subscription) => subscription.status === 'terminated')) return t('renew_your_subscription'); //todo: is this correct?
return '';
};

const handleStartWatchingClick = useCallback(() => {
if (!user) return history.push('?u=login');
if (!allowedToWatch) return history.push('/u/payment');

return item && history.push(videoUrl(item, searchParams.get('r'), true));
}, [user, history, allowedToWatch, item, searchParams]);

// Effects
useEffect(() => {
document.body.style.overflowY = play ? 'hidden' : '';
return () => {
Expand All @@ -88,21 +138,11 @@ const Movie = ({
(document.scrollingElement || document.body).scroll({ top: 0, behavior: 'smooth' });
}, [id]);

const { data: playlist } = useRecommendedPlaylist(config.recommendationsPlaylist || '', item);

const handleComplete = useCallback(() => {
if (!id || !playlist) return;

const index = playlist.playlist.findIndex(({ mediaid }) => mediaid === id);
const nextItem = playlist.playlist[index + 1];

return nextItem && history.push(videoUrl(nextItem, searchParams.get('r'), true));
}, [history, id, playlist, searchParams]);

if (isLoading && !item) return <LoadingOverlay />;
// UI
if ((isLoading && !item) || isSubscriptionsLoading) return <LoadingOverlay />;
if ((!isLoading && error) || !item) return <ErrorPage title="Video not found!" />;

const pageTitle = `${item.title} - ${config.siteName}`;
const pageTitle = `${item.title} - ${siteName}`;
const canonicalUrl = item ? `${window.location.origin}${movieURL(item)}` : window.location.href;

return (
Expand Down Expand Up @@ -137,10 +177,12 @@ const Movie = ({
feedId={feedId ?? undefined}
trailerItem={trailerItem}
play={play}
startPlay={startPlay}
allowedToWatch={allowedToWatch}
startWatchingLabel={formatStartWatchingLabel()}
onStartWatchingClick={handleStartWatchingClick}
goBack={goBack}
onComplete={handleComplete}
progress={watchHistoryItem?.progress}
progress={progress}
poster={posterFading ? 'fading' : 'normal'}
enableSharing={enableSharing}
hasShared={hasShared}
Expand All @@ -162,7 +204,7 @@ const Movie = ({
isLoading={isLoading}
currentCardItem={item}
currentCardLabel={t('currently_playing')}
enableCardTitles={config.options.shelveTitles}
enableCardTitles={options.shelveTitles}
/>
</>
) : undefined}
Expand Down
Loading

0 comments on commit 9915acb

Please sign in to comment.