Skip to content

Commit

Permalink
feat(project): support watchlists
Browse files Browse the repository at this point in the history
- Parse query param function fix
- Review warning fixes
  • Loading branch information
“Anton committed Jul 5, 2022
1 parent df60310 commit e71e6b4
Show file tree
Hide file tree
Showing 16 changed files with 40 additions and 62 deletions.
2 changes: 1 addition & 1 deletion src/components/Alert/Alert.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ vi.mock('../Dialog/Dialog', () => ({

describe('<Alert>', () => {
test('renders and matches snapshot', () => {
const { container } = render(<Alert body="Body" title="Title" open={true} onClose={vi.fn()} />);
const { container } = render(<Alert message="Body" open={true} onClose={vi.fn()} />);
expect(container).toMatchSnapshot();
});
});
9 changes: 4 additions & 5 deletions src/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ import styles from './Alert.module.scss';

type Props = {
open: boolean;
title: string;
body: string;
message: string | null;
onClose: () => void;
};

const Alert: React.FC<Props> = ({ open, title, body, onClose }: Props) => {
const Alert: React.FC<Props> = ({ open, message, onClose }: Props) => {
const { t } = useTranslation('common');

return (
<Dialog open={open} onClose={onClose}>
<h2 className={styles.title}>{title}</h2>
<p className={styles.body}>{body}</p>
<h2 className={styles.title}>{t('alert.title')}</h2>
<p className={styles.body}>{message}</p>
<Button label={t('alert.close')} variant="outlined" onClick={onClose} fullWidth />
</Dialog>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Alert/__snapshots__/Alert.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ exports[`<Alert> > renders and matches snapshot 1`] = `
<h2
class="title"
>
Title
alert.title
</h2>
<p
class="body"
Expand Down
8 changes: 8 additions & 0 deletions src/components/Video/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Button from '#src/components/Button/Button';
import IconButton from '#src/components/IconButton/IconButton';
import Modal from '#src/components/Modal/Modal';
import Fade from '#src/components/Animation/Fade/Fade';
import Alert from '#src/components/Alert/Alert';
import ModalCloseButton from '#src/components/ModalCloseButton/ModalCloseButton';
import Cinema from '#src/containers/Cinema/Cinema';
import useBreakpoint, { Breakpoint } from '#src/hooks/useBreakpoint';
Expand All @@ -20,6 +21,7 @@ import ArrowLeft from '#src/icons/ArrowLeft';
import { formatDuration } from '#src/utils/formatting';
import FavoriteBorder from '#src/icons/FavoriteBorder';
import type { PlaylistItem } from '#types/playlist';
import { useFavoritesStore } from '#src/stores/FavoritesStore';

type Poster = 'fading' | 'normal';

Expand Down Expand Up @@ -81,6 +83,11 @@ const Video: React.FC<Props> = ({
const handlePause = useCallback(() => setIsPlaying(false), []);
const handleComplete = useCallback(() => onComplete && onComplete(), [onComplete]);

const { clearWarning, warning } = useFavoritesStore((state) => ({
clearWarning: state.clearWarning,
warning: state.warning,
}));

const isLargeScreen = breakpoint >= Breakpoint.md;
const isMobile = breakpoint === Breakpoint.xs;
const imageSourceWidth = 640 * (window.devicePixelRatio > 1 || isLargeScreen ? 2 : 1);
Expand Down Expand Up @@ -189,6 +196,7 @@ const Video: React.FC<Props> = ({
</Fade>
</div>
</Fade>
<Alert open={warning !== null} message={warning} onClose={clearWarning} />
{!!trailerItem && (
<Modal open={playTrailer} onClose={onTrailerClose}>
<div className={styles.trailerModal}>
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/en_US/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"confirm": "Yes"
},
"alert": {
"title": "An error occurred",
"close": "Close"
},
"filter_videos_by_genre": "Filter videos by genre",
Expand Down
5 changes: 1 addition & 4 deletions src/i18n/locales/en_US/video.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,5 @@
"video_not_found": "Video not found",
"watch_trailer": "Watch the trailer",
"share_video": "Share this video",
"favorites_warning": {
"title": "Maximum amount of favorite videos exceeded",
"body": "You can only add up to {{ count }} favorite videos. Please delete one and repeat the operation."
}
"favorites_warning": "Maximum amount of favorite videos exceeded. You can only add up to {{ count }} favorite videos. Please delete one and repeat the operation."
}
4 changes: 4 additions & 0 deletions src/i18n/locales/nl_NL/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"close": "",
"confirm": ""
},
"alert": {
"title": "",
"close": ""
},
"filter_videos_by_genre": "",
"home": "",
"live": "",
Expand Down
5 changes: 1 addition & 4 deletions src/i18n/locales/nl_NL/video.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,5 @@
"video_not_found": "",
"watch_trailer": "",
"share_video": "",
"favorites_warning": {
"title": "",
"body": ""
}
"favorites_warning": ""
}
17 changes: 1 addition & 16 deletions src/screens/Movie/Movie.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import type { PlaylistItem } from '#types/playlist';
import VideoComponent from '#src/components/Video/Video';
import ErrorPage from '#src/components/ErrorPage/ErrorPage';
import CardGrid from '#src/components/CardGrid/CardGrid';
import Alert from '#src/components/Alert/Alert';
import useMedia from '#src/hooks/useMedia';
import { generateMovieJSONLD } from '#src/utils/structuredData';
import { copyToClipboard } from '#src/utils/dom';
Expand All @@ -26,7 +25,6 @@ import { addConfigParamToUrl } from '#src/utils/configOverride';
import usePlaylist from '#src/hooks/usePlaylist';
import useEntitlement from '#src/hooks/useEntitlement';
import StartWatchingButton from '#src/containers/StartWatchingButton/StartWatchingButton';
import { MAX_WATCHLIST_ITEMS_COUNT } from '#src/config';

type MovieRouteParams = {
id: string;
Expand Down Expand Up @@ -59,10 +57,8 @@ const Movie = ({ match, location }: RouteComponentProps<MovieRouteParams>): JSX.
const { data: playlist } = usePlaylist(features?.recommendationsPlaylist || '', { related_media_id: id });

// Favorite
const { isFavorited, toggleWarning, isWarningShown } = useFavoritesStore((state) => ({
const { isFavorited } = useFavoritesStore((state) => ({
isFavorited: !!item && state.hasItem(item),
isWarningShown: state.isWarningShown,
toggleWarning: state.toggleWarning,
}));

// User, entitlement
Expand All @@ -74,10 +70,6 @@ const Movie = ({ match, location }: RouteComponentProps<MovieRouteParams>): JSX.
toggleFavorite(item);
}, [item]);

const onToggleWarning = useCallback(() => {
toggleWarning();
}, [toggleWarning]);

const goBack = () => item && history.push(videoUrl(item, searchParams.get('r'), false));
const onCardClick = (item: PlaylistItem) => history.push(cardUrl(item));
const onShareClick = (): void => {
Expand Down Expand Up @@ -184,13 +176,6 @@ const Movie = ({ match, location }: RouteComponentProps<MovieRouteParams>): JSX.
/>
</>
) : undefined}

<Alert
open={isWarningShown}
title={t('video:favorites_warning.title')}
body={t('video:favorites_warning.body', { count: MAX_WATCHLIST_ITEMS_COUNT })}
onClose={onToggleWarning}
/>
</>
</VideoComponent>
</React.Fragment>
Expand Down
15 changes: 1 addition & 14 deletions src/screens/Series/Series.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import styles from './Series.module.scss';

import useEntitlement from '#src/hooks/useEntitlement';
import CardGrid from '#src/components/CardGrid/CardGrid';
import { MAX_WATCHLIST_ITEMS_COUNT } from '#src/config';
import useBlurImageUpdater from '#src/hooks/useBlurImageUpdater';
import { episodeURL } from '#src/utils/formatting';
import Filter from '#src/components/Filter/Filter';
Expand All @@ -28,7 +27,6 @@ import { useAccountStore } from '#src/stores/AccountStore';
import { useFavoritesStore } from '#src/stores/FavoritesStore';
import { toggleFavorite } from '#src/stores/FavoritesController';
import StartWatchingButton from '#src/containers/StartWatchingButton/StartWatchingButton';
import Alert from '#src/components/Alert/Alert';

type SeriesRouteParams = {
id: string;
Expand Down Expand Up @@ -64,10 +62,8 @@ const Series = ({ match, location }: RouteComponentProps<SeriesRouteParams>): JS
const filteredPlaylist = useMemo(() => filterSeries(seriesPlaylist.playlist, seasonFilter), [seriesPlaylist, seasonFilter]);

// Favorite
const { isFavorited, toggleWarning, isWarningShown } = useFavoritesStore((state) => ({
const { isFavorited } = useFavoritesStore((state) => ({
isFavorited: !!item && state.hasItem(item),
isWarningShown: state.isWarningShown,
toggleWarning: state.toggleWarning,
}));

const watchHistoryDictionary = useWatchHistoryStore((state) => state.getDictionary());
Expand All @@ -81,9 +77,6 @@ const Series = ({ match, location }: RouteComponentProps<SeriesRouteParams>): JS
toggleFavorite(item);
}, [item]);

const onToggleWarning = useCallback(() => {
toggleWarning();
}, [toggleWarning]);
const goBack = () => item && seriesPlaylist && history.push(episodeURL(seriesPlaylist, item.mediaid, false));
const onCardClick = (item: PlaylistItem) => seriesPlaylist && history.push(episodeURL(seriesPlaylist, item.mediaid));
const onShareClick = (): void => {
Expand Down Expand Up @@ -207,12 +200,6 @@ const Series = ({ match, location }: RouteComponentProps<SeriesRouteParams>): JS
isLoggedIn={!!user}
hasSubscription={!!subscription}
/>
<Alert
open={isWarningShown}
title={t('video:favorites_warning.title')}
body={t('video:favorites_warning.body', { count: MAX_WATCHLIST_ITEMS_COUNT })}
onClose={onToggleWarning}
/>
</>
</VideoComponent>
</React.Fragment>
Expand Down
5 changes: 3 additions & 2 deletions src/stores/FavoritesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useConfigStore } from '#src/stores/ConfigStore';
import type { Favorite, SerializedFavorite } from '#types/favorite';
import type { PlaylistItem } from '#types/playlist';
import { MAX_WATCHLIST_ITEMS_COUNT } from '#src/config';
import i18n from '#src/i18n/config';

const PERSIST_KEY_FAVORITES = `favorites${window.configId ? `-${window.configId}` : ''}`;

Expand Down Expand Up @@ -65,7 +66,7 @@ export const removeItem = (item: PlaylistItem) => {
};

export const toggleFavorite = (item: PlaylistItem | undefined) => {
const { favorites, hasItem, toggleWarning } = useFavoritesStore.getState();
const { favorites, hasItem, setWarning } = useFavoritesStore.getState();

if (!item) {
return;
Expand All @@ -81,7 +82,7 @@ export const toggleFavorite = (item: PlaylistItem | undefined) => {

// If we exceed the max available number of favorites, we show a warning
if (favorites?.length >= MAX_WATCHLIST_ITEMS_COUNT) {
toggleWarning();
setWarning(i18n.t('video:favorites_warning', { count: MAX_WATCHLIST_ITEMS_COUNT }));
return;
}

Expand Down
10 changes: 6 additions & 4 deletions src/stores/FavoritesStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import type { Playlist, PlaylistItem } from '#types/playlist';

type FavoritesState = {
favorites: Favorite[];
isWarningShown: boolean;
warning: string | null;
hasItem: (item: PlaylistItem) => boolean;
getPlaylist: () => Playlist;
toggleWarning: () => void;
setWarning: (warning: string) => void;
clearWarning: () => void;
};

export const useFavoritesStore = createStore<FavoritesState>('FavoritesState', (set, get) => ({
favorites: [],
isWarningShown: false,
toggleWarning: () => set({ isWarningShown: !get().isWarningShown }),
warning: null,
setWarning: (message: string | null) => set({ warning: message }),
clearWarning: () => set({ warning: null }),
hasItem: (item: PlaylistItem) => get().favorites.some((favoriteItem) => favoriteItem.mediaid === item.mediaid),
getPlaylist: () =>
({
Expand Down
10 changes: 3 additions & 7 deletions src/stores/WatchHistoryController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,9 @@ export const saveItem = (item: PlaylistItem, videoProgress: number | null) => {

const watchHistoryItem = createWatchHistoryItem(item, videoProgress);

let updatedHistory = watchHistory;

updatedHistory = [watchHistoryItem, ...watchHistory.filter(({ mediaid }) => mediaid !== watchHistoryItem.mediaid)];

if (watchHistory.length > MAX_WATCHLIST_ITEMS_COUNT) {
updatedHistory = updatedHistory.slice(0, MAX_WATCHLIST_ITEMS_COUNT - 1);
}
const updatedHistory = watchHistory.filter(({ mediaid }) => mediaid !== watchHistoryItem.mediaid);
updatedHistory.unshift(watchHistoryItem);
updatedHistory.splice(MAX_WATCHLIST_ITEMS_COUNT);

useWatchHistoryStore.setState({ watchHistory: updatedHistory });

Expand Down
5 changes: 3 additions & 2 deletions src/utils/formatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ export const addQueryParams = (url: string, queryParams: { [key: string]: string
Object.keys(queryParams).forEach((key) => {
const value = queryParams[key];

// null or undefined
if (value == null || !value?.length) return;
if (value == null) return;

if (typeof value === 'object' && !value?.length) return;

const formattedValue = Array.isArray(value) ? value.join(',') : value;

Expand Down
2 changes: 1 addition & 1 deletion test-e2e/codecept.desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ exports.config = {
enabled: true,
},
screenshotOnFail: {
enabled: !process.env.CI,
enabled: true,
},
allure: {
enabled: true,
Expand Down
2 changes: 1 addition & 1 deletion test-e2e/codecept.mobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ exports.config = {
enabled: true,
},
screenshotOnFail: {
enabled: !process.env.CI,
enabled: true,
},
allure: {
enabled: true,
Expand Down

0 comments on commit e71e6b4

Please sign in to comment.