-
Notifications
You must be signed in to change notification settings - Fork 303
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: adding support for chat scenarios in react-contoso (#2620)
Adding the Chat components Removing random text from the Chat component
- Loading branch information
1 parent
5cf7961
commit 46b0d0f
Showing
9 changed files
with
410 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
BROWSER="none" | ||
SKIP_PREFLIGHT_CHECK=true | ||
FAST_REFRESH = false | ||
FAST_REFRESH=false | ||
REACT_APP_SITE_NAME="Contoso Dashboard" | ||
REACT_APP_CLIENT_ID="ed072e38-e76e-45ae-ab76-073cb95495bb" | ||
REACT_APP_CLIENT_ID="00000000-0000-0000-0000-000000000000" | ||
REACT_APP_URL_AZURE_FUNCTION="https://contoso.azurewebsites.net" | ||
REACT_APP_BACKEND_CLIENT_ID="00000000-0000-0000-0000-000000000000" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import * as React 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'; | ||
|
||
const useStyles = makeStyles({ | ||
container: { | ||
display: 'flex', | ||
flexDirection: 'row' | ||
}, | ||
panels: { | ||
...shorthands.padding('10px') | ||
}, | ||
main: { | ||
display: 'flex', | ||
flexDirection: 'column', | ||
flexWrap: 'nowrap', | ||
width: '300px', | ||
minWidth: '300px', | ||
...shorthands.overflow('auto'), | ||
maxHeight: '80vh', | ||
borderRightColor: 'var(--neutral-stroke-rest)', | ||
borderRightStyle: 'solid', | ||
borderRightWidth: '1px' | ||
}, | ||
side: { | ||
display: 'flex', | ||
flexDirection: 'column', | ||
flexWrap: 'nowrap', | ||
width: '80%', | ||
...shorthands.overflow('auto'), | ||
maxHeight: '80vh', | ||
height: '100%' | ||
}, | ||
newChat: { | ||
paddingBottom: '10px', | ||
marginRight: '0px', | ||
marginLeft: 'auto' | ||
}, | ||
dialog: { | ||
display: 'block' | ||
} | ||
}); | ||
|
||
export const ChatPage: React.FunctionComponent = () => { | ||
const styles = useStyles(); | ||
const [selectedChat, setSelectedChat] = React.useState<GraphChat>(); | ||
const [isNewChatOpen, setIsNewChatOpen] = React.useState(false); | ||
|
||
const chatSelected = (e: GraphChat) => { | ||
if (e.id !== selectedChat?.id && isNewChatOpen) { | ||
setIsNewChatOpen(false); | ||
} | ||
setSelectedChat(e); | ||
}; | ||
|
||
return ( | ||
<> | ||
<PageHeader | ||
title={'Chats'} | ||
description={'Stay in touch with your teammates and navigate your chats'} | ||
></PageHeader> | ||
|
||
<div className={styles.container}> | ||
<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> | ||
<DialogBody className={styles.dialog}> | ||
<DialogTitle>New Chat</DialogTitle> | ||
<NewChat | ||
onChatCreated={chatSelected} | ||
onCancelClicked={() => { | ||
setIsNewChatOpen(false); | ||
}} | ||
hideTitle={true} | ||
></NewChat> | ||
</DialogBody> | ||
</DialogSurface> | ||
</Dialog> | ||
</div> | ||
<ChatList chatSelected={selectedChat} onChatSelected={setSelectedChat}></ChatList> | ||
</div> | ||
<div className={styles.side}>{selectedChat && <Chat chatId={selectedChat.id!}></Chat>}</div> | ||
</div> | ||
</> | ||
); | ||
}; | ||
|
||
interface ChatListProps { | ||
onChatSelected: (e: GraphChat) => void; | ||
chatSelected: GraphChat | undefined; | ||
} | ||
|
||
const ChatList = React.memo((props: ChatListProps) => { | ||
const getPreviousDate = (months: number) => { | ||
const date = new Date(); | ||
date.setMonth(date.getMonth() - months); | ||
return date.toISOString(); | ||
}; | ||
|
||
return ( | ||
<Get | ||
resource={`me/chats?$expand=members,lastMessagePreview&$orderBy=lastMessagePreview/createdDateTime desc&$filter=viewpoint/lastMessageReadDateTime ge ${getPreviousDate( | ||
9 | ||
)}`} | ||
scopes={['chat.read']} | ||
cacheEnabled={true} | ||
> | ||
<ChatListTemplate | ||
template="default" | ||
onSelected={props.onChatSelected} | ||
selectedChat={props.chatSelected} | ||
></ChatListTemplate> | ||
<Loading template="loading" message={'Loading your chats...'}></Loading> | ||
</Get> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import { Persona, makeStyles, mergeClasses, shorthands } from '@fluentui/react-components'; | ||
import { Providers } from '@microsoft/mgt-element'; | ||
import { MgtTemplateProps, Person, ViewType } from '@microsoft/mgt-react'; | ||
import { Chat, AadUserConversationMember } from '@microsoft/microsoft-graph-types'; | ||
import React, { useCallback, useEffect, useState } from 'react'; | ||
import { PeopleCommunityRegular, CalendarMonthRegular } from '@fluentui/react-icons'; | ||
|
||
const useStyles = makeStyles({ | ||
chat: { | ||
paddingLeft: '5px', | ||
paddingRight: '5px', | ||
display: 'flex', | ||
alignItems: 'center', | ||
height: '50px', | ||
cursor: 'pointer', | ||
':hover': { | ||
backgroundColor: 'var(--colorNeutralBackground1Hover)' | ||
} | ||
}, | ||
active: { | ||
backgroundColor: 'var(--colorNeutralBackground1Selected)' | ||
}, | ||
person: { | ||
'--person-avatar-size-small': '40px', | ||
'& .fui-Persona__primaryText': { | ||
fontSize: 'var(--fontSizeBase300);' | ||
}, | ||
'& .fui-Persona__secondaryText': { | ||
whiteSpace: 'nowrap', | ||
textOverflow: 'ellipsis', | ||
width: '200px', | ||
display: 'inline-block', | ||
...shorthands.overflow('hidden') | ||
} | ||
}, | ||
messagePreview: { | ||
whiteSpace: 'nowrap', | ||
textOverflow: 'ellipsis', | ||
width: '200px', | ||
display: 'inline-block', | ||
...shorthands.overflow('hidden') | ||
} | ||
}); | ||
|
||
export interface ChatInteractionProps { | ||
onSelected: (selected: Chat) => void; | ||
selectedChat?: Chat; | ||
} | ||
|
||
interface ChatItemProps { | ||
chat: Chat; | ||
isSelected?: boolean; | ||
} | ||
|
||
const getMessagePreview = (chat: Chat) => { | ||
return chat?.lastMessagePreview?.body?.contentType === 'text' ? chat?.lastMessagePreview?.body?.content : '...'; | ||
}; | ||
|
||
const ChatItem = ({ chat, isSelected, onSelected }: ChatItemProps & ChatInteractionProps) => { | ||
const styles = useStyles(); | ||
const [myId, setMyId] = useState<string>(); | ||
|
||
useEffect(() => { | ||
const getMyId = async () => { | ||
const me = await Providers.me(); | ||
setMyId(me.id); | ||
}; | ||
if (!myId) { | ||
void getMyId(); | ||
} | ||
}, [myId]); | ||
|
||
const getOtherParticipantId = useCallback( | ||
(chat: Chat) => { | ||
const member = chat.members?.find(m => (m as AadUserConversationMember).userId !== myId); | ||
|
||
if (member) { | ||
console.log('member', member); | ||
return (member as AadUserConversationMember).userId as string; | ||
} else if (chat.members?.length === 1 && (chat.members[0] as AadUserConversationMember).userId === myId) { | ||
return myId; | ||
} | ||
|
||
return undefined; | ||
}, | ||
[myId] | ||
); | ||
|
||
const getGroupTitle = useCallback((chat: Chat) => { | ||
let groupTitle: string | undefined = ''; | ||
if (chat.topic) { | ||
groupTitle = chat.topic; | ||
} else { | ||
groupTitle = chat.members | ||
?.map(member => { | ||
return member.displayName?.split(' ')[0]; | ||
}) | ||
.join(', '); | ||
} | ||
|
||
return groupTitle; | ||
}, []); | ||
|
||
return ( | ||
<> | ||
{myId && ( | ||
<div className={mergeClasses(styles.chat, `${isSelected && styles.active}`)}> | ||
{chat.chatType === 'oneOnOne' && ( | ||
<Person | ||
userId={getOtherParticipantId(chat)} | ||
view={ViewType.twolines} | ||
avatarSize="auto" | ||
showPresence={true} | ||
onClick={() => onSelected(chat)} | ||
className={styles.person} | ||
> | ||
<MessagePreview template="line2" chat={chat} /> | ||
</Person> | ||
)} | ||
{chat.chatType === 'group' && ( | ||
<div onClick={() => onSelected(chat)}> | ||
<Persona | ||
textAlignment="center" | ||
size="extra-large" | ||
name={getGroupTitle(chat)} | ||
secondaryText={getMessagePreview(chat)} | ||
avatar={{ icon: <PeopleCommunityRegular />, initials: null }} | ||
className={styles.person} | ||
/> | ||
<span></span> | ||
</div> | ||
)} | ||
{chat.chatType === 'meeting' && ( | ||
<div onClick={() => onSelected(chat)}> | ||
<Persona | ||
textAlignment="center" | ||
size="extra-large" | ||
className={styles.person} | ||
avatar={{ icon: <CalendarMonthRegular />, initials: null }} | ||
name={getGroupTitle(chat)} | ||
secondaryText={getMessagePreview(chat)} | ||
/> | ||
</div> | ||
)} | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
const MessagePreview = (props: MgtTemplateProps & ChatItemProps) => { | ||
const styles = useStyles(); | ||
|
||
return ( | ||
<> | ||
<span className={styles.messagePreview}>{getMessagePreview(props.chat)}</span> | ||
</> | ||
); | ||
}; | ||
|
||
export default ChatItem; |
Oops, something went wrong.