Merge pull request #122 from arc53/taylorcopy

Taylorcopy
pull/123/head
Pavel 2 years ago committed by GitHub
commit 8f0f664393
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,8 +16,9 @@ module.exports = {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['react'],
plugins: ['react', 'unused-imports'],
rules: {
'unused-imports/no-unused-imports': 'error',
'react/react-in-jsx-scope': 'off',
'prettier/prettier': [
'error',

@ -1805,6 +1805,21 @@
}
}
},
"eslint-plugin-unused-imports": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz",
"integrity": "sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==",
"dev": true,
"requires": {
"eslint-rule-composer": "^0.3.0"
}
},
"eslint-rule-composer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz",
"integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==",
"dev": true
},
"eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",

@ -40,6 +40,7 @@
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-unused-imports": "^2.0.0",
"husky": "^8.0.0",
"lint-staged": "^13.1.1",
"postcss": "^8.4.21",

@ -1,3 +1,4 @@
import { useState } from 'react';
import { NavLink } from 'react-router-dom';
import Arrow1 from './assets/arrow.svg';
import Message from './assets/message.svg';
@ -8,15 +9,14 @@ import Link from './assets/link.svg';
import { ActiveState } from './models/misc';
import APIKeyModal from './preferences/APIKeyModal';
import SelectDocsModal from './preferences/SelectDocsModal';
import { useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import {
selectApiKeyStatus,
selectSelectedDocs,
selectSelectedDocsStatus,
selectSourceDocs,
setSelectedDocs,
} from './preferences/preferenceSlice';
import { useState } from 'react';
//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
export default function Navigation({
navState,
@ -25,6 +25,12 @@ export default function Navigation({
navState: ActiveState;
setNavState: (val: ActiveState) => void;
}) {
const dispatch = useDispatch();
const docs = useSelector(selectSourceDocs);
const selectedDocs = useSelector(selectSelectedDocs);
const [isDocsListOpen, setIsDocsListOpen] = useState(false);
const isApiKeySet = useSelector(selectApiKeyStatus);
const [apiKeyModalState, setApiKeyModalState] = useState<ActiveState>(
isApiKeySet ? 'INACTIVE' : 'ACTIVE',
@ -39,9 +45,9 @@ export default function Navigation({
<div
className={`${
navState === 'INACTIVE' && '-ml-96 md:-ml-[14rem] lg:-ml-80'
} fixed z-10 flex h-full w-72 flex-col border-r-2 border-gray-100 bg-gray-50 transition-all duration-200 lg:w-96`}
} fixed z-10 flex h-full w-72 flex-col border-r-2 bg-gray-50 transition-all duration-200 lg:w-96`}
>
<div className={'h-16 w-full border-b-2 border-gray-100'}>
<div className={'h-16 w-full border-b-2'}>
<button
className="float-right mr-5 mt-5 h-5 w-5"
onClick={() =>
@ -70,8 +76,50 @@ export default function Navigation({
</NavLink>
<div className="flex-grow border-b-2 border-gray-100"></div>
<div className="flex flex-col gap-2 border-b-2 border-gray-100 py-2">
<div className="flex flex-grow flex-col-reverse border-b-2">
<div className="relative my-4 px-6 ">
<div
className="h-12 w-full cursor-pointer rounded-md border-2"
onClick={() => setIsDocsListOpen(!isDocsListOpen)}
>
{selectedDocs && (
<p className="my-3 mx-4">
{selectedDocs.name} {selectedDocs.version}
</p>
)}
</div>
{isDocsListOpen && (
<div className="absolute top-12 left-0 right-0 mx-6 max-h-52 overflow-y-scroll bg-white shadow-lg">
{docs ? (
docs.map((doc, index) => {
if (doc.model) {
return (
<div
key={index}
onClick={() => {
dispatch(setSelectedDocs(doc));
setIsDocsListOpen(false);
}}
className="h-10 w-full cursor-pointer border-x-2 border-b-2 hover:bg-gray-100"
>
<p className="ml-5 py-3">
{doc.name} {doc.version}
</p>
</div>
);
}
})
) : (
<div className="h-10 w-full cursor-pointer border-x-2 border-b-2 hover:bg-gray-100">
<p className="ml-5 py-3">No default documentation.</p>
</div>
)}
</div>
)}
</div>
<p className="ml-6 font-bold text-jet">Source Docs</p>
</div>
<div className="flex flex-col gap-2 border-b-2 py-2">
<div
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
onClick={() => {
@ -81,21 +129,9 @@ export default function Navigation({
<img src={Key} alt="key" className="ml-2 w-6" />
<p className="my-auto text-eerie-black">Reset Key</p>
</div>
<div
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
onClick={() => {
setSelectedDocsModalState('ACTIVE');
}}
>
<img src={Link} alt="key" className="ml-2 w-5" />
<p className="my-auto text-eerie-black">
Select Source Documentation
</p>
</div>
</div>
<div className="flex h-48 flex-col border-b-2 border-gray-100">
<div className="flex flex-col gap-2 border-b-2 py-2">
<NavLink
to="/about"
className={({ isActive }) =>
@ -108,15 +144,25 @@ export default function Navigation({
<p className="my-auto text-eerie-black">About</p>
</NavLink>
<div className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100">
<a
href="https://discord.gg/WHJdfbQDR4"
target="_blank"
rel="noreferrer"
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
>
<img src={Link} alt="link" className="ml-2 w-5" />
<p className="my-auto text-eerie-black">Discord</p>
</div>
</a>
<div className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100">
<a
href="https://github.com/arc53/DocsGPT"
target="_blank"
rel="noreferrer"
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
>
<img src={Link} alt="link" className="ml-2 w-5" />
<p className="my-auto text-eerie-black">Github</p>
</div>
</a>
</div>
</div>
<button

@ -1,7 +1,7 @@
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { ActiveState } from '../models/misc';
import { setApiKey } from './preferenceSlice';
import { selectApiKey, setApiKey } from './preferenceSlice';
export default function APIKeyModal({
modalState,
@ -13,7 +13,8 @@ export default function APIKeyModal({
isCancellable?: boolean;
}) {
const dispatch = useDispatch();
const [key, setKey] = useState('');
const apiKey = useSelector(selectApiKey);
const [key, setKey] = useState(apiKey);
const [isError, setIsError] = useState(false);
function handleSubmit() {
@ -22,13 +23,12 @@ export default function APIKeyModal({
} else {
dispatch(setApiKey(key));
setModalState('INACTIVE');
setKey('');
setIsError(false);
}
}
function handleCancel() {
setKey('');
setKey(apiKey);
setIsError(false);
setModalState('INACTIVE');
}

@ -5,6 +5,7 @@ import {
setSelectedDocs,
setSourceDocs,
selectSourceDocs,
selectSelectedDocs,
} from './preferenceSlice';
import { getDocs, Doc } from './selectDocsApi';
@ -19,7 +20,10 @@ export default function APIKeyModal({
}) {
const dispatch = useDispatch();
const docs = useSelector(selectSourceDocs);
const [localSelectedDocs, setLocalSelectedDocs] = useState<Doc | null>(null);
const selectedDoc = useSelector(selectSelectedDocs);
const [localSelectedDocs, setLocalSelectedDocs] = useState<Doc | null>(
selectedDoc,
);
const [isDocsListOpen, setIsDocsListOpen] = useState(false);
const [isError, setIsError] = useState(false);
@ -29,13 +33,11 @@ export default function APIKeyModal({
} else {
dispatch(setSelectedDocs(localSelectedDocs));
setModalState('INACTIVE');
setLocalSelectedDocs(null);
setIsError(false);
}
}
function handleCancel() {
setLocalSelectedDocs(null);
setIsError(false);
setModalState('INACTIVE');
}
@ -57,7 +59,7 @@ export default function APIKeyModal({
>
<article className="mx-auto mt-24 flex w-[90vw] max-w-lg flex-col gap-4 rounded-lg bg-white p-6 shadow-lg">
<p className="text-xl text-jet">Select Source Documentation</p>
<p className="text-md leading-6 text-gray-500">
<p className="text-lg leading-5 text-gray-500">
Please select the library of documentation that you would like to use
with our app.
</p>

@ -0,0 +1,49 @@
export type Doc = {
name: string;
language: string;
version: string;
description: string;
fullName: string;
dat: string;
docLink: string;
model: string;
};
//Fetches all JSON objects from the source. We only use the objects with the "model" property in SelectDocsModal.tsx. Hopefully can clean up the source file later.
export async function getDocs(): Promise<Doc[] | null> {
try {
const response = await fetch(
'https://d3dg1063dc54p9.cloudfront.net/combined.json',
);
const data = await response.json();
const docs: Doc[] = [];
data.forEach((doc: object) => {
docs.push(doc as Doc);
});
return docs;
} catch (error) {
console.log(error);
return null;
}
}
export function getLocalApiKey(): string | null {
const key = localStorage.getItem('DocsGPTApiKey');
return key;
}
export function getLocalRecentDocs(): string | null {
const doc = localStorage.getItem('DocsGPTRecentDocs');
return doc;
}
export function setLocalApiKey(key: string): void {
localStorage.setItem('DocsGPTApiKey', key);
}
export function setLocalRecentDocs(doc: Doc): void {
localStorage.setItem('DocsGPTRecentDocs', JSON.stringify(doc));
}

@ -1,6 +1,10 @@
import { createSlice } from '@reduxjs/toolkit';
import { Doc } from './selectDocsApi';
import store from '../store';
import {
createListenerMiddleware,
createSlice,
isAnyOf,
} from '@reduxjs/toolkit';
import { Doc, setLocalApiKey, setLocalRecentDocs } from './preferenceApi';
import { RootState } from '../store';
interface Preference {
apiKey: string;
@ -33,7 +37,23 @@ export const prefSlice = createSlice({
export const { setApiKey, setSelectedDocs, setSourceDocs } = prefSlice.actions;
export default prefSlice.reducer;
type RootState = ReturnType<typeof store.getState>;
export const prefListenerMiddleware = createListenerMiddleware();
prefListenerMiddleware.startListening({
matcher: isAnyOf(setApiKey),
effect: (action, listenerApi) => {
setLocalApiKey((listenerApi.getState() as RootState).preference.apiKey);
},
});
prefListenerMiddleware.startListening({
matcher: isAnyOf(setSelectedDocs),
effect: (action, listenerApi) => {
setLocalRecentDocs(
(listenerApi.getState() as RootState).preference.selectedDocs ??
([] as unknown as Doc),
);
},
});
export const selectApiKey = (state: RootState) => state.preference.apiKey;
export const selectApiKeyStatus = (state: RootState) =>
@ -42,3 +62,5 @@ export const selectSelectedDocsStatus = (state: RootState) =>
!!state.preference.selectedDocs;
export const selectSourceDocs = (state: RootState) =>
state.preference.sourceDocs;
export const selectSelectedDocs = (state: RootState) =>
state.preference.selectedDocs;

@ -1,33 +1,33 @@
//Exporting Doc type from here since its the first place its used and seems needless to make an entire file for it.
export type Doc = {
name: string;
language: string;
version: string;
description: string;
fullName: string;
dat: string;
docLink: string;
model: string;
};
//Fetches all JSON objects from the source. We only use the objects with the "model" property in SelectDocsModal.tsx. Hopefully can clean up the source file later.
export async function getDocs(): Promise<Doc[] | null> {
try {
//Fetch default source docs
const response = await fetch(
'https://d3dg1063dc54p9.cloudfront.net/combined.json',
);
const data = await response.json();
//Create array of Doc objects
const docs: Doc[] = [];
data.forEach((doc: object) => {
docs.push(doc as Doc);
});
return docs;
} catch (error) {
return null;
}
}
//Exporting Doc type from here since its the first place its used and seems needless to make an entire file for it.
export type Doc = {
name: string;
language: string;
version: string;
description: string;
fullName: string;
dat: string;
docLink: string;
model: string;
};
//Fetches all JSON objects from the source. We only use the objects with the "model" property in SelectDocsModal.tsx. Hopefully can clean up the source file later.
export async function getDocs(): Promise<Doc[] | null> {
try {
//Fetch default source docs
const response = await fetch(
'https://d3dg1063dc54p9.cloudfront.net/combined.json',
);
const data = await response.json();
//Create array of Doc objects
const docs: Doc[] = [];
data.forEach((doc: object) => {
docs.push(doc as Doc);
});
return docs;
} catch (error) {
return null;
}
}

@ -1,13 +1,31 @@
import { configureStore } from '@reduxjs/toolkit';
import { conversationSlice } from './conversation/conversationSlice';
import { prefSlice } from './preferences/preferenceSlice';
import {
prefListenerMiddleware,
prefSlice,
} from './preferences/preferenceSlice';
const key = localStorage.getItem('DocsGPTApiKey');
const doc = localStorage.getItem('DocsGPTRecentDocs');
const store = configureStore({
preloadedState: {
preference: {
apiKey: key ?? '',
selectedDocs: doc !== null ? JSON.parse(doc) : null,
sourceDocs: null,
},
},
reducer: {
preference: prefSlice.reducer,
conversation: conversationSlice.reducer,
},
middleware: (getDefaultMiddleware) => [
...getDefaultMiddleware(),
prefListenerMiddleware.middleware,
],
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;

Loading…
Cancel
Save