diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx index c50ad8a6b..5b39b6745 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx @@ -9,7 +9,6 @@ import { ChatDataContext } from '../../../context'; import { useChatData } from '../../../hooks'; import { ReplyIcon } from '../../../icons/PushIcons'; import { Div, Image, Section, Span } from '../../reusables'; -import { checkTwitterUrl } from '../helpers/twitter'; import { ThemeContext } from '../theme/ThemeProvider'; import { useConnectWallet, useSetChain } from '@web3-onboard/react'; diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubble/reactions/Reactions.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubble/reactions/Reactions.tsx index 36b883948..554163c03 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubble/reactions/Reactions.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubble/reactions/Reactions.tsx @@ -1,10 +1,10 @@ // React + Web3 Essentials -import { useContext, useRef, useState, useEffect, RefObject } from 'react'; +import { RefObject, useContext, useEffect, useRef, useState } from 'react'; // External Packages // Internal Compoonents -import { Image, Section, Button, Spinner, Span } from '../../../reusables'; +import { Button, Image, Section, Span, Spinner } from '../../../reusables'; import { ThemeContext } from '../../theme/ThemeProvider'; // Internal Configs @@ -42,6 +42,9 @@ export const Reactions = ({ chatReactions }: { chatReactions: IReactionsForChatM return acc; }, {} as IReactions); + // generate a unique key for the reactions + const reactionsKey = chatReactions.map((reaction) => reaction.reference).join('-'); + console.debug('UIWeb::components::ChatViewBubble::Reactions::uniqueReactions', uniqueReactions); // render reactions @@ -50,6 +53,7 @@ export const Reactions = ({ chatReactions }: { chatReactions: IReactionsForChatM <> {Object.keys(uniqueReactions).length > 2 ? (
(
{ +export const CardRenderer = ({ + chat, + position, + previewMode = false, + activeMode = false, +}: { + chat: IMessagePayload; + position: number; + previewMode?: boolean; + activeMode?: boolean; +}) => { // get theme const theme = useContext(ThemeContext); @@ -26,15 +36,6 @@ export const CardRenderer = ({ chat, position }: { chat: IMessagePayload; positi ? (typeof chat.messageObj?.content === 'string' ? chat.messageObj?.content : '') ?? '' : (chat.messageObj as string); - // check and render tweets - const { tweetId, messageType }: TwitterFeedReturnType = checkTwitterUrl({ - message: message, - }); - - if (messageType === 'TwitterFeedLink') { - chat.messageType = 'TwitterFeedLink'; - } - // test if the payload is encrypted, if so convert it to text if (isMessageEncrypted(message)) { chat.messageType = 'Text'; @@ -43,32 +44,71 @@ export const CardRenderer = ({ chat, position }: { chat: IMessagePayload; positi // get user account const account = user?.account ?? ''; + // deduce font color + const fontColor = + position && !activeMode ? theme.textColor?.chatSentBubbleText : theme.textColor?.chatReceivedBubbleText; + // Render the card render return ( <> {/* Message Card */} + {/* Twitter Card is handled by PreviewRenderer */} + {/* Frame Card is handled by PreviewRenderer */} + {/* Code Card is handled by CodeRenderer */} {chat && chat.messageType === 'Text' && ( )} {/* Image Card */} - {chat.messageType === 'Image' && } + {chat.messageType === 'Image' && ( + // Background only valid when no preview or active mode + + )} {/* File Card */} - {chat.messageType === 'File' && } + {chat.messageType === 'File' && ( + + )} {/* Gif Card */} - {chat.messageType === 'GIF' && } - - {/* Twitter Card */} - {chat.messageType === 'TwitterFeedLink' && ( - )} @@ -78,6 +118,9 @@ export const CardRenderer = ({ chat, position }: { chat: IMessagePayload; positi chat={chat} position={position} account={account} + color={fontColor} + previewMode={previewMode} + activeMode={activeMode} /> )} diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/ChatViewBubbleCore.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/ChatViewBubbleCore.tsx index f762f6e5f..ee261f0f7 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/ChatViewBubbleCore.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/ChatViewBubbleCore.tsx @@ -1,15 +1,32 @@ -import { ReactNode, useContext, useEffect, useRef, useState } from 'react'; +// React + Web3 Essentials +import { useContext } from 'react'; +// External Packages +import styled from 'styled-components'; + +// Internal Components import { useChatData } from '../../../hooks'; -import { checkTwitterUrl } from '../helpers/twitter'; import { ThemeContext } from '../theme/ThemeProvider'; import { isMessageEncrypted, pCAIP10ToWallet } from '../../../helpers'; import { IMessagePayload, TwitterFeedReturnType } from '../exportedTypes'; +import { Section } from '../../reusables'; import { CardRenderer } from './CardRenderer'; import { ReplyCard } from './cards/reply/ReplyCard'; +// Internal Configs + +// Assets + +// Interfaces & Types +interface ChatViewBubbleCoreProps extends React.ComponentProps { + borderBG?: string; + previewMode?: boolean; +} + +// Main Logic +// Deep Copy Helper Function function deepCopy(obj: T): T { if (obj === null || typeof obj !== 'object') { return obj; @@ -36,7 +53,18 @@ function deepCopy(obj: T): T { throw new Error(`Unable to copy obj! Its type isn't supported.`); } -export const ChatViewBubbleCore = ({ chat, chatId }: { chat: IMessagePayload; chatId: string | undefined }) => { +// Exported Default Component +export const ChatViewBubbleCore = ({ + chat, + chatId, + previewMode = false, + activeMode = false, +}: { + chat: IMessagePayload; + chatId: string | undefined; + previewMode?: boolean; + activeMode?: boolean; +}) => { // get theme const theme = useContext(ThemeContext); @@ -47,36 +75,6 @@ export const ChatViewBubbleCore = ({ chat, chatId }: { chat: IMessagePayload; ch const chatPosition = pCAIP10ToWallet(chat.fromDID).toLowerCase() !== pCAIP10ToWallet(user?.account ?? '')?.toLowerCase() ? 0 : 1; - // // manale reply payload loader - // type ReplyPayloadManagerType = { - // payload: IMessagePayload | null; - // loading: boolean; - // loaded: boolean; - // err: string | null; - // }; - - // const [replyPayloadManager, setReplyPayloadManager] = useState({ - // payload: null, - // loading: true, - // loaded: false, - // err: null, - // }); - - // const resolveReplyPayload = async (chat: IMessagePayload, reference: string | null) => { - // if (reference && chatId) { - // try { - // const payloads = await user?.chat.history(chatId, { reference: reference, limit: 1 }); - // const payload = payloads ? payloads[0] : null; - // console.log('resolving reply payload', payload); - // setReplyPayloadManager({ payload: payload, loading: false, loaded: true, err: null }); - // } catch (err) { - // setReplyPayloadManager({ payload: null, loading: false, loaded: true, err: 'Unable to load Preview' }); - // } - // } else { - // setReplyPayloadManager({ payload: null, loading: false, loaded: true, err: 'Reference not found' }); - // } - // }; - const renderBubble = (chat: IMessagePayload, position: number) => { const components: JSX.Element[] = []; @@ -97,8 +95,9 @@ export const ChatViewBubbleCore = ({ chat, chatId }: { chat: IMessagePayload; ch // Reply is it's own card that calls ChatViewBubbleCardRenderer // This avoids transitive recursion - // Use replyReference to check and call reply card - if (replyReference !== '') { + // Use replyReference to check and call reply card but only if activeMode is false + // as activeMode will be true when user is replying to a message + if (replyReference !== '' && !activeMode) { // Add Reply Card components.push( ); } - return <>{components}; + // deduce background color + // if active mode, use the normal background color as this is user replying to a message + // if preview mode, use the reply background color + // if not preview mode, use the normal background color + const background = activeMode + ? theme.backgroundColor?.chatActivePreviewBubbleBackground + : position + ? previewMode + ? theme.backgroundColor?.chatPreviewSentBubbleBackground + : theme.backgroundColor?.chatSentBubbleBackground + : previewMode + ? theme.backgroundColor?.chatPreviewRecievedBubbleBackground + : theme.backgroundColor?.chatReceivedBubbleBackground; + + return ( + + {components} + + ); }; return renderBubble(chat, chatPosition); }; + +const ChatViewBubbleCoreSection = styled(Section)` + border-left: ${({ borderBG, previewMode }) => (previewMode ? `4px solid ${borderBG || 'transparent'}` : 'none')}; +`; diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/file/FileCard.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/file/FileCard.tsx index b64a3f281..5d43f53dd 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/file/FileCard.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/file/FileCard.tsx @@ -1,4 +1,5 @@ // React + Web3 Essentials +import { useContext } from 'react'; // External Packages import styled from 'styled-components'; @@ -13,6 +14,7 @@ import { toSerialisedHexString, } from '../../../../../helpers'; import { Image, Section, Span } from '../../../../reusables'; +import { ThemeContext } from '../../../theme/ThemeProvider'; // Internal Configs import { FILE_ICON, allowedNetworks } from '../../../../../config'; @@ -44,7 +46,22 @@ const getParsedMessage = (message: string): FileMessageContent => { } }; -export const FileCard = ({ chat }: { chat: IMessagePayload }) => { +export const FileCard = ({ + chat, + background, + color, + previewMode, + activeMode, +}: { + chat: IMessagePayload; + background?: string; + color?: string; + previewMode: boolean; + activeMode: boolean; +}) => { + // get theme + const theme = useContext(ThemeContext); + // derive message const message = typeof chat.messageObj === 'object' ? (chat.messageObj?.content as string) ?? '' : (chat.messageObj as string); @@ -54,13 +71,14 @@ export const FileCard = ({ chat }: { chat: IMessagePayload }) => { return (
{ />
{shortenText(parsedMessage.name, 11)} {formatFileSize(parsedMessage.size)} @@ -91,7 +111,7 @@ export const FileCard = ({ chat }: { chat: IMessagePayload }) => { rel="noopener noreferrer" download > - +
); diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/gif/GIFCard.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/gif/GIFCard.tsx index e4bdb3f53..48b80a091 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/gif/GIFCard.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/gif/GIFCard.tsx @@ -1,9 +1,12 @@ // React + Web3 Essentials +import { useContext } from 'react'; // External Packages // Internal Compoonents import { Image, Section, Span } from '../../../../reusables'; +import { ThemeContext } from '../../../theme/ThemeProvider'; +import { Tag } from '../../tag/Tag'; // Internal Configs @@ -17,21 +20,55 @@ import { IMessagePayload } from '../../../exportedTypes'; // Exported Interfaces & Types // Exported Functions -export const GIFCard = ({ chat }: { chat: IMessagePayload }) => { +export const GIFCard = ({ + chat, + background = 'transparent', + color = 'inherit', // default to inherit + previewMode = false, + activeMode = false, +}: { + chat: IMessagePayload; + background?: string; + color?: string; + previewMode?: boolean; + activeMode?: boolean; +}) => { + // get theme + const theme = useContext(ThemeContext); + // derive message const message = typeof chat.messageObj === 'object' ? (chat.messageObj?.content as string) ?? '' : (chat.messageObj as string); return (
- +
+ +
+ + {previewMode && ( +
+ +
+ )}
); }; diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/image/ImageCard.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/image/ImageCard.tsx index ad63c299f..94ff160cf 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/image/ImageCard.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/image/ImageCard.tsx @@ -1,9 +1,12 @@ // React + Web3 Essentials +import { useContext } from 'react'; // External Packages // Internal Compoonents -import { Image, Section } from '../../../../reusables'; +import { Image, Section, Span } from '../../../../reusables'; +import { ThemeContext } from '../../../theme/ThemeProvider'; +import { Tag } from '../../tag/Tag'; // Internal Configs @@ -28,21 +31,55 @@ const getParsedMessage = (message: string) => { const getImageContent = (message: string) => getParsedMessage(message)?.content ?? ''; -export const ImageCard = ({ chat }: { chat: IMessagePayload }) => { +export const ImageCard = ({ + chat, + background = 'transparent', + color = 'inherit', // default to inherit + previewMode = false, + activeMode = false, +}: { + chat: IMessagePayload; + background?: string; + color?: string; + previewMode?: boolean; + activeMode?: boolean; +}) => { + // get theme + const theme = useContext(ThemeContext); + // derive message const message = typeof chat.messageObj === 'object' ? (chat.messageObj?.content as string) ?? '' : (chat.messageObj as string); return (
- +
+ +
+ + {previewMode && ( +
+ +
+ )}
); }; diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/message/MessageCard.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/message/MessageCard.tsx index bc7f9bd84..80bb888d4 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/message/MessageCard.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/message/MessageCard.tsx @@ -34,10 +34,16 @@ export const MessageCard = ({ chat, position, account, + color = 'inherit', // default to inherit + previewMode = false, + activeMode = false, }: { chat: IMessagePayload; position: number; account: string; + color?: string; + previewMode?: boolean; + activeMode?: boolean; }) => { // get theme const theme = useContext(ThemeContext); @@ -126,8 +132,19 @@ export const MessageCard = ({ return chunks; }; + // if preview mode, reduce the message to 100 characters and only 3 lines + const reduceMessage = (message: string) => { + const limitedMessage = message.slice(0, 100); + const lines = limitedMessage.split('\n'); + const reducedMessage = lines.slice(0, 3).join(' '); + return reducedMessage; + }; + // convert to fragments which can have different types - const fragments = splitMessageToMessages({ msg: message, type: 'text' }); + // if preview mode, skip fragments and only reduce message + const fragments = previewMode + ? [{ msg: reduceMessage(message), type: 'text' }] + : splitMessageToMessages({ msg: message, type: 'text' }); // To render individual fragments const renderTxtFragments = (message: string, fragmentIndex: number): ReactNode => { @@ -141,7 +158,7 @@ export const MessageCard = ({ fontWeight={ position ? `${theme.fontWeight?.chatSentBubbleText}` : `${theme.fontWeight?.chatReceivedBubbleText}` } - color={position ? `${theme.textColor?.chatSentBubbleText}` : `${theme.textColor?.chatReceivedBubbleText}`} + color={color} > {line.split(' ').map((word: string, wordIndex: number) => { const link = hasWebLink(word) ? extractWebLink(word) : ''; @@ -191,37 +208,37 @@ export const MessageCard = ({ // Render entire message return ( - + {/* Preview Renderer - Start with assuming preview is there, callback handles no preview */} {/* Message Rendering - Always happens */}
- {/* Timestamp rendering */} - - {time} - + {/* Timestamp rendering only when no preview mode */} + {!previewMode && ( + + {time} + + )} ); diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/message/PreviewRenderer.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/message/PreviewRenderer.tsx index 5a0380774..3fa37a08d 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/message/PreviewRenderer.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/message/PreviewRenderer.tsx @@ -2,10 +2,12 @@ import { useEffect, useState } from 'react'; // External Packages +import { TwitterTweetEmbed } from 'react-twitter-embed'; // Internal Compoonents import { IFrame } from '../../../../../types'; import { extractWebLink, getFormattedMetadata, hasWebLink, isSupportedVideoLink } from '../../../../../utilities'; +import { checkTwitterUrl } from '../../../helpers/twitter'; import { FrameRenderer } from './FrameRenderer'; import { VideoRenderer } from './VideoRenderer'; @@ -22,7 +24,7 @@ const PROXY_SERVER = 'https://proxy.push.org'; // Exported Interfaces & Types export interface IPreviewCallback { loading: boolean; - urlType: 'video' | 'frame' | 'other'; + urlType: 'video' | 'frame' | 'twitter' | 'other'; error: unknown | null; } @@ -32,18 +34,20 @@ export const PreviewRenderer = ({ account, messageId, previewCallback, + previewMode = false, }: { message: string | undefined; account: string; messageId: string; previewCallback?: (callback: IPreviewCallback) => void; + previewMode?: boolean; }) => { // setup frame data const [initialized, setInitialized] = useState({ loading: true, frameData: {} as IFrame, url: null as string | null, - urlType: 'other' as 'video' | 'frame' | 'other', + urlType: 'other' as 'video' | 'frame' | 'twitter' | 'other', error: null as unknown | null, }); @@ -90,9 +94,23 @@ export const PreviewRenderer = ({ } }; - if (message && hasWebLink(message)) { - const url = extractWebLink(message); - fetchMetaTags(url ?? ''); + if (message && hasWebLink(message) && !previewMode) { + // first check for twitter url + const twitterUrl = checkTwitterUrl(message); + + if (twitterUrl.isTweet) { + setInitialized((prevState) => ({ + ...prevState, + loading: false, + error: null, + url: `${twitterUrl.tweetId}`, + urlType: 'twitter', + })); + } else { + // extract web link and process + const url = extractWebLink(message); + fetchMetaTags(url ?? ''); + } } else { // Initiate the callback setInitialized((prevState) => ({ @@ -130,5 +148,7 @@ export const PreviewRenderer = ({ url={initialized.url} frameData={initialized.frameData} /> + ) : !initialized.loading && !initialized.error && initialized.url && initialized.urlType === 'twitter' ? ( + ) : null; }; diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/reply/ReplyCard.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/reply/ReplyCard.tsx index 74935e1e7..9a617f8df 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/reply/ReplyCard.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/cards/reply/ReplyCard.tsx @@ -1,12 +1,14 @@ // React + Web3 Essentials -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; // External Packages +import styled from 'styled-components'; // Internal Compoonents import { useChatData } from '../../../../../hooks'; -import { Image, Section } from '../../../../reusables'; +import { Section, Span } from '../../../../reusables'; +import { ThemeContext } from '../../../theme/ThemeProvider'; import { CardRenderer } from '../../CardRenderer'; // Internal Configs @@ -19,19 +21,12 @@ import { IMessagePayload } from '../../../exportedTypes'; // Constants // Exported Interfaces & Types +// Extend Section via ReplySectionProps +interface ReplySectionProps extends React.ComponentProps { + borderBG?: string; +} // Exported Functions -const getParsedMessage = (message: string) => { - try { - return JSON.parse(message); - } catch (error) { - console.error('UIWeb::components::ChatViewBubble::ImageCard::error while parsing image', error); - return null; - } -}; - -const getImageContent = (message: string) => getParsedMessage(message)?.content ?? ''; - export const ReplyCard = ({ reference, chatId, @@ -41,7 +36,8 @@ export const ReplyCard = ({ chatId: string | undefined; position?: number; }) => { - console.debug('UIWeb::components::ChatViewBubble::ReplyCard::chat', reference); + // get theme + const theme = useContext(ThemeContext); // get user const { user } = useChatData(); @@ -94,19 +90,73 @@ export const ReplyCard = ({ // render return ( -
- {!replyPayloadManager.loaded &&
Loading...
} + {/* Initial State */} + {!replyPayloadManager.loaded && ( + + Loading Preview... + + )} + {/* Error State */} + {replyPayloadManager.loaded && replyPayloadManager.err && ( + + {replyPayloadManager.err} + + )} + + {/* Loaded State */} {replyPayloadManager.loaded && replyPayloadManager.payload && ( )} -
+ ); }; + +const ReplySection = styled(Section)` + border-left: 4px solid ${({ borderBG }) => borderBG || 'transparent'}; +`; diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/tag/Tag.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/tag/Tag.tsx new file mode 100644 index 000000000..e9296472e --- /dev/null +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubbleCore/tag/Tag.tsx @@ -0,0 +1,47 @@ +// React + Web3 Essentials +import React, { useContext } from 'react'; + +// External Packages +import styled from 'styled-components'; + +// Internal Compoonents +import { Span } from '../../../reusables'; +import { ThemeContext } from '../../theme/ThemeProvider'; + +// Internal Configs + +// Assets + +// Interfaces & Types +import { IMessagePayload } from '../../exportedTypes'; + +// Constants + +// Exported Interfaces & Types +interface TagProps { + type: 'Image' | 'GIF' | 'Video' | 'Audio'; +} + +export const Tag = ({ type }: TagProps) => { + // get theme + const theme = useContext(ThemeContext); + + return ( + + {type} + + ); +}; diff --git a/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx b/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx index 00448d82d..ecb41dad5 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx @@ -526,6 +526,7 @@ export const ChatViewList: React.FC = (options: IChatViewLis { = ({ setAccessControl(chatId, true); setVerified(false); setVerificationSuccessfull(false); - setReplyPayload?.(null); } } catch (error) { console.log(error); + } finally { + // reset reply payload + setReplyPayload?.(null); } }; @@ -421,7 +423,12 @@ export const MessageInput: React.FC = ({ setGifOpen(false); }; - console.log('UIWeb::MessageInput::sendTextMsg::replyPayload', replyPayload); + // To focus when replyPayload is truthly + useEffect(() => { + if (replyPayload) { + textAreaRef.current?.focus(); + } + }, [replyPayload]); return !(user && !user?.readmode()) && isConnected ? ( = ({ flexDirection="column" alignItems="flex-start" overflow="hidden" - gap="4px" + gap="8px" >
= ({
)} @@ -649,6 +658,7 @@ export const MessageInput: React.FC = ({ )} { @@ -660,7 +670,6 @@ export const MessageInput: React.FC = ({ placeholder="Type your message..." onChange={(e) => onChangeTypedMessage(e.target.value)} value={typedMessage} - ref={textAreaRef} rows={1} /> {gif && ( diff --git a/packages/uiweb/src/lib/components/chat/exportedTypes.ts b/packages/uiweb/src/lib/components/chat/exportedTypes.ts index 4ccd98fb1..2afedbd38 100644 --- a/packages/uiweb/src/lib/components/chat/exportedTypes.ts +++ b/packages/uiweb/src/lib/components/chat/exportedTypes.ts @@ -93,7 +93,7 @@ export interface IChatProfile { export interface TwitterFeedReturnType { tweetId: string; - messageType: string; + isTweet: boolean; } export interface IToast { diff --git a/packages/uiweb/src/lib/components/chat/helpers/helper.ts b/packages/uiweb/src/lib/components/chat/helpers/helper.ts index a9b238719..027b843e7 100644 --- a/packages/uiweb/src/lib/components/chat/helpers/helper.ts +++ b/packages/uiweb/src/lib/components/chat/helpers/helper.ts @@ -168,12 +168,20 @@ export const transformChatItems: (items: IFeeds[]) => IChatPreviewPayload[] = (i // Typescript doesn't know about the messageObj property // Workaround: cast to any const modItem = item as any; - if (modItem.msg.messageType !== 'Reply') { - messageType = modItem.msg.messageType; - messageContent = modItem.msg.messageObj.content; + + if (modItem.msg.messageType === 'Reply') { + if (typeof modItem.msg.messageObj === 'object' && !Array.isArray(modItem.msg.messageObj)) { + messageType = modItem.msg.messageObj.content.messageType; + + if (modItem.msg.messageObj.content.messageObj) { + messageContent = modItem.msg.messageObj.content.messageObj.content; + } + } } else if (typeof modItem.msg.messageObj === 'object' && !Array.isArray(modItem.msg.messageObj)) { - messageType = modItem.msg.messageObj.content.messageType; - messageContent = modItem.msg.messageObj.content.messageObj.content; + messageType = modItem.msg.messageType; + if (modItem.msg.messageObj) { + messageContent = modItem.msg.messageObj.content; + } } return { diff --git a/packages/uiweb/src/lib/components/chat/helpers/twitter.ts b/packages/uiweb/src/lib/components/chat/helpers/twitter.ts index 5f4ee0034..8414bd317 100644 --- a/packages/uiweb/src/lib/components/chat/helpers/twitter.ts +++ b/packages/uiweb/src/lib/components/chat/helpers/twitter.ts @@ -1,30 +1,29 @@ import { TwitterFeedReturnType } from '../exportedTypes'; -interface TwitterFeedProps { - message: string; -} - -export const checkTwitterUrl = ({ message }: TwitterFeedProps): TwitterFeedReturnType => { +export const checkTwitterUrl = (message: string): TwitterFeedReturnType => { let tweetId = ''; - let messageType = ''; + let isTweet = false; const URL_REGEX = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/)?([\w#!:.?+=&%@!-]+)/; const messageContent = typeof message === 'string' ? message.split(' ') : []; for (let i = 0; i < messageContent?.length; i++) { - if (URL_REGEX.test(messageContent[i]) && messageContent[i].toLowerCase().includes('twitter')) { + if ( + (URL_REGEX.test(messageContent[i]) && messageContent[i].toLowerCase().includes('twitter')) || + messageContent[i].toLowerCase().includes('x') + ) { // Extracting tweetId const wordArray = messageContent[i].split('?')[0].split('/'); // split url at '?' and take first element and split at '/' if (wordArray?.length >= 6) { tweetId = wordArray[wordArray?.length - 1]; - messageType = 'TwitterFeedLink'; + isTweet = true; break; } else { - messageType = 'Text'; + isTweet = false; break; } } } - return { tweetId, messageType }; + return { tweetId, isTweet }; }; diff --git a/packages/uiweb/src/lib/components/chat/theme/index.ts b/packages/uiweb/src/lib/components/chat/theme/index.ts index 98e0ac550..36d8417da 100644 --- a/packages/uiweb/src/lib/components/chat/theme/index.ts +++ b/packages/uiweb/src/lib/components/chat/theme/index.ts @@ -1,8 +1,8 @@ /** * @file theme file: all the predefined themes are defined here */ +import styled, { css, keyframes } from 'styled-components'; import { CHAT_THEME_OPTIONS } from '../exportedTypes'; -import styled, { keyframes, css } from 'styled-components'; // bgColorPrimary: "#fff", // bgColorSecondary: "#D53A94", // textColorPrimary: "#1e1e1e", @@ -39,6 +39,8 @@ interface IBorderRadius { userProfile?: string; chatWidget?: string; chatBubbleBorderRadius?: string; + chatBubbleContentBorderRadius?: string; + chatBubbleReplyBorderRadius?: string; reactionsPickerBorderRadius?: string; reactionsBorderRadius?: string; } @@ -52,6 +54,8 @@ interface IPadding { messageInputPadding?: string; chatBubbleSenderPadding?: string; chatBubbleReceiverPadding?: string; + chatBubbleContentPadding?: string; + chatBubbleInnerContentPadding?: string; reactionsPickerPadding?: string; reactionsPadding?: string; } @@ -65,6 +69,8 @@ interface IMargin { messageInputMargin?: string; chatBubbleSenderMargin?: string; chatBubbleReceiverMargin?: string; + chatBubbleContentMargin?: string; + chatBubbleReplyMargin?: string; } interface IBackgroundColor { @@ -75,6 +81,13 @@ interface IBackgroundColor { messageInputBackground?: string; chatSentBubbleBackground?: string; chatReceivedBubbleBackground?: string; + chatPreviewSentBubbleBackground?: string; + chatPreviewSentBorderBubbleBackground?: string; + chatPreviewRecievedBubbleBackground?: string; + chatPreviewRecievedBorderBubbleBackground?: string; + chatActivePreviewBubbleBackground?: string; + chatActivePreviewBorderBubbleBackground?: string; + chatPreviewTagBackground?: string; chatFrameBackground?: string; encryptionMessageBackground?: string; buttonBackground?: string; @@ -237,6 +250,8 @@ export const lightChatTheme: IChatTheme = { userProfile: '0px', chatWidget: '24px', chatBubbleBorderRadius: '12px', + chatBubbleContentBorderRadius: '8px', + chatBubbleReplyBorderRadius: '12px', reactionsPickerBorderRadius: '12px', reactionsBorderRadius: '24px', }, @@ -250,6 +265,8 @@ export const lightChatTheme: IChatTheme = { messageInputPadding: '0px', chatBubbleSenderPadding: '0px', chatBubbleReceiverPadding: '0px', + chatBubbleContentPadding: '8px 16px', + chatBubbleInnerContentPadding: '8px 12px', reactionsPickerPadding: '4px', reactionsPadding: '4px 8px', }, @@ -263,6 +280,8 @@ export const lightChatTheme: IChatTheme = { messageInputMargin: '2px 10px 10px 10px', chatBubbleSenderMargin: '16px 8px', chatBubbleReceiverMargin: '16px 8px', + chatBubbleContentMargin: '8px', + chatBubbleReplyMargin: '8px 8px 0px 8px', }, backgroundColor: { @@ -274,6 +293,13 @@ export const lightChatTheme: IChatTheme = { messageInputBackground: '#fff', chatSentBubbleBackground: 'rgb(202, 89, 155)', chatReceivedBubbleBackground: '#fff', + chatPreviewSentBubbleBackground: 'rgba(255, 255, 255, 0.1)', + chatPreviewSentBorderBubbleBackground: 'rgba(255, 255, 255, 0.5)', + chatPreviewRecievedBubbleBackground: 'rgba(0, 0, 0, 0.1)', + chatPreviewRecievedBorderBubbleBackground: 'rgba(0, 0, 0, 0.5)', + chatActivePreviewBubbleBackground: '#22222210', + chatActivePreviewBorderBubbleBackground: '#22222299', + chatPreviewTagBackground: 'rgba(0, 0, 0, 0.25)', chatFrameBackground: '#f5f5f5', encryptionMessageBackground: '#fff', buttonBackground: 'rgb(202, 89, 155)', @@ -412,6 +438,8 @@ export const darkChatTheme: IChatTheme = { userProfile: '0px', chatWidget: '24px', chatBubbleBorderRadius: '12px', + chatBubbleContentBorderRadius: '8px', + chatBubbleReplyBorderRadius: '8px', reactionsPickerBorderRadius: '12px', reactionsBorderRadius: '24px', }, @@ -425,6 +453,8 @@ export const darkChatTheme: IChatTheme = { messageInputPadding: '0px', chatBubbleSenderPadding: '0px', chatBubbleReceiverPadding: '0px', + chatBubbleContentPadding: '8px 16px', + chatBubbleInnerContentPadding: '8px 12px', reactionsPickerPadding: '4px', reactionsPadding: '4px 8px', }, @@ -438,6 +468,8 @@ export const darkChatTheme: IChatTheme = { messageInputMargin: '2px 10px 10px 10px', chatBubbleSenderMargin: '16px 8px', chatBubbleReceiverMargin: '16px 8px', + chatBubbleContentMargin: '8px', + chatBubbleReplyMargin: '8px', }, backgroundColor: { @@ -449,6 +481,13 @@ export const darkChatTheme: IChatTheme = { messageInputBackground: 'rgb(64, 70, 80)', chatSentBubbleBackground: 'rgb(202, 89, 155)', chatReceivedBubbleBackground: 'rgb(64, 70, 80)', + chatPreviewSentBubbleBackground: 'rgba(255, 255, 255, 0.1)', + chatPreviewSentBorderBubbleBackground: 'rgba(255, 255, 255, 0.5)', + chatPreviewRecievedBubbleBackground: 'rgba(0, 0, 0, 0.1)', + chatPreviewRecievedBorderBubbleBackground: 'rgba(0, 0, 0, 0.5)', + chatActivePreviewBubbleBackground: '#ffffff10', + chatActivePreviewBorderBubbleBackground: '#ffffff99', + chatPreviewTagBackground: 'rgba(255, 255, 255, 0.25)', chatFrameBackground: '#343536', encryptionMessageBackground: 'rgb(64, 70, 80)', buttonBackground: 'rgb(202, 89, 155)', diff --git a/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx b/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx index d9aeec563..967de57f0 100644 --- a/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx +++ b/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx @@ -225,7 +225,7 @@ export const ChatUIProvider = ({ enableConsole(); } else { console.warn('UIWeb::ChatDataProvider::Debug mode is turned off, console logs are suppressed'); - // disableConsole(); + disableConsole(); } }, [debug]);