major changes

state management now handled with redux
responsiveness uses custom hook (hooks.ts)
pull/72/head^2
TaylorS15 1 year ago
parent c44e1804bf
commit 5e5f13b664

@ -1,62 +1,17 @@
import { useEffect, useState } from 'react';
import { Routes, Route } from 'react-router-dom';
import Navigation from './components/Navigation/Navigation';
import DocsGPT from './components/DocsGPT/DocsGPT';
import Navigation from './components/Navigation';
import Conversation from './components/Conversation/Conversation';
import APIKeyModal from './components/APIKeyModal';
import About from './components/About';
export default function App() {
//Currently using primitive state management. Will most likely be replaced with Redux.
const [isMobile, setIsMobile] = useState(true);
const [isMenuOpen, setIsMenuOpen] = useState(true);
const [isApiModalOpen, setIsApiModalOpen] = useState(false);
const [apiKey, setApiKey] = useState('');
const handleResize = () => {
if (window.innerWidth > 768 && isMobile) {
setIsMobile(false);
} else {
setIsMobile(true);
setIsMenuOpen(false);
}
};
useEffect(() => {
window.addEventListener('resize', handleResize);
handleResize();
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div
className={`${
isMobile ? 'flex-col' : 'flex-row'
} relative flex transition-all`}
>
<APIKeyModal
apiKey={apiKey}
setApiKey={setApiKey}
isApiModalOpen={isApiModalOpen}
setIsApiModalOpen={setIsApiModalOpen}
/>
<Navigation
isMobile={isMobile}
isMenuOpen={isMenuOpen}
setIsMenuOpen={setIsMenuOpen}
setIsApiModalOpen={setIsApiModalOpen}
/>
<div className="relative flex flex-col transition-all md:flex-row">
<APIKeyModal />
<Navigation />
<Routes>
<Route
path="/"
element={<DocsGPT isMenuOpen={isMenuOpen} isMobile={isMobile} />}
/>
<Route
path="/about"
element={<About isMenuOpen={isMenuOpen} isMobile={isMobile} />}
/>
<Route path="/" element={<Conversation />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
);

@ -1,30 +1,29 @@
import { useState } from 'react';
export default function APIKeyModal({
isApiModalOpen,
setIsApiModalOpen,
apiKey,
import { useDispatch, useSelector } from 'react-redux';
import {
setApiKey,
}: {
isApiModalOpen: boolean;
setIsApiModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
apiKey: string;
setApiKey: React.Dispatch<React.SetStateAction<string>>;
}) {
//TODO - Add form validation
toggleApiKeyModal,
selectIsApiKeyModalOpen,
} from '../store';
export default function APIKeyModal({}) {
//TODO - Add form validation?
//TODO - Connect to backend
//TODO - Add link to OpenAI API Key page
const dispatch = useDispatch();
const isApiModalOpen = useSelector(selectIsApiKeyModalOpen);
const [key, setKey] = useState('');
const [formError, setFormError] = useState(false);
const handleResetKey = () => {
if (!apiKey) {
function handleSubmit() {
if (key.length < 1) {
setFormError(true);
} else {
setFormError(false);
setIsApiModalOpen(false);
return;
}
};
dispatch(setApiKey(key));
dispatch(toggleApiKeyModal());
}
return (
<div
@ -42,17 +41,17 @@ export default function APIKeyModal({
<input
type="text"
className="h-10 w-full border-b-2 border-jet focus:outline-none"
value={apiKey}
value={key}
maxLength={100}
placeholder="API Key"
onChange={(e) => setApiKey(e.target.value)}
onChange={(e) => setKey(e.target.value)}
/>
<div className="flex justify-between">
{formError && (
<p className="text-sm text-red-500">Please enter a valid API key</p>
)}
<button
onClick={handleResetKey}
onClick={() => handleSubmit()}
className="ml-auto h-10 w-20 rounded-lg bg-violet-800 text-white transition-all hover:bg-violet-700"
>
Save

@ -1,12 +1,13 @@
export default function About({
isMenuOpen,
isMobile,
}: {
isMenuOpen: boolean;
isMobile: boolean;
}) {
//TODO - Add hyperlinks to text
//TODO - Styling
import { useSelector } from 'react-redux';
import { useMediaQuery } from '../hooks';
import { selectIsMenuOpen } from '../store';
//TODO - Add hyperlinks to text
//TODO - Styling
export default function About() {
const isMobile = useMediaQuery('(max-width: 768px)');
const isMenuOpen = useSelector(selectIsMenuOpen);
return (
//Parent div for all content shown through App.tsx routing needs to have this styling. Might change when state management is updated.

@ -1,12 +1,13 @@
export default function DocsGPT({
isMenuOpen,
isMobile,
}: {
isMenuOpen: boolean;
isMobile: boolean;
}) {
import { useMediaQuery } from '../../hooks';
import { selectIsMenuOpen } from '../../store';
import { useSelector } from 'react-redux';
export default function Conversation() {
const isMobile = useMediaQuery('(max-width: 768px)');
const isMenuOpen = useSelector(selectIsMenuOpen);
return (
//Parent div for all content shown through App.tsx routing needs to have this styling. Might change when state management is updated.
//Parent div for all content shown through App.tsx routing needs to have this styling.
<div
className={`${
isMobile

@ -1,23 +1,25 @@
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { NavLink } from 'react-router-dom';
import Arrow1 from './imgs/arrow.svg';
import Hamburger from './imgs/hamburger.svg';
import Key from './imgs/key.svg';
import Info from './imgs/info.svg';
import Link from './imgs/link.svg';
import Exit from './imgs/exit.svg';
import { useMediaQuery } from '../hooks';
import {
toggleApiKeyModal,
selectIsMenuOpen,
toggleIsMenuOpen,
} from '../store';
import Arrow1 from '../imgs/arrow.svg';
import Hamburger from '../imgs/hamburger.svg';
import Key from '../imgs/key.svg';
import Info from '../imgs/info.svg';
import Link from '../imgs/link.svg';
import Exit from '../imgs/exit.svg';
//TODO - Need to replace Chat button to open secondary nav with scrollable past chats option and new chat at top
//TODO - Need to add Discord and Github links
function MobileNavigation({}) {
const dispatch = useDispatch();
const isMenuOpen = useSelector(selectIsMenuOpen);
function MobileNavigation({
isMenuOpen,
setIsMenuOpen,
setIsApiModalOpen,
}: {
isMenuOpen: boolean;
setIsMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;
setIsApiModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) {
//TODO - Need to replace Chat button to open secondary nav with scrollable past chats option and new chat at top
//TODO - Need to add Discord and Github links
return (
<div
className={`${
@ -29,7 +31,7 @@ function MobileNavigation({
<>
<button
className="mt-5 ml-6 h-6 w-6"
onClick={() => setIsMenuOpen(!isMenuOpen)}
onClick={() => dispatch(toggleIsMenuOpen())}
>
<img src={Exit} alt="menu toggle" className="w-5" />
</button>
@ -38,7 +40,7 @@ function MobileNavigation({
<>
<button
className="mt-5 ml-6 h-6 w-6"
onClick={() => setIsMenuOpen(!isMenuOpen)}
onClick={() => dispatch(toggleIsMenuOpen())}
>
<img src={Hamburger} alt="menu toggle" className="w-7" />
</button>
@ -71,7 +73,7 @@ function MobileNavigation({
</div>
<div
className="flex h-12 cursor-pointer gap-4 rounded-md px-6 hover:bg-gray-100"
onClick={() => setIsApiModalOpen(true)}
onClick={() => dispatch(toggleApiKeyModal())}
>
<img src={Key} alt="info" className="ml-2 w-5" />
<p className="my-auto text-eerie-black">Reset Key</p>
@ -82,15 +84,10 @@ function MobileNavigation({
);
}
function DesktopNavigation({
isMenuOpen,
setIsMenuOpen,
setIsApiModalOpen,
}: {
isMenuOpen: boolean;
setIsMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;
setIsApiModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) {
function DesktopNavigation() {
const dispatch = useDispatch();
const isMenuOpen = useSelector(selectIsMenuOpen);
return (
<div
className={`${
@ -104,7 +101,7 @@ function DesktopNavigation({
>
<button
className="float-right mr-5 mt-5 h-5 w-5"
onClick={() => setIsMenuOpen(!isMenuOpen)}
onClick={() => dispatch(toggleIsMenuOpen())}
>
<img
src={Arrow1}
@ -123,7 +120,7 @@ function DesktopNavigation({
<div className="flex h-16 flex-col border-b-2 border-gray-100">
<div
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
onClick={() => setIsApiModalOpen(true)}
onClick={() => dispatch(toggleApiKeyModal())}
>
<img src={Key} alt="key" className="ml-2 w-6" />
<p className="my-auto text-eerie-black">Reset Key</p>
@ -155,32 +152,12 @@ function DesktopNavigation({
);
}
export default function Navigation({
isMobile,
isMenuOpen,
setIsMenuOpen,
setIsApiModalOpen,
}: {
isMobile: boolean;
isMenuOpen: boolean;
setIsMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;
setIsApiModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) {
export default function Navigation() {
const isMobile = useMediaQuery('(max-width: 768px)');
if (isMobile) {
return (
<MobileNavigation
isMenuOpen={isMenuOpen}
setIsMenuOpen={setIsMenuOpen}
setIsApiModalOpen={setIsApiModalOpen}
/>
);
return <MobileNavigation />;
} else {
return (
<DesktopNavigation
isMenuOpen={isMenuOpen}
setIsMenuOpen={setIsMenuOpen}
setIsApiModalOpen={setIsApiModalOpen}
/>
);
return <DesktopNavigation />;
}
}

@ -1 +0,0 @@
export default function PastChat() {}

@ -0,0 +1,22 @@
import { useState, useEffect } from 'react';
export function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
if (media.matches !== matches) {
setMatches(media.matches);
}
const listener = () => {
setMatches(media.matches);
};
media.addEventListener('resize', listener);
return () => media.removeEventListener('resize', listener);
}, [matches, query]);
return matches;
}

@ -30,6 +30,7 @@ body {
margin: 0;
min-height: 100vh;
overflow-x: hidden;
font-family: 'Inter', sans-serif;
}
/**

@ -2,12 +2,16 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import store from './store';
import './index.css';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<BrowserRouter>
<App />
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
</React.StrictMode>,
);

@ -0,0 +1,48 @@
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit';
interface State {
isApiKeyModalOpen: boolean;
apiKey: string;
isMenuOpen: boolean;
}
const initialState: State = {
isApiKeyModalOpen: true,
apiKey: '',
isMenuOpen: true,
};
export const slice = createSlice({
name: 'app',
initialState,
reducers: {
toggleApiKeyModal: (state) => {
state.isApiKeyModalOpen = !state.isApiKeyModalOpen;
console.log('showApiKeyModal', state.isApiKeyModalOpen);
},
setApiKey: (state, action: PayloadAction<string>) => {
state.apiKey = action.payload;
console.log('setApiKey', action.payload);
},
toggleIsMenuOpen: (state) => {
state.isMenuOpen = !state.isMenuOpen;
},
},
});
export const { toggleApiKeyModal, setApiKey, toggleIsMenuOpen } = slice.actions;
const store = configureStore({
reducer: {
app: slice.reducer,
},
});
type RootState = ReturnType<typeof store.getState>;
export const selectIsApiKeyModalOpen = (state: RootState) =>
state.app.isApiKeyModalOpen;
export const selectApiKey = (state: RootState) => state.app.apiKey;
export const selectIsMenuOpen = (state: RootState) => state.app.isMenuOpen;
export default store;
Loading…
Cancel
Save