From ccd09ad44d7561108ae5e2e8de50d718b441459b Mon Sep 17 00:00:00 2001 From: nicholas ma Date: Wed, 13 Mar 2024 16:16:43 -0700 Subject: [PATCH 1/5] fix: removed ao query for ticker --- src/routes/popup/notifications.tsx | 86 +++++++++++++++++++++++------- src/routes/popup/send/index.tsx | 2 +- src/utils/notifications.ts | 31 +++++++++++ 3 files changed, 99 insertions(+), 20 deletions(-) diff --git a/src/routes/popup/notifications.tsx b/src/routes/popup/notifications.tsx index 1df9aa432..63ece5772 100644 --- a/src/routes/popup/notifications.tsx +++ b/src/routes/popup/notifications.tsx @@ -1,4 +1,9 @@ -import { fetchNotifications } from "~utils/notifications"; +import { + extractQuantityTransferred, + fetchNotifications, + fetchTokenById, + fetchTokenByProcessId +} from "~utils/notifications"; import { getTokenInfo } from "~tokens/aoTokens/router"; import aoLogo from "url:/assets/ecosystem/ao-logo.svg"; import { useHistory } from "~utils/hash_router"; @@ -9,6 +14,7 @@ import browser from "webextension-polyfill"; import { useEffect, useState } from "react"; import { useAo } from "~tokens/aoTokens/ao"; import styled from "styled-components"; +import { balanceToFractioned, formatTokenBalance } from "~tokens/currency"; export default function Notifications() { const [notifications, setNotifications] = useState([]); @@ -83,24 +89,66 @@ export default function Notifications() { const formatTxMessage = async (notifications) => { const formattedTxMsgs = []; for (const notification of notifications) { - const ticker = - notification.isAo && notification.transactionType !== "Message" - ? await getTicker(notification.tokenId) - : notification.tokenId; - let formattedMessage: string; - if (notification.transactionType === "Sent") { - formattedMessage = `Sent ${Number(notification.quantity).toFixed( - 2 - )} ${ticker} to ${ - notification.ao - ? findRecipient(notification) - : formatAddress(notification.node.recipient, 4) - }`; - } else if (notification.transactionType === "Received") { - formattedMessage = `Received ${Number(notification.quantity).toFixed( - 2 - )} ${ticker} from ${formatAddress(notification.node.owner.address, 4)}`; - } else if (notification.transactionType === "Message") { + let formattedMessage; + if (notification.transactionType !== "Message") { + let ticker; + let quantityTransfered; + if (notification.isAo) { + // handle ao messages/sents/receives + let token = await fetchTokenByProcessId(notification.tokenId); + if (!token) { + ticker = formatAddress(notification.tokenId, 4); + quantityTransfered = notification.quantity; + } else { + ticker = token.Ticker; + quantityTransfered = balanceToFractioned( + Number(notification.quantity), + { + id: notification.tokenId, + decimals: token.Denomination, + divisibility: token.Denomination + } + ); + } + } else if (notification.transactionType !== "Transaction") { + let token = await fetchTokenById(notification.tokenId); + if (!token) { + ticker = formatAddress(notification.tokenId, 5); + quantityTransfered = extractQuantityTransferred( + notification.node.tags + ); + } else if (token.ticker !== "AR") { + ticker = token.ticker; + quantityTransfered = extractQuantityTransferred( + notification.node.tags + ); + quantityTransfered = formatTokenBalance( + balanceToFractioned(Number(quantityTransfered), { + id: notification.tokenId, + decimals: token.decimals, + divisibility: token.divisibility + }) + ); + } else { + ticker = token.ticker; + quantityTransfered = formatTokenBalance( + Number(notification.quantity) + ); + } + } + if (notification.transactionType === "Sent") { + formattedMessage = `Sent ${quantityTransfered} ${ticker} to ${ + notification.ao + ? findRecipient(notification) + : formatAddress(notification.node.recipient, 4) + }`; + } else if (notification.transactionType === "Received") { + formattedMessage = `Received ${quantityTransfered} ${ticker} from ${formatAddress( + notification.node.owner.address, + 4 + )}`; + } + } else { formattedMessage = `New message from ${formatAddress( notification.node.owner.address, 4 diff --git a/src/routes/popup/send/index.tsx b/src/routes/popup/send/index.tsx index 20e692835..428c98a9e 100644 --- a/src/routes/popup/send/index.tsx +++ b/src/routes/popup/send/index.tsx @@ -70,7 +70,7 @@ import { useAoTokens } from "~tokens/aoTokens/ao"; // default size for the qty text const defaulQtytSize = 3.7; -const arPlaceholder: TokenInterface = { +export const arPlaceholder: TokenInterface = { id: "AR", name: "Arweave", ticker: "AR", diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 11cf43de5..a309001ac 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,3 +1,4 @@ +import { arPlaceholder } from "~routes/popup/send"; import { ExtensionStorage } from "./storage"; import { getActiveAddress } from "~wallets"; @@ -14,3 +15,33 @@ export const fetchNotifications = async () => { } return notifications; }; + +export const fetchTokenByProcessId = async (processId: string) => { + const tokens = await ExtensionStorage.get("ao_tokens"); + if (!tokens || !processId) return processId; + + return tokens.find((token) => token.processId === processId); +}; + +export const fetchTokenById = async (tokenId: string) => { + if (tokenId === "AR") { + return arPlaceholder; + } + const tokens = await ExtensionStorage.get("tokens"); + + if (!tokens || !tokenId) return tokenId; + return tokens.find((token) => token.id === tokenId); +}; + +export const extractQuantityTransferred = (tags: any[]): number | null => { + const inputTag = tags.find((tag) => tag.name === "Input"); + if (!inputTag) return null; + + try { + const inputValue = JSON.parse(inputTag.value); + return inputValue.qty ? inputValue.qty : null; + } catch (error) { + console.error("Error parsing Input tag value:", error); + return null; + } +}; From b6357927f379118f627b976848d890ccbda7da8b Mon Sep 17 00:00:00 2001 From: nicholas ma Date: Thu, 14 Mar 2024 08:52:32 -0700 Subject: [PATCH 2/5] fix: added types --- src/notifications/api.ts | 28 +++++++++++++++- src/routes/popup/notifications.tsx | 50 ++++++----------------------- src/utils/notifications.ts | 51 ++++++++++++++++++++++++++---- 3 files changed, 82 insertions(+), 47 deletions(-) diff --git a/src/notifications/api.ts b/src/notifications/api.ts index 234139a35..0bee0e292 100644 --- a/src/notifications/api.ts +++ b/src/notifications/api.ts @@ -15,7 +15,33 @@ import { processTransactions } from "./utils"; -type ArNotificationsHandlerReturnType = [any[], number, any[]]; +export type Transaction = { + node: { + id: string; + recipient: string; + owner: { + address: string; + }; + quantity: { + ar: string; + }; + block: { + timestamp: number; + height: number; + }; + tags: Array<{ + name: string; + value: string; + }>; + }; + transactionType: string; + quantity: string; + isAo?: boolean; + tokenId?: string; + warpContract?: boolean; +}; + +type ArNotificationsHandlerReturnType = [Transaction[], number, any[]]; export async function notificationsHandler() { const notificationSetting: boolean = await ExtensionStorage.get( diff --git a/src/routes/popup/notifications.tsx b/src/routes/popup/notifications.tsx index 63ece5772..0951ca6d7 100644 --- a/src/routes/popup/notifications.tsx +++ b/src/routes/popup/notifications.tsx @@ -2,7 +2,8 @@ import { extractQuantityTransferred, fetchNotifications, fetchTokenById, - fetchTokenByProcessId + fetchTokenByProcessId, + mergeAndSortNotifications } from "~utils/notifications"; import { getTokenInfo } from "~tokens/aoTokens/router"; import aoLogo from "url:/assets/ecosystem/ao-logo.svg"; @@ -15,45 +16,17 @@ import { useEffect, useState } from "react"; import { useAo } from "~tokens/aoTokens/ao"; import styled from "styled-components"; import { balanceToFractioned, formatTokenBalance } from "~tokens/currency"; +import type { Transaction } from "~notifications/api"; export default function Notifications() { - const [notifications, setNotifications] = useState([]); - const [formattedTxMsgs, setFormattedTxMsgs] = useState([]); + const [notifications, setNotifications] = useState([]); + const [formattedTxMsgs, setFormattedTxMsgs] = useState([]); const [loading, setLoading] = useState(false); const [empty, setEmpty] = useState(false); const ao = useAo(); const [push] = useHistory(); - const mergeAndSortNotifications = (arNotifications, aoNotifications) => { - const mergedNotifications = [...arNotifications, ...aoNotifications]; - - // filter notifications without timestamps - const pendingNotifications = mergedNotifications.filter( - (notification) => !notification.node.block?.timestamp - ); - - // set status to "pending" for notifications without timestamps - pendingNotifications.forEach((notification) => { - notification.node.block = { timestamp: "pending" }; - }); - - // remove pending notifications from the merged array - const sortedNotifications = mergedNotifications.filter( - (notification) => notification.node.block.timestamp !== "pending" - ); - - // sort notifications with timestamps - sortedNotifications.sort( - (a, b) => b.node.block.timestamp - a.node.block.timestamp - ); - - // place pending notifications at the most recent index - sortedNotifications.unshift(...pendingNotifications); - - return sortedNotifications; - }; - useEffect(() => { (async () => { try { @@ -86,10 +59,12 @@ export default function Notifications() { return "Recipient not found"; }; - const formatTxMessage = async (notifications) => { + const formatTxMessage = async ( + notifications: Transaction[] + ): Promise => { const formattedTxMsgs = []; for (const notification of notifications) { - let formattedMessage; + let formattedMessage: string = ""; if (notification.transactionType !== "Message") { let ticker; let quantityTransfered; @@ -138,7 +113,7 @@ export default function Notifications() { } if (notification.transactionType === "Sent") { formattedMessage = `Sent ${quantityTransfered} ${ticker} to ${ - notification.ao + notification.isAo ? findRecipient(notification) : formatAddress(notification.node.recipient, 4) }`; @@ -159,11 +134,6 @@ export default function Notifications() { return formattedTxMsgs; }; - const getTicker = async (tokenId: string) => { - const result = await getTokenInfo(tokenId, ao); - return result.Ticker; - }; - const formatDate = (timestamp) => { if (timestamp === "pending") { return "Pending"; diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index a309001ac..f415d3f08 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,6 +1,9 @@ import { arPlaceholder } from "~routes/popup/send"; import { ExtensionStorage } from "./storage"; import { getActiveAddress } from "~wallets"; +import type { Transaction } from "~notifications/api"; +import type { Token } from "~tokens/token"; +import type { TokenInfo } from "~tokens/aoTokens/ao"; export const fetchNotifications = async () => { const address = await getActiveAddress(); @@ -16,20 +19,56 @@ export const fetchNotifications = async () => { return notifications; }; -export const fetchTokenByProcessId = async (processId: string) => { - const tokens = await ExtensionStorage.get("ao_tokens"); - if (!tokens || !processId) return processId; +export const mergeAndSortNotifications = ( + arNotifications, + aoNotifications +): Transaction[] => { + const mergedNotifications = [...arNotifications, ...aoNotifications]; + + // filter notifications without timestamps + const pendingNotifications = mergedNotifications.filter( + (notification) => !notification.node.block?.timestamp + ); + + // set status to "pending" for notifications without timestamps + pendingNotifications.forEach((notification) => { + notification.node.block = { timestamp: "pending" }; + }); + + // remove pending notifications from the merged array + const sortedNotifications = mergedNotifications.filter( + (notification) => notification.node.block.timestamp !== "pending" + ); + + // sort notifications with timestamps + sortedNotifications.sort( + (a, b) => b.node.block.timestamp - a.node.block.timestamp + ); + + // place pending notifications at the most recent index + sortedNotifications.unshift(...pendingNotifications); + + return sortedNotifications; +}; + +export const fetchTokenByProcessId = async ( + processId: string +): Promise => { + const tokens = await ExtensionStorage.get< + (TokenInfo & { processId: string })[] + >("ao_tokens"); + if (!tokens || !processId) return null; return tokens.find((token) => token.processId === processId); }; -export const fetchTokenById = async (tokenId: string) => { +export const fetchTokenById = async (tokenId: string): Promise => { if (tokenId === "AR") { return arPlaceholder; } - const tokens = await ExtensionStorage.get("tokens"); + const tokens = await ExtensionStorage.get("tokens"); - if (!tokens || !tokenId) return tokenId; + if (!tokens || !tokenId) return null; return tokens.find((token) => token.id === tokenId); }; From 0f6df310eac2cbc568fb26f7ac14d3dd1d765c99 Mon Sep 17 00:00:00 2001 From: nicholas ma Date: Thu, 14 Mar 2024 11:47:53 -0700 Subject: [PATCH 3/5] chore: disable announcement --- src/utils/runtime.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/runtime.ts b/src/utils/runtime.ts index 06e79f4bd..52c7c8e2b 100644 --- a/src/utils/runtime.ts +++ b/src/utils/runtime.ts @@ -25,7 +25,7 @@ export async function onInstalled(details: Runtime.OnInstalledDetailsType) { browser.alarms.create("notifications", { periodInMinutes: 1 }); // reset notifications - await ExtensionStorage.set("show_announcement", true); + // await ExtensionStorage.set("show_announcement", true); // wayfinder await scheduleGatewayUpdate(); From d3a891666c3f269389d593fc446ba9cc0789b366 Mon Sep 17 00:00:00 2001 From: nicholas ma Date: Thu, 14 Mar 2024 11:52:54 -0700 Subject: [PATCH 4/5] chore: version bump for beta --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a797a478..cd3c58af6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "arconnect", "displayName": "ArConnect", - "version": "1.5.1", + "version": "1.6.1", "description": "__MSG_extensionDescription__", "author": "th8ta", "packageManager": "yarn@1.22.18", From e441f3338f69528a5b405a5426784104186f4053 Mon Sep 17 00:00:00 2001 From: nicholas ma Date: Thu, 14 Mar 2024 11:55:54 -0700 Subject: [PATCH 5/5] Revert "Merge branch 'production' into staging" This reverts commit cc8aeb46f7b0261bfeeb85867a81b53da68d8099, reversing changes made to d3a891666c3f269389d593fc446ba9cc0789b366. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b83b9b2b..cd3c58af6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "arconnect", "displayName": "ArConnect", - "version": "1.6.0", + "version": "1.6.1", "description": "__MSG_extensionDescription__", "author": "th8ta", "packageManager": "yarn@1.22.18",