refactor: UI page layout component nesting and move app context to redux (#265)

* refactor: remove app context to use redux instead

* refactor: gradient overlay to single element

* refactor: app state backgroundColor to overlayColor

* refactor: app page nesting and positioning
This commit is contained in:
Felipe Mendes 2023-05-19 15:57:12 -03:00 committed by GitHub
parent 07db609798
commit 74d4a4eb9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 101 additions and 92 deletions

View File

@ -1,33 +0,0 @@
import { useState } from 'react';
import { createContext } from './utils';
export type AppContext = {
backgroundColor: string;
setBackgroundColor: (color: string) => void;
};
const [AppProvider, useContext] = createContext<AppContext>({
name: 'App.Context',
hookName: 'App.useContext',
providerName: 'App.Provider',
});
export abstract class App {
static readonly useContext = useContext;
static readonly Provider: React.FC<App.AppProps> = ({ children }) => {
const [backgroundColor, setBackgroundColor] = useState('');
return (
<AppProvider value={{ backgroundColor, setBackgroundColor }}>
{children}
</AppProvider>
);
};
}
export namespace App {
export type AppProps = {
children: React.ReactNode;
};
}

View File

@ -2,7 +2,6 @@ import { HashRouter, Navigate, Route, Routes } from 'react-router-dom';
import { themeGlobals } from '@/theme/globals';
import { App as AppContext } from './app.context';
import { AppPage, ToastProvider } from './components';
import {
ComponentsTest,
@ -18,19 +17,17 @@ export const App: React.FC = () => {
<>
<HashRouter>
<ToastProvider />
<AppContext.Provider>
<AppPage>
<Routes>
<Route path="/" element={<ExploreView />} />
<Route path="/mint" element={<Mint />} />
<Route path="/create-ap/:id" element={<CreateAP />} />
<Route path="/nfa/:id" element={<IndexedNFAView />} />
{/** TODO remove for release */}
<Route path="/components-test" element={<ComponentsTest />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</AppPage>
</AppContext.Provider>
<AppPage>
<Routes>
<Route path="/" element={<ExploreView />} />
<Route path="/mint" element={<Mint />} />
<Route path="/create-ap/:id" element={<CreateAP />} />
<Route path="/nfa/:id" element={<IndexedNFAView />} />
{/** TODO remove for release */}
<Route path="/components-test" element={<ComponentsTest />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</AppPage>
</HashRouter>
</>
);

View File

@ -1,6 +1,8 @@
import { App } from '@/app.context';
import React from 'react';
import { NavBar } from '@/components';
import { GradientOverlay } from './gradient-overlay';
import { PageStyles as PS } from './page.styles';
export type AppPageProps = {
@ -8,18 +10,12 @@ export type AppPageProps = {
};
export const AppPage: React.FC<AppPageProps> = ({ children }: AppPageProps) => {
const { backgroundColor } = App.useContext();
const background = `linear-gradient(180deg, #${backgroundColor}59 0%, #000000 30%)`;
return (
<PS.Container
css={{
background: background,
}}
>
<>
<GradientOverlay />
<NavBar />
<PS.Content as="main">{children}</PS.Content>
</PS.Container>
<PS.Content>{children}</PS.Content>
</>
);
};

View File

@ -0,0 +1,17 @@
import { useAppStore } from '@/store';
import { PageStyles as PS } from './page.styles';
export const GradientOverlay: React.FC = () => {
const { overlayColor } = useAppStore();
if (!overlayColor) return null;
return (
<PS.GradientOverlay
css={{
background: `linear-gradient(180deg, #${overlayColor}59 0%, transparent 30%)`,
}}
/>
);
};

View File

@ -1,12 +1,16 @@
import { styled } from '@/theme';
export abstract class PageStyles {
public static readonly Container = styled('div', {
minHeight: '100vh',
position: 'relative',
});
export const PageStyles = {
GradientOverlay: styled('div', {
position: 'absolute',
inset: 0,
pointerEvents: 'none',
}),
public static readonly Content = styled('div', {
Content: styled('main', {
position: 'relative',
display: 'flex',
flexDirection: 'column',
width: '100%',
minHeight: '85vh',
maxWidth: '$6xl',
@ -16,5 +20,5 @@ export abstract class PageStyles {
'@md': {
padding: '0 $6',
},
});
}
}),
};

View File

@ -0,0 +1,33 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState } from '@/store';
import { useAppSelector } from '@/store/hooks';
export interface AppState {
overlayColor?: string;
}
const initialState: AppState = {
overlayColor: undefined,
};
export const appSlice = createSlice({
name: 'AppSlice',
initialState,
reducers: {
setOverlayColor: (state, action: PayloadAction<string>) => {
state.overlayColor = action.payload;
},
clearOverlayColor: (state) => {
state.overlayColor = undefined;
},
},
});
export const appActions = appSlice.actions;
const selectAppState = (state: RootState): AppState => state.app;
export const useAppStore = (): AppState => useAppSelector(selectAppState);
export default appSlice.reducer;

View File

@ -0,0 +1 @@
export * from './app-slice';

View File

@ -43,9 +43,9 @@ export const bunnyCDNActions = {
...asyncThunk,
};
const selectENSState = (state: RootState): BunnyCDNState => state.bunnyCDN;
const selectBunnyCDNState = (state: RootState): BunnyCDNState => state.bunnyCDN;
export const useBunnyCDNStore = (): BunnyCDNState =>
useAppSelector(selectENSState);
useAppSelector(selectBunnyCDNState);
export default bunnyCDNSlice.reducer;

View File

@ -3,3 +3,4 @@ export * from './github';
export * from './toasts';
export * from './ens';
export * from './bunny-cdn';
export * from './app';

View File

@ -1,5 +1,6 @@
import { configureStore } from '@reduxjs/toolkit';
import appReducer from './features/app/app-slice';
import bunnyCDNReducer from './features/bunny-cdn/bunny-cdn-slice';
import ENSReducer from './features/ens/ens-slice';
import fleekERC721Reducer from './features/fleek-erc721/fleek-erc721-slice';
@ -8,6 +9,7 @@ import toastsReducer from './features/toasts/toasts-slice';
export const store = configureStore({
reducer: {
app: appReducer,
bunnyCDN: bunnyCDNReducer,
ENS: ENSReducer,
fleekERC721: fleekERC721Reducer,

View File

@ -3,14 +3,8 @@ import { styled } from '@/theme';
export const CreateApStyles = {
Container: styled(Flex, {
height: '100%',
flexDirection: 'column',
minHeight: '85vh',
alignItems: 'flex-start',
'@md': {
alignItems: 'center',
justifyContent: 'center',
},
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}),
};

View File

@ -1,8 +1,8 @@
import { useEffect, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { App } from '@/app.context';
import { Button } from '@/components';
import { useAppStore } from '@/store';
import { parseNumberToHexColor } from '@/utils/color';
import { IndexedNFA } from '../../indexed-nfa.context';
@ -18,8 +18,8 @@ export const IndexedNFAAsideFragment: React.FC = () => {
const [top, setTop] = useState<number>();
const { nfa } = IndexedNFA.useContext();
const { backgroundColor } = App.useContext();
const background = `radial-gradient(closest-corner circle at 90% 45%, #${backgroundColor}8c 1% ,#${backgroundColor}57 20%, transparent 40%), radial-gradient(closest-corner circle at 60% 25%, #${backgroundColor} 3%, #${backgroundColor}73 30%, #181818 70%)`;
const { overlayColor } = useAppStore();
const background = `radial-gradient(closest-corner circle at 90% 45%, #${overlayColor}8c 1% ,#${overlayColor}57 20%, transparent 40%), radial-gradient(closest-corner circle at 60% 25%, #${overlayColor} 3%, #${overlayColor}73 30%, #181818 70%)`;
useEffect(() => {
setTop(ref.current?.getBoundingClientRect().top);

View File

@ -3,8 +3,8 @@ import { ethers } from 'ethers';
import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { App } from '@/app.context';
import { getNFADetailDocument } from '@/graphclient';
import { appActions, useAppDispatch } from '@/store';
import { AppLog } from '@/utils';
import { parseNumberToHexColor } from '@/utils/color';
@ -18,14 +18,14 @@ import { IndexedNFAStyles as S } from './indexed-nfa.styles';
export const IndexedNFAView: React.FC = () => {
const { id } = useParams<{ id: string }>();
const { setBackgroundColor } = App.useContext();
const dispatch = useAppDispatch();
const navigate = useNavigate();
useEffect(() => {
return () => {
setBackgroundColor('000000');
dispatch(appActions.clearOverlayColor());
};
}, [setBackgroundColor]);
}, [dispatch]);
const handleError = (error: unknown): void => {
AppLog.errorToast(
@ -43,7 +43,9 @@ export const IndexedNFAView: React.FC = () => {
onCompleted(data) {
if (!data.token) handleError(new Error('Token not found'));
if (data.token?.color)
setBackgroundColor(parseNumberToHexColor(data.token.color));
dispatch(
appActions.setOverlayColor(parseNumberToHexColor(data.token.color))
);
},
onError(error) {
handleError(error);

View File

@ -3,13 +3,8 @@ import { styled } from '@/theme';
export const MintStyles = {
Container: styled(Flex, {
height: '100%',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
minHeight: '85vh',
alignItems: 'flex-start',
'@md': {
alignItems: 'center',
},
}),
};