change encoding of saved local data

This commit is contained in:
PhilReact 2024-10-31 18:25:52 +02:00
parent b52abbc007
commit 530aa0cacc
2 changed files with 96 additions and 58 deletions

View File

@ -115,29 +115,36 @@ const requestQueueAnnouncements = new RequestQueueWithPromise(1);
let isMobile = true; let isMobile = true;
function handleNotificationClick(notificationId) { function handleNotificationClick(notificationId) {
// Determine the type of notification by parsing notificationId // Decode the notificationId if it was encoded
const isDirect = notificationId.includes("_type=direct_"); const decodedNotificationId = decodeURIComponent(notificationId);
const isGroup = notificationId.includes("_type=group_");
const isGroupAnnouncement = notificationId.includes( // Determine the type of notification by parsing decodedNotificationId
"_type=group-announcement_" const isDirect = decodedNotificationId.includes("_type=direct_");
); const isGroup = decodedNotificationId.includes("_type=group_");
const isNewThreadPost = notificationId.includes("_type=thread-post_"); const isGroupAnnouncement = decodedNotificationId.includes("_type=group-announcement_");
const isNewThreadPost = decodedNotificationId.includes("_type=thread-post_");
// Helper function to extract parameter values safely
function getParameterValue(id, key) {
const match = id.match(new RegExp(`${key}=([^_]+)`));
return match ? match[1] : null;
}
// Handle specific notification types and post the message accordingly // Handle specific notification types and post the message accordingly
if (isDirect) { if (isDirect) {
const fromValue = notificationId.split("_from=")[1]; const fromValue = getParameterValue(decodedNotificationId, "_from");
window.postMessage( window.postMessage(
{ action: "NOTIFICATION_OPEN_DIRECT", payload: { from: fromValue } }, { action: "NOTIFICATION_OPEN_DIRECT", payload: { from: fromValue } },
"*" "*"
); );
} else if (isGroup) { } else if (isGroup) {
const fromValue = notificationId.split("_from=")[1]; const fromValue = getParameterValue(decodedNotificationId, "_from");
window.postMessage( window.postMessage(
{ action: "NOTIFICATION_OPEN_GROUP", payload: { from: fromValue } }, { action: "NOTIFICATION_OPEN_GROUP", payload: { from: fromValue } },
"*" "*"
); );
} else if (isGroupAnnouncement) { } else if (isGroupAnnouncement) {
const fromValue = notificationId.split("_from=")[1]; const fromValue = getParameterValue(decodedNotificationId, "_from");
window.postMessage( window.postMessage(
{ {
action: "NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP", action: "NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP",
@ -146,18 +153,23 @@ function handleNotificationClick(notificationId) {
"*" "*"
); );
} else if (isNewThreadPost) { } else if (isNewThreadPost) {
const dataValue = notificationId.split("_data=")[1]; const dataValue = getParameterValue(decodedNotificationId, "_data");
const dataParsed = JSON.parse(dataValue); try {
window.postMessage( const dataParsed = JSON.parse(dataValue);
{ window.postMessage(
action: "NOTIFICATION_OPEN_THREAD_NEW_POST", {
payload: { data: dataParsed }, action: "NOTIFICATION_OPEN_THREAD_NEW_POST",
}, payload: { data: dataParsed },
"*" },
); "*"
);
} catch (error) {
console.error("Error parsing JSON data for thread post notification:", error);
}
} }
} }
const isMobileDevice = () => { const isMobileDevice = () => {
const userAgent = navigator.userAgent || navigator.vendor || window.opera; const userAgent = navigator.userAgent || navigator.vendor || window.opera;
@ -400,8 +412,8 @@ const handleNotificationDirect = async (directs) => {
if (newActiveChats?.length === 0) return; if (newActiveChats?.length === 0) return;
let newestLatestTimestamp; let newestLatestTimestamp = null;
let oldestLatestTimestamp; let oldestLatestTimestamp = null;
// Find the latest timestamp from newActiveChats // Find the latest timestamp from newActiveChats
newActiveChats?.forEach((newChat) => { newActiveChats?.forEach((newChat) => {
if ( if (
@ -434,10 +446,10 @@ const handleNotificationDirect = async (directs) => {
}`; }`;
const body = "You have received a new direct message"; const body = "You have received a new direct message";
const notificationId = const notificationId =
"chat_notification_" + encodeURIComponent("chat_notification_" +
Date.now() + Date.now() +
"_type=direct" + "_type=direct" +
`_from=${newestLatestTimestamp.address}`; `_from=${newestLatestTimestamp.address}`);
const notification = new window.Notification(title, { const notification = new window.Notification(title, {
body, body,
data: { id: notificationId }, data: { id: notificationId },
@ -451,7 +463,7 @@ const handleNotificationDirect = async (directs) => {
setTimeout(() => { setTimeout(() => {
notification.close(); notification.close();
}, 5000); }, 10000);
} }
} catch (error) { } catch (error) {
if (!isFocused) { if (!isFocused) {
@ -471,10 +483,10 @@ const handleNotificationDirect = async (directs) => {
// Create a unique notification ID with type and sender information // Create a unique notification ID with type and sender information
const notificationId = const notificationId =
"chat_notification_" + encodeURIComponent("chat_notification_" +
Date.now() + Date.now() +
"_type=direct" + "_type=direct" +
`_from=${newestLatestTimestamp.address}`; `_from=${newestLatestTimestamp.address}`);
const title = "New Direct message!"; const title = "New Direct message!";
const body = "You have received a new direct message"; const body = "You have received a new direct message";
@ -493,7 +505,7 @@ const handleNotificationDirect = async (directs) => {
// Automatically close the notification after 5 seconds if not clicked // Automatically close the notification after 5 seconds if not clicked
setTimeout(() => { setTimeout(() => {
notification.close(); notification.close();
}, 5000); // Close after 5 seconds }, 10000); // Close after 5 seconds
} }
} finally { } finally {
setChatHeadsDirect(dataDirects); setChatHeadsDirect(dataDirects);
@ -619,8 +631,8 @@ const handleNotification = async (groups) => {
const oldActiveChats = await getChatHeads(); const oldActiveChats = await getChatHeads();
let results = []; let results = [];
let newestLatestTimestamp; let newestLatestTimestamp = null;
let oldestLatestTimestamp; let oldestLatestTimestamp = null;
// Find the latest timestamp from newActiveChats // Find the latest timestamp from newActiveChats
newActiveChats?.forEach((newChat) => { newActiveChats?.forEach((newChat) => {
if ( if (
@ -659,10 +671,10 @@ const handleNotification = async (groups) => {
// Create a unique notification ID with type and group information // Create a unique notification ID with type and group information
const notificationId = const notificationId =
"chat_notification_" + encodeURIComponent("chat_notification_" +
Date.now() + Date.now() +
"_type=group" + "_type=group" +
`_from=${newestLatestTimestamp.groupId}`; `_from=${newestLatestTimestamp.groupId}`);
const title = "New Group Message!"; const title = "New Group Message!";
const body = `You have received a new message from ${newestLatestTimestamp?.groupName}`; const body = `You have received a new message from ${newestLatestTimestamp?.groupName}`;
@ -681,7 +693,7 @@ const handleNotification = async (groups) => {
// Automatically close the notification after 5 seconds if not clicked // Automatically close the notification after 5 seconds if not clicked
setTimeout(() => { setTimeout(() => {
notification.close(); notification.close();
}, 5000); // Close after 5 seconds }, 10000); // Close after 5 seconds
lastGroupNotification = Date.now(); lastGroupNotification = Date.now();
} }
@ -703,7 +715,7 @@ const handleNotification = async (groups) => {
}); });
// Generate a unique notification ID // Generate a unique notification ID
const notificationId = "chat_notification_" + Date.now(); const notificationId = encodeURIComponent("chat_notification_" + Date.now());
const title = "New Group Message!"; const title = "New Group Message!";
const body = "You have received a new message from one of your groups"; const body = "You have received a new message from one of your groups";
@ -723,7 +735,7 @@ const handleNotification = async (groups) => {
// Automatically close the notification after 5 seconds if its not clicked // Automatically close the notification after 5 seconds if its not clicked
setTimeout(() => { setTimeout(() => {
notification.close(); notification.close();
}, 5000); // Close after 5 seconds }, 10000); // Close after 5 seconds
lastGroupNotification = Date.now(); lastGroupNotification = Date.now();
} }
@ -1332,7 +1344,6 @@ export async function handleActiveGroupDataFromSocket({ groups, directs }) {
groups: groups || [], // Your groups data here groups: groups || [], // Your groups data here
directs: directs || [], // Your directs data here directs: directs || [], // Your directs data here
}; };
// Save the active data to localStorage // Save the active data to localStorage
storeData("active-groups-directs", activeData).catch((error) => { storeData("active-groups-directs", activeData).catch((error) => {
console.error("Error saving data:", error); console.error("Error saving data:", error);
@ -2961,10 +2972,10 @@ export const checkNewMessages = async () => {
) { ) {
// Create a unique notification ID with type and group announcement details // Create a unique notification ID with type and group announcement details
const notificationId = const notificationId =
"chat_notification_" + encodeURIComponent("chat_notification_" +
Date.now() + Date.now() +
"_type=group-announcement" + "_type=group-announcement" +
`_from=${newAnnouncements[0]?.groupId}`; `_from=${newAnnouncements[0]?.groupId}`);
const title = "New group announcement!"; const title = "New group announcement!";
const body = `You have received a new announcement from ${newAnnouncements[0]?.groupName}`; const body = `You have received a new announcement from ${newAnnouncements[0]?.groupName}`;
@ -2984,7 +2995,7 @@ export const checkNewMessages = async () => {
// Automatically close the notification after 5 seconds if its not clicked // Automatically close the notification after 5 seconds if its not clicked
setTimeout(() => { setTimeout(() => {
notification.close(); notification.close();
}, 5000); // Close after 5 seconds }, 10000); // Close after 5 seconds
} }
const savedtimestampAfter = await getTimestampGroupAnnouncement(); const savedtimestampAfter = await getTimestampGroupAnnouncement();
window.postMessage( window.postMessage(
@ -3113,10 +3124,10 @@ export const checkThreads = async (bringBack) => {
if (newAnnouncements.length > 0) { if (newAnnouncements.length > 0) {
const notificationId = const notificationId =
"chat_notification_" + encodeURIComponent("chat_notification_" +
Date.now() + Date.now() +
"_type=thread-post" + "_type=thread-post" +
`_data=${JSON.stringify(newAnnouncements[0])}`; `_data=${JSON.stringify(newAnnouncements[0])}`);
let isDisableNotifications = let isDisableNotifications =
(await getUserSettings({ key: "disable-push-notifications" })) || false; (await getUserSettings({ key: "disable-push-notifications" })) || false;
if (!isDisableNotifications) { if (!isDisableNotifications) {
@ -3146,7 +3157,7 @@ export const checkThreads = async (bringBack) => {
// Automatically close the notification after 5 seconds if its not clicked // Automatically close the notification after 5 seconds if its not clicked
setTimeout(() => { setTimeout(() => {
notification.close(); notification.close();
}, 5000); // Close after 5 seconds }, 10000); // Close after 5 seconds
} }
} }
} }
@ -3187,3 +3198,5 @@ export const checkThreads = async (bringBack) => {
// // Optional timeout callback // // Optional timeout callback
// BackgroundFetch.finish(taskId); // BackgroundFetch.finish(taskId);
// }); // });

View File

@ -55,48 +55,73 @@ async function decryptData(encryptedData: ArrayBuffer, key: CryptoKey, iv: Uint8
return decoder.decode(decryptedData); return decoder.decode(decryptedData);
} }
// Encrypt data, then concatenate the IV and encrypted data for storage // Encode a JSON payload as Base64
function jsonToBase64(payload) {
const utf8Array = new TextEncoder().encode(JSON.stringify(payload));
let binary = '';
utf8Array.forEach((byte) => (binary += String.fromCharCode(byte)));
return btoa(binary);
}
// Decode a Base64 string back to JSON
function base64ToJson(base64) {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return JSON.parse(new TextDecoder().decode(bytes));
}
export const storeData = async (key: string, payload: any): Promise<string> => { export const storeData = async (key: string, payload: any): Promise<string> => {
await initializeKeyAndIV(); await initializeKeyAndIV();
if (keysToEncrypt.includes(key) && inMemoryKey) { const base64Data = jsonToBase64(payload);
// Encrypt the payload if the key is in keysToEncrypt
const { iv, encryptedData } = await encryptData(JSON.stringify(payload), inMemoryKey);
// Combine IV and encrypted data into a single string if (keysToEncrypt.includes(key) && inMemoryKey) {
// Encrypt the Base64-encoded payload
const { iv, encryptedData } = await encryptData(base64Data, inMemoryKey);
// Combine IV and encrypted data into a single Uint8Array
const combinedData = new Uint8Array([...iv, ...new Uint8Array(encryptedData)]); const combinedData = new Uint8Array([...iv, ...new Uint8Array(encryptedData)]);
await SecureStoragePlugin.set({ key, value: btoa(String.fromCharCode(...combinedData)) }); const encryptedBase64Data = btoa(String.fromCharCode(...combinedData));
await SecureStoragePlugin.set({ key, value: encryptedBase64Data });
} else { } else {
// Store data in plain text if not in keysToEncrypt // Store Base64-encoded data in plain text if not in keysToEncrypt
await SecureStoragePlugin.set({ key, value: JSON.stringify(payload) }); await SecureStoragePlugin.set({ key, value: base64Data });
} }
return "Data saved successfully"; return "Data saved successfully";
}; };
// Retrieve data, split the IV and encrypted data, then decrypt
export const getData = async <T = any>(key: string): Promise<T> => { export const getData = async <T = any>(key: string): Promise<T> => {
await initializeKeyAndIV(); await initializeKeyAndIV();
const storedDataBase64 = await SecureStoragePlugin.get({ key }); const storedDataBase64 = await SecureStoragePlugin.get({ key });
if (storedDataBase64.value) { if (storedDataBase64.value) {
if (keysToEncrypt.includes(key) && inMemoryKey) { if (keysToEncrypt.includes(key) && inMemoryKey) {
// If the key is in keysToEncrypt, decrypt the data // Decode the Base64-encoded encrypted data
const combinedData = atob(storedDataBase64.value).split("").map((c) => c.charCodeAt(0)); const combinedData = atob(storedDataBase64.value)
const iv = new Uint8Array(combinedData.slice(0, 12)); // First 12 bytes are the IV .split("")
const encryptedData = new Uint8Array(combinedData.slice(12)).buffer; // The rest is encrypted data .map((c) => c.charCodeAt(0));
const decryptedData = await decryptData(encryptedData, inMemoryKey, iv); const iv = new Uint8Array(combinedData.slice(0, 12)); // First 12 bytes are the IV
return JSON.parse(decryptedData) as T; const encryptedData = new Uint8Array(combinedData.slice(12)).buffer;
const decryptedBase64Data = await decryptData(encryptedData, inMemoryKey, iv);
return base64ToJson(decryptedBase64Data);
} else { } else {
// If the key is not in keysToEncrypt, parse data as plain text // Decode non-encrypted data
return JSON.parse(storedDataBase64.value) as T; return base64ToJson(storedDataBase64.value);
} }
} else { } else {
throw new Error(`No data found for key: ${key}`); throw new Error(`No data found for key: ${key}`);
} }
}; };
// Remove keys from storage and log out // Remove keys from storage and log out
export async function removeKeysAndLogout(keys: string[], event: MessageEvent, request: any) { export async function removeKeysAndLogout(keys: string[], event: MessageEvent, request: any) {
try { try {