Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Improve UX for mobile user #479

Merged
merged 5 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@typescript-eslint/no-explicit-any": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"warn", // or "error"
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
Expand Down
3 changes: 2 additions & 1 deletion cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ export default defineConfig({
bundler: 'vite'
}
},
viewportWidth: 1200,

e2e: {
supportFile: false,
defaultCommandTimeout: 10000,
video: false,
baseUrl: 'http://127.0.0.1:8000',
setupNodeEvents(on, config) {
setupNodeEvents(on) {
on('task', {
log(message) {
console.log(message);
Expand Down
7 changes: 5 additions & 2 deletions cypress/e2e/conversations/spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ describe('Conversations', () => {
cy.intercept('GET', '/project/settings', {
statusCode: 200,
body: {
ui: {},
ui: {
show_readme_as_default: true
},
userEnv: [],
dataPersistence: true,
markdown: 'foo'
markdown: 'foo',
chatProfiles: []
}
}).as('getSettings');

Expand Down
23 changes: 15 additions & 8 deletions frontend/src/components/organisms/chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ const Chat = () => {
[askUser, user, replyMessage]
);

const tasklist = tasklists.at(-1);
const enableMultiModalUpload = !disabled && pSettings?.features.multi_modal;
const tasklist = tasklists[tasklists.length - 1];
const enableMultiModalUpload = !disabled && pSettings?.features?.multi_modal;

return (
<Box
Expand All @@ -156,17 +156,24 @@ const Chat = () => {
</>
) : null}
<SideView>
<TaskList tasklist={tasklist} isMobile={true} />

<Box my={1} />
{error && (
<Alert id="session-error" severity="error">
Could not reach the server.
</Alert>
<Box
sx={{
width: '100%',
maxWidth: '60rem',
mx: 'auto',
my: 2
}}
>
<Alert sx={{ mx: 2 }} id="session-error" severity="error">
Could not reach the server.
</Alert>
</Box>
)}
<TaskList tasklist={tasklist} isMobile={true} />
<ErrorBoundary>
<ChatProfiles />

{!messages.length && pSettings?.ui.show_readme_as_default ? (
<WelcomeScreen />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const UploadButton = ({
options: { noDrag: true }
});

if (!upload || !pSettings?.features.multi_modal) return null;
if (!upload || !pSettings?.features?.multi_modal) return null;
const { getRootProps, getInputProps, uploading } = upload;

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useRecoilState } from 'recoil';

import KeyboardDoubleArrowRightIcon from '@mui/icons-material/KeyboardDoubleArrowRight';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';

import { settingsState } from 'state/settings';

const OpenChatHistoryButton = ({ mode }: { mode: 'mobile' | 'desktop' }) => {
const [settings, setSettings] = useRecoilState(settingsState);
const isDesktop = mode === 'desktop';

return !settings.isChatHistoryOpen ? (
<Box
sx={
isDesktop
? {
zIndex: 1,
mt: 1,
ml: 1,
display: 'none',
'@media (min-width: 66rem)': {
position: 'absolute',
display: 'block'
}
}
: {}
}
>
<IconButton
sx={{
borderRadius: isDesktop ? 1 : 8,
backgroundColor: (theme) => theme.palette.background.paper
}}
onClick={() =>
setSettings((prev) => ({
...prev,
isChatHistoryOpen: true
}))
}
>
<KeyboardDoubleArrowRightIcon />
</IconButton>
</Box>
) : null;
};

export default OpenChatHistoryButton;
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { useEffect, useRef, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';

import KeyboardDoubleArrowLeftIcon from '@mui/icons-material/KeyboardDoubleArrowLeft';
import KeyboardDoubleArrowRightIcon from '@mui/icons-material/KeyboardDoubleArrowRight';
import Box from '@mui/material/Box';
import Drawer from '@mui/material/Drawer';
import IconButton from '@mui/material/IconButton';
import Stack from '@mui/material/Stack';
Expand All @@ -20,6 +18,7 @@ import {
conversationsHistoryState
} from 'state/conversations';
import { projectSettingsState } from 'state/project';
import { settingsState } from 'state/settings';
import { accessTokenState } from 'state/user';

import { ConversationsHistoryList } from './ConversationsHistoryList';
Expand All @@ -31,15 +30,15 @@ const BATCH_SIZE = 20;
let _scrollTop = 0;

const _ConversationsHistorySidebar = () => {
const isMobile = useMediaQuery('(max-width:800px)');
const isMobile = useMediaQuery('(max-width:66rem)');

const [conversations, setConversations] = useRecoilState(
conversationsHistoryState
);
const accessToken = useRecoilValue(accessTokenState);
const filters = useRecoilValue(conversationsFiltersState);
const [settings, setSettings] = useRecoilState(settingsState);

const [open, setOpen] = useState(true);
const [shouldLoadMore, setShouldLoadMore] = useState(false);
const [error, setError] = useState<string | undefined>(undefined);
const [prevFilters, setPrevFilters] =
Expand Down Expand Up @@ -98,10 +97,17 @@ const _ConversationsHistorySidebar = () => {
}
};

const setChatHistoryOpen = (open: boolean) =>
setSettings((prev) => ({ ...prev, isChatHistoryOpen: open }));

useEffect(() => {
if (ref.current) {
ref.current.scrollTop = _scrollTop;
}

if (isMobile) {
setChatHistoryOpen(false);
}
}, []);

useEffect(() => {
Expand Down Expand Up @@ -135,15 +141,15 @@ const _ConversationsHistorySidebar = () => {
<Drawer
className="chat-history-drawer"
anchor="left"
open={open}
open={settings.isChatHistoryOpen}
variant={isMobile ? 'temporary' : 'persistent'}
hideBackdrop
PaperProps={{
ref: ref,
onScroll: handleScroll
}}
sx={{
width: open ? DRAWER_WIDTH : 0,
width: settings.isChatHistoryOpen ? DRAWER_WIDTH : 0,
'& .MuiDrawer-paper': {
position: 'inherit',
gap: 1,
Expand Down Expand Up @@ -171,8 +177,8 @@ const _ConversationsHistorySidebar = () => {
>
Chat History
</Typography>
<IconButton edge="end" onClick={() => setOpen(false)}>
<KeyboardDoubleArrowLeftIcon sx={{ color: 'grey.500' }} />
<IconButton edge="end" onClick={() => setChatHistoryOpen(false)}>
<KeyboardDoubleArrowLeftIcon sx={{ color: 'text.primary' }} />
</IconButton>
</Stack>
<Filters />
Expand All @@ -186,25 +192,6 @@ const _ConversationsHistorySidebar = () => {
/>
) : null}
</Drawer>
<Box
sx={{
position: 'absolute',
mt: 1,
ml: 1,
zIndex: !open ? 1 : -1,
opacity: !open ? 1 : 0
}}
>
<IconButton
sx={{
borderRadius: 1,
backgroundColor: (theme) => theme.palette.background.paper
}}
onClick={() => setOpen(true)}
>
<KeyboardDoubleArrowRightIcon />
</IconButton>
</Box>
</>
);
};
Expand Down
32 changes: 20 additions & 12 deletions frontend/src/components/organisms/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import {
IconButton,
Menu,
Stack,
Theme,
Toolbar,
useTheme
Toolbar
} from '@mui/material';
import useMediaQuery from '@mui/material/useMediaQuery';

Expand All @@ -23,8 +21,12 @@ import UserButton from 'components/atoms/buttons/userButton';
import { Logo } from 'components/atoms/logo';
import NewChatButton from 'components/molecules/newChatButton';

import { useAuth } from 'hooks/auth';

import { projectSettingsState } from 'state/project';

import OpenChatHistoryButton from './conversationsHistory/sidebar/OpenChatHistoryButton';

interface INavItem {
to: string;
label: string;
Expand Down Expand Up @@ -58,12 +60,13 @@ function NavItem({ to, label }: INavItem) {
}

interface NavProps {
dataPersistence?: boolean;
hasReadme?: boolean;
}

function Nav({ hasReadme }: NavProps) {
function Nav({ dataPersistence, hasReadme }: NavProps) {
const location = useLocation();
const theme = useTheme();
const { isAuthenticated } = useAuth();
const [open, setOpen] = useState(false);
const ref = useRef<any>();

Expand All @@ -73,16 +76,15 @@ function Nav({ hasReadme }: NavProps) {
anchorEl = ref.current;
}

const matches = useMediaQuery(theme.breakpoints.down('md'));

const isMobile = useMediaQuery('(max-width: 66rem)');
const tabs = [{ to: '/', label: 'Chat' }];

if (hasReadme) {
tabs.push({ to: '/readme', label: 'Readme' });
}

const nav = (
<Stack direction={matches ? 'column' : 'row'} spacing={1}>
<Stack direction={isMobile ? 'column' : 'row'} spacing={1}>
{tabs.map((t) => {
const active = location.pathname === t.to;
return (
Expand All @@ -94,7 +96,7 @@ function Nav({ hasReadme }: NavProps) {
</Stack>
);

if (matches) {
if (isMobile) {
return (
<>
<IconButton
Expand All @@ -105,6 +107,9 @@ function Nav({ hasReadme }: NavProps) {
>
<MenuIcon />
</IconButton>
{isAuthenticated && dataPersistence ? (
<OpenChatHistoryButton mode="mobile" />
) : null}
<Menu
autoFocus
anchorEl={anchorEl}
Expand Down Expand Up @@ -139,7 +144,7 @@ function Nav({ hasReadme }: NavProps) {

export default function Header() {
const pSettings = useRecoilValue(projectSettingsState);
const matches = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'));
const matches = useMediaQuery('(max-width: 66rem)');

return (
<AppBar elevation={0} color="transparent" position="static">
Expand All @@ -153,9 +158,12 @@ export default function Header() {
borderBottomColor: (theme) => theme.palette.divider
}}
>
<Stack alignItems="center" direction={'row'} gap={3}>
<Stack alignItems="center" direction={'row'} gap={!matches ? 3 : 1}>
{!matches ? <Logo style={{ maxHeight: '25px' }} /> : null}
<Nav hasReadme={!!pSettings?.markdown} />
<Nav
dataPersistence={pSettings?.dataPersistence}
hasReadme={!!pSettings?.markdown}
/>
</Stack>
<Stack
alignItems="center"
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/pages/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useRecoilValue } from 'recoil';
import { Alert, Box, Stack } from '@mui/material';

import { ConversationsHistorySidebar } from 'components/organisms/conversationsHistory/sidebar';
import OpenChatHistoryButton from 'components/organisms/conversationsHistory/sidebar/OpenChatHistoryButton';
import Header from 'components/organisms/header';

import { useAuth } from 'hooks/auth';
Expand Down Expand Up @@ -47,6 +48,7 @@ const Page = ({ children }: Props) => {
) : (
<Stack direction="row" height="100%" width="100%" overflow="auto">
<ConversationsHistorySidebar />
<OpenChatHistoryButton mode={'desktop'} />
{children}
</Stack>
)}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/state/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const defaultSettingsState = {
defaultCollapseContent: true,
expandAll: false,
hideCot: false,
isChatHistoryOpen: true,
theme
};

Expand All @@ -24,6 +25,7 @@ export const settingsState = atom<{
expandAll: boolean;
hideCot: boolean;
theme: ThemeVariant;
isChatHistoryOpen: boolean;
}>({
key: 'AppSettings',
default: defaultSettingsState
Expand Down
5 changes: 0 additions & 5 deletions libs/components/hooks/useChat/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,3 @@ export const firstUserMessageState = atom<IMessage | undefined>({
key: 'FirstUserMessage',
default: undefined
});

export const chatProfile = atom<string | undefined>({
key: 'ChatProfile',
default: undefined
});