Skip to content

Commit

Permalink
Merge pull request #266 from arconnectio/staging
Browse files Browse the repository at this point in the history
ArConnect 1.6.1 - Notification Improvements
  • Loading branch information
nicholaswma authored Mar 14, 2024
2 parents 31cf5f0 + e441f33 commit 638f2c5
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 60 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "arconnect",
"displayName": "ArConnect",
"version": "1.6.0",
"version": "1.6.1",
"description": "__MSG_extensionDescription__",
"author": "th8ta",
"packageManager": "[email protected]",
Expand Down
28 changes: 27 additions & 1 deletion src/notifications/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
130 changes: 74 additions & 56 deletions src/routes/popup/notifications.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { fetchNotifications } from "~utils/notifications";
import {
extractQuantityTransferred,
fetchNotifications,
fetchTokenById,
fetchTokenByProcessId,
mergeAndSortNotifications
} from "~utils/notifications";
import { getTokenInfo } from "~tokens/aoTokens/router";
import aoLogo from "url:/assets/ecosystem/ao-logo.svg";
import { useHistory } from "~utils/hash_router";
Expand All @@ -9,45 +15,18 @@ 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";
import type { Transaction } from "~notifications/api";

export default function Notifications() {
const [notifications, setNotifications] = useState([]);
const [formattedTxMsgs, setFormattedTxMsgs] = useState([]);
const [notifications, setNotifications] = useState<Transaction[]>([]);
const [formattedTxMsgs, setFormattedTxMsgs] = useState<string[]>([]);
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 {
Expand Down Expand Up @@ -80,27 +59,71 @@ export default function Notifications() {
return "Recipient not found";
};

const formatTxMessage = async (notifications) => {
const formatTxMessage = async (
notifications: Transaction[]
): Promise<string[]> => {
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: string = "";
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.isAo
? 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
Expand All @@ -111,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";
Expand Down
2 changes: 1 addition & 1 deletion src/routes/popup/send/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
70 changes: 70 additions & 0 deletions src/utils/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,5 +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();
Expand All @@ -14,3 +18,69 @@ export const fetchNotifications = async () => {
}
return notifications;
};

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<TokenInfo> => {
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): Promise<Token> => {
if (tokenId === "AR") {
return arPlaceholder;
}
const tokens = await ExtensionStorage.get<Token[]>("tokens");

if (!tokens || !tokenId) return null;
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;
}
};
2 changes: 1 addition & 1 deletion src/utils/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 638f2c5

Please sign in to comment.