fix(frontend): run initial props for children components after getting the user

pull/97/head
sct 4 years ago
parent 9131254f33
commit fdf9f38776

@ -1,7 +1,13 @@
import React from 'react';
import React, { useContext } from 'react';
import { useSWRInfinite } from 'swr';
import { MovieResult } from '../../../server/models/Search';
import type { MovieResult } from '../../../server/models/Search';
import ListView from '../Common/ListView';
import { LanguageContext } from '../../context/LanguageContext';
import { defineMessages, FormattedMessage } from 'react-intl';
const messages = defineMessages({
discovermovies: 'Discover Movies',
});
interface SearchResult {
page: number;
@ -11,13 +17,14 @@ interface SearchResult {
}
const DiscoverMovies: React.FC = () => {
const { locale } = useContext(LanguageContext);
const { data, error, size, setSize } = useSWRInfinite<SearchResult>(
(pageIndex: number, previousPageData: SearchResult | null) => {
if (previousPageData && pageIndex + 1 > previousPageData.totalPages) {
return null;
}
return `/api/v1/discover/movies?page=${pageIndex + 1}`;
return `/api/v1/discover/movies?page=${pageIndex + 1}&language=${locale}`;
},
{
initialSize: 3,
@ -47,7 +54,7 @@ const DiscoverMovies: React.FC = () => {
<div className="md:flex md:items-center md:justify-between mb-8 mt-6">
<div className="flex-1 min-w-0">
<h2 className="text-xl leading-7 text-white sm:text-2xl sm:leading-9 sm:truncate">
Discover Movies
<FormattedMessage {...messages.discovermovies} />
</h2>
</div>
</div>

@ -1,7 +1,13 @@
import React from 'react';
import React, { useContext } from 'react';
import { useSWRInfinite } from 'swr';
import { TvResult } from '../../../server/models/Search';
import ListView from '../Common/ListView';
import { defineMessages, FormattedMessage } from 'react-intl';
import { LanguageContext } from '../../context/LanguageContext';
const messages = defineMessages({
discovertv: 'Discover Series',
});
interface SearchResult {
page: number;
@ -11,13 +17,14 @@ interface SearchResult {
}
const DiscoverTv: React.FC = () => {
const { locale } = useContext(LanguageContext);
const { data, error, size, setSize } = useSWRInfinite<SearchResult>(
(pageIndex: number, previousPageData: SearchResult | null) => {
if (previousPageData && pageIndex + 1 > previousPageData.totalPages) {
return null;
}
return `/api/v1/discover/tv?page=${pageIndex + 1}`;
return `/api/v1/discover/tv?page=${pageIndex + 1}&language=${locale}`;
},
{
initialSize: 3,
@ -44,7 +51,7 @@ const DiscoverTv: React.FC = () => {
<div className="md:flex md:items-center md:justify-between mb-8 mt-6">
<div className="flex-1 min-w-0">
<h2 className="text-xl leading-7 text-white sm:text-2xl sm:leading-9 sm:truncate">
Discover Series
<FormattedMessage {...messages.discovertv} />
</h2>
</div>
</div>

@ -1,7 +1,13 @@
import React from 'react';
import useSearchInput from '../../../hooks/useSearchInput';
import { defineMessages, useIntl } from 'react-intl';
const messages = defineMessages({
searchPlaceholder: 'Search Movies & TV',
});
const SearchInput: React.FC = () => {
const intl = useIntl();
const { searchValue, setSearchValue, setIsOpen } = useSearchInput();
return (
<div className="flex-1 flex">
@ -22,7 +28,7 @@ const SearchInput: React.FC = () => {
<input
id="search_field"
className="block w-full h-full pl-8 pr-3 py-2 rounded-md bg-cool-gray-600 text-white placeholder-gray-300 focus:outline-none focus:placeholder-gray-400 sm:text-base"
placeholder="Search"
placeholder={intl.formatMessage(messages.searchPlaceholder)}
type="search"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}

@ -2,6 +2,13 @@ import React, { ReactNode } from 'react';
import Transition from '../../Transition';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { defineMessages, FormattedMessage } from 'react-intl';
const messages = defineMessages({
dashboard: 'Dashboard',
requests: 'Requests',
settings: 'Settings',
});
interface SidebarProps {
open?: boolean;
@ -11,7 +18,7 @@ interface SidebarProps {
interface SidebarLinkProps {
href: string;
svgIcon: ReactNode;
name: string;
messagesKey: keyof typeof messages;
activeRegExp: RegExp;
as?: string;
}
@ -19,7 +26,7 @@ interface SidebarLinkProps {
const SidebarLinks: SidebarLinkProps[] = [
{
href: '/',
name: 'Dashboard',
messagesKey: 'dashboard',
svgIcon: (
<svg
className="mr-3 h-6 w-6 text-gray-300 group-hover:text-gray-300 group-focus:text-gray-300 transition ease-in-out duration-150"
@ -40,7 +47,7 @@ const SidebarLinks: SidebarLinkProps[] = [
},
{
href: '/requests',
name: 'Requests',
messagesKey: 'requests',
svgIcon: (
<svg
className="mr-3 h-6 w-6 text-gray-300 group-hover:text-gray-300 group-focus:text-gray-300 transition ease-in-out duration-150"
@ -61,7 +68,7 @@ const SidebarLinks: SidebarLinkProps[] = [
},
{
href: '/settings',
name: 'Settings',
messagesKey: 'settings',
svgIcon: (
<svg
className="mr-3 h-6 w-6 text-gray-300 group-hover:text-gray-300 group-focus:text-gray-300 transition ease-in-out duration-150"
@ -148,11 +155,19 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
{SidebarLinks.map((sidebarLink) => {
return (
<Link
key={`mobile-${sidebarLink.name}`}
key={`mobile-${sidebarLink.messagesKey}`}
href={sidebarLink.href}
as={sidebarLink.as}
>
<a
onClick={() => setClosed()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
setClosed();
}
}}
role="button"
tabIndex={0}
className={`group flex items-center px-2 py-2 text-base leading-6 font-medium rounded-md text-white focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150
${
router.pathname.match(
@ -164,7 +179,9 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
`}
>
{sidebarLink.svgIcon}
{sidebarLink.name}
<FormattedMessage
{...messages[sidebarLink.messagesKey]}
/>
</a>
</Link>
);
@ -192,7 +209,7 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
{SidebarLinks.map((sidebarLink) => {
return (
<Link
key={`desktop-${sidebarLink.name}`}
key={`desktop-${sidebarLink.messagesKey}`}
href={sidebarLink.href}
as={sidebarLink.as}
>
@ -208,7 +225,9 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
`}
>
{sidebarLink.svgIcon}
{sidebarLink.name}
<FormattedMessage
{...messages[sidebarLink.messagesKey]}
/>
</a>
</Link>
);

@ -30,6 +30,8 @@ const messages = defineMessages({
overview: 'Overview',
runtime: '{minutes} minutes',
cast: 'Cast',
recommendations: 'Recommendations',
similar: 'Similar Titles',
});
interface MovieDetailsProps {
@ -280,14 +282,16 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
</div>
<div className="w-full md:w-80 mt-8 md:mt-0">
<div className="bg-cool-gray-900 rounded-lg shadow border border-cool-gray-800">
<div className="flex px-4 py-2 border-b border-cool-gray-800 last:border-b-0">
<span className="text-sm">
<FormattedMessage {...messages.userrating} />
</span>
<span className="flex-1 text-right text-cool-gray-400 text-sm">
{data.voteAverage}/10
</span>
</div>
{data.voteCount > 0 && (
<div className="flex px-4 py-2 border-b border-cool-gray-800 last:border-b-0">
<span className="text-sm">
<FormattedMessage {...messages.userrating} />
</span>
<span className="flex-1 text-right text-cool-gray-400 text-sm">
{data.voteAverage}/10
</span>
</div>
)}
<div className="flex px-4 py-2 border-b border-cool-gray-800 last:border-b-0">
<span className="text-sm">
<FormattedMessage {...messages.releasedate} />
@ -309,30 +313,34 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
{data.status}
</span>
</div>
<div className="flex px-4 py-2 border-b border-cool-gray-800 last:border-b-0">
<span className="text-sm">
<FormattedMessage {...messages.revenue} />
</span>
<span className="flex-1 text-right text-cool-gray-400 text-sm">
<FormattedNumber
currency="USD"
style="currency"
value={data.revenue}
/>
</span>
</div>
<div className="flex px-4 py-2 border-b border-cool-gray-800 last:border-b-0">
<span className="text-sm">
<FormattedMessage {...messages.budget} />
</span>
<span className="flex-1 text-right text-cool-gray-400 text-sm">
<FormattedNumber
currency="USD"
style="currency"
value={data.budget}
/>
</span>
</div>
{data.revenue > 0 && (
<div className="flex px-4 py-2 border-b border-cool-gray-800 last:border-b-0">
<span className="text-sm">
<FormattedMessage {...messages.revenue} />
</span>
<span className="flex-1 text-right text-cool-gray-400 text-sm">
<FormattedNumber
currency="USD"
style="currency"
value={data.revenue}
/>
</span>
</div>
)}
{data.budget > 0 && (
<div className="flex px-4 py-2 border-b border-cool-gray-800 last:border-b-0">
<span className="text-sm">
<FormattedMessage {...messages.budget} />
</span>
<span className="flex-1 text-right text-cool-gray-400 text-sm">
<FormattedNumber
currency="USD"
style="currency"
value={data.budget}
/>
</span>
</div>
)}
<div className="flex px-4 py-2 border-b border-cool-gray-800 last:border-b-0">
<span className="text-sm">
<FormattedMessage {...messages.originallanguage} />
@ -389,7 +397,9 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
as={`/movie/${data.id}/recommendations`}
>
<a className="inline-flex text-xl leading-7 text-cool-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate items-center">
<span>Recommendations</span>
<span>
<FormattedMessage {...messages.recommendations} />
</span>
<svg
className="w-6 h-6 ml-2"
fill="none"
@ -434,7 +444,9 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
as={`/movie/${data.id}/similar`}
>
<a className="inline-flex text-xl leading-7 text-cool-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate items-center">
<span>Similar Titles</span>
<span>
<FormattedMessage {...messages.similar} />
</span>
<svg
className="w-6 h-6 ml-2"
fill="none"
@ -454,7 +466,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
</div>
</div>
<Slider
sliderKey="recommendations"
sliderKey="similar"
isLoading={!similar && !similarError}
isEmpty={false}
items={similar?.results.map((title) => (

@ -2,6 +2,14 @@ import React from 'react';
import Modal from '../Common/Modal';
import { useUser } from '../../hooks/useUser';
import { Permission } from '../../../server/lib/permissions';
import { defineMessages, useIntl } from 'react-intl';
const messages = defineMessages({
requestadmin:
'Your request will be immediately approved. Do you wish to continue?',
cancelrequest:
'This will remove your request. Are you sure you want to continue?',
});
interface RequestModalProps {
type: 'request' | 'cancel';
@ -18,14 +26,15 @@ const MovieRequestModal: React.FC<RequestModalProps> = ({
onOk,
title,
}) => {
const intl = useIntl();
const { hasPermission } = useUser();
let text = hasPermission(Permission.MANAGE_REQUESTS)
? 'Your request will be immediately approved. Do you wish to continue?'
? intl.formatMessage(messages.requestadmin)
: undefined;
if (type === 'cancel') {
text = 'This will remove your request. Are you sure you want to continue?';
text = intl.formatMessage(messages.cancelrequest);
}
return (

@ -1,4 +1,4 @@
import React from 'react';
import React, { useContext } from 'react';
import { useRouter } from 'next/router';
import {
TvResult,
@ -7,6 +7,7 @@ import {
} from '../../../server/models/Search';
import { useSWRInfinite } from 'swr';
import ListView from '../Common/ListView';
import { LanguageContext } from '../../context/LanguageContext';
interface SearchResult {
page: number;
@ -16,6 +17,7 @@ interface SearchResult {
}
const Search: React.FC = () => {
const { locale } = useContext(LanguageContext);
const router = useRouter();
const { data, error, size, setSize } = useSWRInfinite<SearchResult>(
(pageIndex: number, previousPageData: SearchResult | null) => {
@ -25,7 +27,7 @@ const Search: React.FC = () => {
return `/api/v1/search/?query=${router.query.query}&page=${
pageIndex + 1
}`;
}&language=${locale}`;
},
{
initialSize: 3,

@ -1,15 +1,25 @@
{
"components.Discover.discovermovies": "Discover Movies",
"components.Discover.discovertv": "Discover Series",
"components.Discover.popularmovies": "Popular Movies",
"components.Discover.populartv": "Popular Series",
"components.Discover.recentrequests": "Recent Requests",
"components.Layout.LanguagePicker.changelanguage": "Change Language",
"components.Layout.SearchInput.searchPlaceholder": "Search Movies & TV",
"components.Layout.Sidebar.dashboard": "Dashboard",
"components.Layout.Sidebar.requests": "Requests",
"components.Layout.Sidebar.settings": "Settings",
"components.MovieDetails.budget": "Budget",
"components.MovieDetails.cast": "Cast",
"components.MovieDetails.originallanguage": "Original Language",
"components.MovieDetails.overview": "Overview",
"components.MovieDetails.recommendations": "Recommendations",
"components.MovieDetails.releasedate": "Release Date",
"components.MovieDetails.revenue": "Revenue",
"components.MovieDetails.runtime": "{minutes} minutes",
"components.MovieDetails.similar": "Similar Titles",
"components.MovieDetails.status": "Status",
"components.MovieDetails.userrating": "User Rating"
"components.MovieDetails.userrating": "User Rating",
"components.RequestModal.cancelrequest": "This will remove your request. Are you sure you want to continue?",
"components.RequestModal.requestadmin": "Your request will be immediately approved. Do you wish to continue?"
}

@ -1,15 +1,25 @@
{
"components.Discover.popularmovies": "人気映画",
"components.Discover.populartv": "人気テレビ番組",
"components.Discover.discovermovies": "人気の映画",
"components.Discover.discovertv": "人気のテレビ番組",
"components.Discover.popularmovies": "人気の映画",
"components.Discover.populartv": "人気のテレビ番組",
"components.Discover.recentrequests": "最近のリクエスト",
"components.Layout.LanguagePicker.changelanguage": "言語",
"components.Layout.SearchInput.searchPlaceholder": "作品名で検索",
"components.Layout.Sidebar.dashboard": "ホーム",
"components.Layout.Sidebar.requests": "リクエスト",
"components.Layout.Sidebar.settings": "設定",
"components.MovieDetails.budget": "興行収入",
"components.MovieDetails.cast": "キャスト",
"components.MovieDetails.originallanguage": "言語",
"components.MovieDetails.overview": "ストーリー",
"components.MovieDetails.recommendations": "オススメの作品",
"components.MovieDetails.releasedate": "公開日",
"components.MovieDetails.revenue": "製作費",
"components.MovieDetails.runtime": "{minutes}分",
"components.MovieDetails.similar": "関連作品",
"components.MovieDetails.status": "状態",
"components.MovieDetails.userrating": "ユーザー評価"
"components.MovieDetails.userrating": "ユーザー評価",
"components.RequestModal.cancelrequest": "このリクエストをキャンセルしてよろしいですか?",
"components.RequestModal.requestadmin": "このリクエストが今すぐ承認致します。よろしいですか?"
}

@ -85,22 +85,12 @@ const CoreApp: Omit<NextAppComponentType, 'origGetInitialProps'> = ({
};
CoreApp.getInitialProps = async (initialProps) => {
// Run the default getInitialProps for the main nextjs initialProps
const appInitialProps: AppInitialProps = await App.getInitialProps(
initialProps
);
const { ctx, router } = initialProps;
let user = undefined;
let locale = 'en';
if (ctx.res) {
const cookies = parseCookies(ctx);
if (cookies.locale) {
locale = cookies.locale;
}
try {
// Attempt to get the user by running a request to the local api
const response = await axios.get<User>(
@ -126,8 +116,19 @@ CoreApp.getInitialProps = async (initialProps) => {
ctx.res.end();
}
}
const cookies = parseCookies(ctx);
if (!!cookies.locale) {
locale = cookies.locale;
}
}
// Run the default getInitialProps for the main nextjs initialProps
const appInitialProps: AppInitialProps = await App.getInitialProps(
initialProps
);
const messages = await loadLocaleData(locale);
return { ...appInitialProps, user, messages, locale };

@ -19,8 +19,12 @@ MoviePage.getInitialProps = async (ctx) => {
const response = await axios.get<MovieDetailsType>(
`http://localhost:${process.env.PORT || 3000}/api/v1/movie/${
ctx.query.movieId
}?language=${cookies.locale}`,
{ headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined }
}${cookies.locale ? `?language=${cookies.locale}` : ''}`,
{
headers: ctx.req?.headers?.cookie
? { cookie: ctx.req.headers.cookie }
: undefined,
}
);
return {

Loading…
Cancel
Save