Skip to content

Commit

Permalink
Merge branch 'develop' into feat/custom-reg-fields
Browse files Browse the repository at this point in the history
  • Loading branch information
mirovladimitrovski authored Jul 3, 2023
2 parents 77606db + 5c359d6 commit 411d0dd
Show file tree
Hide file tree
Showing 22 changed files with 142 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ module.exports = {
},
},
{
files: ['*.jsx', '*.tsx'],
files: ['*.jsx', '*.tsx', 'src/hooks/*.ts'],
plugins: [
// Enable linting React code
'react',
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## [4.19.1](https://github.com/jwplayer/ott-web-app/compare/v4.19.0...v4.19.1) (2023-06-20)


### Bug Fixes

* **payment:** infinite render loop when opening choose offer modal ([34fe708](https://github.com/jwplayer/ott-web-app/commit/34fe70860896cd741afddcb50087fd0b6564ed0b))
* **payment:** react error after payment ([58b877f](https://github.com/jwplayer/ott-web-app/commit/58b877fd4be086b90e01301f1c898059509f4bab))



# [4.19.0](https://github.com/jwplayer/ott-web-app/compare/v4.18.0...v4.19.0) (2023-06-20)


### Bug Fixes

* **series:** select All episodes ([3ed1151](https://github.com/jwplayer/ott-web-app/commit/3ed115126974bd251cde7c0b1198d70edc7f2f0d))


### Features

* **series:** add season titles as filters ([c42f9ad](https://github.com/jwplayer/ott-web-app/commit/c42f9ade9e74dd445124578d2f2bb7beaf144ec6))
* **series:** modify i18n keys for seasons ([bcbfa1c](https://github.com/jwplayer/ott-web-app/commit/bcbfa1cb23c60d0c31bb09faa9b85724c935ba94))



# [4.18.0](https://github.com/jwplayer/ott-web-app/compare/v4.17.0...v4.18.0) (2023-06-19)


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jw-ott-webapp",
"version": "4.18.0",
"version": "4.19.1",
"main": "index.js",
"repository": "https://github.com/jwplayer/ott-web-app.git",
"author": "JW Player",
Expand Down
2 changes: 1 addition & 1 deletion public/locales/en/video.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"live": "Live",
"remove_from_favorites": "Remove from favorites",
"season": "season",
"season_prefix": "Season ",
"season_number_filter_template": "Season {{seasonNumber}}",
"series_error": "An error occurred while requesting series",
"share": "Share",
"share_video": "Share this video",
Expand Down
2 changes: 1 addition & 1 deletion public/locales/es/video.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"live": "EN VIVO",
"remove_from_favorites": "Eliminar de favoritos",
"season": "temporada",
"season_prefix": "Temporada ",
"season_number_filter_template": "Temporada {{seasonNumber}}",
"series_error": "Se produjo un error al solicitar la serie",
"share": "Compartir",
"share_video": "Compartir este video",
Expand Down
3 changes: 0 additions & 3 deletions src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ type Props = {
defaultLabel?: string;
options?: (string | { value: string; label: string })[];
optionsStyle?: string;
valuePrefix?: string;
label?: ReactNode;
fullWidth?: boolean;
size?: 'small' | 'medium';
Expand All @@ -34,7 +33,6 @@ const Dropdown: React.FC<Props & React.AriaAttributes> = ({
optionsStyle,
label,
fullWidth,
valuePrefix,
error,
helperText,
required = false,
Expand Down Expand Up @@ -66,7 +64,6 @@ const Dropdown: React.FC<Props & React.AriaAttributes> = ({
key={typeof option === 'string' ? option : option.value}
value={typeof option === 'string' ? option : option.value}
>
{valuePrefix}
{typeof option === 'string' ? option : option.label}
</option>
))}
Expand Down
22 changes: 15 additions & 7 deletions src/components/Filter/Filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ import Dropdown from '#components/Dropdown/Dropdown';
import Button from '#components/Button/Button';
import useBreakpoint, { Breakpoint } from '#src/hooks/useBreakpoint';

type FilterOption =
| {
label: string;
value: string;
}
| string;

type Props = {
name: string;
value: string;
valuePrefix?: string;
defaultLabel: string;
options: string[];
options: FilterOption[];
forceDropdown?: boolean;
setValue: (value: string) => void;
};

const Filter: FC<Props> = ({ name, value, defaultLabel, options, setValue, valuePrefix = '', forceDropdown = false }) => {
const Filter: FC<Props> = ({ name, value, defaultLabel, options, setValue, forceDropdown = false }) => {
const { t } = useTranslation('common');
const breakpoint: Breakpoint = useBreakpoint();

Expand All @@ -32,9 +38,12 @@ const Filter: FC<Props> = ({ name, value, defaultLabel, options, setValue, value
<Fragment>
{showFilterRow ? (
<div className={styles.filterRow} role="listbox" aria-label={t('filter_videos_by', { name })}>
{options.map((option) => (
<Button label={`${valuePrefix}${option}`} onClick={() => setValue(option)} key={option} active={value === option} role="option" />
))}
{options.map((option) => {
const optionLabel = typeof option === 'string' ? option : option.label;
const optionValue = typeof option === 'string' ? option : option.value;

return <Button label={optionLabel} onClick={() => setValue(optionValue)} key={optionValue} active={value === optionValue} role="option" />;
})}
<Button label={defaultLabel} onClick={() => setValue('')} active={value === ''} key={defaultLabel} role="option" />
</div>
) : (
Expand All @@ -44,7 +53,6 @@ const Filter: FC<Props> = ({ name, value, defaultLabel, options, setValue, value
size="small"
options={options}
defaultLabel={defaultLabel}
valuePrefix={valuePrefix}
name={name}
value={value}
onChange={handleChange}
Expand Down
14 changes: 2 additions & 12 deletions src/components/VideoLayout/VideoLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import type { AccessModel } from '#types/Config';

type FilterProps = {
filterMetadata?: React.ReactNode;
filters?: string[];
filters?: (string | { label: string; value: string })[];
currentFilter?: string;
defaultFilterLabel?: string;
filterValuePrefix?: string;
setFilter?: (value: string) => void;
};

Expand Down Expand Up @@ -91,7 +90,6 @@ const VideoLayout: React.FC<Props> = ({
filters,
setFilter,
filterMetadata,
filterValuePrefix,
currentFilter = '',
defaultFilterLabel = '',
children,
Expand All @@ -109,15 +107,7 @@ const VideoLayout: React.FC<Props> = ({
<div className={classNames(styles.filters, { [styles.filtersInline]: inlineLayout })}>
{!!filterMetadata && inlineLayout && <span className={styles.filterMetadata}>{filterMetadata}</span>}
{showFilters && (
<Filter
name="season"
value={currentFilter}
valuePrefix={filterValuePrefix}
defaultLabel={defaultFilterLabel}
options={filters}
setValue={setFilter}
forceDropdown={forceDropdown}
/>
<Filter name="season" value={currentFilter} defaultLabel={defaultFilterLabel} options={filters} setValue={setFilter} forceDropdown={forceDropdown} />
)}
</div>
);
Expand Down
6 changes: 4 additions & 2 deletions src/hooks/useCheckAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,21 @@ type intervalCheckAccessPayload = {
iterations?: number;
offerId?: string;
};

const useCheckAccess = () => {
const intervalRef = useRef<number>();
const navigate = useNavigate();
const location = useLocation();
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { t } = useTranslation('user');
const { clientOffers } = useClientIntegration();

const intervalCheckAccess = useCallback(
({ interval = 3000, iterations = 5, offerId }: intervalCheckAccessPayload) => {
const { clientOffers } = useClientIntegration();
if (!offerId && clientOffers?.[0]) {
offerId = clientOffers[0];
}

intervalRef.current = window.setInterval(async () => {
const hasAccess = await checkEntitlements(offerId);

Expand All @@ -37,7 +39,7 @@ const useCheckAccess = () => {
}
}, interval);
},
[intervalRef.current, errorMessage],
[clientOffers, navigate, location, t],
);

useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useCountdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const useCountdown = (durationSeconds: number, intervalSeconds: number = 1, comp
return () => {
window.clearTimeout(timerRef.current);
};
}, [countdown]);
}, [completeHandler, countdown, intervalSeconds]);

return countdown;
};
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useLiveChannels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const useLiveChannels = (playlist: PlaylistItem[], initialChannelId: string | un
setChannel(updatedChannel);
setProgram(updatedProgram);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [channels]);

// update the selected channel and optionally the program
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useLiveProgram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const useLiveProgram = (program: EpgProgram | undefined, catchupHours: number |
calculateStatus();

return () => clearInterval(intervalId);
}, [program]);
}, [catchupHours, program]);

return {
isLive,
Expand Down
11 changes: 5 additions & 6 deletions src/hooks/useOffers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,15 @@ const useOffers = () => {
return [...(requestedMediaOffers || []).map(({ offerId }) => offerId), ...clientOffers].filter(Boolean);
}, [requestedMediaOffers, clientOffers]);

const { data: allOffers = [], isLoading } = useQuery(['offers', offerIds.join('-')], () => checkoutService.getOffers({ offerIds }, sandbox));
const { data: allOffers, isLoading } = useQuery(['offers', offerIds.join('-')], () => checkoutService.getOffers({ offerIds }, sandbox));

// The `offerQueries` variable mutates on each render which prevents the useMemo to work properly.
return useMemo(() => {
const offers = allOffers.filter((offer: Offer) => (offerType === 'tvod' ? !isSVODOffer(offer) : isSVODOffer(offer)));

const hasMultipleOfferTypes = allOffers.some((offer: Offer) => (offerType === 'tvod' ? isSVODOffer(offer) : !isSVODOffer(offer)));
const offers = (allOffers || []).filter((offer: Offer) => (offerType === 'tvod' ? !isSVODOffer(offer) : isSVODOffer(offer)));
const hasMultipleOfferTypes = (allOffers || []).some((offer: Offer) => (offerType === 'tvod' ? isSVODOffer(offer) : !isSVODOffer(offer)));

const offersDict = (!isLoading && Object.fromEntries(offers.map((offer: Offer) => [offer.offerId, offer]))) || {};
// we need to get the offerIds from the offer responses since it contains different offerIds based on the customers
// we need to get the offerIds from the offer responses since it contains different offerIds based on the customers'
// location. E.g. if an offer is configured as `S12345678` it becomes `S12345678_US` in the US.
const defaultOfferId = (!isLoading && offers[offers.length - 1]?.offerId) || '';

Expand All @@ -52,7 +51,7 @@ const useOffers = () => {
offers,
offersDict,
};
}, [requestedMediaOffers, allOffers, offerType]);
}, [allOffers, isLoading, hasPremierOffer, offerType]);
};

export default useOffers;
59 changes: 30 additions & 29 deletions src/hooks/useOttAnalytics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';

import type { PlaylistItem } from '#types/playlist';
import { useConfigStore } from '#src/stores/ConfigStore';
Expand All @@ -9,44 +9,45 @@ const useOttAnalytics = (item?: PlaylistItem, feedId: string = '') => {
const user = useAccountStore((state) => state.user);

// ott app user id (oaid)
const oaid: number | undefined = user?.id ? Number(user?.id) : undefined;
const oaid: number | undefined = user?.id ? Number(user.id) : undefined;

const [player, setPlayer] = useState<jwplayer.JWPlayer | null>(null);

const timeHandler = useCallback(({ position, duration }: jwplayer.TimeParam) => {
window.jwpltx.time(position, duration);
}, []);
useEffect(() => {
if (!window.jwpltx || !analyticsToken || !player || !item) {
return;
}

const seekHandler = useCallback(({ offset, duration }) => {
window.jwpltx.seek(offset, duration);
}, []);
const timeHandler = ({ position, duration }: jwplayer.TimeParam) => {
window.jwpltx.time(position, duration);
};

const seekedHandler = useCallback(() => {
window.jwpltx.seeked();
}, []);
const seekHandler = ({ offset }: jwplayer.SeekParam) => {
// TODO: according JWPlayer typings, the seek params doesn't contain a `duration` property, but it actually does
window.jwpltx.seek(offset, player.getDuration());
};

const playlistItemHandler = useCallback(() => {
if (!analyticsToken) return;
const seekedHandler = () => {
window.jwpltx.seeked();
};

if (!item) {
return;
}
const playlistItemHandler = () => {
if (!analyticsToken) return;

window.jwpltx.ready(analyticsToken, window.location.hostname, feedId, item.mediaid, item.title, oaid);
}, [item]);
if (!item) {
return;
}

const completeHandler = useCallback(() => {
window.jwpltx.complete();
}, []);
window.jwpltx.ready(analyticsToken, window.location.hostname, feedId, item.mediaid, item.title, oaid);
};

const adImpressionHandler = useCallback(() => {
window.jwpltx.adImpression();
}, []);
const completeHandler = () => {
window.jwpltx.complete();
};

useEffect(() => {
if (!window.jwpltx || !analyticsToken || !player || !item) {
return;
}
const adImpressionHandler = () => {
window.jwpltx.adImpression();
};

player.on('playlistItem', playlistItemHandler);
player.on('complete', completeHandler);
Expand All @@ -65,7 +66,7 @@ const useOttAnalytics = (item?: PlaylistItem, feedId: string = '') => {
player.off('seeked', seekedHandler);
player.off('adImpression', adImpressionHandler);
};
}, [player, item]);
}, [player, item, analyticsToken, feedId, oaid]);

return setPlayer;
};
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/usePlanByEpg.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useEpg } from 'planby';
import { startOfToday, startOfTomorrow } from 'date-fns';
import { startOfDay, startOfToday, startOfTomorrow } from 'date-fns';

import type { EpgChannel } from '#src/services/epg.service';
import { is12HourClock } from '#src/utils/datetime';
Expand Down Expand Up @@ -44,7 +44,7 @@ const usePlanByEpg = (channels: EpgChannel[], sidebarWidth: number, itemHeight:
// in the Planby component. E.g. `[subHours(new Date(), 12), addHours(new Date(), 12)]`. The `date` dependency
// must also be changed to update every hour instead of daily.
const date = startOfToday().toJSON();
const [startDate, endDate] = useMemo(() => [startOfToday(), startOfTomorrow()], [date]);
const [startDate, endDate] = useMemo(() => [startOfDay(new Date(date)), startOfTomorrow()], [date]);

return useEpg({
channels: epgChannels,
Expand Down
15 changes: 9 additions & 6 deletions src/hooks/useSearchQueryUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ const useSearchQueryUpdater = () => {
const updateSearchPath = useDebounce((query: string) => {
navigate(`/q/${encodeURIComponent(query)}`);
}, 350);
const updateSearchQuery = useCallback((query: string) => {
useUIStore.setState({
searchQuery: query,
});
updateSearchPath(query);
}, []);
const updateSearchQuery = useCallback(
(query: string) => {
useUIStore.setState({
searchQuery: query,
});
updateSearchPath(query);
},
[updateSearchPath],
);
const resetSearchQuery = useCallback(() => {
const returnPage = useUIStore.getState().preSearchPage;

Expand Down
Loading

0 comments on commit 411d0dd

Please sign in to comment.