Skip to content

Commit

Permalink
update react contoso to use chatlist per PG (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
seekdavidlee authored Feb 6, 2024
1 parent 47708ad commit 431bd2e
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 52 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"watch:chat": "npm-run-all --parallel watch:chat-library watch:chat-library-deps watch:chat-test-app",
"watch:components": "lerna run --parallel build:watch --scope @microsoft/mgt-components --include-dependents --include-dependencies",
"watch:react-contoso": "lerna run start --scope react-contoso",
"watch:react": "npm-run-all --parallel watch:components watch:react-contoso",
"watch:react": "NODE_OPTIONS=--max_old_space_size=8192 npm-run-all --parallel watch:components watch:react-contoso",
"prettier:base": "prettier --parser typescript",
"prettier:check": "npm run prettier:base -- --check \"packages/**/*.{ts,tsx}\"",
"prettier:write": "npm run prettier:base -- --write \"packages/**/*.{ts,tsx}\"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export type GraphChatListClient = Pick<MessageThreadProps, 'userId'> & {
| 'loading chats'
| 'no chats'
| 'chats loaded'
| 'error';
| 'error'
| 'chats read';
chatThreads: GraphChatThread[];
moreChatThreadsToLoad: boolean | undefined;
} & Pick<ErrorBarProps, 'activeErrorMessages'>;
Expand Down Expand Up @@ -243,6 +244,7 @@ class StatefulGraphChatListClient implements StatefulClient<GraphChatListClient>
// mark as read after chat thread is found in current state
const markedChatThreads: string[] = [];
this.notifyStateChange((draft: GraphChatListClient) => {
draft.status = 'chats read';
draft.chatThreads = this._state.chatThreads.map((chatThread: GraphChatThread) => {
if (chatThread.id && readChatThreads.includes(chatThread.id) && !chatThread.isRead) {
markedChatThreads.push(chatThread.id);
Expand Down
120 changes: 70 additions & 50 deletions samples/react-contoso/src/pages/ChatPage.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import * as React from 'react';
import { memo, useCallback } from 'react';
import { PageHeader } from '../components/PageHeader';
import { Get } from '@microsoft/mgt-react';
import { Loading } from '../components/Loading';
import {
shorthands,
makeStyles,
mergeClasses,
Button,
Dialog,
DialogTrigger,
DialogSurface,
DialogBody,
DialogTitle
} from '@fluentui/react-components';
import { Chat as GraphChat } from '@microsoft/microsoft-graph-types';
import { Chat, NewChat } from '@microsoft/mgt-chat';
import ChatListTemplate from './Chats/ChatListTemplate';
import { Chat as GraphChat, ChatMessage } from '@microsoft/microsoft-graph-types';
import { ChatList, Chat, NewChat, ChatListButtonItem, ChatListMenuItem } from '@microsoft/mgt-chat';
import { Compose24Filled, Compose24Regular, bundleIcon } from '@fluentui/react-icons';

const ChatAddIconBundle = bundleIcon(Compose24Filled, Compose24Regular);

export const ChatAddIcon = (): JSX.Element => {
const iconColor = 'var(--colorBrandForeground2)';
return <ChatAddIconBundle color={iconColor} />;
};

const useStyles = makeStyles({
container: {
Expand Down Expand Up @@ -59,31 +63,69 @@ const useStyles = makeStyles({
}
});

const getPreviousDate = (months: number) => {
const date = new Date();
date.setMonth(date.getMonth() - months);
return date.toISOString();
};
interface ChatListWrapperProps {
onSelected: (e: GraphChat) => void;
onNewChat: () => void;
selectedChatId: string | undefined;
}

const nextResourceUrl = () =>
`me/chats?$expand=members,lastMessagePreview&$orderBy=lastMessagePreview/createdDateTime desc&$filter=viewpoint/lastMessageReadDateTime ge ${getPreviousDate(
9
)}`;
const ChatListWrapper = memo(({ onSelected, onNewChat, selectedChatId }: ChatListWrapperProps) => {
const buttons: ChatListButtonItem[] = [
{
renderIcon: () => <ChatAddIcon />,
onClick: onNewChat
}
];
const menus: ChatListMenuItem[] = [
{
displayText: 'My custom menu item',
onClick: () => console.log('My custom menu item clicked')
}
];
const onAllMessagesRead = useCallback((chatIds: string[]) => {
console.log(`Number of chats marked as read: ${chatIds.length}`);
}, []);
const onLoaded = useCallback(() => {
console.log('Chat threads loaded.');
}, []);
const onMessageReceived = useCallback((msg: ChatMessage) => {
console.log('SampleChatLog: Message received', msg);
}, []);

return (
<ChatList
selectedChatId={selectedChatId}
onLoaded={onLoaded}
chatThreadsPerPage={10}
menuItems={menus}
buttonItems={buttons}
onSelected={onSelected}
onMessageReceived={onMessageReceived}
onAllMessagesRead={onAllMessagesRead}
/>
);
});

const ChatPage: React.FunctionComponent = () => {
const styles = useStyles();
const [chatId, setChatId] = React.useState<string>('');
const [isNewChatOpen, setIsNewChatOpen] = React.useState(false);

const [resourceUrl, setResourceUrl] = React.useState(nextResourceUrl);
const onChatSelected = React.useCallback((e: GraphChat) => {
if (chatId !== e.id) {
setChatId(e.id ?? '');
}
}, []);

const [selectedChat, setSelectedChat] = React.useState<GraphChat>();
const [isNewChatOpen, setIsNewChatOpen] = React.useState(false);
const onNewChat = React.useCallback(() => {
setIsNewChatOpen(true);
}, []);

const onChatCreated = (e: GraphChat) => {
if (e.id !== selectedChat?.id && isNewChatOpen) {
setIsNewChatOpen(false);
setResourceUrl(nextResourceUrl);
setIsNewChatOpen(false);
if (chatId !== e.id) {
setChatId(e.id ?? '');
}
setSelectedChat(e);
};

return (
Expand All @@ -97,44 +139,22 @@ const ChatPage: React.FunctionComponent = () => {
<div className={mergeClasses(styles.panels, styles.main)}>
<div className={styles.newChat}>
<Dialog open={isNewChatOpen}>
<DialogTrigger disableButtonEnhancement>
<Button appearance="primary" onClick={() => setIsNewChatOpen(true)}>
New Chat
</Button>
</DialogTrigger>
<DialogSurface className={styles.dialogSurface}>
<DialogBody className={styles.dialog}>
<DialogTitle>New Chat</DialogTitle>
<NewChat
onChatCreated={onChatCreated}
onCancelClicked={() => {
setIsNewChatOpen(false);
}}
></NewChat>
<NewChat onChatCreated={onChatCreated} onCancelClicked={() => setIsNewChatOpen(false)}></NewChat>
</DialogBody>
</DialogSurface>
</Dialog>
</div>
<ChatList onChatSelected={setSelectedChat} resourceUrl={resourceUrl}></ChatList>
<ChatListWrapper selectedChatId={chatId} onSelected={onChatSelected} onNewChat={onNewChat} />
</div>
<div className={styles.side}>
<Chat chatId={chatId} />
</div>
<div className={styles.side}>{selectedChat && <Chat chatId={selectedChat.id!}></Chat>}</div>
</div>
</>
);
};

interface ChatListProps {
onChatSelected: (e: GraphChat) => void;
resourceUrl: string;
}

const ChatList = React.memo((props: ChatListProps) => {
return (
<Get resource={props.resourceUrl} scopes={['chat.read']}>
<ChatListTemplate template="default" onChatSelected={props.onChatSelected}></ChatListTemplate>
<Loading template="loading" message={'Loading your chats...'}></Loading>
</Get>
);
});

export default ChatPage;

0 comments on commit 431bd2e

Please sign in to comment.