Compare commits
2 Commits
d3301da0ad
...
b031797271
Author | SHA1 | Date | |
---|---|---|---|
|
b031797271 | ||
|
305eb667f4 |
1
.env
1
.env
@ -1,2 +1,3 @@
|
|||||||
NEXT_PUBLIC_FIRST_NAME=Ivan
|
NEXT_PUBLIC_FIRST_NAME=Ivan
|
||||||
NEXT_PUBLIC_LAST_NAME=Li
|
NEXT_PUBLIC_LAST_NAME=Li
|
||||||
|
BACKEND_URI=http://127.0.0.1:7132/graphql
|
||||||
|
@ -1,6 +1,98 @@
|
|||||||
import { ApolloClient, InMemoryCache } from "@apollo/client";
|
import { useMemo } from "react";
|
||||||
|
import { ApolloClient, from, HttpLink, InMemoryCache } from "@apollo/client";
|
||||||
|
import { concatPagination } from "@apollo/client/utilities";
|
||||||
|
import { equals, mergeDeepWith } from "ramda";
|
||||||
|
import { onError } from "@apollo/client/link/error";
|
||||||
|
|
||||||
export const client = new ApolloClient({
|
export const client = new ApolloClient({
|
||||||
uri: "/api/graphql",
|
uri: "/api/graphql",
|
||||||
cache: new InMemoryCache(),
|
cache: new InMemoryCache(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__";
|
||||||
|
|
||||||
|
let apolloClient: ApolloClient<InMemoryCache>;
|
||||||
|
|
||||||
|
function createApolloClient() {
|
||||||
|
const httpLink = new HttpLink({
|
||||||
|
uri:
|
||||||
|
typeof window === "undefined"
|
||||||
|
? process.env.BACKEND_URI
|
||||||
|
: "/api/graphql", // Server URL (must be absolute)
|
||||||
|
credentials: "same-origin", // Additional fetch() options like `credentials` or `headers`
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorLink = onError(({ graphQLErrors, networkError }) => {
|
||||||
|
if (graphQLErrors)
|
||||||
|
graphQLErrors.forEach(({ message, locations, path }) =>
|
||||||
|
console.log(
|
||||||
|
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (networkError) console.log(`[Network error]: ${networkError}`);
|
||||||
|
});
|
||||||
|
return new ApolloClient({
|
||||||
|
ssrMode: typeof window === "undefined",
|
||||||
|
link: from([errorLink, httpLink]),
|
||||||
|
cache: new InMemoryCache({
|
||||||
|
typePolicies: {
|
||||||
|
Query: {
|
||||||
|
fields: {
|
||||||
|
allPosts: concatPagination(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initializeApollo(initialState = null) {
|
||||||
|
const _apolloClient = apolloClient ?? createApolloClient();
|
||||||
|
|
||||||
|
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
|
||||||
|
// gets hydrated here
|
||||||
|
if (initialState) {
|
||||||
|
// Get existing cache, loaded during client side data fetching
|
||||||
|
const existingCache = _apolloClient.extract();
|
||||||
|
|
||||||
|
// Merge the existing cache into data passed from getStaticProps/getServerSideProps
|
||||||
|
const data = mergeDeepWith(
|
||||||
|
(a, b) => {
|
||||||
|
if (Array.isArray(a) && Array.isArray(b)) {
|
||||||
|
return [
|
||||||
|
...a,
|
||||||
|
...b.filter((bi) => a.every((ai) => !equals(ai, bi))),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initialState,
|
||||||
|
existingCache
|
||||||
|
);
|
||||||
|
|
||||||
|
// Restore the cache with the merged data
|
||||||
|
_apolloClient.cache.restore(data);
|
||||||
|
}
|
||||||
|
// For SSG and SSR always create a new Apollo Client
|
||||||
|
if (typeof window === "undefined") return _apolloClient;
|
||||||
|
// Create the Apollo Client once in the client
|
||||||
|
if (!apolloClient) apolloClient = _apolloClient;
|
||||||
|
|
||||||
|
return _apolloClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addApolloState(client, pageProps) {
|
||||||
|
if (pageProps?.props) {
|
||||||
|
pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useApollo(pageProps) {
|
||||||
|
const state = pageProps[APOLLO_STATE_PROP_NAME];
|
||||||
|
const store = useMemo(() => initializeApollo(state), [state]);
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
@ -10,3 +10,14 @@ export const ARTICLE_FOR_HOME = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ARTICLE = gql`
|
||||||
|
query Article($id: String!) {
|
||||||
|
article(id: $id) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
content
|
||||||
|
publishedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
56
commons/theme.tsx
Normal file
56
commons/theme.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React, { createContext, FC, useContext, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const getInitialTheme = (): Mode => {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return 'auto';
|
||||||
|
}
|
||||||
|
const text = window?.localStorage?.getItem('theme');
|
||||||
|
switch (text) {
|
||||||
|
case 'dark': return 'dark';
|
||||||
|
case 'light': return 'light';
|
||||||
|
default: return 'auto';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type RawMode = "light" | "dark";
|
||||||
|
type Mode = RawMode | "auto";
|
||||||
|
|
||||||
|
interface Content {
|
||||||
|
mode: Mode;
|
||||||
|
setMode: (mode: Mode) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ThemeContext = createContext<Content>({
|
||||||
|
mode: 'error' as Mode,
|
||||||
|
setMode: (_) => {console.log},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ThemeProvider: FC = ({ children }) => {
|
||||||
|
const [mode, setMode] = useState<Mode>(() => getInitialTheme());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (window) {
|
||||||
|
localStorage.setItem("theme", mode);
|
||||||
|
let _mode = mode;
|
||||||
|
if (_mode === "auto") {
|
||||||
|
_mode =
|
||||||
|
window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||||
|
}
|
||||||
|
if (_mode === "dark") {
|
||||||
|
window.document.documentElement.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
window.document.documentElement.classList.remove("dark");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [mode]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={{ mode, setMode }}>
|
||||||
|
{children}
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useTheme () {
|
||||||
|
return useContext(ThemeContext);
|
||||||
|
}
|
@ -1,13 +1,12 @@
|
|||||||
.wrapper {
|
.wrapper {
|
||||||
@apply bg-green-400 text-white;
|
@apply bg-green-400 text-white min-h-screen;
|
||||||
:global(.dark) & {
|
:global(.dark) & {
|
||||||
@apply bg-gray-800 text-gray-400;
|
@apply bg-gray-800 text-gray-400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sidebar {
|
.sidebar {
|
||||||
@apply overflow-hidden flex flex-col fixed top-0;
|
@apply overflow-hidden flex flex-col fixed top-0;
|
||||||
@apply text-center shadow-2xl;
|
@apply text-center shadow-2xl h-screen;
|
||||||
height: 100vh;
|
|
||||||
padding-top: 10vh;
|
padding-top: 10vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
components/switch-theme.tsx
Normal file
10
components/switch-theme.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { useTheme } from "../commons/theme";
|
||||||
|
|
||||||
|
export const SwitchTheme = () => {
|
||||||
|
const { setMode, mode } = useTheme();
|
||||||
|
|
||||||
|
if (mode === "light") {
|
||||||
|
return <button onClick={() => setMode("dark")}>暗色</button>;
|
||||||
|
}
|
||||||
|
return <button onClick={() => setMode("light")}>亮色</button>;
|
||||||
|
};
|
42
package-lock.json
generated
42
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"next": "10.2.0",
|
"next": "10.2.0",
|
||||||
"postcss-import": "^14.0.1",
|
"postcss-import": "^14.0.1",
|
||||||
"postcss-nested": "^5.0.5",
|
"postcss-nested": "^5.0.5",
|
||||||
|
"ramda": "^0.27.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2"
|
"react-dom": "17.0.2"
|
||||||
},
|
},
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"@types/graphql": "^14.5.0",
|
"@types/graphql": "^14.5.0",
|
||||||
"@types/postcss-import": "^12.0.0",
|
"@types/postcss-import": "^12.0.0",
|
||||||
"@types/postcss-nested": "^4.2.3",
|
"@types/postcss-nested": "^4.2.3",
|
||||||
|
"@types/ramda": "^0.27.40",
|
||||||
"@types/react": "^17.0.4",
|
"@types/react": "^17.0.4",
|
||||||
"@types/tailwindcss": "^2.0.2",
|
"@types/tailwindcss": "^2.0.2",
|
||||||
"autoprefixer": "^10.2.5",
|
"autoprefixer": "^10.2.5",
|
||||||
@ -2470,6 +2472,15 @@
|
|||||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
|
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/ramda": {
|
||||||
|
"version": "0.27.40",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.40.tgz",
|
||||||
|
"integrity": "sha512-V99ZfTH2tqVYdLDAlgh2uT+N074HPgqnAsMjALKSBqogYd0HbuuGMqNukJ6fk9Ml/Htaus76fsc4Yh3p7q1VdQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ts-toolbelt": "^6.15.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "17.0.4",
|
"version": "17.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
|
||||||
@ -7681,6 +7692,11 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ramda": {
|
||||||
|
"version": "0.27.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz",
|
||||||
|
"integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw=="
|
||||||
|
},
|
||||||
"node_modules/randombytes": {
|
"node_modules/randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@ -8932,6 +8948,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ts-toolbelt": {
|
||||||
|
"version": "6.15.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz",
|
||||||
|
"integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
@ -11517,6 +11539,15 @@
|
|||||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
|
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/ramda": {
|
||||||
|
"version": "0.27.40",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.40.tgz",
|
||||||
|
"integrity": "sha512-V99ZfTH2tqVYdLDAlgh2uT+N074HPgqnAsMjALKSBqogYd0HbuuGMqNukJ6fk9Ml/Htaus76fsc4Yh3p7q1VdQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ts-toolbelt": "^6.15.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"version": "17.0.4",
|
"version": "17.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
|
||||||
@ -15684,6 +15715,11 @@
|
|||||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ramda": {
|
||||||
|
"version": "0.27.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz",
|
||||||
|
"integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw=="
|
||||||
|
},
|
||||||
"randombytes": {
|
"randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@ -16688,6 +16724,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
|
||||||
"integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw=="
|
"integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw=="
|
||||||
},
|
},
|
||||||
|
"ts-toolbelt": {
|
||||||
|
"version": "6.15.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz",
|
||||||
|
"integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"next": "10.2.0",
|
"next": "10.2.0",
|
||||||
"postcss-import": "^14.0.1",
|
"postcss-import": "^14.0.1",
|
||||||
"postcss-nested": "^5.0.5",
|
"postcss-nested": "^5.0.5",
|
||||||
|
"ramda": "^0.27.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2"
|
"react-dom": "17.0.2"
|
||||||
},
|
},
|
||||||
@ -30,6 +31,7 @@
|
|||||||
"@types/graphql": "^14.5.0",
|
"@types/graphql": "^14.5.0",
|
||||||
"@types/postcss-import": "^12.0.0",
|
"@types/postcss-import": "^12.0.0",
|
||||||
"@types/postcss-nested": "^4.2.3",
|
"@types/postcss-nested": "^4.2.3",
|
||||||
|
"@types/ramda": "^0.27.40",
|
||||||
"@types/react": "^17.0.4",
|
"@types/react": "^17.0.4",
|
||||||
"@types/tailwindcss": "^2.0.2",
|
"@types/tailwindcss": "^2.0.2",
|
||||||
"autoprefixer": "^10.2.5",
|
"autoprefixer": "^10.2.5",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
@apply bg-gray-700 text-gray-300;
|
@apply bg-gray-700 text-gray-300;
|
||||||
}
|
}
|
||||||
.wrapper {
|
.wrapper {
|
||||||
@apply mx-auto max-w-screen-xl;
|
@apply mx-auto max-w-screen-lg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pageHeader {
|
.pageHeader {
|
||||||
|
@ -4,40 +4,32 @@ import React from "react";
|
|||||||
import { GlobalSidebar } from "../components/layouts/global-sidebar";
|
import { GlobalSidebar } from "../components/layouts/global-sidebar";
|
||||||
import styles from "./_app.module.css";
|
import styles from "./_app.module.css";
|
||||||
import { ApolloProvider } from "@apollo/client";
|
import { ApolloProvider } from "@apollo/client";
|
||||||
import { client } from "../commons/graphql/client";
|
import { useApollo } from "../commons/graphql/client";
|
||||||
|
import { ThemeProvider, useTheme } from '../commons/theme';
|
||||||
|
import { SwitchTheme } from '../components/switch-theme';
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }) {
|
function MyApp({ Component, pageProps }) {
|
||||||
|
const apolloClient = useApollo(pageProps);
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={apolloClient}>
|
||||||
<div className={styles.page}>
|
<ThemeProvider>
|
||||||
<GlobalSidebar className={styles.sidebar} />
|
<div className={styles.page}>
|
||||||
<div className={styles.primary}>
|
<GlobalSidebar className={styles.sidebar} />
|
||||||
<header className={styles.pageHeader}>
|
<div className={styles.primary}>
|
||||||
<h1>{"Ivan Li 的个人博客"}</h1>
|
<header className={styles.pageHeader}>
|
||||||
<div className={styles.actions}>
|
<h1>{"Ivan Li 的个人博客"}</h1>
|
||||||
<button onClick={() => switchTheme("light")}>亮色</button>
|
<div className={styles.actions}>
|
||||||
<button onClick={() => switchTheme("dark")}>暗色</button>
|
<SwitchTheme />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<Component {...pageProps} />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
|
||||||
<div className={styles.wrapper}>
|
|
||||||
<Component {...pageProps} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ThemeProvider>
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchTheme(mode: "light" | "dark" | "auto") {
|
|
||||||
if (mode === "auto") {
|
|
||||||
mode = "light";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode === "dark") {
|
|
||||||
document.documentElement.classList.add("dark");
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.remove("dark");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MyApp;
|
export default MyApp;
|
||||||
|
44
pages/articles/[id].tsx
Normal file
44
pages/articles/[id].tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import styles from "./article.module.css";
|
||||||
|
import { GetServerSideProps, GetStaticProps } from "next";
|
||||||
|
import { addApolloState, initializeApollo } from "../../commons/graphql/client";
|
||||||
|
import { ARTICLE } from "../../commons/graphql/queries";
|
||||||
|
import { Article } from "../../commons/graphql/generated";
|
||||||
|
import { useQuery } from '@apollo/client';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
article: Article;
|
||||||
|
}
|
||||||
|
const ArticleDetails: FC<Props> = ({ article }) => {
|
||||||
|
// const router = useRouter()
|
||||||
|
// const { data } = useQuery<{ article: Article }>(ARTICLE, {
|
||||||
|
// variables: router.query,
|
||||||
|
// });
|
||||||
|
return (
|
||||||
|
<main className={styles.articleDetails}>
|
||||||
|
<article className={styles.article}>
|
||||||
|
<header>
|
||||||
|
<h1>{article.title}</h1>
|
||||||
|
<time>{article.publishedAt}</time>
|
||||||
|
</header>
|
||||||
|
<div dangerouslySetInnerHTML={{__html: article.content}}></div>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
|
||||||
|
const apolloClient = initializeApollo();
|
||||||
|
|
||||||
|
const { data } = await apolloClient.query<{ article: Article }>({
|
||||||
|
query: ARTICLE,
|
||||||
|
variables: params,
|
||||||
|
});
|
||||||
|
|
||||||
|
return addApolloState(apolloClient, {
|
||||||
|
props: { article: data.article },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArticleDetails;
|
28
pages/articles/article.module.css
Normal file
28
pages/articles/article.module.css
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.articleDetail {
|
||||||
|
}
|
||||||
|
.article {
|
||||||
|
@apply bg-gray-50 m-2 overflow-hidden rounded-xl;
|
||||||
|
|
||||||
|
:global(.dark) & {
|
||||||
|
@apply bg-gray-800;
|
||||||
|
}
|
||||||
|
& > header {
|
||||||
|
@apply my-8 mx-4;
|
||||||
|
h1 {
|
||||||
|
@apply text-2xl font-medium;
|
||||||
|
|
||||||
|
:global(.dark) & {
|
||||||
|
@apply text-gray-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time {
|
||||||
|
@apply text-sm text-gray-500;
|
||||||
|
:global(.dark) & {
|
||||||
|
@apply text-gray-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > div {
|
||||||
|
@apply my-4 leading-8 mx-4 lg:mx-6 xl:mx-8 text-justify;
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,46 @@
|
|||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from "@apollo/client";
|
||||||
import { Article } from '../commons/graphql/generated';
|
import { GetServerSideProps } from 'next';
|
||||||
import { ARTICLE_FOR_HOME } from '../commons/graphql/queries';
|
import Link from "next/link";
|
||||||
import styles from './index.module.css';
|
import React from "react";
|
||||||
|
import { addApolloState, initializeApollo } from '../commons/graphql/client';
|
||||||
|
import { Article } from "../commons/graphql/generated";
|
||||||
|
import { ARTICLE_FOR_HOME } from "../commons/graphql/queries";
|
||||||
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
const { data, loading } = useQuery<{articles: Article[]}>(ARTICLE_FOR_HOME);
|
const { data, loading } = useQuery<{ articles: Article[] }>(ARTICLE_FOR_HOME);
|
||||||
|
|
||||||
return <main className={styles.index}>
|
return (
|
||||||
<ol>
|
<main className={styles.index}>
|
||||||
{
|
<ol>
|
||||||
data?.articles?.map(article => <Item article={article} key={article.id} />)
|
{data?.articles?.map((article) => (
|
||||||
}
|
<Item article={article} key={article.id} />
|
||||||
</ol>
|
))}
|
||||||
</main>;
|
</ol>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Item({article}: {article: Article}) {
|
function Item({ article }: { article: Article }) {
|
||||||
return <li className={styles.item}>
|
return (
|
||||||
<h2 className={styles.title}>{article.title}</h2>
|
<Link href={`/articles/${article.id}`}>
|
||||||
<p className={styles.description}>{article.content}</p>
|
<li className={styles.item}>
|
||||||
</li>
|
<h2 className={styles.title}>{article.title}</h2>
|
||||||
}
|
<p className={styles.description}>{article.content}</p>
|
||||||
|
</li>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
|
||||||
|
const apolloClient = initializeApollo();
|
||||||
|
|
||||||
|
await apolloClient.query({
|
||||||
|
query: ARTICLE_FOR_HOME,
|
||||||
|
});
|
||||||
|
|
||||||
|
return addApolloState(apolloClient, {
|
||||||
|
props: {},
|
||||||
|
});
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user