plane/apps/app/contexts/theme.context.tsx
Anmol Singh Bhatia 1329145173
feat: custom theming (#1028)
* chore: custom theme types and constants

* feat: custom theming

* feat: preferences tab added in profile settings

* feat: remove unneccessary file

* feat:theme apply on page load

* fix: theme switch dropdown fix

* feat: color picker input, theme icon added, chore: code refactor

* style: color picker icon added

* fix: mutation fix

* fix: palette sequence fix

* chore: default custom theme palette updated

* style: join project and not authorized page theming

* fix: merge conflict

* fix: build fix and preferences tab layout fix
2023-05-11 18:40:17 +05:30

114 lines
2.8 KiB
TypeScript

import { createContext, useCallback, useReducer, useEffect } from "react";
import { useRouter } from "next/router";
// swr
import useSWR from "swr";
// components
import ToastAlert from "components/toast-alert";
// hooks
import useUser from "hooks/use-user";
// services
import projectService from "services/project.service";
// fetch-keys
import { USER_PROJECT_VIEW } from "constants/fetch-keys";
// helper
import { applyTheme } from "helpers/theme.helper";
// constants
export const themeContext = createContext<ContextType>({} as ContextType);
type ThemeProps = {
collapsed: boolean;
};
type ReducerActionType = {
type: "TOGGLE_SIDEBAR" | "REHYDRATE_THEME";
payload?: Partial<ThemeProps>;
};
type ContextType = {
collapsed: boolean;
toggleCollapsed: () => void;
};
type StateType = {
collapsed: boolean;
};
type ReducerFunctionType = (state: StateType, action: ReducerActionType) => StateType;
export const initialState: StateType = {
collapsed: false,
};
export const reducer: ReducerFunctionType = (state, action) => {
const { type, payload } = action;
switch (type) {
case "TOGGLE_SIDEBAR":
const newState = {
...state,
collapsed: !state.collapsed,
};
localStorage.setItem("collapsed", JSON.stringify(newState.collapsed));
return newState;
case "REHYDRATE_THEME": {
let collapsed: any = localStorage.getItem("collapsed");
collapsed = collapsed ? JSON.parse(collapsed) : false;
return { ...initialState, ...payload, collapsed };
}
default: {
return state;
}
}
};
export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const { user } = useUser();
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { data: myViewProps } = useSWR(
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId as string) : null,
workspaceSlug && projectId
? () => projectService.projectMemberMe(workspaceSlug as string, projectId as string)
: null
);
const toggleCollapsed = useCallback(() => {
dispatch({
type: "TOGGLE_SIDEBAR",
});
}, []);
useEffect(() => {
dispatch({
type: "REHYDRATE_THEME",
payload: myViewProps?.view_props as any,
});
}, [myViewProps]);
useEffect(() => {
const theme = localStorage.getItem("theme");
if (theme && theme === "custom") {
if (user && user.theme.palette) {
applyTheme(user.theme.palette, user.theme.darkPalette);
}
}
}, [user]);
return (
<themeContext.Provider
value={{
collapsed: state.collapsed,
toggleCollapsed,
}}
>
<ToastAlert />
{children}
</themeContext.Provider>
);
};