Skip to content

Commit

Permalink
feat: season/episode list on series details (#2967)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Cohen committed Aug 24, 2022
1 parent 67f3a38 commit 8a2acb7
Show file tree
Hide file tree
Showing 17 changed files with 377 additions and 12 deletions.
2 changes: 1 addition & 1 deletion cypress/e2e/discover.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const clickFirstTitleCardInSlider = (sliderTitle: string): void => {

describe('Discover', () => {
beforeEach(() => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
cy.loginAsAdmin();
});

it('loads a trending item', () => {
Expand Down
4 changes: 2 additions & 2 deletions cypress/e2e/login.cy.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
describe('Login Page', () => {
it('succesfully logs in as an admin', () => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
cy.loginAsAdmin();
cy.visit('/');
cy.contains('Trending');
});

it('succesfully logs in as a local user', () => {
cy.login(Cypress.env('USER_EMAIL'), Cypress.env('USER_PASSWORD'));
cy.loginAsUser();
cy.visit('/');
cy.contains('Trending');
});
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/movie-details.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('Movie Details', () => {
it('loads a movie page', () => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
cy.loginAsAdmin();
// Try to load minions: rise of gru
cy.visit('/movie/438148');

Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/settings/general-settings.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('General Settings', () => {
beforeEach(() => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
cy.loginAsAdmin();
});

it('opens the settings page from the home page', () => {
Expand Down
20 changes: 18 additions & 2 deletions cypress/e2e/tv-details.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('TV Details', () => {
it('loads a movie page', () => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
it('loads a tv details page', () => {
cy.loginAsAdmin();
// Try to load stranger things
cy.visit('/tv/66732');

Expand All @@ -9,4 +9,20 @@ describe('TV Details', () => {
'Stranger Things (2016)'
);
});

it('shows seasons and expands episodes', () => {
cy.loginAsAdmin();

// Try to load stranger things
cy.visit('/tv/66732');

// intercept request for season info
cy.intercept('/api/v1/tv/66732/season/4').as('season4');

cy.contains('Season 4').should('be.visible').scrollIntoView().click();

cy.wait('@season4');

cy.contains('Chapter Nine').should('be.visible');
});
});
2 changes: 1 addition & 1 deletion cypress/e2e/user/auto-request-settings.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const visitUserEditPage = (email: string): void => {

describe('Auto Request Settings', () => {
beforeEach(() => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
cy.loginAsAdmin();
});

it('should not see watchlist sync settings on an account without permissions', () => {
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/user/profile.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('User Profile', () => {
beforeEach(() => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
cy.loginAsAdmin();
});

it('opens user profile page from the home page', () => {
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/user/user-list.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const testUser = {

describe('User List', () => {
beforeEach(() => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
cy.loginAsAdmin();
});

it('opens the user list from the home page', () => {
Expand Down
8 changes: 8 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ Cypress.Commands.add('login', (email, password) => {
}
);
});

Cypress.Commands.add('loginAsAdmin', () => {
cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD'));
});

Cypress.Commands.add('loginAsUser', () => {
cy.login(Cypress.env('USER_EMAIL'), Cypress.env('USER_PASSWORD'));
});
2 changes: 2 additions & 0 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ declare global {
namespace Cypress {
interface Chainable {
login(email?: string, password?: string): Chainable<Element>;
loginAsAdmin(): Chainable<Element>;
loginAsUser(): Chainable<Element>;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@formatjs/intl-displaynames": "6.0.3",
"@formatjs/intl-locale": "3.0.3",
"@formatjs/intl-pluralrules": "5.0.3",
"@formatjs/intl-utils": "^3.8.4",
"@headlessui/react": "1.6.6",
"@heroicons/react": "1.0.6",
"@supercharge/request-ip": "1.2.0",
Expand Down
62 changes: 62 additions & 0 deletions src/components/AirDateBadge/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Badge from '@app/components/Common/Badge';
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';

const messages = defineMessages({
airedrelative: 'Aired {relativeTime}',
airsrelative: 'Airs {relativeTime}',
});

type AirDateBadgeProps = {
airDate: string;
};

const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
const WEEK = 1000 * 60 * 60 * 24 * 8;
const intl = useIntl();
const dAirDate = new Date(airDate);
const nowDate = new Date();
const alreadyAired = dAirDate.getTime() < nowDate.getTime();

const compareWeek = new Date(
alreadyAired ? Date.now() - WEEK : Date.now() + WEEK
);

let showRelative = false;

if (
(alreadyAired && dAirDate.getTime() > compareWeek.getTime()) ||
(!alreadyAired && dAirDate.getTime() < compareWeek.getTime())
) {
showRelative = true;
}

return (
<div className="flex items-center space-x-2">
<Badge badgeType="light">
{intl.formatDate(dAirDate, {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</Badge>
{showRelative && (
<Badge badgeType="light">
{intl.formatMessage(
alreadyAired ? messages.airedrelative : messages.airsrelative,
{
relativeTime: (
<FormattedRelativeTime
value={(dAirDate.getTime() - Date.now()) / 1000}
numeric="auto"
updateIntervalInSeconds={1}
/>
),
}
)}
</Badge>
)}
</div>
);
};

export default AirDateBadge;
21 changes: 20 additions & 1 deletion src/components/Common/Badge/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import Link from 'next/link';

interface BadgeProps {
badgeType?: 'default' | 'primary' | 'danger' | 'warning' | 'success';
badgeType?:
| 'default'
| 'primary'
| 'danger'
| 'warning'
| 'success'
| 'dark'
| 'light';
className?: string;
href?: string;
children: React.ReactNode;
Expand Down Expand Up @@ -42,6 +49,18 @@ const Badge = ({
badgeStyle.push('hover:bg-green-400');
}
break;
case 'dark':
badgeStyle.push('bg-gray-900 !text-gray-400');
if (href) {
badgeStyle.push('hover:bg-gray-800');
}
break;
case 'light':
badgeStyle.push('bg-gray-700 !text-gray-300');
if (href) {
badgeStyle.push('hover:bg-gray-600');
}
break;
default:
badgeStyle.push('bg-indigo-500 !text-indigo-100');
if (href) {
Expand Down
62 changes: 62 additions & 0 deletions src/components/TvDetails/Season/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import AirDateBadge from '@app/components/AirDateBadge';
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
import type { SeasonWithEpisodes } from '@server/models/Tv';
import { defineMessages, useIntl } from 'react-intl';
import useSWR from 'swr';

const messages = defineMessages({
somethingwentwrong: 'Something went wrong while retrieving season data.',
});

type SeasonProps = {
seasonNumber: number;
tvId: number;
};

const Season = ({ seasonNumber, tvId }: SeasonProps) => {
const intl = useIntl();
const { data, error } = useSWR<SeasonWithEpisodes>(
`/api/v1/tv/${tvId}/season/${seasonNumber}`
);

if (!data && !error) {
return <LoadingSpinner />;
}

if (!data) {
return <div>{intl.formatMessage(messages.somethingwentwrong)}</div>;
}

return (
<div className="flex flex-col justify-center divide-y divide-gray-700">
{data.episodes
.slice()
.reverse()
.map((episode) => {
return (
<div
className="flex flex-col space-y-4 py-4 xl:flex-row xl:space-y-4 xl:space-x-4"
key={`season-${seasonNumber}-episode-${episode.episodeNumber}`}
>
<div className="flex-1">
<div className="flex flex-col space-y-2 xl:flex-row xl:items-center xl:space-y-0 xl:space-x-2">
<h3 className="text-lg">{episode.name}</h3>
<AirDateBadge airDate={episode.airDate} />
</div>
{episode.overview && <p>{episode.overview}</p>}
</div>
{episode.stillPath && (
<img
className="h-auto w-full rounded-lg xl:h-32 xl:w-auto"
src={`https://image.tmdb.org/t/p/original/${episode.stillPath}`}
alt=""
/>
)}
</div>
);
})}
</div>
);
};

export default Season;

0 comments on commit 8a2acb7

Please sign in to comment.