diff --git a/packages/messenger-widget/README.md b/packages/messenger-widget/README.md index da3d62201..efc6e3a0c 100644 --- a/packages/messenger-widget/README.md +++ b/packages/messenger-widget/README.md @@ -475,14 +475,17 @@ Example : push: false, }, profile: { - dm3: boolean | { + dm3: { cloud: false, optimism: true, }, - self: boolean | { + self: { gnosis: true, ens: false, } + }, + settings: { + messageView: true, } } disableDialogOptions: { diff --git a/packages/messenger-widget/src/components/Chat/Chat.tsx b/packages/messenger-widget/src/components/Chat/Chat.tsx index a9552fc7d..6996f77a7 100644 --- a/packages/messenger-widget/src/components/Chat/Chat.tsx +++ b/packages/messenger-widget/src/components/Chat/Chat.tsx @@ -13,6 +13,7 @@ import { MessageInputBox } from '../MessageInputBox/MessageInputBox'; import { scrollToBottomOfChat } from './scrollToBottomOfChat'; import { ModalContext } from '../../context/ModalContext'; import InfiniteScroll from 'react-infinite-scroll-component'; +import { MessagePropsModel } from '../../interfaces/utils'; export function Chat() { const { account } = useContext(AuthContext); @@ -46,6 +47,50 @@ export function Chat() { } }; + // This function adds a property called showProfile which is of type boolean + // It helps to indicate whether a profile preview has to be shown with message or not + // It also adds a property isFirstMsgofDay which indicates its a first message of particular day + const formatMessages = ( + messageList: MessageModel[], + ): MessagePropsModel[] => { + // The message list is reversed and then property is added because the pagination library + // reverses the message list and then it is visible. + // So to keep profile preview updated list is reversed + const list = messageList.reverse().map((m, index) => { + // the sender or current message + const currentMsgSender = m.envelop.message.metadata?.from; + // the sender of message before the current message + const lastMsgSender = index + ? messageList[index - 1].envelop.message.metadata?.from + : ''; + // the date on which current message was sent + const currentMsgDate = new Date( + Number(m.envelop.message.metadata?.timestamp), + ).getDate(); + // the date on which last message was sent + const lastMsgDate = index + ? new Date( + Number( + messageList[index - 1].envelop.message.metadata + ?.timestamp, + ), + ).getDate() + : 0; + return { + ...m, + // if its not a first message and last 2 messages sender is same then no need to show + // profile for this message otherwise show profile + showProfile: !(index && currentMsgSender === lastMsgSender), + // if the index is 0, then its first message of specific date, so set to true + // otherwise check 2 dates, if they are same then its not first msg else it is first msg of day + isFirstMsgOfDay: !index ? true : lastMsgDate !== currentMsgDate, + }; + }); + + // after setting the property, the list is returned back in actual order + return list.reverse(); + }; + useEffect(() => { if (!selectedContact) { return; @@ -59,7 +104,9 @@ export function Chat() { if (!selectedContact) { return []; } - return getMessages(selectedContact.contactDetails.account.ensName!); + return formatMessages( + getMessages(selectedContact.contactDetails.account.ensName!), + ); }, [getMessages, selectedContact]); // handles messages list @@ -192,7 +239,10 @@ export function Chat() { > {messages.length > 0 && messages.map( - (messageModel: MessageModel, index) => ( + ( + messageModel: MessagePropsModel, + index, + ) => (
), diff --git a/packages/messenger-widget/src/components/ConfigureProfile/ConfigureProfile.css b/packages/messenger-widget/src/components/ConfigureProfile/ConfigureProfile.css index 9e6ce861c..e47c00778 100644 --- a/packages/messenger-widget/src/components/ConfigureProfile/ConfigureProfile.css +++ b/packages/messenger-widget/src/components/ConfigureProfile/ConfigureProfile.css @@ -163,47 +163,6 @@ input::placeholder { border-radius: 6px; } -/* Customized CSS for radio button */ -.radio { - input[type="radio"] { - opacity: 0; - + .radio-label { - &:before { - content: ''; - background: #1F2029; - border-radius: 100%; - border: 1px solid var(--text-primary-color); - display: inline-block; - width: 1.4em; - height: 1.4em; - position: relative; - top: -0.2em; - margin-right: 1em; - vertical-align: top; - cursor: pointer; - text-align: center; - transition: all 250ms ease; - } - } - &:checked { - + .radio-label { - &:before { - background-color: var(--configure-profile-modal-background-color); - box-shadow: inset 0 0 0 4px #1F2029; - } - } - } - &:focus { - + .radio-label { - &:before { - outline: none; - border-color: var(--configure-profile-modal-background-color); - } - } - } - } - } - .ens-components-container{ padding: 1.25rem; } @@ -307,17 +266,6 @@ input::placeholder { margin-left: -11px; } - .radio { - input[type="radio"] { - opacity: 0; - + .radio-label { - &:before { - width: 2em !important; - } - } - } - } - .dm3-prof-select-type{ font-size: 12px; } diff --git a/packages/messenger-widget/src/components/Message/Message.css b/packages/messenger-widget/src/components/Message/Message.css index 40ac6f31b..36af095c8 100644 --- a/packages/messenger-widget/src/components/Message/Message.css +++ b/packages/messenger-widget/src/components/Message/Message.css @@ -1,12 +1,15 @@ .content-style { padding: 10px; - max-width: 38rem; margin-right: 0.4rem; font-size: 14px; word-break: break-word; hyphens: auto; } +.old-content-style { + max-width: 38rem; +} + .action-container { height: fit-content; width: fit-content; @@ -80,10 +83,14 @@ background-color: var(--scrollbar-background-color); } -.contact-reacted-msg { +.old-contact-reacted-msg { border-bottom-left-radius: 0px !important; } +.contact-reacted-msg { + border-bottom-right-radius: 0px !important; +} + .own-reacted-msg { border-bottom-right-radius: 0px !important; } @@ -150,10 +157,45 @@ color: var(--text-primary-color); } +.chat-profile-pic{ + height: 1.5rem; + width: 1.5rem; + border-radius: 3px; +} + +.msg-group-head { + display: flex; + flex-direction: row; + color: #FFFFFF; + font-size: 13px; + margin-bottom: 0.7rem; + margin-left: 0.5rem; + margin-right: 0.5rem; +} + +.msg-group-head:before, +.msg-group-head:after { + content: ""; + flex: 1 1; + border-bottom: 1px solid #FFFFFF; + margin: auto; +} + +.msg-group-head:before { + margin-right: 5px; +} + +.msg-group-head:after { + margin-left: 5px; +} + /* =================== Mobile Responsive CSS =================== */ @media only screen and (max-width: 800px) { .content-style { font-size: 12px; + } + + .old-content-style{ max-width: 19rem; } @@ -164,4 +206,4 @@ .msg div .content-style + .msg-action-container { visibility: visible; } -} +} \ No newline at end of file diff --git a/packages/messenger-widget/src/components/Message/Message.tsx b/packages/messenger-widget/src/components/Message/Message.tsx index 45d635daa..fcddc22f6 100644 --- a/packages/messenger-widget/src/components/Message/Message.tsx +++ b/packages/messenger-widget/src/components/Message/Message.tsx @@ -13,64 +13,146 @@ import { ReplyMessagePreview } from './ReplyMessagePreview'; import { MessageReactions } from './MessageReactions'; import { Action } from './Action'; import { MessageDetail } from './MessageDetail'; +import { ProfilePreview } from './ProfilePreview'; +import { AuthContext } from '../../context/AuthContext'; +import { ConversationContext } from '../../context/ConversationContext'; +import { DM3UserProfileContext } from '../../context/DM3UserProfileContext'; +import { SettingsContext } from '../../context/SettingsContext'; +import { MsgViewType } from '../../hooks/settings/useSettings'; export function Message(props: MessageProps) { const { messageView } = useContext(UiViewContext); + const { displayName } = useContext(AuthContext); + + const { selectedContact } = useContext(ConversationContext); + + const { accountProfilePicture } = useContext(DM3UserProfileContext); + + const { msgViewSelected } = useContext(SettingsContext); + + const formatDate = (msgDate: number): string => { + // create a date object + const normalFormat = new Date(msgDate); + // extract year from the date object + const year = normalFormat.getFullYear(); + // extract month from the date object + const month = normalFormat.toLocaleString('default', { month: 'long' }); + // extract date from the date object + const date = normalFormat.getDate(); + // return in format date, month & year date Ex: 08 October, 2024 + return `${date} ${month}, ${year}`; + }; + return ( - -
-
- {/* Reply message preview */} - - - {/* Attachments preview */} - {props.envelop.message.attachments && - props.envelop.message.attachments.length > 0 && - props.envelop.message.metadata.type !== - MessageActionType.DELETE && ( - + {/* + 1. Shows the date, month and year + 2. Only visible when new message layout is enabled + 3. Visible only before the first message of each date + */} + {msgViewSelected.viewType === MsgViewType.NEW && + props.isFirstMsgOfDay && ( + + {formatDate(props.envelop.message.metadata?.timestamp)} + + )} + +
+
+ {/* Profile preview before every message content to show the actual sender of it */} + {msgViewSelected.viewType === MsgViewType.NEW && ( + + + + )} + +
+ {msgViewSelected.viewType === MsgViewType.NEW && + props.showProfile && ( +
+ {props.ownMessage + ? displayName + : selectedContact?.name}{' '} +
+ )} + + {/* Reply message preview */} + + + {/* Attachments preview */} + {props.envelop.message.attachments && + props.envelop.message.attachments.length > 0 && + props.envelop.message.metadata.type !== + MessageActionType.DELETE && ( + + )} - {/* actual message */} - {props.message - ? props.message - : props.envelop.message.attachments && - props.envelop.message.attachments.length > 0 && - props.envelop.message.metadata.type !== - MessageActionType.DELETE - ? '' - : props.ownMessage - ? 'You deleted this message.' - : 'This message was deleted.'} + {/* actual message */} + {props.message + ? props.message + : props.envelop.message.attachments && + props.envelop.message.attachments.length > 0 && + props.envelop.message.metadata.type !== + MessageActionType.DELETE + ? '' + : props.ownMessage + ? 'You deleted this message.' + : 'This message was deleted.'} +
+ {/* action 3 dots */} +
- {/* action 3 dots */} - -
-
- {/* Own message */} - {props.ownMessage && } +
+ {/* Own message */} + {props.ownMessage && } + + {/* Contact's message */} + {msgViewSelected.viewType === MsgViewType.NEW && + !props.ownMessage && } - {/* Reaction emojis */} - + {/* Reaction emojis */} + - {/* Contact's message */} - {!props.ownMessage && } + {/* Contact's message */} + {msgViewSelected.viewType === MsgViewType.OLD && + !props.ownMessage && } +
- + ); } diff --git a/packages/messenger-widget/src/components/Message/MessageDetail.tsx b/packages/messenger-widget/src/components/Message/MessageDetail.tsx index 7a36598e7..a27f1c845 100644 --- a/packages/messenger-widget/src/components/Message/MessageDetail.tsx +++ b/packages/messenger-widget/src/components/Message/MessageDetail.tsx @@ -4,8 +4,13 @@ import blueTickIcon from '../../assets/images/tick.svg'; import whiteTickIcon from '../../assets/images/white-tick.svg'; import { MessageProps } from '../../interfaces/props'; import { MessageIndicator } from '../../hooks/messages/useMessage'; +import { useContext } from 'react'; +import { SettingsContext } from '../../context/SettingsContext'; +import { MsgViewType } from '../../hooks/settings/useSettings'; export function MessageDetail(props: MessageProps) { + const { msgViewSelected } = useContext(SettingsContext); + const getMessageIndicatorView = ( indicator: MessageIndicator | undefined, ) => { @@ -49,8 +54,10 @@ export function MessageDetail(props: MessageProps) { {props.ownMessage ? ( getMessageIndicatorView(props.indicator) - ) : ( + ) : msgViewSelected.viewType === MsgViewType.OLD ? ( <> + ) : ( + getMessageIndicatorView(MessageIndicator.READED) )}
diff --git a/packages/messenger-widget/src/components/Message/ProfilePreview.tsx b/packages/messenger-widget/src/components/Message/ProfilePreview.tsx new file mode 100644 index 000000000..998c56ed4 --- /dev/null +++ b/packages/messenger-widget/src/components/Message/ProfilePreview.tsx @@ -0,0 +1,43 @@ +import { useContext } from 'react'; +import './Message.css'; +import { UiViewContext } from '../../context/UiViewContext'; +import { RightViewSelected } from '../../utils/enum-type-utils'; +import { closeContactMenu } from '../../utils/common-utils'; +import { ConversationContext } from '../../context/ConversationContext'; + +export interface ProfilePreviewProps { + picture: string; + ownMessage: boolean; +} + +export function ProfilePreview(props: ProfilePreviewProps) { + const { setSelectedRightView } = useContext(UiViewContext); + const { setSelectedContactName } = useContext(ConversationContext); + + // method to open the profile of selected account + const openProfile = () => { + // if our own profile icon is clicked + if (props.ownMessage) { + // select and open profile component + setSelectedRightView(RightViewSelected.Profile); + // unselect the contact + setSelectedContactName(undefined); + return; + } + // open contact info of the selected contact + setSelectedRightView(RightViewSelected.ContactInfo); + // close the contact menu + closeContactMenu(); + }; + + return ( +
+ {/* profile picture of the account or contact selected */} + openProfile()} + /> +
+ ); +} diff --git a/packages/messenger-widget/src/components/Message/bl.ts b/packages/messenger-widget/src/components/Message/bl.ts index 747b05226..876a72830 100644 --- a/packages/messenger-widget/src/components/Message/bl.ts +++ b/packages/messenger-widget/src/components/Message/bl.ts @@ -47,8 +47,11 @@ export const getFilesAttachments = (files: Attachment[]) => { export const getMessageStyleClasses = ( props: MessageProps, messageView: MessageAction, + isOldMsgStyle: boolean, ): string => { return 'width-fill text-left border-radius-6 content-style'.concat( + ' ', + isOldMsgStyle ? 'old-content-style' : '', ' ', (props.ownMessage ? !props.message && @@ -61,7 +64,7 @@ export const getMessageStyleClasses = ( messageView.messageData?.envelop.message.signature === props.envelop.message.signature ? 'msg-editing-active' - : 'ms-3 own-msg-background own-msg-text' + : 'own-msg-background own-msg-text' : !props.message && props.envelop.message.metadata.type === MessageActionType.DELETE && @@ -74,6 +77,8 @@ export const getMessageStyleClasses = ( props.reactions.length > 0 ? props.ownMessage ? 'own-reacted-msg' + : isOldMsgStyle + ? 'old-contact-reacted-msg' : 'contact-reacted-msg' : '', ), @@ -81,13 +86,18 @@ export const getMessageStyleClasses = ( }; // returns the css classes based on the reactions -export const getMessageReactionStyleClassses = (props: MessageProps) => { +export const getMessageReactionStyleClassses = ( + props: MessageProps, + isOldMsgType: boolean, +) => { return 'd-flex justify-content-end text-secondary-color time-style'.concat( ' ', props.reactions.length > 0 - ? !props.ownMessage - ? 'justify-content-between' - : 'ms-3 justify-content-end' + ? isOldMsgType + ? !props.ownMessage + ? 'justify-content-between' + : 'ms-3 justify-content-end' + : '' : props.ownMessage ? 'ms-3' : '', diff --git a/packages/messenger-widget/src/components/MessageAction/MessageAction.css b/packages/messenger-widget/src/components/MessageAction/MessageAction.css index 3d712ddc4..7292d3a52 100644 --- a/packages/messenger-widget/src/components/MessageAction/MessageAction.css +++ b/packages/messenger-widget/src/components/MessageAction/MessageAction.css @@ -14,6 +14,10 @@ margin-left: -8.7rem; } +.contact-msg{ + margin-left: -11.7rem; +} + .msg-dropdown-content-top-align { margin-top: -6.9rem !important; } diff --git a/packages/messenger-widget/src/components/MessageAction/MessageAction.tsx b/packages/messenger-widget/src/components/MessageAction/MessageAction.tsx index 3c62b4c7a..5cf6d3442 100644 --- a/packages/messenger-widget/src/components/MessageAction/MessageAction.tsx +++ b/packages/messenger-widget/src/components/MessageAction/MessageAction.tsx @@ -20,6 +20,8 @@ import { hideMsgActionDropdown } from '../MessageInputBox/bl'; import { DM3ConfigurationContext } from '../../context/DM3ConfigurationContext'; import { UiViewContext } from '../../context/UiViewContext'; import { ModalContext } from '../../context/ModalContext'; +import { SettingsContext } from '../../context/SettingsContext'; +import { MsgViewType } from '../../hooks/settings/useSettings'; export function MessageAction(props: MessageProps) { const { account, profileKeys } = useContext(AuthContext); @@ -27,6 +29,7 @@ export function MessageAction(props: MessageProps) { const { selectedContact } = useContext(ConversationContext); const { screenWidth } = useContext(DM3ConfigurationContext); const { setMessageView } = useContext(UiViewContext); + const { msgViewSelected } = useContext(SettingsContext); const { setOpenEmojiPopup, setLastMessageAction } = useContext(ModalContext); @@ -143,7 +146,14 @@ export function MessageAction(props: MessageProps) {
setAction(MessageActionType.REPLY)} > - delete + reply Reply
)} diff --git a/packages/messenger-widget/src/components/MessageInputBox/MessageInputBox.tsx b/packages/messenger-widget/src/components/MessageInputBox/MessageInputBox.tsx index 5c737d509..3cf30f8f1 100644 --- a/packages/messenger-widget/src/components/MessageInputBox/MessageInputBox.tsx +++ b/packages/messenger-widget/src/components/MessageInputBox/MessageInputBox.tsx @@ -78,7 +78,7 @@ export function MessageInputBox() {
{/* Reply message preview */} {messageView.actionType === MessageActionType.REPLY && ( diff --git a/packages/messenger-widget/src/components/Preferences/NormalView.tsx b/packages/messenger-widget/src/components/Preferences/NormalView.tsx index bbba597a4..423aac94a 100644 --- a/packages/messenger-widget/src/components/Preferences/NormalView.tsx +++ b/packages/messenger-widget/src/components/Preferences/NormalView.tsx @@ -1,6 +1,6 @@ import './Preferences.css'; import infoIcon from './../../assets/images/preferences-info.svg'; -import { useContext, useEffect, useState } from 'react'; +import { useContext, useEffect } from 'react'; import closeIcon from '../../assets/images/cross.svg'; import { closeConfigurationModal } from '../ConfigureProfile/bl'; import { ModalContext } from '../../context/ModalContext'; diff --git a/packages/messenger-widget/src/components/Preferences/Preferences.css b/packages/messenger-widget/src/components/Preferences/Preferences.css index 0a6ea1029..708d46e68 100644 --- a/packages/messenger-widget/src/components/Preferences/Preferences.css +++ b/packages/messenger-widget/src/components/Preferences/Preferences.css @@ -35,6 +35,7 @@ cursor: pointer; padding: 0.5rem 1.3rem 0.5rem 1.3rem; font-size: 1rem; + align-items: center; } .preferences-aside-content { diff --git a/packages/messenger-widget/src/components/Preferences/Settings/Settings.css b/packages/messenger-widget/src/components/Preferences/Settings/Settings.css new file mode 100644 index 000000000..f100e047e --- /dev/null +++ b/packages/messenger-widget/src/components/Preferences/Settings/Settings.css @@ -0,0 +1,27 @@ +.settings-option-container{ + padding: 2rem 1rem 1rem 1rem; +} + +.settings-option{ + color: var(--text-primary-color); + font-weight: 500; + font-size: 14px; + line-height: 24px; + margin-left: 0.5rem; + display: flex; + align-items: center; +} + +.settings-msg-view{ + padding-left: 2rem; +} + +@media only screen and (max-width: 500px) { + .msg-view-heading{ + font-size: 0.9rem; + } + + .msg-view-desc{ + font-size: 12px; + } +} \ No newline at end of file diff --git a/packages/messenger-widget/src/components/Preferences/Settings/Settings.tsx b/packages/messenger-widget/src/components/Preferences/Settings/Settings.tsx new file mode 100644 index 000000000..5ab66b17a --- /dev/null +++ b/packages/messenger-widget/src/components/Preferences/Settings/Settings.tsx @@ -0,0 +1,67 @@ +import './Settings.css'; +import { useContext } from 'react'; +import { Heading } from '../Heading/Heading'; +import { ModalContext } from '../../../context/ModalContext'; +import { SettingsContext } from '../../../context/SettingsContext'; + +export function Settings() { + // heading of the page + const heading = 'Settings'; + + // description of the page + const description = 'Define how you want to enable/disable components'; + + const { disabledOptions } = useContext(ModalContext); + const { msgViewOptions, msgViewSelected, updateMsgView } = + useContext(SettingsContext); + + return ( +
+ {/* heading of the page */} + + + {/* Show message option selection when option is not disabled */} + {!disabledOptions.settings.messageView && ( + <> + {/* title and description of the property */} +
+
+ Message View +
+
+ Select a view how the message should look like +
+
+ + {/* radio button options to select the message view type */} +
+
+ {msgViewOptions.map((option, index) => ( +
{ + updateMsgView(option); + }} + > + + +
+ ))} +
+
+ + )} +
+ ); +} diff --git a/packages/messenger-widget/src/components/Preferences/bl.tsx b/packages/messenger-widget/src/components/Preferences/bl.tsx index 9f84bcb8f..a31985850 100644 --- a/packages/messenger-widget/src/components/Preferences/bl.tsx +++ b/packages/messenger-widget/src/components/Preferences/bl.tsx @@ -4,12 +4,14 @@ import spamIcon from './../../assets/images/spam.svg'; import notificationIcon from './../../assets/images/notification.svg'; import networkIcon from './../../assets/images/network.svg'; import storageIcon from './../../assets/images/storage.svg'; +import settingsIcon from './../../assets/images/settings.svg'; import { Spam } from './Spam/Spam'; import { Properties } from './Properties/Properties'; import { Notification } from './Notification/Notification'; import { Network } from './Network/Network'; import { Storage } from './Storage/Storage'; import { DM3Profile } from './DM3Profile/DM3Profile'; +import { Settings } from './Settings/Settings'; export enum PREFERENCES_ITEMS { PROPERTIES = 'PROPERTIES', @@ -18,6 +20,7 @@ export enum PREFERENCES_ITEMS { NOTIFICATION = 'NOTIFICATION', NETWORK = 'NETWORK', STORAGE = 'STORAGE', + SETTINGS = 'SETTINGS', } export const preferencesItems = [ @@ -79,4 +82,13 @@ export const preferencesItems = [ ticker: PREFERENCES_ITEMS.STORAGE, isEnabled: false, }, + { + icon: ( + network + ), + name: 'Settings', + ticker: PREFERENCES_ITEMS.SETTINGS, + component: , + isEnabled: true, + }, ]; diff --git a/packages/messenger-widget/src/components/RightHeader/NormalView.tsx b/packages/messenger-widget/src/components/RightHeader/NormalView.tsx index b7234d545..c291719b8 100644 --- a/packages/messenger-widget/src/components/RightHeader/NormalView.tsx +++ b/packages/messenger-widget/src/components/RightHeader/NormalView.tsx @@ -1,17 +1,16 @@ import './RightHeader.css'; -import { useContext, useEffect, useState } from 'react'; +import { useContext } from 'react'; import humanIcon from '../../assets/images/human.svg'; import menuIcon from '../../assets/images/menu.svg'; import { AuthContext } from '../../context/AuthContext'; -import { useMainnetProvider } from '../../hooks/mainnetprovider/useMainnetProvider'; -import { getAvatarProfilePic } from '../../utils/ens-utils'; import { RightViewSelected } from '../../utils/enum-type-utils'; import { ConversationContext } from '../../context/ConversationContext'; import { DM3ConfigurationContext } from '../../context/DM3ConfigurationContext'; import { UiViewContext } from '../../context/UiViewContext'; +import { DM3UserProfileContext } from '../../context/DM3UserProfileContext'; export function NormalView() { - const { account, displayName } = useContext(AuthContext); + const { displayName } = useContext(AuthContext); const { setSelectedContactName } = useContext(ConversationContext); @@ -20,21 +19,7 @@ export function NormalView() { const { setSelectedRightView, selectedRightView } = useContext(UiViewContext); - const mainnetProvider = useMainnetProvider(); - - // state to store profile pic of signed in user - const [profilePic, setProfilePic] = useState(''); - - // fetched profile pic of signed in user - const fetchAndSetProfilePic = async () => { - setProfilePic( - await getAvatarProfilePic( - mainnetProvider, - account?.ensName as string, - dm3Configuration.addressEnsSubdomain, - ), - ); - }; + const { accountProfilePicture } = useContext(DM3UserProfileContext); // method to set profile page and set contact const updateView = () => { @@ -42,11 +27,6 @@ export function NormalView() { setSelectedContactName(undefined); }; - // loads the profile pic on page render - useEffect(() => { - fetchAndSetProfilePic(); - }, []); - return (
menu updateView()} diff --git a/packages/messenger-widget/src/components/RightHeader/RightHeader.tsx b/packages/messenger-widget/src/components/RightHeader/RightHeader.tsx index 8be56ba58..6035447ff 100644 --- a/packages/messenger-widget/src/components/RightHeader/RightHeader.tsx +++ b/packages/messenger-widget/src/components/RightHeader/RightHeader.tsx @@ -1,11 +1,39 @@ -import { useContext } from 'react'; +import { useContext, useEffect } from 'react'; import { MobileView } from './MobileView'; import { NormalView } from './NormalView'; import { MOBILE_SCREEN_WIDTH } from '../../utils/common-utils'; import { DM3ConfigurationContext } from '../../context/DM3ConfigurationContext'; +import { DM3UserProfileContext } from '../../context/DM3UserProfileContext'; +import { useMainnetProvider } from '../../hooks/mainnetprovider/useMainnetProvider'; +import { AuthContext } from '../../context/AuthContext'; +import { getAvatarProfilePic } from '../../utils/ens-utils'; export function RightHeader() { - const { screenWidth } = useContext(DM3ConfigurationContext); + const { account } = useContext(AuthContext); + + const { screenWidth, dm3Configuration } = useContext( + DM3ConfigurationContext, + ); + + const { setAccountProfilePicture } = useContext(DM3UserProfileContext); + + const mainnetProvider = useMainnetProvider(); + + // fetched profile pic of signed in user + const fetchAndSetProfilePic = async () => { + setAccountProfilePicture( + await getAvatarProfilePic( + mainnetProvider, + account?.ensName as string, + dm3Configuration.addressEnsSubdomain, + ), + ); + }; + + // loads the profile pic on page render + useEffect(() => { + fetchAndSetProfilePic(); + }, []); return ( <> diff --git a/packages/messenger-widget/src/context/DM3UserProfileContext.tsx b/packages/messenger-widget/src/context/DM3UserProfileContext.tsx index dcb9ca6e5..d98c84800 100644 --- a/packages/messenger-widget/src/context/DM3UserProfileContext.tsx +++ b/packages/messenger-widget/src/context/DM3UserProfileContext.tsx @@ -22,6 +22,8 @@ export type DM3UserProfileContextType = { isProfileUpdatedForAddrName: boolean; isProfileUpdatedForDm3Name: boolean; isProfileUpdatedForEnsName: boolean; + accountProfilePicture: string; + setAccountProfilePicture: (pic: string) => void; }; export const DM3UserProfileContext = @@ -45,6 +47,8 @@ export const DM3UserProfileContext = isProfileUpdatedForAddrName: true, isProfileUpdatedForDm3Name: true, isProfileUpdatedForEnsName: true, + accountProfilePicture: '', + setAccountProfilePicture: (pic: string) => {}, }); export const DM3UserProfileContextProvider = ({ @@ -70,6 +74,8 @@ export const DM3UserProfileContextProvider = ({ isProfileUpdatedForDm3Name, isProfileUpdatedForEnsName, setNodeName, + accountProfilePicture, + setAccountProfilePicture, } = useDm3UserProfile(); return ( @@ -92,6 +98,8 @@ export const DM3UserProfileContextProvider = ({ isProfileUpdatedForDm3Name, isProfileUpdatedForEnsName, setNodeName, + accountProfilePicture, + setAccountProfilePicture, }} > {children} diff --git a/packages/messenger-widget/src/context/ModalContext.tsx b/packages/messenger-widget/src/context/ModalContext.tsx index 446d846f7..b4fc14360 100644 --- a/packages/messenger-widget/src/context/ModalContext.tsx +++ b/packages/messenger-widget/src/context/ModalContext.tsx @@ -94,6 +94,9 @@ export const ModalContext = React.createContext({ { key: 'gnosis', value: false }, ], }, + settings: { + messageView: false, + }, }, isProfileDialogDisabled: () => false, }); diff --git a/packages/messenger-widget/src/context/SettingsContext.tsx b/packages/messenger-widget/src/context/SettingsContext.tsx new file mode 100644 index 000000000..15def5fb0 --- /dev/null +++ b/packages/messenger-widget/src/context/SettingsContext.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { + MSG_VIEW_OPTIONS, + MsgViewOptionType, + useSettings, +} from '../hooks/settings/useSettings'; + +export type SettingsContextType = { + msgViewOptions: MsgViewOptionType[]; + updateMsgView: (msgView: MsgViewOptionType) => void; + msgViewSelected: MsgViewOptionType; +}; + +export const SettingsContext = React.createContext({ + msgViewOptions: MSG_VIEW_OPTIONS, + updateMsgView: (msgView: MsgViewOptionType) => {}, + msgViewSelected: MSG_VIEW_OPTIONS[0], +}); + +export const SettingsContextProvider = ({ children }: { children?: any }) => { + const { msgViewOptions, updateMsgView, msgViewSelected } = useSettings(); + + return ( + + {children} + + ); +}; diff --git a/packages/messenger-widget/src/hooks/modals/useModal.ts b/packages/messenger-widget/src/hooks/modals/useModal.ts index aad17f4a5..9431be395 100644 --- a/packages/messenger-widget/src/hooks/modals/useModal.ts +++ b/packages/messenger-widget/src/hooks/modals/useModal.ts @@ -39,6 +39,9 @@ export type DisabledOptionsType = { dm3: { key: string; value: boolean }[]; own: { key: string; value: boolean }[]; }; + settings: { + messageView: boolean; + }; }; export const useModal = () => { @@ -102,6 +105,9 @@ export const useModal = () => { { key: 'gnosis', value: false }, ], }, + settings: { + messageView: false, + }, }, ); @@ -247,9 +253,30 @@ export const useModal = () => { } } + // update settings dialog + const updatedSettingsOptions = + dialogDisabled.settings === true + ? updatedProfileOptions.map((pref) => { + return { + ...pref, + isEnabled: + pref.ticker === PREFERENCES_ITEMS.SETTINGS + ? false + : pref.isEnabled, + }; + }) + : updatedProfileOptions; + + // disable specific settings option + if (typeof dialogDisabled.settings === 'object') { + optionsToDisable.settings.messageView = + dialogDisabled.settings.messageView ?? false; + } + + // update the states for disabled options setDisabledOptions(optionsToDisable); setPreferencesOptions( - updatedProfileOptions as PreferencesOptionType[], + updatedSettingsOptions as PreferencesOptionType[], ); return; diff --git a/packages/messenger-widget/src/hooks/settings/useSettings.tsx b/packages/messenger-widget/src/hooks/settings/useSettings.tsx new file mode 100644 index 000000000..2027776ce --- /dev/null +++ b/packages/messenger-widget/src/hooks/settings/useSettings.tsx @@ -0,0 +1,110 @@ +import { useEffect, useState } from 'react'; + +// message view option type ex : OLD/NEW +export type MsgViewOptionType = { + name: string; + viewType: string; + isEnabled: boolean; +}; + +// settings data type to be used to store in local storage +export type SettingsDataType = { + messageView: string; +}; + +export enum MsgViewType { + OLD = 'OLD', + NEW = 'NEW', +} + +// available message view options +export const MSG_VIEW_OPTIONS = [ + { + name: 'Old message view', + viewType: MsgViewType.OLD, + isEnabled: true, + }, + { + name: 'New message view', + viewType: MsgViewType.NEW, + isEnabled: true, + }, +]; + +export const useSettings = () => { + const msgViewOptions: MsgViewOptionType[] = MSG_VIEW_OPTIONS; + + // state to handle current message view type selected to show in chat screen + const [msgViewSelected, setMsgViewSelected] = useState( + MSG_VIEW_OPTIONS[0], + ); + + const getSettingsFromLocalStorage = () => { + // fetch settings data from local storage + const settingsData = localStorage.getItem('settings'); + // return data if found else return null + return settingsData ? JSON.parse(settingsData) : null; + }; + + const configureMsgView = () => { + // fetch message view type from local storage + const settingsData: SettingsDataType | null = + getSettingsFromLocalStorage(); + // if message view found in local storage + if (settingsData && settingsData.messageView) { + // filter out the message type selected from available options + const msgViewType = msgViewOptions.filter( + (m) => m.viewType === settingsData.messageView, + ); + // set the selected view type + setMsgViewSelected( + msgViewType.length ? msgViewType[0] : msgViewOptions[0], + ); + } + }; + + const updateSettingsInLocalStorage = (data: SettingsDataType) => { + // convert JSON data of settings into string + const settingsData = JSON.stringify(data); + // save the data into local storage + localStorage.setItem('settings', settingsData); + }; + + const updateMsgView = (msgView: MsgViewOptionType) => { + // get the view type from selected message view type object + const { viewType } = msgView; + + // update local state of message view type selected + setMsgViewSelected(msgView); + + // fetch local storage data for settings + const settingsData: SettingsDataType | null = + getSettingsFromLocalStorage(); + + // update the property of message view + if (settingsData) { + settingsData.messageView = viewType; + } + + // create a updated object based on the data + const updatedSettings = settingsData + ? settingsData + : { + messageView: viewType, + }; + + // update the local storage with the new message view type + updateSettingsInLocalStorage(updatedSettings); + }; + + // on screen load, get the settings data from local storage ad update the states + useEffect(() => { + configureMsgView(); + }, []); + + return { + msgViewSelected, + updateMsgView, + msgViewOptions, + }; +}; diff --git a/packages/messenger-widget/src/hooks/userProfile/useDm3UserProfile.tsx b/packages/messenger-widget/src/hooks/userProfile/useDm3UserProfile.tsx index 74de4f04f..ec4d81bd9 100644 --- a/packages/messenger-widget/src/hooks/userProfile/useDm3UserProfile.tsx +++ b/packages/messenger-widget/src/hooks/userProfile/useDm3UserProfile.tsx @@ -32,6 +32,7 @@ import { fetchChainIdFromServiceName, NAME_SERVICES, } from '../../components/ConfigureProfile/bl'; +import profPicture from '../../assets/images/human.svg'; export interface INodeDetails { dsNames: string[]; @@ -100,6 +101,9 @@ export const useDm3UserProfile = () => { ensName: null, }); + const [accountProfilePicture, setAccountProfilePicture] = + useState(profPicture); + // adds DS nodes in local storage of browser const setDsNodesInLocalStorage = (data: INodeDetails) => { // fetch data from local storage @@ -751,5 +755,7 @@ export const useDm3UserProfile = () => { isProfileUpdatedForAddrName, isProfileUpdatedForDm3Name, isProfileUpdatedForEnsName, + accountProfilePicture, + setAccountProfilePicture, }; }; diff --git a/packages/messenger-widget/src/interfaces/config.ts b/packages/messenger-widget/src/interfaces/config.ts index 0bc7374a7..369bac42c 100644 --- a/packages/messenger-widget/src/interfaces/config.ts +++ b/packages/messenger-widget/src/interfaces/config.ts @@ -20,10 +20,15 @@ export type DisableDialogType = self?: | boolean | { - gnosis: boolean; - ens: boolean; + gnosis?: boolean; + ens?: boolean; }; }; + settings?: + | boolean + | { + messageView?: boolean; + }; }; export interface DM3Configuration { diff --git a/packages/messenger-widget/src/interfaces/props.ts b/packages/messenger-widget/src/interfaces/props.ts index e8425748a..b15b3bb66 100644 --- a/packages/messenger-widget/src/interfaces/props.ts +++ b/packages/messenger-widget/src/interfaces/props.ts @@ -25,6 +25,8 @@ export interface MessageProps { isLastMessage?: boolean; hideFunction?: string; indicator?: MessageIndicator; + showProfile?: boolean; + isFirstMsgOfDay?: boolean; } export interface MessageAction { diff --git a/packages/messenger-widget/src/interfaces/utils.ts b/packages/messenger-widget/src/interfaces/utils.ts index 06b825c62..debdf4618 100644 --- a/packages/messenger-widget/src/interfaces/utils.ts +++ b/packages/messenger-widget/src/interfaces/utils.ts @@ -3,6 +3,9 @@ import { Contact } from './context'; import humanIcon from '../assets/images/human.svg'; import { Socket } from 'socket.io-client'; import { DefaultEventsMap } from 'socket.io/dist/typed-events'; +import { Envelop } from '@dm3-org/dm3-lib-messaging'; +import { MessageIndicator, MessageSource } from '../hooks/messages/useMessage'; +import { StorageEnvelopContainer as StorageEnvelopContainerNew } from '@dm3-org/dm3-lib-storage'; export interface Connection { socket?: Socket; @@ -50,6 +53,15 @@ export interface IAttachmentPreview { isImage: boolean; } +export type MessagePropsModel = StorageEnvelopContainerNew & { + reactions: Envelop[]; + replyToMessageEnvelop?: Envelop; + source: MessageSource; + indicator?: MessageIndicator; + showProfile?: boolean; + isFirstMsgOfDay?: boolean; +}; + export const getEmptyContact = ( ensName: string, message: string | undefined, diff --git a/packages/messenger-widget/src/styles/common.css b/packages/messenger-widget/src/styles/common.css index 7336bc3af..a9132dd7c 100644 --- a/packages/messenger-widget/src/styles/common.css +++ b/packages/messenger-widget/src/styles/common.css @@ -154,6 +154,47 @@ input:focus { height: inherit; } +/* Customized CSS for radio button */ +.radio { + input[type="radio"] { + opacity: 0; + + .radio-label { + &:before { + content: ''; + background: #1F2029; + border-radius: 100%; + border: 1px solid var(--text-primary-color); + display: inline-block; + width: 1.4em; + height: 1.4em; + position: relative; + top: -0.2em; + margin-right: 1em; + vertical-align: top; + cursor: pointer; + text-align: center; + transition: all 250ms ease; + } + } + &:checked { + + .radio-label { + &:before { + background-color: var(--configure-profile-modal-background-color); + box-shadow: inset 0 0 0 4px #1F2029; + } + } + } + &:focus { + + .radio-label { + &:before { + outline: none; + border-color: var(--configure-profile-modal-background-color); + } + } + } + } + } + /* CSS to rotate the spinner svg image */ @-webkit-keyframes rotating { from { @@ -246,6 +287,17 @@ input:focus { backdrop-filter: brightness(0.3); } + .radio { + input[type="radio"] { + opacity: 0; + + .radio-label { + &:before { + width: 1.5em !important; + } + } + } + } + } @media only screen and (min-width: 800px) { diff --git a/packages/messenger-widget/src/version.ts b/packages/messenger-widget/src/version.ts index 824005cf5..61bffdb07 100644 --- a/packages/messenger-widget/src/version.ts +++ b/packages/messenger-widget/src/version.ts @@ -1 +1 @@ -export const version = '1.6.1'; +export const version = '1.7.0'; diff --git a/packages/messenger-widget/src/views/Home/Home.tsx b/packages/messenger-widget/src/views/Home/Home.tsx index 334e84f63..4636bc2c1 100644 --- a/packages/messenger-widget/src/views/Home/Home.tsx +++ b/packages/messenger-widget/src/views/Home/Home.tsx @@ -33,6 +33,7 @@ import { TLDContextProvider } from '../../context/TLDContext'; import { UiViewContextProvider } from '../../context/UiViewContext'; import { Dm3Props } from '../../interfaces/config'; import './Home.css'; +import { SettingsContextProvider } from '../../context/SettingsContext'; export function Home(props: Dm3Props) { /** @@ -101,17 +102,19 @@ export function Home(props: Dm3Props) { > - - - - - - - + + + + + + + + +