chore: reorganize client files for docker

This commit is contained in:
Danny Avila 2023-03-06 10:47:06 -05:00
parent affbaaf1a5
commit f5e079742a
93 changed files with 178726 additions and 1 deletions

View file

@ -0,0 +1,37 @@
import React from 'react';
import TrashIcon from '../svg/TrashIcon';
import { useSWRConfig } from "swr"
import manualSWR from '~/utils/fetchers';
import { useDispatch } from 'react-redux';
import { setConversation } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
export default function ClearConvos() {
const dispatch = useDispatch();
const { mutate } = useSWRConfig()
const { trigger } = manualSWR(
`http://localhost:3080/convos/clear`,
'post',
() => {
dispatch(setMessages([]));
dispatch(setConversation({ error: false, title: 'New chat', conversationId: null, parentMessageId: null }));
mutate(`http://localhost:3080/convos`);
}
);
const clickHandler = () => {
console.log('Clearing conversations...');
trigger({});
};
return (
<a
className="flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
onClick={clickHandler}
>
<TrashIcon />
Clear conversations
</a>
);
}

View file

@ -0,0 +1,21 @@
import React, { useState, useContext } from 'react';
import DarkModeIcon from '../svg/DarkModeIcon';
import LightModeIcon from '../svg/LightModeIcon';
import { ThemeContext } from '~/hooks/ThemeContext';
export default function DarkMode() {
const { theme, setTheme } = useContext(ThemeContext);
const clickHandler = () => setTheme(theme === 'dark' ? 'light' : 'dark');
const mode = theme === 'dark' ? 'Light mode' : 'Dark mode';
return (
<a
className="flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
onClick={clickHandler}
>
{theme === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
{mode}
</a>
);
}

View file

@ -0,0 +1,76 @@
import React from 'react';
export default function MobileNav({ title = 'New Chat' }) {
return (
<div className="sticky top-0 z-10 flex items-center border-b border-white/20 bg-gray-800 pl-1 pt-1 text-gray-200 sm:pl-3 md:hidden">
<button
type="button"
className="-ml-0.5 -mt-0.5 inline-flex h-10 w-10 items-center justify-center rounded-md hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white dark:hover:text-white"
>
<span className="sr-only">Open sidebar</span>
<svg
stroke="currentColor"
fill="none"
strokeWidth="1.5"
viewBox="0 0 24 24"
strokeLinecap="round"
strokeLinejoin="round"
className="h-6 w-6"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="3"
y1="12"
x2="21"
y2="12"
/>
<line
x1="3"
y1="6"
x2="21"
y2="6"
/>
<line
x1="3"
y1="18"
x2="21"
y2="18"
/>
</svg>
</button>
<h1 className="flex-1 text-center text-base font-normal">{title}</h1>
<button
type="button"
className="px-3"
>
<svg
stroke="currentColor"
fill="none"
strokeWidth="1.5"
viewBox="0 0 24 24"
strokeLinecap="round"
strokeLinejoin="round"
className="h-6 w-6"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="12"
y1="5"
x2="12"
y2="19"
/>
<line
x1="5"
y1="12"
x2="19"
y2="12"
/>
</svg>
</button>
</div>
);
}

View file

@ -0,0 +1,20 @@
import React from 'react';
export default function NavLink({ svg, text, clickHandler }) {
const props = {
className:
'flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10'
};
if (clickHandler) {
props.onClick = clickHandler;
console.log('clickHandler: ', clickHandler);
}
return (
<a {...props}>
{svg()}
{text}
</a>
);
}

View file

@ -0,0 +1,18 @@
import React from 'react';
import NavLink from './NavLink';
import LogOutIcon from '../svg/LogOutIcon';
import ClearConvos from './ClearConvos';
import DarkMode from './DarkMode';
export default function NavLinks() {
return (
<>
<ClearConvos />
<DarkMode />
<NavLink
svg={LogOutIcon}
text="Log out"
/>
</>
);
}

View file

@ -0,0 +1,49 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import { setConversation } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
import { setText } from '~/store/textSlice';
export default function NewChat() {
const dispatch = useDispatch();
const clickHandler = () => {
dispatch(setText(''));
dispatch(setMessages([]));
dispatch(setConversation({ title: 'New Chat', error: false, conversationId: null, parentMessageId: null }));
};
return (
<a
onClick={clickHandler}
className="mb-2 flex flex-shrink-0 cursor-pointer items-center gap-3 rounded-md border border-white/20 py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
>
<svg
stroke="currentColor"
fill="none"
strokeWidth="2"
viewBox="0 0 24 24"
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="12"
y1="5"
x2="12"
y2="19"
/>
<line
x1="5"
y1="12"
x2="19"
y2="12"
/>
</svg>
New chat
</a>
);
}

View file

@ -0,0 +1,84 @@
import React, { useState, useEffect, useRef } from 'react';
import NewChat from './NewChat';
import Spinner from '../svg/Spinner';
import Conversations from '../Conversations';
import NavLinks from './NavLinks';
import useDidMountEffect from '~/hooks/useDidMountEffect';
import { swr } from '~/utils/fetchers';
import { useDispatch, useSelector } from 'react-redux';
import { incrementPage, setConvos } from '~/store/convoSlice';
export default function Nav() {
const dispatch = useDispatch();
const [isHovering, setIsHovering] = useState(false);
const { conversationId, convos, pageNumber } = useSelector((state) => state.convo);
const onSuccess = (data) => {
dispatch(setConvos(data));
};
const { data, isLoading, mutate } = swr(
`http://localhost:3080/convos?pageNumber=${pageNumber}`
, onSuccess);
const containerRef = useRef(null);
const scrollPositionRef = useRef(null);
const showMore = async () => {
const container = containerRef.current;
if (container) {
scrollPositionRef.current = container.scrollTop;
}
dispatch(incrementPage());
await mutate();
};
useDidMountEffect(() => mutate(), [conversationId]);
useEffect(() => {
const container = containerRef.current;
if (container && scrollPositionRef.current !== null) {
const { scrollHeight, clientHeight } = container;
const maxScrollTop = scrollHeight - clientHeight;
container.scrollTop = Math.min(maxScrollTop, scrollPositionRef.current);
}
}, [data]);
const containerClasses = isLoading && pageNumber === 1
? 'flex flex-col gap-2 text-gray-100 text-sm h-full justify-center items-center'
: 'flex flex-col gap-2 text-gray-100 text-sm';
return (
<div className="dark hidden bg-gray-900 md:fixed md:inset-y-0 md:flex md:w-[260px] md:flex-col">
<div className="flex h-full min-h-0 flex-col ">
<div className="scrollbar-trigger flex h-full w-full flex-1 items-start border-white/20">
<nav className="flex h-full flex-1 flex-col space-y-1 p-2">
<NewChat />
<div
className={`-mr-2 flex-1 flex-col overflow-y-auto ${
isHovering ? '' : 'scrollbar-transparent'
} border-b border-white/20`}
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
ref={containerRef}
>
<div className={containerClasses}>
{isLoading && pageNumber === 1 ? (
<Spinner />
) : (
<Conversations
conversations={convos}
conversationId={conversationId}
showMore={showMore}
pageNumber={pageNumber}
/>
)}
</div>
</div>
<NavLinks />
</nav>
</div>
</div>
</div>
);
}