feat: 亮色暗色模式切换。

This commit is contained in:
Ivan Li
2021-05-02 20:04:54 +08:00
parent d3301da0ad
commit 305eb667f4
12 changed files with 314 additions and 48 deletions

View File

@@ -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({
uri: "/api/graphql",
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;
}

View File

@@ -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
View 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);
}