Skip to content

Commit

Permalink
Great working Shared Links history
Browse files Browse the repository at this point in the history
  • Loading branch information
enricoros committed Oct 19, 2023
1 parent e81acdf commit 188a18d
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 148 deletions.
8 changes: 3 additions & 5 deletions src/apps/chat/components/applayout/ChatDrawerItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import FileUploadIcon from '@mui/icons-material/FileUpload';

import { OpenAIIcon } from '~/common/components/icons/OpenAIIcon';
import { setLayoutDrawerAnchor } from '~/common/layout/store-applayout';
import { closeLayoutDrawerMenu } from '~/common/layout/store-applayout';
import { useChatStore } from '~/common/state/store-chats';
import { useUIPreferencesStore } from '~/common/state/store-ui';

Expand Down Expand Up @@ -45,21 +45,19 @@ export function ChatDrawerItems(props: {
const singleChat = totalConversations === 1;
const softMaxReached = totalConversations >= 50;

const closeDrawerMenu = () => setLayoutDrawerAnchor(null);

const handleNew = () => {
// if the first in the stack is a new conversation, just activate it
if (topNewConversationId)
setActiveConversationId(topNewConversationId);
else
createConversation();
closeDrawerMenu();
closeLayoutDrawerMenu();
};

const handleConversationActivate = React.useCallback((conversationId: string, closeMenu: boolean) => {
setActiveConversationId(conversationId);
if (closeMenu)
closeDrawerMenu();
closeLayoutDrawerMenu();
}, [setActiveConversationId]);

const handleConversationDelete = React.useCallback((conversationId: string) => {
Expand Down
6 changes: 3 additions & 3 deletions src/apps/chat/trade/trade-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { shallow } from 'zustand/shallow';
interface ChatLinkItem {
chatTitle?: string;
objectId: string;
createdAt: Date;
expiresAt: Date | null;
createdAt: string;
expiresAt: string | null;
deletionKey: string;
}

Expand All @@ -33,7 +33,7 @@ const useTradeStore = create<TradeStore>()(

chatLinkItems: [],
addChatLinkItem: (chatTitle: string | undefined, objectId: string, createdAt: Date, expiresAt: Date | null, deletionKey: string) => set(state => ({
chatLinkItems: [...state.chatLinkItems, { chatTitle, objectId, createdAt, expiresAt, deletionKey }],
chatLinkItems: [...state.chatLinkItems, { chatTitle, objectId, createdAt: createdAt.toISOString(), expiresAt: expiresAt?.toISOString() ?? null, deletionKey }],
})),
removeChatLinkItem: (objectId: string) => set(state => ({
chatLinkItems: state.chatLinkItems.filter(item => item.objectId !== objectId),
Expand Down
13 changes: 13 additions & 0 deletions src/apps/link/AppChatLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import { useQuery } from '@tanstack/react-query';
import { Box, Typography } from '@mui/joy';

import { createConversationFromJsonV1 } from '../chat/trade/trade.client';
import { useHasChatLinkItems } from '../chat/trade/trade-store';

import { Brand } from '~/common/brand';
import { InlineError } from '~/common/components/InlineError';
import { LogoProgress } from '~/common/components/LogoProgress';
import { apiAsyncNode } from '~/common/util/trpc.client';
import { capitalizeFirstLetter } from '~/common/util/textUtils';
import { conversationTitle } from '~/common/state/store-chats';
import { useLayoutPluggable } from '~/common/layout/store-applayout';

import { AppChatLinkDrawerItems } from './AppChatLinkDrawerItems';
import { AppChatLinkMenuItems } from './AppChatLinkMenuItems';
import { ViewChatLink } from './ViewChatLink';


Expand Down Expand Up @@ -74,6 +78,15 @@ export function AppChatLink(props: { linkId: string }) {
refetchOnWindowFocus: false,
staleTime: 1000 * 60 * 60 * 24, // 24 hours
});
const hasLinkItems = useHasChatLinkItems();


// pluggable UI

const drawerItems = React.useMemo(() => <AppChatLinkDrawerItems />, []);
const menuItems = React.useMemo(() => <AppChatLinkMenuItems />, []);
useLayoutPluggable(null, hasLinkItems ? drawerItems : null, menuItems);


const pageTitle = (data?.conversation && conversationTitle(data.conversation)) || 'Chat Link';

Expand Down
68 changes: 68 additions & 0 deletions src/apps/link/AppChatLinkDrawerItems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as React from 'react';
import TimeAgo from 'react-timeago';

import { Box, ListDivider, ListItem, ListItemDecorator, MenuItem, Typography } from '@mui/joy';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';

import { useChatLinkItems } from '../chat/trade/trade-store';

import { Brand } from '~/common/brand';
import { Link } from '~/common/components/Link';
import { closeLayoutDrawerMenu } from '~/common/layout/store-applayout';
import { getChatLinkRelativePath, getHomeLink } from '~/common/routes';


/**
* Drawer Items are all the links already shared, for quick access.
* This is stores in the Trade Store (local storage).
*/
export function AppChatLinkDrawerItems() {

// external state
const chatLinkItems = useChatLinkItems()
.slice()
.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
const notEmpty = chatLinkItems.length > 0;

return <>

<MenuItem
onClick={closeLayoutDrawerMenu}
component={Link} href={getHomeLink()} noLinkStyle
>
<ListItemDecorator><ArrowBackIcon /></ListItemDecorator>
{Brand.Title.Base}
</MenuItem>

{notEmpty && <ListDivider />}

{notEmpty && <ListItem>
<Typography level='body-sm'>
Links shared by you
</Typography>
</ListItem>}

{notEmpty && <Box sx={{ overflowY: 'auto' }}>
{chatLinkItems.map(item => (

<MenuItem
key={'chat-link-' + item.objectId}
component={Link} href={getChatLinkRelativePath(item.objectId)} noLinkStyle
sx={{
display: 'flex', flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<Typography level='title-sm'>
{item.chatTitle || 'Untitled Chat'}
</Typography>
<Typography level='body-xs'>
<TimeAgo date={item.createdAt} />
</Typography>
</MenuItem>

))}
</Box>}
</>;

}
69 changes: 69 additions & 0 deletions src/apps/link/AppChatLinkMenuItems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as React from 'react';
import { shallow } from 'zustand/shallow';

import { MenuItem, Switch, Typography } from '@mui/joy';

import { useUIPreferencesStore } from '~/common/state/store-ui';


/**
* Menu Items are the settings for the chat.
*/
export function AppChatLinkMenuItems() {

// external state
const {
showSystemMessages, setShowSystemMessages,
renderMarkdown, setRenderMarkdown,
zenMode, setZenMode,
} = useUIPreferencesStore(state => ({
showSystemMessages: state.showSystemMessages, setShowSystemMessages: state.setShowSystemMessages,
renderMarkdown: state.renderMarkdown, setRenderMarkdown: state.setRenderMarkdown,
zenMode: state.zenMode, setZenMode: state.setZenMode,
}), shallow);


const handleRenderSystemMessageChange = (event: React.ChangeEvent<HTMLInputElement>) => setShowSystemMessages(event.target.checked);
const handleRenderMarkdownChange = (event: React.ChangeEvent<HTMLInputElement>) => setRenderMarkdown(event.target.checked);
const handleZenModeChange = (event: React.ChangeEvent<HTMLInputElement>) => setZenMode(event.target.checked ? 'cleaner' : 'clean');

const zenOn = zenMode === 'cleaner';


return <>

<MenuItem onClick={() => setShowSystemMessages(!showSystemMessages)} sx={{ justifyContent: 'space-between' }}>
<Typography>
System message
</Typography>
<Switch
checked={showSystemMessages} onChange={handleRenderSystemMessageChange}
// endDecorator={showSystemMessages ? 'On' : 'Off'}
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }}
/>
</MenuItem>

<MenuItem onClick={() => setRenderMarkdown(!renderMarkdown)} sx={{ justifyContent: 'space-between' }}>
<Typography>
Markdown
</Typography>
<Switch
checked={renderMarkdown} onChange={handleRenderMarkdownChange}
// endDecorator={renderMarkdown ? 'On' : 'Off'}
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }}
/>
</MenuItem>

<MenuItem onClick={() => setZenMode(zenOn ? 'clean' : 'cleaner')} sx={{ justifyContent: 'space-between' }}>
<Typography>
Zen
</Typography>
<Switch
checked={zenOn} onChange={handleZenModeChange}
// endDecorator={zenOn ? 'On' : 'Off'}
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }}
/>
</MenuItem>

</>;
}
135 changes: 2 additions & 133 deletions src/apps/link/ViewChatLink.tsx
Original file line number Diff line number Diff line change
@@ -1,134 +1,16 @@
import * as React from 'react';
import TimeAgo from 'react-timeago';
import { shallow } from 'zustand/shallow';
import { useRouter } from 'next/router';

import { Box, Button, Card, List, ListDivider, ListItem, ListItemDecorator, MenuItem, Switch, Tooltip, Typography } from '@mui/joy';
import { Box, Button, Card, List, ListItem, Tooltip, Typography } from '@mui/joy';
import TelegramIcon from '@mui/icons-material/Telegram';

import { ChatMessage } from '../chat/components/message/ChatMessage';
import { useChatLinkItems, useHasChatLinkItems } from '../chat/trade/trade-store';

import { Link } from '~/common/components/Link';
import { Brand } from '~/common/brand';
import { conversationTitle, DConversation, useChatStore } from '~/common/state/store-chats';
import { getChatLinkRelativePath, getHomeLink, navigateToChat } from '~/common/routes';
import { useLayoutPluggable } from '~/common/layout/store-applayout';
import { navigateToChat } from '~/common/routes';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';


/*const cssMagicSwapKeyframes = keyframes`
0% {
opacity: 0;
transform-origin: 0 100%;
transform: scale(0, 0) translate(-300px, 0px);
}
100% {
opacity: 1;
transform-origin: 100% 100%;
transform: scale(1, 1) translate(0px, 0px);
}`;
*/


/**
* Drawer Items are all the links already shared, for quick access.
* This is stores in the Trade Store (local storage).
*/
function AppChatLinkDrawerItems() {

// external state
const chatLinkItems = useChatLinkItems();
const hasChatLinks = chatLinkItems.length > 0;

return <>

<MenuItem component={Link} href={getHomeLink()} noLinkStyle>
<ListItemDecorator><ArrowBackIcon /></ListItemDecorator>
{Brand.Title.Base}
</MenuItem>

{hasChatLinks && <ListDivider />}

{hasChatLinks && <ListItem>
<Typography level='body-sm'>
Links shared by you
</Typography>
</ListItem>}

{hasChatLinks && <Box sx={{ overflowY: 'auto' }}>
{chatLinkItems.map(item => (

<MenuItem
key={'chat-link-' + item.objectId}
component={Link} href={getChatLinkRelativePath(item.objectId)} noLinkStyle
sx={{
display: 'flex', flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<Typography level='title-sm'>
{item.chatTitle || 'Untitled Chat'}
</Typography>
<Typography level='body-xs'>
<TimeAgo date={item.createdAt} />
</Typography>
</MenuItem>

))}
</Box>}
</>;

}


/**
* Menu Items are the settings for the chat.
*/
function AppChatLinkMenuItems() {

// external state
const {
renderMarkdown, setRenderMarkdown,
zenMode, setZenMode,
} = useUIPreferencesStore(state => ({
renderMarkdown: state.renderMarkdown, setRenderMarkdown: state.setRenderMarkdown,
zenMode: state.zenMode, setZenMode: state.setZenMode,
}), shallow);

const zenOn = zenMode === 'cleaner';

const handleRenderMarkdownChange = (event: React.ChangeEvent<HTMLInputElement>) => setRenderMarkdown(event.target.checked);

const handleZenModeChange = (event: React.ChangeEvent<HTMLInputElement>) => setZenMode(event.target.checked ? 'cleaner' : 'clean');

return <>

<MenuItem onClick={() => setRenderMarkdown(!renderMarkdown)} sx={{ justifyContent: 'space-between' }}>
<Typography>
Markdown
</Typography>
<Switch
checked={renderMarkdown} onChange={handleRenderMarkdownChange}
endDecorator={renderMarkdown ? 'On' : 'Off'}
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }}
/>
</MenuItem>

<MenuItem onClick={() => setZenMode(zenOn ? 'clean' : 'cleaner')} sx={{ justifyContent: 'space-between' }}>
<Typography>
Zen
</Typography>
<Switch
checked={zenOn} onChange={handleZenModeChange}
endDecorator={zenOn ? 'On' : 'Off'}
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }}
/>
</MenuItem>

</>;
}


/**
Expand All @@ -144,26 +26,13 @@ export function ViewChatLink(props: { conversation: DConversation, storedAt: Dat
const { push: routerPush } = useRouter();
const showSystemMessages = useUIPreferencesStore(state => state.showSystemMessages);
const hasExistingChat = useChatStore(state => state.conversations.some(c => c.id === props.conversation.id));
const hasLinkItems = useHasChatLinkItems();

// derived state
const messages = props.conversation.messages;
const filteredMessages = messages.filter(m => m.role !== 'system' || showSystemMessages);
const hasMessages = filteredMessages.length > 0;


// pluggable UI
const drawerItems = React.useMemo(() =>
hasLinkItems ? <AppChatLinkDrawerItems /> : null,
[hasLinkItems]);

const menuItems = React.useMemo(() =>
<AppChatLinkMenuItems />,
[]);

useLayoutPluggable(null, drawerItems, menuItems);


// Effect: Turn on Markdown (globally) if there are tables in the chat

React.useEffect(() => {
Expand Down
Loading

1 comment on commit 188a18d

@vercel
Copy link

@vercel vercel bot commented on 188a18d Oct 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

big-agi – ./

get.big-agi.com
big-agi-enricoros.vercel.app
big-agi-git-main-enricoros.vercel.app

Please sign in to comment.