chat portion of unencrypted open groups

This commit is contained in:
PhilReact 2024-12-15 08:05:11 +02:00
parent 50955941e4
commit 4cafd57abe
10 changed files with 335 additions and 143 deletions

View File

@ -664,8 +664,7 @@ const handleNotification = async (groups) => {
const data = groups.filter(
(group) =>
group?.sender !== address &&
!mutedGroups.includes(group.groupId) &&
!isUpdateMsg(group?.data)
!mutedGroups.includes(group.groupId)
);
const dataWithUpdates = groups.filter(
(group) => group?.sender !== address && !mutedGroups.includes(group.groupId)
@ -716,8 +715,7 @@ const handleNotification = async (groups) => {
Date.now() - lastGroupNotification >= 120000
) {
if (
!newestLatestTimestamp?.data ||
!isExtMsg(newestLatestTimestamp?.data)
!newestLatestTimestamp?.data
)
return;

View File

@ -27,7 +27,7 @@ import { throttle } from 'lodash'
const uid = new ShortUniqueId({ length: 5 });
export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, hide, handleSecretKeyCreationInProgress, triedToFetchSecretKey, myName, balance, getTimestampEnterChatParent, hideView}) => {
export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, hide, handleSecretKeyCreationInProgress, triedToFetchSecretKey, myName, balance, getTimestampEnterChatParent, hideView, isPrivate}) => {
const [messages, setMessages] = useState([])
const [chatReferences, setChatReferences] = useState({})
const [isSending, setIsSending] = useState(false)
@ -223,7 +223,7 @@ const [messageSize, setMessageSize] = useState(0)
setChatReferences((prev) => {
const organizedChatReferences = { ...prev };
combineUIAndExtensionMsgs
.filter((rawItem) => rawItem && rawItem.chatReference && (rawItem.decryptedData?.type === "reaction" || rawItem.decryptedData?.type === "edit"))
.filter((rawItem) => rawItem && rawItem.chatReference && (rawItem.decryptedData?.type === "reaction" || rawItem.decryptedData?.type === "edit" || rawItem?.type === "edit" || rawItem?.type === "reaction"))
.forEach((item) => {
try {
if(item.decryptedData?.type === "edit"){
@ -231,11 +231,16 @@ const [messageSize, setMessageSize] = useState(0)
...(organizedChatReferences[item.chatReference] || {}),
edit: item.decryptedData,
};
} else if(item?.type === "edit"){
organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}),
edit: item,
};
} else {
const content = item.decryptedData?.content;
const content = item?.content || item.decryptedData?.content;
const sender = item.sender;
const newTimestamp = item.timestamp;
const contentState = item.decryptedData?.contentState;
const contentState = item?.contentState || item.decryptedData?.contentState;
if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) {
console.warn("Invalid content, sender, or timestamp in reaction data", item);
@ -306,7 +311,7 @@ const [messageSize, setMessageSize] = useState(0)
const organizedChatReferences = { ...prev };
combineUIAndExtensionMsgs
.filter((rawItem) => rawItem && rawItem.chatReference && (rawItem.decryptedData?.type === "reaction" || rawItem.decryptedData?.type === "edit"))
.filter((rawItem) => rawItem && rawItem.chatReference && (rawItem.decryptedData?.type === "reaction" || rawItem.decryptedData?.type === "edit" || rawItem?.type === "edit" || rawItem?.type === "reaction"))
.forEach((item) => {
try {
if(item.decryptedData?.type === "edit"){
@ -314,11 +319,16 @@ const [messageSize, setMessageSize] = useState(0)
...(organizedChatReferences[item.chatReference] || {}),
edit: item.decryptedData,
};
} else if(item?.type === "edit"){
organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}),
edit: item,
};
} else {
const content = item.decryptedData?.content;
const content = item?.content || item.decryptedData?.content;
const sender = item.sender;
const newTimestamp = item.timestamp;
const contentState = item.decryptedData?.contentState;
const contentState = item?.contentState || item.decryptedData?.contentState;
if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) {
console.warn("Invalid content, sender, or timestamp in reaction data", item);
@ -453,10 +463,11 @@ const [messageSize, setMessageSize] = useState(0)
setIsLoading(true)
initWebsocketMessageGroup()
}
}, [triedToFetchSecretKey, secretKey])
}, [triedToFetchSecretKey, secretKey, isPrivate])
useEffect(()=> {
if(!secretKey || hasInitializedWebsocket.current) return
if(isPrivate === null) return
if(isPrivate === false || !secretKey || hasInitializedWebsocket.current) return
forceCloseWebSocket()
setMessages([])
setIsLoading(true)
@ -466,7 +477,7 @@ const [messageSize, setMessageSize] = useState(0)
}, 6000);
initWebsocketMessageGroup()
hasInitializedWebsocket.current = true
}, [secretKey])
}, [secretKey, isPrivate])
useEffect(()=> {
@ -551,6 +562,7 @@ const clearEditorContent = () => {
const sendMessage = async ()=> {
try {
if(isPrivate === null) throw new Error('Unable to determine if group is private')
if(isSending) return
if(+balance < 4) throw new Error('You need at least 4 QORT to send a message')
pauseAllQueues()
@ -558,8 +570,10 @@ const clearEditorContent = () => {
const htmlContent = editorRef.current.getHTML();
if(!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') return
setIsSending(true)
const message = htmlContent
const message = isPrivate === false ? editorRef.current.getJSON() : htmlContent
const secretKeyObject = await getSecretKey(false, true)
let repliedTo = replyMessage?.signature
@ -569,19 +583,24 @@ const clearEditorContent = () => {
}
let chatReference = onEditMessage?.signature
const publicData = isPrivate ? {} : {
isEdited : chatReference ? true : false,
}
const otherData = {
repliedTo,
...(onEditMessage?.decryptedData || {}),
type: chatReference ? 'edit' : '',
specialId: uid.rnd(),
...publicData
}
const objectMessage = {
...(otherData || {}),
message
[isPrivate ? 'message' : 'messageText']: message,
version: 3
}
const message64: any = await objectToBase64(objectMessage)
const encryptSingle = await encryptChatMessage(message64, secretKeyObject)
const encryptSingle = isPrivate === false ? JSON.stringify(objectMessage) : await encryptChatMessage(message64, secretKeyObject)
// const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
const sendMessageFunc = async () => {
@ -591,7 +610,7 @@ const clearEditorContent = () => {
// Add the function to the queue
const messageObj = {
message: {
text: message,
text: htmlContent,
timestamp: Date.now(),
senderName: myName,
sender: myAddress,
@ -668,7 +687,7 @@ const clearEditorContent = () => {
const onEdit = useCallback((message)=> {
setOnEditMessage(message)
setReplyMessage(null)
editorRef.current.chain().focus().setContent(message?.text).run();
editorRef.current.chain().focus().setContent(message?.messageText || message?.text).run();
}, [])
const handleReaction = useCallback(async (reaction, chatMessage, reactionState = true)=> {
@ -696,7 +715,7 @@ const clearEditorContent = () => {
}
const message64: any = await objectToBase64(objectMessage)
const reactiontypeNumber = RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS
const encryptSingle = await encryptChatMessage(message64, secretKeyObject, reactiontypeNumber)
const encryptSingle = isPrivate === false ? JSON.stringify(objectMessage) : await encryptChatMessage(message64, secretKeyObject, reactiontypeNumber)
// const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
const sendMessageFunc = async () => {
@ -752,9 +771,9 @@ const clearEditorContent = () => {
left: hide && '-100000px',
}}>
<ChatList hasSecretKey={!!secretKey} openQManager={openQManager} enableMentions onReply={onReply} onEdit={onEdit} chatId={selectedGroup} initialMessages={messages} myAddress={myAddress} tempMessages={tempMessages} handleReaction={handleReaction} chatReferences={chatReferences} tempChatReferences={tempChatReferences} members={members} myName={myName} selectedGroup={selectedGroup} />
<ChatList isPrivate={isPrivate} hasSecretKey={!!secretKey} openQManager={openQManager} enableMentions onReply={onReply} onEdit={onEdit} chatId={selectedGroup} initialMessages={messages} myAddress={myAddress} tempMessages={tempMessages} handleReaction={handleReaction} chatReferences={chatReferences} tempChatReferences={tempChatReferences} members={members} myName={myName} selectedGroup={selectedGroup} />
{!!secretKey && (
{(!!secretKey || isPrivate === false) && (
<div style={{
// position: 'fixed',
// bottom: '0px',

View File

@ -28,13 +28,13 @@ export const ChatList = ({
selectedGroup,
enableMentions,
openQManager,
hasSecretKey
hasSecretKey,
isPrivate
}) => {
const parentRef = useRef();
const [messages, setMessages] = useState(initialMessages);
const [showScrollButton, setShowScrollButton] = useState(false);
const [showScrollDownButton, setShowScrollDownButton] = useState(false);
const hasLoadedInitialRef = useRef(false);
const scrollingIntervalRef = useRef(null);
const lastSeenUnreadMessageTimestamp = useRef(null);
@ -272,7 +272,10 @@ export const ChatList = ({
message.text = chatReferences[message.signature]?.edit?.message;
message.isEdit = true
}
if (chatReferences[message.signature]?.edit?.messageText && message?.messageText) {
message.messageText = chatReferences[message.signature]?.edit?.messageText;
message.isEdit = true
}
}
@ -315,7 +318,6 @@ export const ChatList = ({
);
}
return (
<div
data-index={virtualRow.index} //needed for dynamic row height measurement
@ -357,6 +359,7 @@ export const ChatList = ({
handleReaction={handleReaction}
reactions={reactions}
isUpdating={isUpdating}
isPrivate={isPrivate}
/>
</ErrorBoundary>
</div>
@ -408,7 +411,7 @@ export const ChatList = ({
</button>
)}
</div>
{enableMentions && hasSecretKey && (
{enableMentions && (hasSecretKey || isPrivate === false) && (
<ChatOptions
openQManager={openQManager}
messages={messages}
@ -416,6 +419,7 @@ export const ChatList = ({
members={members}
myName={myName}
selectedGroup={selectedGroup}
isPrivate={isPrivate}
/>
)}
</Box>

View File

@ -13,6 +13,10 @@ import { Spacer } from "../../common/Spacer";
import AlternateEmailIcon from "@mui/icons-material/AlternateEmail";
import CloseIcon from "@mui/icons-material/Close";
import InsertLinkIcon from '@mui/icons-material/InsertLink';
import Highlight from "@tiptap/extension-highlight";
import Mention from "@tiptap/extension-mention";
import StarterKit from "@tiptap/starter-kit";
import Underline from "@tiptap/extension-underline";
import {
AppsSearchContainer,
AppsSearchLeft,
@ -32,6 +36,8 @@ import { useVirtualizer } from "@tanstack/react-virtual";
import { formatTimestamp } from "../../utils/time";
import { ContextMenuMentions } from "../ContextMenuMentions";
import { convert } from 'html-to-text';
import { generateHTML } from "@tiptap/react";
import ErrorBoundary from "../../common/ErrorBoundary";
const extractTextFromHTML = (htmlString = '') => {
return convert(htmlString, {
@ -43,7 +49,7 @@ const cache = new CellMeasurerCache({
defaultHeight: 50,
});
export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGroup, openQManager }) => {
export const ChatOptions = ({ messages : untransformedMessages, goToMessage, members, myName, selectedGroup, openQManager, isPrivate }) => {
const [mode, setMode] = useState("default");
const [searchValue, setSearchValue] = useState("");
const [selectedMember, setSelectedMember] = useState(0);
@ -52,7 +58,27 @@ export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGr
const parentRefMentions = useRef();
const [lastMentionTimestamp, setLastMentionTimestamp] = useState(null)
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
const messages = useMemo(()=> {
return untransformedMessages?.map((item)=> {
if(item?.messageText){
let transformedMessage = item?.messageText
try {
transformedMessage = generateHTML(item?.messageText, [
StarterKit,
Underline,
Highlight,
Mention
])
return {
...item,
messageText: transformedMessage
}
} catch (error) {
// error
}
} else return item
})
}, [untransformedMessages])
const getTimestampMention = async () => {
try {
return new Promise((res, rej) => {
@ -124,7 +150,7 @@ export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGr
.filter(
(message) =>
message?.senderName === selectedMember &&
extractTextFromHTML(message?.decryptedData?.message)?.includes(
extractTextFromHTML(isPrivate ? message?.messageText : message?.decryptedData?.message)?.includes(
debouncedValue.toLowerCase()
)
)
@ -132,20 +158,27 @@ export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGr
}
return messages
.filter((message) =>
extractTextFromHTML(message?.decryptedData?.message)?.includes(debouncedValue.toLowerCase())
extractTextFromHTML(isPrivate === false ? message?.messageText : message?.decryptedData?.message)?.includes(debouncedValue.toLowerCase())
)
?.sort((a, b) => b?.timestamp - a?.timestamp);
}, [debouncedValue, messages, selectedMember]);
}, [debouncedValue, messages, selectedMember, isPrivate]);
const mentionList = useMemo(() => {
if(!messages || messages.length === 0 || !myName) return []
if(isPrivate === false){
return messages
.filter((message) =>
extractTextFromHTML(message?.messageText)?.includes(`@${myName}`)
)
?.sort((a, b) => b?.timestamp - a?.timestamp);
}
return messages
.filter((message) =>
extractTextFromHTML(message?.decryptedData?.message)?.includes(`@${myName}`)
)
?.sort((a, b) => b?.timestamp - a?.timestamp);
}, [messages, myName]);
}, [messages, myName, isPrivate]);
const rowVirtualizer = useVirtualizer({
count: searchedList.length,
@ -291,7 +324,8 @@ export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGr
gap: "5px",
}}
>
<Box
<ShowMessage messages={messages} goToMessage={goToMessage} message={message} />
{/* <Box
sx={{
display: "flex",
flexDirection: "column",
@ -363,7 +397,7 @@ export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGr
}
/>
</Box>
</Box>
</Box> */}
</div>
);
})}
@ -544,6 +578,7 @@ export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGr
const index = virtualRow.index;
let message = searchedList[index];
return (
<div
data-index={virtualRow.index} //needed for dynamic row height measurement
ref={rowVirtualizer.measureElement} //measure dynamic row height
@ -562,80 +597,17 @@ export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGr
gap: "5px",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
padding: "0px 20px",
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "15px",
}}
>
<Avatar
sx={{
backgroundColor: "#27282c",
color: "white",
height: "25px",
width: "25px",
}}
alt={message?.senderName}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
message?.senderName
}/qortal_avatar?async=true`}
>
{message?.senderName?.charAt(0)}
</Avatar>
<Typography
sx={{
fontWight: 600,
fontFamily: "Inter",
color: "cadetBlue",
}}
>
{message?.senderName}
</Typography>
</Box>
</Box>
<Spacer height="5px" />
<Typography sx={{
fontSize: '12px'
}}>{formatTimestamp(message.timestamp)}</Typography>
<Box
style={{
cursor: "pointer",
}}
onClick={() => {
const findMsgIndex = messages.findIndex(
(item) =>
item?.signature === message?.signature
);
if (findMsgIndex !== -1) {
goToMessage(findMsgIndex);
}
}}
>
<MessageDisplay
htmlContent={
message?.decryptedData?.message || "<p></p>"
}
/>
</Box>
</Box>
<ErrorBoundary
fallback={
<Typography>
Error loading content: Invalid Data
</Typography>
}
>
<ShowMessage message={message} goToMessage={goToMessage} messages={messages} />
</ErrorBoundary>
</div>
);
})}
</div>
@ -705,3 +677,91 @@ export const ChatOptions = ({ messages, goToMessage, members, myName, selectedGr
</Box>
);
};
const ShowMessage = ({message, goToMessage, messages})=> {
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
padding: "0px 20px",
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "15px",
}}
>
<Avatar
sx={{
backgroundColor: "#27282c",
color: "white",
height: "25px",
width: "25px",
}}
alt={message?.senderName}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
message?.senderName
}/qortal_avatar?async=true`}
>
{message?.senderName?.charAt(0)}
</Avatar>
<Typography
sx={{
fontWight: 600,
fontFamily: "Inter",
color: "cadetBlue",
}}
>
{message?.senderName}
</Typography>
</Box>
</Box>
<Spacer height="5px" />
<Typography sx={{
fontSize: '12px'
}}>{formatTimestamp(message.timestamp)}</Typography>
<Box
style={{
cursor: "pointer",
}}
onClick={() => {
const findMsgIndex = messages.findIndex(
(item) =>
item?.signature === message?.signature
);
if (findMsgIndex !== -1) {
goToMessage(findMsgIndex);
}
}}
>
{message?.messageText && (
<MessageDisplay
htmlContent={message?.messageText}
/>
)}
{message?.decryptedData?.message && (
<MessageDisplay
htmlContent={
message?.decryptedData?.message || "<p></p>"
}
/>
)}
</Box>
</Box>
)
}

View File

@ -106,7 +106,7 @@ export const MessageDisplay = ({ htmlContent, isReply }) => {
}
};
const embedLink = htmlContent.match(/qortal:\/\/use-embed\/[^\s<>]+/);
const embedLink = htmlContent?.match(/qortal:\/\/use-embed\/[^\s<>]+/);
let embedData = null;

View File

@ -8,6 +8,7 @@ import { getBaseApi } from "../../background";
import { getBaseApiReact } from "../../App";
import { generateHTML } from "@tiptap/react";
import Highlight from "@tiptap/extension-highlight";
import Mention from "@tiptap/extension-mention";
import StarterKit from "@tiptap/starter-kit";
import Underline from "@tiptap/extension-underline";
import { executeEvent } from "../../utils/events";
@ -33,13 +34,15 @@ export const MessageItem = ({
reactions,
isUpdating,
lastSignature,
onEdit
onEdit,
isPrivate
}) => {
const { ref, inView } = useInView({
threshold: 0.7, // Fully visible
triggerOnce: false, // Only trigger once when it becomes visible
});
const [anchorEl, setAnchorEl] = useState(null);
const [selectedReaction, setSelectedReaction] = useState(null);
@ -136,7 +139,7 @@ export const MessageItem = ({
gap: '10px',
alignItems: 'center'
}}>
{message?.sender === myAddress && !message?.isNotEncrypted && (
{message?.sender === myAddress && (!message?.isNotEncrypted || isPrivate === false) && (
<ButtonBase
onClick={() => {
onEdit(message);
@ -205,6 +208,7 @@ export const MessageItem = ({
StarterKit,
Underline,
Highlight,
Mention
])}
/>
)}
@ -223,6 +227,7 @@ export const MessageItem = ({
StarterKit,
Underline,
Highlight,
Mention
])}
/>
)}
@ -341,7 +346,7 @@ export const MessageItem = ({
alignItems: 'center',
gap: '15px'
}}>
{message?.isNotEncrypted && (
{message?.isNotEncrypted && isPrivate && (
<KeyOffIcon sx={{
color: 'white',
marginLeft: '10px'
@ -456,6 +461,7 @@ export const ReplyPreview = ({message, isEdit})=> {
StarterKit,
Underline,
Highlight,
Mention
])}
/>
)}

View File

@ -125,7 +125,7 @@
font-size: 12px !important;
}
.tiptap .mention {
.tiptap [data-type="mention"] {
box-decoration-break: clone;
color: lightblue;
padding: 0.1rem 0.3rem;

View File

@ -19,6 +19,8 @@ import { ChatIcon } from "../../assets/Icons/ChatIcon";
import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon";
import { MembersIcon } from "../../assets/Icons/MembersIcon";
import { AdminsIcon } from "../../assets/Icons/AdminsIcon";
import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
const IconWrapper = ({ children, label, color, selected, selectColor, customHeight }) => {
return (
@ -80,7 +82,8 @@ export const DesktopHeader = ({
hasUnreadChat,
isChat,
isForum,
setGroupSection
setGroupSection,
isPrivate
}) => {
const [value, setValue] = React.useState(0);
return (
@ -95,7 +98,20 @@ export const DesktopHeader = ({
padding: "10px",
}}
>
<Box>
<Box sx={{
display: 'flex',
gap: '10px'
}}>
{isPrivate && (
<LockIcon sx={{
color: 'var(--green)'
}} />
)}
{isPrivate === false && (
<NoEncryptionGmailerrorredIcon sx={{
color: 'var(--unread)'
}} />
)}
<Typography
sx={{
fontSize: "16px",

View File

@ -9,6 +9,7 @@ import {
Typography,
} from "@mui/material";
import React, {
useCallback,
useContext,
useEffect,
useMemo,
@ -77,8 +78,8 @@ import { useSetRecoilState } from "recoil";
import { selectedGroupIdAtom } from "../../atoms/global";
import { sortArrayByTimestampAndGroupName } from "../../utils/time";
import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
export const getPublishesFromAdmins = async (admins: string[], groupId) => {
@ -347,6 +348,19 @@ export const getNamesForAdmins = async (admins) => {
return members;
};
function areKeysEqual(array1, array2) {
// If lengths differ, the arrays cannot be equal
if (array1?.length !== array2?.length) {
return false;
}
// Sort both arrays and compare their elements
const sortedArray1 = [...array1].sort();
const sortedArray2 = [...array2].sort();
return sortedArray1.every((key, index) => key === sortedArray2[index]);
}
export const Group = ({
myAddress,
isFocused,
@ -418,6 +432,17 @@ export const Group = ({
const [appsModeDev, setAppsModeDev] = useState('home')
const [isOpenSideViewDirects, setIsOpenSideViewDirects] = useState(false)
const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false)
const [groupsProperties, setGroupsProperties] = useState({})
const isPrivate = useMemo(()=> {
if(!selectedGroup?.groupId || !groupsProperties[selectedGroup?.groupId]) return null
if(groupsProperties[selectedGroup?.groupId]?.isOpen === true) return false
if(groupsProperties[selectedGroup?.groupId]?.isOpen === false) return true
return null
}, [selectedGroup])
const setSelectedGroupId = useSetRecoilState(selectedGroupIdAtom)
const toggleSideViewDirects = ()=> {
if(isOpenSideViewGroups){
@ -569,9 +594,8 @@ export const Group = ({
if (
group?.data &&
isExtMsg(group?.data) &&
group?.sender !== myAddress &&
group?.timestamp && (!isUpdateMsg(group?.data) || groupChatTimestamps[group?.groupId]) &&
group?.timestamp && groupChatTimestamps[group?.groupId] &&
((!timestampEnterData[group?.groupId] &&
Date.now() - group?.timestamp < timeDifferenceForNotificationChats) ||
timestampEnterData[group?.groupId] < group?.timestamp)
@ -706,12 +730,19 @@ export const Group = ({
useEffect(() => {
if (selectedGroup) {
setTriedToFetchSecretKey(false);
getSecretKey(true);
if (selectedGroup && isPrivate !== null) {
if(isPrivate){
setTriedToFetchSecretKey(false);
getSecretKey(true);
}
getGroupOwner(selectedGroup?.groupId);
}
}, [selectedGroup]);
if(isPrivate === false){
setTriedToFetchSecretKey(true);
}
}, [selectedGroup, isPrivate]);
@ -735,9 +766,8 @@ export const Group = ({
const groupData = {}
const getGroupData = groups.map(async(group)=> {
const isUpdate = isUpdateMsg(group?.data)
if(!group.groupId || !group?.timestamp) return null
if(isUpdate && (!groupData[group.groupId] || groupData[group.groupId] < group.timestamp)){
if((!groupData[group.groupId] || groupData[group.groupId] < group.timestamp)){
const hasMoreRecentMsg = await getCountNewMesg(group.groupId, timestampEnterDataRef.current[group?.groupId] || Date.now() - 24 * 60 * 60 * 1000)
if(hasMoreRecentMsg){
groupData[group.groupId] = hasMoreRecentMsg
@ -754,6 +784,32 @@ export const Group = ({
}
}
const getGroupsProperties = useCallback(async(address)=> {
try {
const url = `${getBaseApiReact()}/groups/member/${address}`;
const response = await fetch(url);
if(!response.ok) throw new Error('Cannot get group properties')
let data = await response.json();
const transformToObject = data.reduce((result, item) => {
result[item.groupId] = item
return result;
}, {});
setGroupsProperties(transformToObject)
} catch (error) {
// error
}
}, [])
useEffect(()=> {
if(!myAddress) return
if(areKeysEqual(groups?.map((grp)=> grp?.groupId), Object.keys(groupsProperties))){
} else {
getGroupsProperties(myAddress)
}
}, [groups, myAddress])
useEffect(() => {
@ -941,9 +997,9 @@ export const Group = ({
.filter((group) => group?.sender !== myAddress)
.find((gr) => gr?.groupId === selectedGroup?.groupId);
if (!findGroup) return false;
if (!findGroup?.data || !isExtMsg(findGroup?.data)) return false;
if (!findGroup?.data) return false;
return (
findGroup?.timestamp && (!isUpdateMsg(findGroup?.data) || groupChatTimestamps[findGroup?.groupId]) &&
findGroup?.timestamp && groupChatTimestamps[findGroup?.groupId] &&
((!timestampEnterData[selectedGroup?.groupId] &&
Date.now() - findGroup?.timestamp <
timeDifferenceForNotificationChats) ||
@ -1657,6 +1713,7 @@ export const Group = ({
);
};
const renderGroups = () => {
return (
<div
@ -1805,15 +1862,45 @@ export const Group = ({
}}
>
<ListItemAvatar>
<Avatar
sx={{
{groupsProperties[group?.groupId]?.isOpen === false ? (
<Box sx={{
width: '40px',
height: '40px',
borderRadius: '50%',
background: "#232428",
color: "white",
}}
alt={group?.groupName}
>
{group.groupName?.charAt(0)}
</Avatar>
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<LockIcon sx={{
color: 'var(--green)'
}} />
</Box>
): (
<Box sx={{
width: '40px',
height: '40px',
borderRadius: '50%',
background: "#232428",
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<NoEncryptionGmailerrorredIcon sx={{
color: 'var(--unread)'
}} />
</Box>
// <Avatar
// sx={{
// background: "#232428",
// color: "white",
// }}
// alt={group?.groupName}
// >
// {group.groupName?.charAt(0)}
// </Avatar>
)}
</ListItemAvatar>
<ListItemText
primary={group.groupName}
@ -1849,7 +1936,7 @@ export const Group = ({
/>
)}
{group?.data &&
isExtMsg(group?.data) && (!isUpdateMsg(group?.data) || groupChatTimestamps[group?.groupId]) &&
groupChatTimestamps[group?.groupId] &&
group?.sender !== myAddress &&
group?.timestamp &&
((!timestampEnterData[group?.groupId] &&
@ -2085,6 +2172,7 @@ export const Group = ({
{!isMobile && (
<DesktopHeader
isPrivate={isPrivate}
selectedGroup={selectedGroup}
groupSection={groupSection}
isUnread={isUnread}
@ -2138,6 +2226,7 @@ export const Group = ({
selectedGroup={selectedGroup?.groupId}
getSecretKey={getSecretKey}
secretKey={secretKey}
isPrivate={isPrivate}
setSecretKey={setSecretKey}
handleNewEncryptionNotification={
setNewEncryptionNotification
@ -2153,7 +2242,7 @@ export const Group = ({
getTimestampEnterChatParent={getTimestampEnterChat}
/>
)}
{firstSecretKeyInCreation &&
{isPrivate &&firstSecretKeyInCreation &&
triedToFetchSecretKey &&
!secretKeyPublishDate && (
<div
@ -2174,7 +2263,7 @@ export const Group = ({
</Typography>
</div>
)}
{!admins.includes(myAddress) &&
{isPrivate && !admins.includes(myAddress) &&
!secretKey &&
triedToFetchSecretKey ? (
<>
@ -2231,7 +2320,7 @@ export const Group = ({
) : null}
</>
) : admins.includes(myAddress) &&
!secretKey &&
(!secretKey && isPrivate) &&
triedToFetchSecretKey ? null : !triedToFetchSecretKey ? null : (
<>
<GroupAnnouncements
@ -2273,7 +2362,7 @@ export const Group = ({
zIndex: 100,
}}
>
{admins.includes(myAddress) &&
{isPrivate && admins.includes(myAddress) &&
shouldReEncrypt &&
triedToFetchSecretKey &&
!firstSecretKeyInCreation &&

View File

@ -217,12 +217,12 @@ export const decodeBase64ForUIChatMessages = (messages)=> {
try {
const decoded = atob(msg?.data);
const parseDecoded =JSON.parse(decodeURIComponent(escape(decoded)))
if(parseDecoded?.messageText){
msgs.push({
...msg,
...parseDecoded
})
}
} catch (error) {
}