mirror of
https://github.com/tubearchivist/tubearchivist-frontend.git
synced 2024-11-22 11:50:14 +00:00
chore: protect pages with auth
This commit is contained in:
parent
6bf77b05aa
commit
85f1a62e84
@ -1,5 +1,3 @@
|
|||||||
const { withPlaiceholder } = require("@plaiceholder/next");
|
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
images: {
|
images: {
|
||||||
@ -8,4 +6,4 @@ const nextConfig = {
|
|||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = withPlaiceholder(nextConfig);
|
module.exports = nextConfig;
|
||||||
|
3
tubearchivist/www/src/components/BoxedContent.tsx
Normal file
3
tubearchivist/www/src/components/BoxedContent.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const BoxedContent: React.FC = ({ children }) => (
|
||||||
|
<div className="boxed-content">{children}</div>
|
||||||
|
);
|
@ -1,18 +1,22 @@
|
|||||||
|
import { useSession } from "next-auth/react";
|
||||||
import NextImage from "next/image";
|
import NextImage from "next/image";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import ReactPlayer from "react-player/file";
|
|
||||||
import { useQuery } from "react-query";
|
import { useQuery } from "react-query";
|
||||||
import IconClose from "../images/icon-close.svg";
|
|
||||||
import IconPlay from "../images/icon-play.svg";
|
import IconPlay from "../images/icon-play.svg";
|
||||||
import { TA_BASE_URL } from "../lib/constants";
|
import { TA_BASE_URL } from "../lib/constants";
|
||||||
import { getVideos } from "../lib/getVideos";
|
import { getVideos } from "../lib/getVideos";
|
||||||
import { formatNumbers } from "../lib/utils";
|
import type { Datum } from "../types/video";
|
||||||
import { Datum, Videos } from "../types/video";
|
import { VideoPlayer } from "./VideoPlayer";
|
||||||
|
|
||||||
|
type ViewStyle = "grid" | "list";
|
||||||
|
|
||||||
export const VideoList = () => {
|
export const VideoList = () => {
|
||||||
const [selectedVideoUrl, setSelectedVideoUrl] = useState<Datum>();
|
const [selectedVideoUrl, setSelectedVideoUrl] = useState<Datum>();
|
||||||
const { data: queryData } = useQuery("videos", getVideos);
|
const [viewStyle, setViewStyle] = useState<ViewStyle>("grid");
|
||||||
const { data: videos } = queryData;
|
const { data: session } = useSession();
|
||||||
|
const { data, error, isLoading } = useQuery("videos", () =>
|
||||||
|
getVideos(session.ta_token.token)
|
||||||
|
);
|
||||||
|
|
||||||
const handleSelectedVideo = (video: Datum) => {
|
const handleSelectedVideo = (video: Datum) => {
|
||||||
setSelectedVideoUrl(video);
|
setSelectedVideoUrl(video);
|
||||||
@ -22,12 +26,16 @@ export const VideoList = () => {
|
|||||||
setSelectedVideoUrl(undefined);
|
setSelectedVideoUrl(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!videos) {
|
const handleSetViewstyle = (selectedViewStyle: ViewStyle) => {
|
||||||
|
setViewStyle(selectedViewStyle);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isLoading && !data?.data) {
|
||||||
return (
|
return (
|
||||||
<div className="boxed-content">
|
<div className="boxed-content">
|
||||||
<h2>No videos found...</h2>
|
<h2>No videos found...</h2>
|
||||||
<p>
|
<p>
|
||||||
If you`'`ve already added a channel or playlist, try going to the{" "}
|
If you've already added a channel or playlist, try going to the{" "}
|
||||||
<a href="{% url 'downloads">downloads page</a> to start the scan and
|
<a href="{% url 'downloads">downloads page</a> to start the scan and
|
||||||
download tasks.
|
download tasks.
|
||||||
</p>
|
</p>
|
||||||
@ -37,63 +45,10 @@ export const VideoList = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{selectedVideoUrl && (
|
<VideoPlayer
|
||||||
<>
|
handleRemoveVideoPlayer={handleRemoveVideoPlayer}
|
||||||
<div className="player-wrapper">
|
selectedVideoUrl={selectedVideoUrl}
|
||||||
<div className="video-player">
|
/>
|
||||||
<ReactPlayer
|
|
||||||
controls={true}
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
light="false"
|
|
||||||
playing // TODO: Not currently working
|
|
||||||
playsinline
|
|
||||||
url={`${process.env.NEXT_PUBLIC_TUBEARCHIVIST_URL}/media/${selectedVideoUrl.media_url}`}
|
|
||||||
/>
|
|
||||||
<div className="player-title boxed-content">
|
|
||||||
<NextImage
|
|
||||||
className="close-button"
|
|
||||||
src={IconClose}
|
|
||||||
width={30}
|
|
||||||
height={30}
|
|
||||||
alt="close-icon"
|
|
||||||
onClick={handleRemoveVideoPlayer}
|
|
||||||
title="Close player"
|
|
||||||
/>
|
|
||||||
{/* ${watchStatusIndicator}
|
|
||||||
${castButton}
|
|
||||||
*/}
|
|
||||||
<div className="thumb-icon player-stats">
|
|
||||||
<img src="/img/icon-eye.svg" alt="views icon" />
|
|
||||||
<span>
|
|
||||||
{formatNumbers(
|
|
||||||
selectedVideoUrl.stats.view_count.toString()
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<span>|</span>
|
|
||||||
<img src="/img/icon-thumb.svg" alt="thumbs-up" />
|
|
||||||
<span>
|
|
||||||
{formatNumbers(
|
|
||||||
selectedVideoUrl.stats.like_count.toString()
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="player-channel-playlist">
|
|
||||||
<h3>
|
|
||||||
<a href="/channel/${channelId}/">
|
|
||||||
{selectedVideoUrl.channel.channel_name}
|
|
||||||
</a>
|
|
||||||
</h3>
|
|
||||||
{/* ${playlist} */}
|
|
||||||
</div>
|
|
||||||
<a href="/video/${videoId}/">
|
|
||||||
<h2 id="video-title">{selectedVideoUrl.title}</h2>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="boxed-content">
|
<div className="boxed-content">
|
||||||
<div className="title-bar">
|
<div className="title-bar">
|
||||||
@ -151,25 +106,24 @@ export const VideoList = () => {
|
|||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src="/img/icon-gridview.svg"
|
src="/img/icon-gridview.svg"
|
||||||
onClick={() => console.log("grid view")}
|
onClick={() => handleSetViewstyle("grid")}
|
||||||
data-origin="home"
|
|
||||||
data-value="grid"
|
|
||||||
alt="grid view"
|
alt="grid view"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src="/img/icon-listview.svg"
|
src="/img/icon-listview.svg"
|
||||||
onClick={() => console.log("list view")}
|
onClick={() => handleSetViewstyle("list")}
|
||||||
data-origin="home"
|
|
||||||
data-value="list"
|
|
||||||
alt="list view"
|
alt="list view"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="video-list list">
|
<div className={`video-list ${viewStyle}`}>
|
||||||
{videos &&
|
{data &&
|
||||||
videos?.map((video) => {
|
data.data?.map((video) => {
|
||||||
return (
|
return (
|
||||||
<div key={video.youtube_id} className="video-item list">
|
<div
|
||||||
|
key={video.youtube_id}
|
||||||
|
className={`video-item ${viewStyle}`}
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
style={{ cursor: "pointer" }}
|
style={{ cursor: "pointer" }}
|
||||||
onClick={() => handleSelectedVideo(video)}
|
onClick={() => handleSelectedVideo(video)}
|
||||||
|
61
tubearchivist/www/src/components/VideoPlayer.tsx
Normal file
61
tubearchivist/www/src/components/VideoPlayer.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import NextImage from "next/image";
|
||||||
|
import ReactPlayer from "react-player";
|
||||||
|
import IconClose from "../images/icon-close.svg";
|
||||||
|
import { formatNumbers } from "../lib/utils";
|
||||||
|
|
||||||
|
export const VideoPlayer = ({ selectedVideoUrl, handleRemoveVideoPlayer }) => {
|
||||||
|
if (!selectedVideoUrl) return;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="player-wrapper">
|
||||||
|
<div className="video-player">
|
||||||
|
<ReactPlayer
|
||||||
|
controls={true}
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
light="false"
|
||||||
|
playing // TODO: Not currently working
|
||||||
|
playsinline
|
||||||
|
url={`${process.env.NEXT_PUBLIC_TUBEARCHIVIST_URL}/media/${selectedVideoUrl.media_url}`}
|
||||||
|
/>
|
||||||
|
<div className="player-title boxed-content">
|
||||||
|
<NextImage
|
||||||
|
className="close-button"
|
||||||
|
src={IconClose}
|
||||||
|
width={30}
|
||||||
|
height={30}
|
||||||
|
alt="close-icon"
|
||||||
|
onClick={handleRemoveVideoPlayer}
|
||||||
|
title="Close player"
|
||||||
|
/>
|
||||||
|
{/* ${watchStatusIndicator}
|
||||||
|
${castButton}
|
||||||
|
*/}
|
||||||
|
<div className="thumb-icon player-stats">
|
||||||
|
<img src="/img/icon-eye.svg" alt="views icon" />
|
||||||
|
<span>
|
||||||
|
{formatNumbers(selectedVideoUrl.stats.view_count.toString())}
|
||||||
|
</span>
|
||||||
|
<span>|</span>
|
||||||
|
<img src="/img/icon-thumb.svg" alt="thumbs-up" />
|
||||||
|
<span>
|
||||||
|
{formatNumbers(selectedVideoUrl.stats.like_count.toString())}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="player-channel-playlist">
|
||||||
|
<h3>
|
||||||
|
<a href="/channel/${channelId}/">
|
||||||
|
{selectedVideoUrl.channel.channel_name}
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
{/* ${playlist} */}
|
||||||
|
</div>
|
||||||
|
<a href="/video/${videoId}/">
|
||||||
|
<h2 id="video-title">{selectedVideoUrl.title}</h2>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -1,15 +1,19 @@
|
|||||||
import { Channel } from "../types/channel";
|
import { Channel } from "../types/channel";
|
||||||
|
|
||||||
export const getChannels = async (): Promise<Channel> => {
|
export const getChannels = async (token: string): Promise<Channel> => {
|
||||||
return await fetch(
|
const response = await fetch(
|
||||||
`${process.env.NEXT_PUBLIC_TUBEARCHIVIST_URL}/api/channel/`,
|
`${process.env.NEXT_PUBLIC_TUBEARCHIVIST_URL}/api/channel/`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Token b4d4330462c7fc16c51873e45579b29a1a12fc90`,
|
Authorization: `Token ${token}`,
|
||||||
mode: "no-cors",
|
mode: "no-cors",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
).then((res) => res.json());
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Error getting channel information");
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,26 @@
|
|||||||
import { Videos } from "../types/video";
|
import { Videos } from "../types/video";
|
||||||
|
|
||||||
export const getVideos = async (): Promise<Videos> => {
|
export const getVideos = async (token: string): Promise<Videos> => {
|
||||||
return await fetch(
|
if (!token) {
|
||||||
|
throw new Error("Missing API token in request to get videos");
|
||||||
|
}
|
||||||
|
const response = await fetch(
|
||||||
`${process.env.NEXT_PUBLIC_TUBEARCHIVIST_URL}/api/video/`,
|
`${process.env.NEXT_PUBLIC_TUBEARCHIVIST_URL}/api/video/`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Token b4d4330462c7fc16c51873e45579b29a1a12fc90`,
|
Authorization: `Token ${token}`,
|
||||||
mode: "no-cors",
|
mode: "no-cors",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
).then((res) => res.json());
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to fetch videos");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// b4d4330462c7fc16c51873e45579b29a1a12fc90
|
||||||
|
@ -1,40 +1,16 @@
|
|||||||
import type { AppProps } from "next/app";
|
|
||||||
import { SessionProvider } from "next-auth/react";
|
import { SessionProvider } from "next-auth/react";
|
||||||
import Script, { ScriptProps } from "next/script";
|
import type { AppProps } from "next/app";
|
||||||
|
import { useState } from "react";
|
||||||
import { Hydrate, QueryClient, QueryClientProvider } from "react-query";
|
import { Hydrate, QueryClient, QueryClientProvider } from "react-query";
|
||||||
import { ReactQueryDevtools } from "react-query/devtools";
|
import { ReactQueryDevtools } from "react-query/devtools";
|
||||||
import "../styles/globals.css";
|
|
||||||
import "../styles/dark.css"; // TODO: Setup themeing the React way
|
import "../styles/dark.css"; // TODO: Setup themeing the React way
|
||||||
import { useState } from "react";
|
import "../styles/globals.css";
|
||||||
|
|
||||||
// TODO: Do these scripts need to be on every page?
|
|
||||||
|
|
||||||
type ClientOnlyScriptProps = {
|
|
||||||
src: string;
|
|
||||||
} & ScriptProps;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This wraps next/script and returns early if `window` is not detected
|
|
||||||
* due to next using SSR
|
|
||||||
*/
|
|
||||||
const ClientOnlyScript = ({ src, ...props }: ClientOnlyScriptProps) => {
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return <Script src={src} {...props} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
|
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
|
||||||
const [queryClient] = useState(() => new QueryClient());
|
const [queryClient] = useState(() => new QueryClient());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <Script
|
|
||||||
strategy="lazyOnload"
|
|
||||||
src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
{/** TODO: Detect casting before loading this? */}
|
|
||||||
{/* <ClientOnlyScript strategy="lazyOnload" src="/js/cast-videos.js" /> */}
|
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<ReactQueryDevtools />
|
<ReactQueryDevtools />
|
||||||
<Hydrate state={pageProps.dehydratedState}>
|
<Hydrate state={pageProps.dehydratedState}>
|
||||||
|
@ -1,16 +1,49 @@
|
|||||||
import { GetServerSideProps, NextPage } from "next";
|
import type { GetServerSideProps, NextPage } from "next";
|
||||||
|
import { getSession, useSession } from "next-auth/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { dehydrate, QueryClient, useQuery } from "react-query";
|
||||||
import { CustomHead } from "../components/CustomHead";
|
import { CustomHead } from "../components/CustomHead";
|
||||||
import { Layout } from "../components/Layout";
|
import { Layout } from "../components/Layout";
|
||||||
import { TA_BASE_URL } from "../lib/constants";
|
import { TA_BASE_URL } from "../lib/constants";
|
||||||
import { getChannels } from "../lib/getChannels";
|
import { getChannels } from "../lib/getChannels";
|
||||||
import { Channel } from "../types/channel";
|
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async () => {
|
type ViewStyle = "grid" | "list";
|
||||||
const channels = await getChannels();
|
|
||||||
return { props: { channels } };
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
const session = await getSession(context);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: "/auth/login",
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await queryClient.prefetchQuery("channels", () =>
|
||||||
|
getChannels(session.ta_token.token)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
dehydratedState: dehydrate(queryClient),
|
||||||
|
},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const Channel: NextPage<{ channels: Channel }> = ({ channels }) => {
|
const Channel: NextPage = () => {
|
||||||
|
const { data: session } = useSession();
|
||||||
|
const {
|
||||||
|
data: { data: channels },
|
||||||
|
} = useQuery("channels", () => getChannels(session.ta_token.token));
|
||||||
|
const [viewStyle, setViewStyle] = useState<ViewStyle>("grid");
|
||||||
|
|
||||||
|
const handleSetViewstyle = (selectedViewStyle: ViewStyle) => {
|
||||||
|
setViewStyle(selectedViewStyle);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CustomHead title="Channels" />
|
<CustomHead title="Channels" />
|
||||||
@ -46,7 +79,6 @@ const Channel: NextPage<{ channels: Channel }> = ({ channels }) => {
|
|||||||
id="show_subed_only"
|
id="show_subed_only"
|
||||||
onClick={() => console.log("toggleCheckbox(this)")}
|
onClick={() => console.log("toggleCheckbox(this)")}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked
|
|
||||||
/>
|
/>
|
||||||
{/* {% if not show_subed_only %} */}
|
{/* {% if not show_subed_only %} */}
|
||||||
<label htmlFor="" className="ofbtn">
|
<label htmlFor="" className="ofbtn">
|
||||||
@ -62,29 +94,28 @@ const Channel: NextPage<{ channels: Channel }> = ({ channels }) => {
|
|||||||
<div className="view-icons">
|
<div className="view-icons">
|
||||||
<img
|
<img
|
||||||
src="/img/icon-gridview.svg"
|
src="/img/icon-gridview.svg"
|
||||||
onClick={() => console.log("changeView(this)")}
|
onClick={() => handleSetViewstyle("grid")}
|
||||||
data-origin="channel"
|
|
||||||
data-value="grid"
|
|
||||||
alt="grid view"
|
alt="grid view"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src="/img/icon-listview.svg"
|
src="/img/icon-listview.svg"
|
||||||
onClick={() => console.log("changeView(this)")}
|
onClick={() => handleSetViewstyle("list")}
|
||||||
data-origin="channel"
|
|
||||||
data-value="list"
|
|
||||||
alt="list view"
|
alt="list view"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h2>Total matching channels: {channels?.data?.length} </h2>
|
<h2>Total matching channels: {channels?.length} </h2>
|
||||||
<div className="channel-list list">
|
<div className={`channel-list ${viewStyle}`}>
|
||||||
{/* {% if results %}
|
{!channels ? (
|
||||||
{% for channel in results %} */}
|
<h2>No channels found...</h2>
|
||||||
{channels &&
|
) : (
|
||||||
channels?.data?.map((channel) => {
|
channels?.map((channel) => {
|
||||||
return (
|
return (
|
||||||
<div key={channel?.channel_id} className="channel-item list">
|
<div
|
||||||
<div className="channel-banner list">
|
key={channel?.channel_id}
|
||||||
|
className={`channel-item ${viewStyle}`}
|
||||||
|
>
|
||||||
|
<div className={`channel-banner ${viewStyle}`}>
|
||||||
<a href="{% url 'channel_id' channel.source.channel_id %}">
|
<a href="{% url 'channel_id' channel.source.channel_id %}">
|
||||||
<img
|
<img
|
||||||
src={`${TA_BASE_URL}${channel?.channel_banner_url}`}
|
src={`${TA_BASE_URL}${channel?.channel_banner_url}`}
|
||||||
@ -92,7 +123,7 @@ const Channel: NextPage<{ channels: Channel }> = ({ channels }) => {
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="info-box info-box-2 list">
|
<div className={`info-box info-box-2 ${viewStyle}`}>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<div className="round-img">
|
<div className="round-img">
|
||||||
<a href="{% url 'channel_id' channel.source.channel_id %}">
|
<a href="{% url 'channel_id' channel.source.channel_id %}">
|
||||||
@ -144,7 +175,8 @@ const Channel: NextPage<{ channels: Channel }> = ({ channels }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})
|
||||||
|
)}
|
||||||
{/* {% endfor %}
|
{/* {% endfor %}
|
||||||
{% else %} */}
|
{% else %} */}
|
||||||
{/* <h2>No channels found...</h2> */}
|
{/* <h2>No channels found...</h2> */}
|
||||||
|
@ -1,38 +1,23 @@
|
|||||||
import type { GetServerSideProps, GetStaticProps, NextPage } from "next";
|
import type { GetServerSideProps, NextPage } from "next";
|
||||||
import { signIn, signOut, useSession } from "next-auth/react";
|
import { getSession } from "next-auth/react";
|
||||||
import { dehydrate, QueryClient } from "react-query";
|
import { dehydrate, QueryClient } from "react-query";
|
||||||
import { CustomHead } from "../components/CustomHead";
|
import { CustomHead } from "../components/CustomHead";
|
||||||
import { Layout } from "../components/Layout";
|
import { Layout } from "../components/Layout";
|
||||||
import { VideoList } from "../components/VideoList";
|
import { VideoList } from "../components/VideoList";
|
||||||
import { getVideos } from "../lib/getVideos";
|
import { getVideos } from "../lib/getVideos";
|
||||||
import { Videos } from "../types/video";
|
import type { Videos } from "../types/video";
|
||||||
|
|
||||||
type HomeProps = {
|
type HomeProps = {
|
||||||
videos: Videos;
|
videos: Videos;
|
||||||
imagePlaceholders?: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const SignInOutButton = ({ isSignedIn }: { isSignedIn: boolean }) => {
|
|
||||||
if (isSignedIn) {
|
|
||||||
return <button onClick={() => signOut()}>Sign Out</button>;
|
|
||||||
}
|
|
||||||
return <button onClick={() => signIn()}>Sign in</button>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Home: NextPage<HomeProps> = () => {
|
const Home: NextPage<HomeProps> = () => {
|
||||||
const { data: session, status } = useSession();
|
|
||||||
const authData = {
|
|
||||||
session,
|
|
||||||
status,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CustomHead />
|
<CustomHead />
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<VideoList />
|
<VideoList />
|
||||||
<SignInOutButton isSignedIn={!!session?.user} />
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -40,18 +25,22 @@ const Home: NextPage<HomeProps> = () => {
|
|||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|
||||||
// http://localhost:8000/cache/videos/3/37Kn-kIsVu8.jpg
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
|
|
||||||
// export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
|
||||||
// const videos = await getVideos();
|
|
||||||
|
|
||||||
// return { props: { videos } };
|
|
||||||
// };
|
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async () => {
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
const session = await getSession(context);
|
||||||
|
|
||||||
await queryClient.prefetchQuery("videos", getVideos);
|
if (!session) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: "/auth/login",
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await queryClient.prefetchQuery("videos", () =>
|
||||||
|
getVideos(session.ta_token.token)
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
Loading…
Reference in New Issue
Block a user