mirror of
https://github.com/Qortal/chrome-extension.git
synced 2025-02-15 11:45:50 +00:00
fixed notifications for reactions
This commit is contained in:
parent
c577b91dfe
commit
53c23bf5ea
@ -134,7 +134,7 @@ const defaultValues: MyContextInterface = {
|
|||||||
message: "",
|
message: "",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export let isMobile = false
|
export let isMobile = true
|
||||||
|
|
||||||
const isMobileDevice = () => {
|
const isMobileDevice = () => {
|
||||||
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
||||||
@ -1215,7 +1215,9 @@ function App() {
|
|||||||
}} /></Box>
|
}} /></Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<AuthenticatedContainerInnerLeft>
|
<AuthenticatedContainerInnerLeft sx={{
|
||||||
|
overflowY: isMobile && 'auto'
|
||||||
|
}}>
|
||||||
<Spacer height="48px" />
|
<Spacer height="48px" />
|
||||||
|
|
||||||
{authenticatedMode === "ltc" ? (
|
{authenticatedMode === "ltc" ? (
|
||||||
|
@ -27,6 +27,7 @@ import { RequestQueueWithPromise } from "./utils/queue/queue";
|
|||||||
import { validateAddress } from "./utils/validateAddress";
|
import { validateAddress } from "./utils/validateAddress";
|
||||||
import { Sha256 } from "asmcrypto.js";
|
import { Sha256 } from "asmcrypto.js";
|
||||||
import { TradeBotRespondMultipleRequest } from "./transactions/TradeBotRespondMultipleRequest";
|
import { TradeBotRespondMultipleRequest } from "./transactions/TradeBotRespondMultipleRequest";
|
||||||
|
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from "./constants/resourceTypes";
|
||||||
|
|
||||||
let lastGroupNotification;
|
let lastGroupNotification;
|
||||||
export const groupApi = "https://ext-node.qortal.link";
|
export const groupApi = "https://ext-node.qortal.link";
|
||||||
@ -225,6 +226,27 @@ export function isExtMsg(data) {
|
|||||||
return isMsgFromExtensionGroup;
|
return isMsgFromExtensionGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isUpdateMsg(data) {
|
||||||
|
let isUpdateMessage = true;
|
||||||
|
try {
|
||||||
|
const decode1 = atob(data);
|
||||||
|
const decode2 = atob(decode1);
|
||||||
|
const keyStr = decode2.slice(10, 13);
|
||||||
|
|
||||||
|
// Convert the key string back to a number
|
||||||
|
const numberKey = parseInt(keyStr, 10);
|
||||||
|
if (isNaN(numberKey)) {
|
||||||
|
isUpdateMessage = false;
|
||||||
|
} else if(numberKey !== RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS){
|
||||||
|
isUpdateMessage = false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
isUpdateMessage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isUpdateMessage;
|
||||||
|
}
|
||||||
|
|
||||||
async function checkWebviewFocus() {
|
async function checkWebviewFocus() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// Set a timeout for 1 second
|
// Set a timeout for 1 second
|
||||||
@ -478,7 +500,7 @@ const handleNotification = async (groups) => {
|
|||||||
if(!isArray(mutedGroups)) mutedGroups = []
|
if(!isArray(mutedGroups)) mutedGroups = []
|
||||||
|
|
||||||
let isFocused;
|
let isFocused;
|
||||||
const data = groups.filter((group) => group?.sender !== address && !mutedGroups.includes(group.groupId) && !group?.chatReference);
|
const data = groups.filter((group) => group?.sender !== address && !mutedGroups.includes(group.groupId) && !isUpdateMsg(group?.data));
|
||||||
try {
|
try {
|
||||||
if(isDisableNotifications) return
|
if(isDisableNotifications) return
|
||||||
if (!data || data?.length === 0) return;
|
if (!data || data?.length === 0) return;
|
||||||
@ -3924,9 +3946,9 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "encryptSingle": {
|
case "encryptSingle": {
|
||||||
const { data, secretKeyObject } = request.payload;
|
const { data, secretKeyObject, typeNumber } = request.payload;
|
||||||
|
|
||||||
encryptSingle({ data64: data, secretKeyObject: secretKeyObject })
|
encryptSingle({ data64: data, secretKeyObject: secretKeyObject, typeNumber })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
sendResponse(res);
|
sendResponse(res);
|
||||||
})
|
})
|
||||||
|
@ -19,6 +19,7 @@ import { Box, ButtonBase } from '@mui/material'
|
|||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import { ReplyPreview } from './MessageItem'
|
import { ReplyPreview } from './MessageItem'
|
||||||
import { ExitIcon } from '../../assets/Icons/ExitIcon'
|
import { ExitIcon } from '../../assets/Icons/ExitIcon'
|
||||||
|
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/resourceTypes'
|
||||||
|
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 5 });
|
const uid = new ShortUniqueId({ length: 5 });
|
||||||
@ -123,6 +124,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
|
|||||||
if(hasInitialized.current){
|
if(hasInitialized.current){
|
||||||
|
|
||||||
const formatted = response.filter((rawItem)=> !rawItem?.chatReference).map((item: any)=> {
|
const formatted = response.filter((rawItem)=> !rawItem?.chatReference).map((item: any)=> {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
id: item.signature,
|
id: item.signature,
|
||||||
@ -376,12 +378,13 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
|
|||||||
}, [messages])
|
}, [messages])
|
||||||
|
|
||||||
|
|
||||||
const encryptChatMessage = async (data: string, secretKeyObject: any)=> {
|
const encryptChatMessage = async (data: string, secretKeyObject: any, reactiontypeNumber?: number)=> {
|
||||||
try {
|
try {
|
||||||
return new Promise((res, rej)=> {
|
return new Promise((res, rej)=> {
|
||||||
chrome?.runtime?.sendMessage({ action: "encryptSingle", payload: {
|
chrome?.runtime?.sendMessage({ action: "encryptSingle", payload: {
|
||||||
data,
|
data,
|
||||||
secretKeyObject
|
secretKeyObject,
|
||||||
|
typeNumber: reactiontypeNumber
|
||||||
}}, (response) => {
|
}}, (response) => {
|
||||||
|
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
@ -535,8 +538,8 @@ const clearEditorContent = () => {
|
|||||||
...(otherData || {})
|
...(otherData || {})
|
||||||
}
|
}
|
||||||
const message64: any = await objectToBase64(objectMessage)
|
const message64: any = await objectToBase64(objectMessage)
|
||||||
|
const reactiontypeNumber = RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS
|
||||||
const encryptSingle = await encryptChatMessage(message64, secretKeyObject)
|
const encryptSingle = await encryptChatMessage(message64, secretKeyObject, reactiontypeNumber)
|
||||||
// const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
|
// const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
|
||||||
|
|
||||||
const sendMessageFunc = async () => {
|
const sendMessageFunc = async () => {
|
||||||
|
@ -155,6 +155,10 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
|
|||||||
|
|
||||||
const handleAtBottomStateChange = (atBottom) => {
|
const handleAtBottomStateChange = (atBottom) => {
|
||||||
isAtBottomRef.current = atBottom;
|
isAtBottomRef.current = atBottom;
|
||||||
|
if(atBottom){
|
||||||
|
handleMessageSeen();
|
||||||
|
setShowScrollButton(false)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -165,7 +169,6 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
|
|||||||
itemContent={rowRenderer}
|
itemContent={rowRenderer}
|
||||||
atBottomThreshold={50}
|
atBottomThreshold={50}
|
||||||
followOutput="smooth"
|
followOutput="smooth"
|
||||||
onScroll={handleScroll}
|
|
||||||
atBottomStateChange={handleAtBottomStateChange} // Detect bottom status
|
atBottomStateChange={handleAtBottomStateChange} // Detect bottom status
|
||||||
increaseViewportBy={3000}
|
increaseViewportBy={3000}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ import { WebSocketActive } from "./WebsocketActive";
|
|||||||
import { flushSync } from "react-dom";
|
import { flushSync } from "react-dom";
|
||||||
import { useMessageQueue } from "../../MessageQueueContext";
|
import { useMessageQueue } from "../../MessageQueueContext";
|
||||||
import { DrawerComponent } from "../Drawer/Drawer";
|
import { DrawerComponent } from "../Drawer/Drawer";
|
||||||
import { isExtMsg } from "../../background";
|
import { isExtMsg, isUpdateMsg } from "../../background";
|
||||||
import { ContextMenu } from "../ContextMenu";
|
import { ContextMenu } from "../ContextMenu";
|
||||||
import { MobileFooter } from "../Mobile/MobileFooter";
|
import { MobileFooter } from "../Mobile/MobileFooter";
|
||||||
import Header from "../Mobile/MobileHeader";
|
import Header from "../Mobile/MobileHeader";
|
||||||
@ -420,6 +420,7 @@ export const Group = ({
|
|||||||
const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState("");
|
const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState("");
|
||||||
const [desktopSideView, setDesktopSideView] = useState('groups')
|
const [desktopSideView, setDesktopSideView] = useState('groups')
|
||||||
const isFocusedRef = useRef(true);
|
const isFocusedRef = useRef(true);
|
||||||
|
const timestampEnterDataRef = useRef({});
|
||||||
const selectedGroupRef = useRef(null);
|
const selectedGroupRef = useRef(null);
|
||||||
const selectedDirectRef = useRef(null);
|
const selectedDirectRef = useRef(null);
|
||||||
const groupSectionRef = useRef(null);
|
const groupSectionRef = useRef(null);
|
||||||
@ -429,9 +430,11 @@ export const Group = ({
|
|||||||
const settimeoutForRefetchSecretKey = useRef(null);
|
const settimeoutForRefetchSecretKey = useRef(null);
|
||||||
const { clearStatesMessageQueueProvider } = useMessageQueue();
|
const { clearStatesMessageQueueProvider } = useMessageQueue();
|
||||||
const initiatedGetMembers = useRef(false);
|
const initiatedGetMembers = useRef(false);
|
||||||
// useEffect(()=> {
|
const [groupChatTimestamps, setGroupChatTimestamps] = React.useState({});
|
||||||
// setFullHeight()
|
|
||||||
// }, [])
|
useEffect(()=> {
|
||||||
|
timestampEnterDataRef.current = timestampEnterData
|
||||||
|
}, [timestampEnterData])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
isFocusedRef.current = isFocused;
|
isFocusedRef.current = isFocused;
|
||||||
@ -616,12 +619,14 @@ export const Group = ({
|
|||||||
const groupChatHasUnread = useMemo(() => {
|
const groupChatHasUnread = useMemo(() => {
|
||||||
let hasUnread = false;
|
let hasUnread = false;
|
||||||
groups.forEach((group) => {
|
groups.forEach((group) => {
|
||||||
|
console.log('isUpdateMsg(group?.data)', isUpdateMsg(group?.data))
|
||||||
|
|
||||||
if (
|
if (
|
||||||
group?.data &&
|
group?.data &&
|
||||||
isExtMsg(group?.data) &&
|
isExtMsg(group?.data) &&
|
||||||
group?.sender !== myAddress &&
|
group?.sender !== myAddress &&
|
||||||
group?.timestamp &&
|
group?.timestamp && (!isUpdateMsg(group?.data) || groupChatTimestamps[group?.groupId]) &&
|
||||||
((!timestampEnterData[group?.groupId] && !group?.chatReference &&
|
((!timestampEnterData[group?.groupId] &&
|
||||||
Date.now() - group?.timestamp < timeDifferenceForNotificationChats) ||
|
Date.now() - group?.timestamp < timeDifferenceForNotificationChats) ||
|
||||||
timestampEnterData[group?.groupId] < group?.timestamp)
|
timestampEnterData[group?.groupId] < group?.timestamp)
|
||||||
) {
|
) {
|
||||||
@ -918,13 +923,52 @@ export const Group = ({
|
|||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getCountNewMesg = async (groupId, after)=> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${getBaseApiReact()}/chat/messages?after=${after}&txGroupId=${groupId}&haschatreference=false&encoding=BASE64&limit=1`
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
if(data && data[0]) return data[0].timestamp
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLatestRegularChat = async (groups)=> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
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)){
|
||||||
|
const hasMoreRecentMsg = await getCountNewMesg(group.groupId, timestampEnterDataRef.current[group?.groupId] || Date.now() - 24 * 60 * 60 * 1000)
|
||||||
|
if(hasMoreRecentMsg){
|
||||||
|
groupData[group.groupId] = hasMoreRecentMsg
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.all(getGroupData)
|
||||||
|
setGroupChatTimestamps(groupData)
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Listen for messages from the background script
|
// Listen for messages from the background script
|
||||||
chrome?.runtime?.onMessage.addListener((message, sender, sendResponse) => {
|
chrome?.runtime?.onMessage.addListener((message, sender, sendResponse) => {
|
||||||
if (message.action === "SET_GROUPS") {
|
if (message.action === "SET_GROUPS") {
|
||||||
// Update the component state with the received 'sendqort' state
|
// Update the component state with the received 'sendqort' state
|
||||||
setGroups(message.payload);
|
setGroups(message.payload);
|
||||||
|
getLatestRegularChat(message.payload)
|
||||||
setMemberGroups(message.payload);
|
setMemberGroups(message.payload);
|
||||||
|
|
||||||
if (selectedGroupRef.current && groupSectionRef.current === "chat") {
|
if (selectedGroupRef.current && groupSectionRef.current === "chat") {
|
||||||
@ -1102,13 +1146,13 @@ export const Group = ({
|
|||||||
if (!findGroup) return false;
|
if (!findGroup) return false;
|
||||||
if (!findGroup?.data || !isExtMsg(findGroup?.data)) return false;
|
if (!findGroup?.data || !isExtMsg(findGroup?.data)) return false;
|
||||||
return (
|
return (
|
||||||
findGroup?.timestamp && !findGroup?.chatReference &&
|
findGroup?.timestamp && (!isUpdateMsg(findGroup?.data) || groupChatTimestamps[findGroup?.groupId]) &&
|
||||||
((!timestampEnterData[selectedGroup?.groupId] &&
|
((!timestampEnterData[selectedGroup?.groupId] &&
|
||||||
Date.now() - findGroup?.timestamp <
|
Date.now() - findGroup?.timestamp <
|
||||||
timeDifferenceForNotificationChats) ||
|
timeDifferenceForNotificationChats) ||
|
||||||
timestampEnterData?.[selectedGroup?.groupId] < findGroup?.timestamp)
|
timestampEnterData?.[selectedGroup?.groupId] < findGroup?.timestamp)
|
||||||
);
|
);
|
||||||
}, [timestampEnterData, selectedGroup]);
|
}, [timestampEnterData, selectedGroup, groupChatTimestamps]);
|
||||||
|
|
||||||
const isUnread = useMemo(() => {
|
const isUnread = useMemo(() => {
|
||||||
if (!selectedGroup) return false;
|
if (!selectedGroup) return false;
|
||||||
@ -2101,7 +2145,7 @@ export const Group = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{group?.data &&
|
{group?.data &&
|
||||||
isExtMsg(group?.data) && !group?.chatReference &&
|
isExtMsg(group?.data) && (!isUpdateMsg(group?.data) || groupChatTimestamps[group?.groupId]) &&
|
||||||
group?.sender !== myAddress &&
|
group?.sender !== myAddress &&
|
||||||
group?.timestamp &&
|
group?.timestamp &&
|
||||||
((!timestampEnterData[group?.groupId] &&
|
((!timestampEnterData[group?.groupId] &&
|
||||||
|
@ -128,7 +128,7 @@ export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers,
|
|||||||
|
|
||||||
minWidth: '24px !important'
|
minWidth: '24px !important'
|
||||||
}}>
|
}}>
|
||||||
<ChatIcon sx={{ color: hasUnreadChat ? 'var(--unread)' : "#fff" }} />
|
<ChatIcon color={hasUnreadChat ? 'var(--unread)' : "#fff"} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText sx={{
|
<ListItemText sx={{
|
||||||
"& .MuiTypography-root": {
|
"& .MuiTypography-root": {
|
||||||
@ -147,7 +147,7 @@ export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers,
|
|||||||
|
|
||||||
minWidth: '24px !important'
|
minWidth: '24px !important'
|
||||||
}}>
|
}}>
|
||||||
<NotificationIcon2 sx={{ color: hasUnreadAnnouncements ? 'var(--unread)' : "#fff" }} />
|
<NotificationIcon2 color={hasUnreadAnnouncements ? 'var(--unread)' : "#fff" } />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText sx={{
|
<ListItemText sx={{
|
||||||
"& .MuiTypography-root": {
|
"& .MuiTypography-root": {
|
||||||
@ -165,7 +165,7 @@ export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers,
|
|||||||
<ListItemIcon sx={{
|
<ListItemIcon sx={{
|
||||||
minWidth: '24px !important'
|
minWidth: '24px !important'
|
||||||
}}>
|
}}>
|
||||||
<ThreadsIcon sx={{ color: "#fff" }} />
|
<ThreadsIcon color={"#fff"} />
|
||||||
|
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText sx={{
|
<ListItemText sx={{
|
||||||
|
@ -67,8 +67,8 @@ export const ReactionPicker = ({ onReaction }) => {
|
|||||||
{showPicker && (
|
{showPicker && (
|
||||||
<div className="emoji-picker" ref={pickerRef} onClick={(e) => e.preventDefault()}>
|
<div className="emoji-picker" ref={pickerRef} onClick={(e) => e.preventDefault()}>
|
||||||
<Picker
|
<Picker
|
||||||
height={isMobile ? 300 : 450}
|
height={isMobile ? 350 : 450}
|
||||||
width={isMobile ? 250 : 350 }
|
width={isMobile ? 300 : 350 }
|
||||||
reactionsDefaultOpen={true}
|
reactionsDefaultOpen={true}
|
||||||
onReactionClick={handleReaction}
|
onReactionClick={handleReaction}
|
||||||
onEmojiClick={handlePicker}
|
onEmojiClick={handlePicker}
|
||||||
|
1
src/constants/resourceTypes.ts
Normal file
1
src/constants/resourceTypes.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS = 102
|
@ -137,7 +137,7 @@ export const encryptDataGroup = ({ data64, publicKeys, privateKey, userPublicKey
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encryptSingle = async ({ data64, secretKeyObject }: any) => {
|
export const encryptSingle = async ({ data64, secretKeyObject, typeNumber = 1 }: any) => {
|
||||||
// Find the highest key in the secretKeyObject
|
// Find the highest key in the secretKeyObject
|
||||||
const highestKey = Math.max(...Object.keys(secretKeyObject).filter(item => !isNaN(+item)).map(Number));
|
const highestKey = Math.max(...Object.keys(secretKeyObject).filter(item => !isNaN(+item)).map(Number));
|
||||||
const highestKeyObject = secretKeyObject[highestKey];
|
const highestKeyObject = secretKeyObject[highestKey];
|
||||||
@ -152,6 +152,9 @@ export const encryptSingle = async ({ data64, secretKeyObject }: any) => {
|
|||||||
|
|
||||||
let nonce, encryptedData, encryptedDataBase64, finalEncryptedData;
|
let nonce, encryptedData, encryptedDataBase64, finalEncryptedData;
|
||||||
|
|
||||||
|
// Convert type number to a fixed length of 3 digits
|
||||||
|
const typeNumberStr = typeNumber.toString().padStart(3, '0');
|
||||||
|
|
||||||
if (highestKeyObject.nonce) {
|
if (highestKeyObject.nonce) {
|
||||||
// Old format: Use the nonce from secretKeyObject
|
// Old format: Use the nonce from secretKeyObject
|
||||||
nonce = base64ToUint8Array(highestKeyObject.nonce);
|
nonce = base64ToUint8Array(highestKeyObject.nonce);
|
||||||
@ -160,15 +163,14 @@ export const encryptSingle = async ({ data64, secretKeyObject }: any) => {
|
|||||||
encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey);
|
encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey);
|
||||||
encryptedDataBase64 = uint8ArrayToBase64(encryptedData);
|
encryptedDataBase64 = uint8ArrayToBase64(encryptedData);
|
||||||
|
|
||||||
// Concatenate the highest key with the encrypted data (old format)
|
// Concatenate the highest key, type number, and encrypted data (old format)
|
||||||
const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits
|
const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits
|
||||||
finalEncryptedData = btoa(highestKeyStr + encryptedDataBase64);
|
finalEncryptedData = btoa(highestKeyStr + encryptedDataBase64);
|
||||||
} else {
|
} else {
|
||||||
// New format: Generate a random nonce and embed it in the message
|
// New format: Generate a random nonce and embed it in the message
|
||||||
nonce = new Uint8Array(24); // 24 bytes for the nonce
|
nonce = new Uint8Array(24); // 24 bytes for the nonce
|
||||||
crypto.getRandomValues(nonce);
|
crypto.getRandomValues(nonce);
|
||||||
|
|
||||||
|
|
||||||
// Encrypt the data with the new nonce and message key
|
// Encrypt the data with the new nonce and message key
|
||||||
encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey);
|
encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey);
|
||||||
encryptedDataBase64 = uint8ArrayToBase64(encryptedData);
|
encryptedDataBase64 = uint8ArrayToBase64(encryptedData);
|
||||||
@ -176,23 +178,24 @@ export const encryptSingle = async ({ data64, secretKeyObject }: any) => {
|
|||||||
// Convert the nonce to base64
|
// Convert the nonce to base64
|
||||||
const nonceBase64 = uint8ArrayToBase64(nonce);
|
const nonceBase64 = uint8ArrayToBase64(nonce);
|
||||||
|
|
||||||
// Concatenate the highest key, nonce, and encrypted data (new format)
|
// Concatenate the highest key, type number, nonce, and encrypted data (new format)
|
||||||
const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits
|
const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits
|
||||||
finalEncryptedData = btoa(highestKeyStr + nonceBase64 + encryptedDataBase64);
|
finalEncryptedData = btoa(highestKeyStr + typeNumberStr + nonceBase64 + encryptedDataBase64);
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalEncryptedData;
|
return finalEncryptedData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const decryptSingle = async ({ data64, secretKeyObject, skipDecodeBase64 }: any) => {
|
|
||||||
|
export const decryptSingle = async ({ data64, secretKeyObject, skipDecodeBase64 }: any) => {
|
||||||
// First, decode the base64-encoded input (if skipDecodeBase64 is not set)
|
// First, decode the base64-encoded input (if skipDecodeBase64 is not set)
|
||||||
const decodedData = skipDecodeBase64 ? data64 : atob(data64);
|
const decodedData = skipDecodeBase64 ? data64 : atob(data64);
|
||||||
|
|
||||||
// Then, decode it again for the specific format (if double encoding is used)
|
// Then, decode it again for the specific format (if double encoding is used)
|
||||||
const decodeForNumber = atob(decodedData);
|
const decodeForNumber = atob(decodedData);
|
||||||
|
|
||||||
// Extract the key (assuming it's 10 characters long)
|
// Extract the key (assuming it's always the first 10 characters)
|
||||||
const keyStr = decodeForNumber.slice(0, 10);
|
const keyStr = decodeForNumber.slice(0, 10);
|
||||||
|
|
||||||
// Convert the key string back to a number
|
// Convert the key string back to a number
|
||||||
@ -205,18 +208,27 @@ export const decryptSingle = async ({ data64, secretKeyObject, skipDecodeBase64
|
|||||||
|
|
||||||
const secretKeyEntry = secretKeyObject[highestKey];
|
const secretKeyEntry = secretKeyObject[highestKey];
|
||||||
|
|
||||||
let nonceBase64, encryptedDataBase64;
|
let typeNumberStr, nonceBase64, encryptedDataBase64;
|
||||||
|
|
||||||
|
// Determine if typeNumber exists by checking if the next 3 characters after keyStr are digits
|
||||||
|
const possibleTypeNumberStr = decodeForNumber.slice(10, 13);
|
||||||
|
const hasTypeNumber = /^\d{3}$/.test(possibleTypeNumberStr); // Check if next 3 characters are digits
|
||||||
|
|
||||||
if (secretKeyEntry.nonce) {
|
if (secretKeyEntry.nonce) {
|
||||||
// Old format: nonce is present in the secretKeyObject
|
// Old format: nonce is present in the secretKeyObject, so no type number exists
|
||||||
nonceBase64 = secretKeyEntry.nonce;
|
nonceBase64 = secretKeyEntry.nonce;
|
||||||
encryptedDataBase64 = decodeForNumber.slice(10); // The remaining part is the encrypted data
|
encryptedDataBase64 = decodeForNumber.slice(10); // The remaining part is the encrypted data
|
||||||
} else {
|
} else {
|
||||||
// New format: nonce is included in the message (first 32 characters)
|
if (hasTypeNumber) {
|
||||||
nonceBase64 = decodeForNumber.slice(10, 42); // First 32 characters for the nonce
|
// New format: Extract type number and nonce
|
||||||
encryptedDataBase64 = decodeForNumber.slice(42); // The remaining part is the encrypted data
|
typeNumberStr = possibleTypeNumberStr; // Extract type number
|
||||||
|
nonceBase64 = decodeForNumber.slice(13, 45); // Extract nonce (next 32 characters after type number)
|
||||||
|
encryptedDataBase64 = decodeForNumber.slice(45); // The remaining part is the encrypted data
|
||||||
|
} else {
|
||||||
|
// Old format without type number (nonce is embedded in the message, first 32 characters after keyStr)
|
||||||
|
nonceBase64 = decodeForNumber.slice(10, 42); // First 32 characters for the nonce
|
||||||
|
encryptedDataBase64 = decodeForNumber.slice(42); // The remaining part is the encrypted data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Base64 strings to Uint8Array
|
// Convert Base64 strings to Uint8Array
|
||||||
@ -243,6 +255,8 @@ export const decryptSingle = async ({ data64, secretKeyObject, skipDecodeBase64
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function decryptGroupData(data64EncryptedData: string, privateKey: string) {
|
export function decryptGroupData(data64EncryptedData: string, privateKey: string) {
|
||||||
|
|
||||||
const allCombined = base64ToUint8Array(data64EncryptedData)
|
const allCombined = base64ToUint8Array(data64EncryptedData)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user