Skip to content

Commit

Permalink
feat(project): add paywall lock icon to shelf/grid items
Browse files Browse the repository at this point in the history
  • Loading branch information
royschut committed Jul 30, 2021
1 parent f01075e commit 345ac68
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 22 deletions.
19 changes: 17 additions & 2 deletions src/components/Card/Card.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

& .poster {
box-shadow: 0 0 0 3px var(--highlight-color, variables.$white), 0 8px 10px rgb(0 0 0 / 14%), 0 3px 14px rgb(0 0 0 / 12%),
0 4px 5px rgb(0 0 0 / 20%);
0 4px 5px rgb(0 0 0 / 20%);
}
}
}
Expand Down Expand Up @@ -162,12 +162,18 @@ $aspects: ((1, 1), (2, 1), (2, 3), (4, 3), (5, 3), (16, 9), (9, 16));
color: var(--card-color);
}

.tags {
display: flex;
}

.tag {
display: flex;
align-items: center;
padding: 4px 8px;
color: var(--card-color);
font-family: var(--body-font-family);
font-weight: 600;
font-size: 13px;
font-size: 16px;
white-space: nowrap;
background-color: rgba(variables.$black, 0.6);
border-radius: 4px;
Expand All @@ -176,6 +182,15 @@ $aspects: ((1, 1), (2, 1), (2, 3), (4, 3), (5, 3), (16, 9), (9, 16));
}
}

.lock {
margin-right: variables.$base-spacing / 2;
padding: 2px 6px;
> svg {
width: 16px;
height: 21px;
}
}

.live {
background-color: variables.$red;
}
Expand Down
12 changes: 11 additions & 1 deletion src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import classNames from 'classnames';
import { useTranslation } from 'react-i18next';

import { formatDurationTag } from '../../utils/formatting';
import Lock from '../../icons/Lock';

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

Expand All @@ -21,6 +22,7 @@ type CardProps = {
disabled?: boolean;
loading?: boolean;
isCurrent?: boolean;
isLocked?: boolean;
currentLabel?: string;
enableTitle?: boolean;
};
Expand All @@ -41,6 +43,7 @@ function Card({
disabled = false,
loading = false,
isCurrent = false,
isLocked = true,
currentLabel,
}: CardProps): JSX.Element {
const { t } = useTranslation('common');
Expand Down Expand Up @@ -85,7 +88,14 @@ function Card({
{!loading && (
<div className={styles.meta}>
{featured && !disabled && enableTitle && <div className={classNames(styles.title, { [styles.loading]: loading })}>{title}</div>}
{renderTag()}
<div className={styles.tags}>
{isLocked && (
<div className={classNames(styles.tag, styles.lock)}>
<Lock />
</div>
)}
{renderTag()}
</div>
</div>
)}
{progress ? (
Expand Down
5 changes: 5 additions & 0 deletions src/components/CardGrid/CardGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type CardGridProps = {
cols?: Breakpoints;
currentCardItem?: PlaylistItem;
currentCardLabel?: string;
hasActiveSubscription: boolean;
requiresSubscription: boolean;
};

function CardGrid({
Expand All @@ -39,6 +41,8 @@ function CardGrid({
cols = defaultCols,
currentCardItem,
currentCardLabel,
requiresSubscription,
hasActiveSubscription,
}: CardGridProps) {
const breakpoint: Breakpoint = useBreakpoint();
const isLargeScreen = breakpoint >= Breakpoint.md;
Expand Down Expand Up @@ -69,6 +73,7 @@ function CardGrid({
loading={isLoading}
isCurrent={currentCardItem && currentCardItem.mediaid === mediaid}
currentLabel={currentCardLabel}
isLocked={requiresSubscription && !hasActiveSubscription && playlistItem.requiresSubscription !== 'false'}
/>
</div>
</div>
Expand Down
18 changes: 17 additions & 1 deletion src/components/Shelf/Shelf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export type ShelfProps = {
loading?: boolean;
error?: unknown;
title?: string;
hasActiveSubscription: boolean;
requiresSubscription: boolean;
};

const Shelf: React.FC<ShelfProps> = ({
Expand All @@ -52,6 +54,8 @@ const Shelf: React.FC<ShelfProps> = ({
featured = false,
loading = false,
error = null,
requiresSubscription,
hasActiveSubscription,
}: ShelfProps) => {
const breakpoint: Breakpoint = useBreakpoint();
const { t } = useTranslation('common');
Expand All @@ -76,9 +80,21 @@ const Shelf: React.FC<ShelfProps> = ({
featured={featured}
disabled={!isInView}
loading={loading}
isLocked={requiresSubscription && !hasActiveSubscription && item.requiresSubscription !== 'false'}
/>
),
[enableCardTitles, featured, imageSourceWidth, loading, onCardClick, onCardHover, playlist.feedid, watchHistory],
[
enableCardTitles,
featured,
imageSourceWidth,
loading,
onCardClick,
onCardHover,
playlist.feedid,
watchHistory,
requiresSubscription,
hasActiveSubscription,
],
);

const renderRightControl = useCallback(
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Subscription/SubscriptionContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const SubscriptionContainer = ({ children }: Props): JSX.Element => {
const { data: transactions, isLoading: isTransactionsLoading } = getTransactionsQuery;

return children({
activeSubscription: subscriptions?.responseData.items.find(
activeSubscription: subscriptions?.responseData?.items.find(
(subscription) => subscription.status !== 'expired' && subscription.status !== 'terminated',
),
activePaymentDetail: paymentDetails?.responseData.paymentDetails.find((paymentDetails) => paymentDetails.active),
Expand Down
11 changes: 11 additions & 0 deletions src/icons/Lock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

import createIcon from './Icon';

export default createIcon(
'0 0 24 24',
<path
fill="currentColor"
d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z"
/>,
);
22 changes: 14 additions & 8 deletions src/screens/Home/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import React, { CSSProperties, useContext, useRef, useEffect, useCallback } from 'react';
import React, { CSSProperties, useRef, useEffect, useCallback } from 'react';
import memoize from 'memoize-one';
import WindowScroller from 'react-virtualized/dist/commonjs/WindowScroller';
import List from 'react-virtualized/dist/commonjs/List';
import { useHistory } from 'react-router-dom';
import type { Config, Content } from 'types/Config';
import type { Content } from 'types/Config';
import type { PlaylistItem } from 'types/playlist';
import classNames from 'classnames';

import PlaylistContainer from '../../containers/Playlist/PlaylistContainer';
import { favoritesStore } from '../../stores/FavoritesStore';
import { AccountStore } from '../../stores/AccountStore';
import { ConfigStore } from '../../stores/ConfigStore';
import { PersonalShelf } from '../../enum/PersonalShelf';
import { useWatchHistory } from '../../stores/WatchHistoryStore';
import useBlurImageUpdater from '../../hooks/useBlurImageUpdater';
import ShelfComponent, { featuredTileBreakpoints, tileBreakpoints } from '../../components/Shelf/Shelf';
import { ConfigContext } from '../../providers/ConfigProvider';
import usePlaylist from '../../hooks/usePlaylist';
import useBreakpoint, { Breakpoint } from '../../hooks/useBreakpoint';
import scrollbarSize from '../../utils/dom';
import { cardUrl } from '../../utils/formatting';
import { configHasCleengOffer } from '../../utils/cleeng';

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

Expand All @@ -35,10 +37,11 @@ const createItemData = memoize((content) => ({ content }));

const Home = (): JSX.Element => {
const history = useHistory();
const config: Config = useContext(ConfigContext);
const config = ConfigStore.useState((state) => state.config);
const breakpoint = useBreakpoint();
const listRef = useRef<List>() as React.MutableRefObject<List>;
const content: Content[] = config?.content;
const itemData: ItemData = createItemData(content);

const { getPlaylist: getWatchHistoryPlaylist, getDictionary: getWatchHistoryDictionary } = useWatchHistory();
const watchHistory = getWatchHistoryPlaylist();
Expand All @@ -48,6 +51,9 @@ const Home = (): JSX.Element => {
const { data: { playlist } = { playlist: [] } } = usePlaylist(content[0]?.playlistId);
const updateBlurImage = useBlurImageUpdater(playlist);

const hasActiveSubscription = !!AccountStore.useState((state) => state.subscription);
const requiresSubscription = !!config.cleengId && configHasCleengOffer(config);

const onCardClick = useCallback(
(playlistItem: PlaylistItem, playlistId?: string) => {
history.push(cardUrl(playlistItem, playlistId, playlistId === PersonalShelf.ContinueWatching));
Expand All @@ -56,8 +62,6 @@ const Home = (): JSX.Element => {
);
const onCardHover = useCallback((playlistItem: PlaylistItem) => updateBlurImage(playlistItem.image), [updateBlurImage]);

const itemData: ItemData = createItemData(content);

const rowRenderer = ({ index, key, style, itemData }: rowData) => {
if (!itemData?.content?.[index]) return null;

Expand All @@ -79,6 +83,8 @@ const Home = (): JSX.Element => {
enableCardTitles={config.options.shelveTitles}
title={playlist.title}
featured={contentItem.featured === true}
hasActiveSubscription={hasActiveSubscription}
requiresSubscription={requiresSubscription}
/>
</div>
</div>
Expand Down Expand Up @@ -122,13 +128,13 @@ const Home = (): JSX.Element => {

useEffect(() => {
if (favorites || watchHistory) {
((listRef.current as unknown) as List)?.recomputeRowHeights();
(listRef.current as unknown as List)?.recomputeRowHeights();
}
}, [favorites, watchHistory]);

return (
<div className={styles.home}>
<WindowScroller onResize={() => ((listRef.current as unknown) as List)?.recomputeRowHeights()}>
<WindowScroller onResize={() => (listRef.current as unknown as List)?.recomputeRowHeights()}>
{({ height, isScrolling, onChildScroll, scrollTop }) => (
<List
className={styles.list}
Expand Down
4 changes: 3 additions & 1 deletion src/screens/Movie/Movie.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const Movie = ({ match, location }: RouteComponentProps<MovieRouteParams>): JSX.
});
const { data: subscriptionsResult, isLoading: isSubscriptionsLoading } = getSubscriptionsQuery;
const subscriptions = subscriptionsResult?.responseData?.items;
const hasActiveSubscription = subscriptions?.find(
const hasActiveSubscription = !!subscriptions?.find(
(subscription: Subscription) => subscription.status === 'active' || subscription.status === 'cancelled',
);
const allowedToWatch = useMemo<boolean>(
Expand Down Expand Up @@ -205,6 +205,8 @@ const Movie = ({ match, location }: RouteComponentProps<MovieRouteParams>): JSX.
currentCardItem={item}
currentCardLabel={t('currently_playing')}
enableCardTitles={options.shelveTitles}
hasActiveSubscription={hasActiveSubscription}
requiresSubscription={!!cleengId && configHasOffer}
/>
</>
) : undefined}
Expand Down
14 changes: 10 additions & 4 deletions src/screens/Playlist/Playlist.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React, { useContext, useEffect, useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { RouteComponentProps, useHistory } from 'react-router-dom';
import type { PlaylistItem } from 'types/playlist';
import type { Config } from 'types/Config';
import { Helmet } from 'react-helmet';

import { ConfigContext } from '../../providers/ConfigProvider';
import { cardUrl } from '../../utils/formatting';
import usePlaylist from '../../hooks/usePlaylist';
import { filterPlaylist, getFiltersFromConfig } from '../../utils/collection';
import CardGrid from '../../components/CardGrid/CardGrid';
import ErrorPage from '../../components/ErrorPage/ErrorPage';
import Filter from '../../components/Filter/Filter';
import useBlurImageUpdater from '../../hooks/useBlurImageUpdater';
import { AccountStore } from '../../stores/AccountStore';
import { ConfigStore } from '../../stores/ConfigStore';
import { configHasCleengOffer } from '../../utils/cleeng';

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

Expand All @@ -25,7 +26,7 @@ function Playlist({
},
}: RouteComponentProps<PlaylistRouteParams>) {
const history = useHistory();
const config: Config = useContext(ConfigContext);
const config = ConfigStore.useState((state) => state.config);
const { isLoading, isPlaceholderData, error, data: { title, playlist } = { title: '', playlist: [] } } = usePlaylist(id);

const [filter, setFilter] = useState<string>('');
Expand All @@ -34,6 +35,9 @@ function Playlist({
const filteredPlaylist = useMemo(() => filterPlaylist(playlist, filter), [playlist, filter]);
const updateBlurImage = useBlurImageUpdater(filteredPlaylist);

const hasActiveSubscription = !!AccountStore.useState((state) => state.subscription);
const requiresSubscription = !!config.cleengId && configHasCleengOffer(config);

useEffect(() => {
// reset filter when the playlist id changes
setFilter('');
Expand Down Expand Up @@ -66,6 +70,8 @@ function Playlist({
onCardHover={onCardHover}
isLoading={isLoading}
enableCardTitles={config.options.shelveTitles}
hasActiveSubscription={hasActiveSubscription}
requiresSubscription={requiresSubscription}
/>
</main>
</div>
Expand Down
14 changes: 11 additions & 3 deletions src/screens/Search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useEffect } from 'react';
import React, { useEffect } from 'react';
import type { RouteComponentProps } from 'react-router-dom';
import { useHistory } from 'react-router';
import { Helmet } from 'react-helmet';
Expand All @@ -10,10 +10,12 @@ import useSearchQueryUpdater from '../../hooks/useSearchQueryUpdater';
import ErrorPage from '../../components/ErrorPage/ErrorPage';
import type { PlaylistItem } from '../../../types/playlist';
import CardGrid from '../../components/CardGrid/CardGrid';
import { ConfigContext } from '../../providers/ConfigProvider';
import { cardUrl } from '../../utils/formatting';
import useFirstRender from '../../hooks/useFirstRender';
import useSearchPlaylist from '../../hooks/useSearchPlaylist';
import { AccountStore } from '../../stores/AccountStore';
import { ConfigStore } from '../../stores/ConfigStore';
import { configHasCleengOffer } from '../../utils/cleeng';

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

Expand All @@ -27,7 +29,8 @@ const Search: React.FC<RouteComponentProps<SearchRouteParams>> = ({
},
}) => {
const { t } = useTranslation('search');
const { siteName, searchPlaylist, options } = useContext(ConfigContext);
const config = ConfigStore.useState((state) => state.config);
const { siteName, searchPlaylist, options } = config;
const firstRender = useFirstRender();
const searchQuery = UIStore.useState((s) => s.searchQuery);
const { updateSearchQuery } = useSearchQueryUpdater();
Expand All @@ -36,6 +39,9 @@ const Search: React.FC<RouteComponentProps<SearchRouteParams>> = ({

const updateBlurImage = useBlurImageUpdater(playlist);

const hasActiveSubscription = !!AccountStore.useState((state) => state.subscription);
const requiresSubscription = !!config.cleengId && configHasCleengOffer(config);

// Update the search bar query to match the route param on mount
useEffect(() => {
if (!firstRender) {
Expand Down Expand Up @@ -100,6 +106,8 @@ const Search: React.FC<RouteComponentProps<SearchRouteParams>> = ({
onCardHover={onCardHover}
isLoading={firstRender}
enableCardTitles={options.shelveTitles}
hasActiveSubscription={hasActiveSubscription}
requiresSubscription={requiresSubscription}
/>
</main>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/screens/Series/Series.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const Series = ({ match, location }: RouteComponentProps<SeriesRouteParams>): JS
});
const { data: subscriptionsResult, isLoading: isSubscriptionsLoading } = getSubscriptionsQuery;
const subscriptions = subscriptionsResult?.responseData?.items;
const hasActiveSubscription = subscriptions?.find(
const hasActiveSubscription = !!subscriptions?.find(
(subscription: Subscription) => subscription.status === 'active' || subscription.status === 'cancelled',
);
const allowedToWatch = useMemo<boolean>(
Expand Down Expand Up @@ -236,6 +236,8 @@ const Series = ({ match, location }: RouteComponentProps<SeriesRouteParams>): JS
currentCardItem={item}
currentCardLabel={t('current_episode')}
enableCardTitles={options.shelveTitles}
hasActiveSubscription={hasActiveSubscription}
requiresSubscription={!!cleengId && configHasOffer}
/>
</>
</VideoComponent>
Expand Down

0 comments on commit 345ac68

Please sign in to comment.