Mobile overlay structure

This commit is contained in:
Vince 2021-02-04 16:42:15 +11:00
parent 2e92324a74
commit 00960af1f7
31 changed files with 178 additions and 1673 deletions

View File

@ -1,7 +0,0 @@
{
"projects": {
"default": "oxen-io",
"staging": "oxen-io",
"production": "oxen-io"
}
}

View File

@ -1,80 +0,0 @@
import classNames from 'classnames';
import React, { ReactNode, useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useClickAway } from 'react-use';
// import ExitSVG from '../assets/svgs/exit-primary.svg';
import { UI } from '../constants';
import { collapseSearchOverlay } from '../state/navigation';
import { IState } from '../state/reducers';
interface Props {
modalId: string;
isOpen: boolean;
children: ReactNode;
isMobileFullscreen?: boolean;
className?: string;
close?: () => void;
}
export function Modal(props: Props) {
const { modalId, isOpen, close, className, children } = props;
const { searchOverlayExpanded, openedModal } = useSelector(
(state: IState) => state.navigation,
);
const dispatch = useDispatch();
const [shouldRender, setShouldRender] = useState(false);
const ref = useRef(null);
useClickAway(ref, close);
useEffect(() => {
// If modal is open, close search overlay
if (isOpen && searchOverlayExpanded) {
dispatch(collapseSearchOverlay());
}
// Refuse to open if another modal is currently open
if (modalId !== openedModal) {
console.log(
`Cannot open modal ${modalId}, ${openedModal} is already open.`,
);
setShouldRender(true);
}
}, []);
if (!isOpen || !shouldRender) {
return null;
}
return (
<div
ref={ref}
style={{
zIndex: UI.Z_INDEX_MODAL_OVERLAY,
paddingLeft: `${UI.PAGE_CONTAINED_PADDING_VW}vw`,
paddingRight: `${UI.PAGE_CONTAINED_PADDING_VW}vw`,
}}
className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-25"
>
<div
style={{
minWidth: '200px',
maxWidth: '100%',
minHeight: '150px',
maxHeight: '80vh',
}}
className={classNames(
'relative border-2 border-gray px-6 pb-4 pt-12 bg-white',
className,
)}
>
<div className="absolute inset-0 flex justify-end pt-3 pr-3">
{/* <ExitSVG onClick={close} className="h-8 cursor-pointer" /> */}
</div>
{children}
</div>
</div>
);
}

View File

@ -1,6 +1,6 @@
import React, { useContext } from 'react';
import { ScreenContext } from '../../contexts/screen';
import { IPost } from '../../types/blog';
import { IPost } from '../../types/cms';
import { Contained } from '../Contained';
import { ArticleSectionContent } from './sections/ArticleSectionContent';
import { ArticleSectionTitle } from './sections/ArticleSectionTitle';
@ -23,7 +23,7 @@ function ArticleMobile(props: IPost) {
return (
<article>
<Contained>
<div className="flex flex-col items-center space-y-6 mt-12 mb-6">
<div className="flex flex-col items-center mt-12 mb-6 space-y-6">
<ArticleSectionTitle title={title} />
<ArticleWidgetAuthor author={author} publishedDate={publishedDate} />
<ArticleSubtitleSection subtitle={subtitle} />
@ -39,7 +39,7 @@ function ArticleDesktop(props: IPost) {
return (
<article>
<div className="flex flex-col items-center space-y-4 mt-20 mb-10">
<div className="flex flex-col items-center mt-20 mb-10 space-y-4">
<ArticleSectionTitle title={title} />
<ArticleWidgetAuthor author={author} publishedDate={publishedDate} />
<ArticleSubtitleSection subtitle={subtitle} />

View File

@ -1,6 +1,6 @@
import React, { useContext } from 'react';
import { ScreenContext } from '../../../contexts/screen';
import { IPost } from '../../../types/blog';
import { IPost } from '../../../types/cms';
import { Contained } from '../../Contained';
import { ArticleBody } from '../ArticleBody';
import { ArticleSectionFeatureImage } from './ArticleSectionFeatureImage';
@ -27,7 +27,7 @@ const MobileContent = (post: IPost) => {
const DesktopContent = (post: IPost) => {
return (
<div className="flex flex-col space-y-10 items-center">
<div className="flex flex-col items-center space-y-10">
<ArticleSectionFeatureImage featureImage={post.featureImage} />
<div className="my-10">
<ArticleBody body={post.body} />

View File

@ -1,4 +1,4 @@
import { IFigureImage } from '../../../types/blog';
import { IFigureImage } from '../../../types/cms';
interface Props {
featureImage: IFigureImage;
@ -6,10 +6,10 @@ interface Props {
export function ArticleSectionFeatureImage({ featureImage }: Props) {
return (
<div className="pb-4 w-full desktop:pb-0">
<div className="w-full pb-4 desktop:pb-0">
<div
style={{ paddingBottom: '40%' }}
className="relative w-full h-0 mb-4 bg-gray-300 rounded-md overflow-hidden"
className="relative w-full h-0 mb-4 overflow-hidden bg-gray-300 rounded-md"
>
<div className="absolute inset-0">
<img
@ -22,7 +22,7 @@ export function ArticleSectionFeatureImage({ featureImage }: Props) {
</div>
{featureImage?.description && (
<div className="w-8/12 italic text-sm">{featureImage.description}</div>
<div className="w-8/12 text-sm italic">{featureImage.description}</div>
)}
</div>
);

View File

@ -1,5 +1,5 @@
import React from 'react';
import { IAuthor } from '../../../types/blog';
import { IAuthor } from '../../../types/cms';
import { Avatar } from '../../Avatar';
interface Props {
@ -13,7 +13,7 @@ export function ArticleWidgetAuthor({ author, publishedDate }: Props) {
<Avatar size={10} imageSrc={author?.avatar.imageUrl} />
<div className="flex flex-col leading-tight">
<span className="font-roboto tracking-wider text-sm font-bold">
<span className="text-sm font-bold tracking-wider font-roboto">
By: {author?.name}
</span>
<span>{publishedDate}</span>

View File

@ -2,7 +2,7 @@ import classNames from 'classnames';
import router from 'next/dist/client/router';
import { SyntheticEvent } from 'react';
import { useMeasure } from 'react-use';
import { IPost } from '../../types/blog';
import { IPost } from '../../types/cms';
import { generateURL } from '../../utils/routing';
import { titleCase } from '../../utils/text';
import { OutlineBlock } from '../OutlineBlock';

View File

@ -1,7 +1,7 @@
import Link from 'next/link';
import React, { useContext } from 'react';
import { ScreenContext } from '../../contexts/screen';
import { IPost } from '../../types/blog';
import { IPost } from '../../types/cms';
import { generateURL } from '../../utils/routing';
export function ArticleCardRow(post: IPost) {
@ -15,7 +15,7 @@ export function ArticleCardRow(post: IPost) {
lineHeight: '1.33em',
height: '4em',
}}
className="text-base overflow-hidden"
className="overflow-hidden text-base"
>
{post.subtitle}
</p>
@ -27,13 +27,13 @@ export function ArticleCardRow(post: IPost) {
width: isMobile ? '33%' : '10rem',
height: isMobile ? '66%' : '6rem',
}}
className="relative rounded-lg bg-primary bg-opacity-10 overflow-hidden"
className="relative overflow-hidden rounded-lg bg-primary bg-opacity-10"
>
{post?.featureImage?.imageUrl && (
<img
src={post.featureImage.imageUrl}
alt={post.featureImage.description}
className="w-full h-full rounded-lg object-cover"
className="object-cover w-full h-full rounded-lg"
/>
)}
</div>
@ -42,7 +42,7 @@ export function ArticleCardRow(post: IPost) {
return (
<>
{isMobile ? (
<div className="flex flex-col w-full space-y-4 mb-6">
<div className="flex flex-col w-full mb-6 space-y-4">
<div className="flex w-full space-x-6">
<ArticlePreviewImage />
<div className="w-2/3">
@ -62,7 +62,7 @@ export function ArticleCardRow(post: IPost) {
className="flex flex-col flex-grow"
>
<Link href={href} as={as}>
<a className="font-roboto text-xl text-primary">{post.title}</a>
<a className="text-xl font-roboto text-primary">{post.title}</a>
</Link>
<ArticlePreviewContent />

View File

@ -1,42 +0,0 @@
import React from 'react';
// import OxenLogo from '../../assets/svgs/brand.svg';
// import EmailLogoSVG from '../../assets/svgs/hot.svg';
import { ModalInstance } from '../../state/navigation';
import { Button } from '../Button';
import { Modal } from '../Modal';
interface Props {
isOpen: boolean;
close?: () => void;
}
export function LoginModal(props: Props) {
return (
<Modal modalId={ModalInstance.LOGIN} {...props}>
<div className="">
<div className="flex flex-col items-center space-y-6 mb-32">
{/* <OxenLogo className="fill-current h-6" /> */}
<h1 className="font-roboto text-3xl mb-2">Hello!</h1>
<Button
type="outline"
color="secondary"
// prefix={<EmailLogoSVG className="h-6 w-8" />}
suffix={<div className="w-6"></div>}
onClick={() => alert('sdf')}
>
Continue with email
</Button>
</div>
<div>
By proceeding, you agree to our{' '}
<a href="#" className="underline font-semibold">
Terms of Use
</a>
</div>
</div>
</Modal>
);
}

View File

@ -1,15 +1,22 @@
import classNames from 'classnames';
import Link from 'next/link';
import React from 'react';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import OxenLogoSVG from '../../assets/svgs/brand.svg';
import TriangleSVG from '../../assets/svgs/triangle.svg';
import { UI } from '../../constants';
import { toggleMobileMenu } from '../../state/navigation';
import { collapseSideMenu, expandSideMenu } from '../../state/navigation';
import { IState } from '../../state/reducers';
export function MobileHeader() {
const { sideMenuExpanded: expanded } = useSelector(
(state: IState) => state.navigation,
);
const dispatch = useDispatch();
const toggleSideMenu = () =>
dispatch(expanded ? collapseSideMenu() : expandSideMenu());
return (
<div
style={{
@ -30,8 +37,11 @@ export function MobileHeader() {
</div>
<TriangleSVG
onClick={() => dispatch(toggleMobileMenu())}
className="h-12 transform rotate-90 outline-none"
onClick={() => toggleSideMenu()}
className={classNames(
'h-5 transform outline-none duration-300',
expanded ? '-rotate-60' : 'rotate-180',
)}
/>
</div>
</div>

View File

@ -34,7 +34,11 @@ export function SideMenuActiveIndicator() {
)}
</div>
<div className="flex items-center justify-start w-0 h-0 transform -rotate-90">
<div
className={classNames(
'flex items-center justify-start w-0 h-0 duration-300 transform -rotate-90',
)}
>
<span className="whitespace-no-wrap">
{NAVIGATION.SIDE_MENU_ITEMS[active].label}
</span>

View File

@ -16,13 +16,13 @@ export function SideMenuDefault() {
width: '50vw',
minWidth: '375px',
}}
className="relative flex text-primary"
className="relative flex text-primary bg-alt"
>
<div
style={{
height: `calc(100vh - ${UI.HEADER_HEIGHT_PX}px`,
}}
className="w-full overflow-y-auto duration-300 children:last:border-b-0"
className="w-full overflow-y-auto"
>
<SideMenuInner />
</div>

View File

@ -4,7 +4,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { NAVIGATION } from '../../constants';
import { setSideMenuActive, SideMenuItem } from '../../state/navigation';
import { IState } from '../../state/reducers';
import { SideMenuRow } from './SideMenuRowProps';
import { SideMenuRow } from './SideMenuRow';
export function SideMenuInner() {
const { sideMenuActive: active } = useSelector(
@ -15,7 +15,7 @@ export function SideMenuInner() {
const router = useRouter();
return (
<div className="h-full overflow-y-auto duration-300 children:last:border-b-0">
<div className="flex flex-col h-full duration-300 mobile:children:last:border-b-0">
{Object.entries(NAVIGATION.SIDE_MENU_ITEMS).map(([key, item]) => (
<SideMenuRow
item={item}

View File

@ -1,9 +1,26 @@
import classNames from 'classnames';
import _ from 'lodash';
import React from 'react';
import { UI } from '../../constants';
import { SideMenuActiveIndicator } from './SideMenuActiveIndicator';
import { useSelector } from 'react-redux';
import { v4 as uuid } from 'uuid';
import FacebookSVG from '../../assets/svgs/socials/facebook.svg';
import InstargamSVG from '../../assets/svgs/socials/instagram.svg';
import TwitterSVG from '../../assets/svgs/socials/twitter.svg';
import { NAVIGATION, UI } from '../../constants';
import { IState } from '../../state/reducers';
import { Contained } from '../Contained';
import { SideMenuInner } from './SideMenuInner';
export function SideMenuMobile() {
const { sideMenuExpanded: expanded } = useSelector(
(state: IState) => state.navigation,
);
console.log(
'SideMenuMobile ➡️ NAVIGATION.MENU_ITEMS:',
NAVIGATION.MENU_ITEMS,
);
return (
<div
style={{
@ -11,13 +28,38 @@ export function SideMenuMobile() {
top: `${UI.HEADER_HEIGHT_PX}px`,
zIndex: 30000,
}}
className="fixed inset-0 flex bg-alt"
className={classNames(
'fixed inset-0 flex duration-300 transform border-t bg-alt border-primary',
expanded ? '-translate-x-full' : 'translate-x-0',
)}
>
<div className="flex flex-col w-full">
<SideMenuInner />
</div>
<div className="flex flex-col w-full space-y-4">
<div className="flex flex-col flex-grow">
<SideMenuInner />
</div>
<SideMenuActiveIndicator />
<Contained>
<div className="flex flex-col w-full space-y-4">
<div className="flex justify-between pt-8 pb-2 text-sm font-medium uppercase font-prompt">
{_.chunk(NAVIGATION.MENU_ITEMS, 3).map(group => (
<div key={uuid()} className="flex flex-col space-y-2">
{group.map(item => (
<a key={item.label} href={item.href}>
{item.label}
</a>
))}
</div>
))}
</div>
<div className="flex space-x-3">
<FacebookSVG className="h-10" />
<TwitterSVG className="h-10" />
<InstargamSVG className="h-10" />
</div>
</div>
</Contained>
</div>
</div>
);
}

View File

@ -2,6 +2,7 @@ import classNames from 'classnames';
import React, { useContext } from 'react';
import TriangleOutlinedSVG from '../../assets/svgs/triangle-outlined.svg';
import TriangleSVG from '../../assets/svgs/triangle.svg';
import { UI } from '../../constants';
import { ScreenContext } from '../../contexts/screen';
import { ISideMenuItem } from './SideMenu';
@ -18,17 +19,29 @@ export function SideMenuRow({ item, isActive, onClick }: SideMenuRowProps) {
return (
<div
onClick={onClick}
style={{
maxHeight: '5rem',
padding:
isMobile || isTablet
? `0 ${UI.PAGE_CONTAINED_PADDING_VW}vw`
: 'unset',
}}
className={classNames(
'flex flex-1 space-x-6 justify-between items-center cursor-pointer border-b border-black px-4 py-4 hover:bg-secondary duration-300',
isHuge ? 'text-3xl' : isDesktop ? 'text-xl' : '',
'flex flex-1 space-x-6 justify-between text-primary items-center cursor-pointer border-b border-black py-4 hover:bg-secondary duration-300',
isHuge ? 'text-3xl' : isDesktop ? 'text-xl' : 'text-xl',
isActive ? 'bg-secondary' : 'bg-transparent',
)}
>
<span className="whitespace-no-wrap">{item.label}</span>
{!isCollapsible && isActive ? (
<TriangleSVG className="h-4" />
) : (
<TriangleOutlinedSVG className="h-4" />
<span className="pl-6 whitespace-no-wrap">{item.label}</span>
{!isMobile && !isTablet && (
<>
{!isCollapsible && isActive ? (
<TriangleSVG className="h-4 pr-6" />
) : (
<TriangleOutlinedSVG className="h-4 pr-6" />
)}
</>
)}
</div>
);

View File

@ -1,4 +1,3 @@
// import FIREBASE from './firebase';
import METADATA from './metadata';
import NAVIGATION from './navigation';
import SEARCH from './search';

View File

@ -32,9 +32,6 @@
"contentful": "^8.1.7",
"dotenv": "^8.2.0",
"eslint-plugin-jsx-a11y": "^6.3.1",
"firebase": "^8.2.1",
"firebase-admin": "^9.4.2",
"firebase-functions": "^3.13.0",
"firestore-pagination-hook": "^1.0.0",
"global": "^4.4.0",
"groq": "^1.149.16",
@ -53,7 +50,6 @@
"react-dom": "^17.0.0",
"react-paginate": "^6.5.0",
"react-redux": "^7.2.1",
"react-redux-firebase": "^3.9.0",
"react-use": "^15.3.4",
"redux": "^4.0.5",
"redux-firestore": "^0.14.0",

View File

@ -1,5 +1,3 @@
import 'firebase/auth';
import 'firebase/firestore'; // <- needed if using firestore
import type { AppProps } from 'next/app';
import Head from 'next/head';
import React, { useEffect } from 'react';
@ -10,16 +8,16 @@ import '../assets/style.scss';
import Layout from '../components/layout';
import { METADATA } from '../constants';
import ScreenProvider from '../contexts/screen';
import { collapseSearchOverlay } from '../state/navigation';
import { collapseSideMenu } from '../state/navigation';
import { rootReducer } from '../state/reducers';
const store = createStore(rootReducer);
function App({ Component, pageProps }: AppProps) {
// Close search overlay on page changed
// Close side menu on page changed
const location = useLocation();
useEffect(() => {
store.dispatch(collapseSearchOverlay());
store.dispatch(collapseSideMenu());
}, [location.pathname, location.search]);
return (

View File

@ -2,13 +2,13 @@
import Head from 'next/head';
import React, { useEffect } from 'react';
import { Article } from '../../components/article/Article';
import { BlogApi } from '../../services/blog';
import { IPost } from '../../types/blog';
import { CmsApi } from '../../services/cms';
import { IPost } from '../../types/cms';
import { generateTitle } from '../../utils/metadata';
export async function getServerSideProps({ params }) {
console.log('Sulg', params);
const api = new BlogApi();
const api = new CmsApi();
const post = await api.fetchBlogBySlug(String(params.slug) ?? '');
console.log('index ➡️ post:', post);

View File

@ -3,11 +3,11 @@ import Head from 'next/head';
import React from 'react';
import { ArticleCard } from '../../components/cards/ArticleCard';
import { CardGrid } from '../../components/cards/CardGrid';
import { BlogApi } from '../../services/blog';
import { CmsApi } from '../../services/cms';
import { generateTitle } from '../../utils/metadata';
export async function getServerSideProps(context) {
const api = new BlogApi();
const api = new CmsApi();
const posts = await api.fetchBlogEntries();
console.log('index ➡️ posts:', posts);

View File

@ -1,16 +1,23 @@
import { InferGetServerSidePropsType } from 'next';
import Head from 'next/head';
import React from 'react';
import { SideMenu } from '../components/navigation/SideMenu';
import { HomeLanding } from '../components/pages/home/HomeLanding';
import { METADATA } from '../constants';
import { CmsApi } from '../services/cms';
import { SideMenuItem } from '../state/navigation';
// interface Props {
// posts: Array<ISanityArticle>;
// AuthUserInfo: any;
// }
export async function getServerSideProps(context) {
const api = new CmsApi();
const page = await api.fetchPageById(SideMenuItem.WHO_ARE_WE);
// const Index: NextPage<Props> = () => {
const Index = () => {
console.log('index ➡️ page:', page);
return { props: page };
}
const Index = (
page: InferGetServerSidePropsType<typeof getServerSideProps>,
) => {
// const cards = posts
// ? posts.slice(0, 4).map(post => <ArticleCard key={post.id} {...post} />)
// : [];

View File

@ -1,8 +1,9 @@
import { ContentfulClientApi, createClient } from 'contentful';
import moment from 'moment';
import { IAuthor, IFigureImage, IPost } from '../types/blog';
import { SideMenuItem } from '../state/navigation';
import { IAuthor, IFigureImage, IPost, ISplitPage } from '../types/cms';
export class BlogApi {
export class CmsApi {
client: ContentfulClientApi;
constructor() {
@ -36,6 +37,16 @@ export class BlogApi {
});
}
public async fetchPageById(id: SideMenuItem): Promise<ISplitPage> {
return this.client.getEntry(id).then(entry => {
if (entry) {
console.log('cms ➡️ entry:', entry);
// return this.convertPage(entry);
}
return null;
});
}
public async fetchBlogBySlug(slug: string): Promise<IPost> {
return this.client
.getEntries({

View File

@ -1,7 +1,3 @@
export enum ModalInstance {
LOGIN = 'LOGIN',
}
export enum SideMenuItem {
WHO_ARE_WE = 'WHO_ARE_WE',
MISSION = 'MISSION',
@ -16,9 +12,6 @@ export enum SideMenuItem {
export interface INavigation {
sideMenuExpanded: boolean;
sideMenuActive: SideMenuItem;
mobileMenuExpanded: boolean;
searchOverlayExpanded: boolean;
openedModal: ModalInstance | null;
}
export const initialNavigationState: INavigation = {
@ -26,22 +19,12 @@ export const initialNavigationState: INavigation = {
// On desktop it's always open (if it fits).
sideMenuExpanded: false,
sideMenuActive: SideMenuItem.WHO_ARE_WE,
mobileMenuExpanded: false,
searchOverlayExpanded: false,
openedModal: null,
};
export enum NavigationActions {
EXPAND_SIDE_MENU = 'EXPAND_SIDE_MENU',
SET_SIDE_MENU_ACTIVE = 'SET_SIDE_MENU_ACTIVE',
COLLAPSE_SIDE_MENU = 'COLLAPSE_SIDE_MENU',
OPEN_MOBILE_MENU = 'OPEN_MOBILE_MENU',
CLOSE_MOBILE_MENU = 'CLOSE_MOBILE_MENU',
TOGGLE_MOBILE_MENU = 'TOGGLE_MOBILE_MENU',
EXPAND_SEARCH_OVERLAY = 'EXPAND_SEARCH_OVERLAY',
COLLAPSE_SEARCH_OVERLAY = 'COLLAPSE_SEARCH_OVERLAY',
TOGGLE_SEARCH_OVERLAY = 'TOGGLE_SEARCH_OVERLAY',
SET_MODAL_IS_OPEN = 'SET_MODAL_IS_OPEN',
}
// ////////////////////////////// //
@ -59,32 +42,3 @@ export const setSideMenuActive = (active: SideMenuItem) => ({
export const collapseSideMenu = () => ({
type: NavigationActions.COLLAPSE_SIDE_MENU,
});
export const openMobileMenu = () => ({
type: NavigationActions.OPEN_MOBILE_MENU,
});
export const closeMobileMenu = () => ({
type: NavigationActions.CLOSE_MOBILE_MENU,
});
export const toggleMobileMenu = () => ({
type: NavigationActions.TOGGLE_MOBILE_MENU,
});
export const expandSearchOverlay = () => ({
type: NavigationActions.EXPAND_SEARCH_OVERLAY,
});
export const collapseSearchOverlay = () => ({
type: NavigationActions.COLLAPSE_SEARCH_OVERLAY,
});
export const toggleSearchOverlay = () => ({
type: NavigationActions.TOGGLE_SEARCH_OVERLAY,
});
export const setCurrentOpenModal = (isOpen: boolean) => ({
type: NavigationActions.SET_MODAL_IS_OPEN,
payload: isOpen,
});

View File

@ -1,4 +1,4 @@
import { IPost } from '../../types/blog';
import { IPost } from '../../types/cms';
export const initialArticleState: IPost | Record<string, unknown> = {};

View File

@ -1,28 +1,15 @@
import 'firebase/auth';
import 'firebase/firestore'; // <- needed if using firestore
import { firebaseReducer } from 'react-redux-firebase';
import { combineReducers } from 'redux';
import { firestoreReducer } from 'redux-firestore'; // <- needed if using firestore
import { IFirestore } from '../../constants/firebase';
import { IPost } from '../../types/blog';
import { IPost } from '../../types/cms';
import { INavigation } from '../navigation';
import { ISearch } from '../search';
import { articleReducer } from './article';
import { navigationReducer } from './navigation';
import { searchReducer } from './search';
export interface IState {
navigation: INavigation;
search: ISearch;
article: IPost;
firestore: IFirestore;
}
export const rootReducer = combineReducers({
navigation: navigationReducer,
search: searchReducer,
article: articleReducer,
firebase: firebaseReducer,
firestore: firestoreReducer, // <- needed if using firestore
});

View File

@ -23,27 +23,6 @@ export const navigationReducer = (
case NavigationActions.COLLAPSE_SIDE_MENU: {
return { ...state, sideMenuExpanded: false };
}
case NavigationActions.OPEN_MOBILE_MENU: {
return { ...state, mobileMenuExpanded: true };
}
case NavigationActions.CLOSE_MOBILE_MENU: {
return { ...state, mobileMenuExpanded: false };
}
case NavigationActions.TOGGLE_MOBILE_MENU: {
return { ...state, mobileMenuExpanded: !state.mobileMenuExpanded };
}
case NavigationActions.EXPAND_SEARCH_OVERLAY: {
return { ...state, searchOverlayExpanded: true };
}
case NavigationActions.COLLAPSE_SEARCH_OVERLAY: {
return { ...state, searchOverlayExpanded: false };
}
case NavigationActions.TOGGLE_SEARCH_OVERLAY: {
return { ...state, searchOverlayExpanded: !state.searchOverlayExpanded };
}
case NavigationActions.SET_MODAL_IS_OPEN: {
return { ...state, modalIsOpen: action.payload };
}
default:
return state;
}

View File

@ -1,4 +1,4 @@
import { IPost } from '../types/blog';
import { IPost } from '../types/cms';
export interface ISearch {
searchQuery: string;

View File

@ -34,3 +34,11 @@ export type BodyDocument = {
nodeType: 'document';
content: any;
};
export interface ISplitPage {
id: ISplitPage;
label: string;
title: string;
body: Document;
hero?: IFigureImage;
}

View File

@ -1,17 +0,0 @@
export enum UserData {
RECENT_SEARCHES = 'recentSearches',
REFERRED_FROM = 'referredFrom',
USER_SESSIONS = 'userSessions',
USER_DEVICE = 'userDevice',
}
export interface IUserSession {
device: 'mobile' | 'tablet' | 'desktop';
sessionStartTimestamp: number;
sessionEndTimestamp: number;
}
export interface IRecentSearch {
query: string;
timestamp: number;
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import { ArticleCard } from '../components/cards/ArticleCard';
import { IPost } from '../types/blog';
import { IPost } from '../types/cms';
export function postsToCards(posts: Array<IPost>) {
const cards = posts

1401
yarn.lock

File diff suppressed because it is too large Load Diff