Skip to content

Commit

Permalink
Merge pull request #502 from arconnectio/development
Browse files Browse the repository at this point in the history
ArConnect BETA 1.19.2
  • Loading branch information
nicholaswma authored Oct 18, 2024
2 parents 544e93d + 242e859 commit d6158b2
Show file tree
Hide file tree
Showing 20 changed files with 376 additions and 41 deletions.
4 changes: 4 additions & 0 deletions assets/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,10 @@
"message": "View all",
"description": "View all link text"
},
"view_all_assets": {
"message": "View all assets",
"description": "View all link text"
},
"setting_tokens": {
"message": "Tokens",
"description": "Tokens setting title"
Expand Down
4 changes: 4 additions & 0 deletions assets/_locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,10 @@
"message": "查看全部",
"description": "View all link text"
},
"view_all_assets": {
"message": "查看所有资产",
"description": "View all assets link text"
},
"setting_tokens": {
"message": "代币",
"description": "Tokens setting title"
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"contextMenus",
"tabs",
"webNavigation",
"notifications"
"notifications",
"printerProvider"
],
"web_accessible_resources": [
{
Expand Down
3 changes: 3 additions & 0 deletions src/api/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import subscriptionModule from "./modules/subscription";
import subscription from "./modules/subscription/subscription.background";
import userTokensModule from "./modules/user_tokens";
import userTokens from "./modules/user_tokens/user_tokens.background";
import tokenBalanceModule from "./modules/token_balance";
import tokenBalance from "./modules/token_balance/token_balance.background";

/** Background modules */
const modules: BackgroundModule<any>[] = [
Expand All @@ -69,6 +71,7 @@ const modules: BackgroundModule<any>[] = [
{ ...signDataItemModule, function: signDataItem },
{ ...subscriptionModule, function: subscription },
{ ...userTokensModule, function: userTokens },
{ ...tokenBalanceModule, function: tokenBalance },
{ ...batchSignDataItemModule, function: batchSignDataItem }
];

Expand Down
3 changes: 3 additions & 0 deletions src/api/foreground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import signDataItem, {
} from "./modules/sign_data_item/sign_data_item.foreground";
import userTokensModule from "./modules/user_tokens";
import userTokens from "./modules/user_tokens/user_tokens.foreground";
import tokenBalanceModule from "./modules/token_balance";
import tokenBalance from "./modules/token_balance/token_balance.foreground";

/** Foreground modules */
const modules: ForegroundModule[] = [
Expand Down Expand Up @@ -101,6 +103,7 @@ const modules: ForegroundModule[] = [
},
{ ...subscriptionModule, function: subscription },
{ ...userTokensModule, function: userTokens },
{ ...tokenBalanceModule, function: tokenBalance },
{
...batchSignDataItemModule,
function: batchSignDataItem,
Expand Down
11 changes: 11 additions & 0 deletions src/api/modules/token_balance/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { PermissionType } from "~applications/permissions";
import type { ModuleProperties } from "~api/module";

const permissions: PermissionType[] = ["ACCESS_TOKENS"];

const tokenBalanceModule: ModuleProperties = {
functionName: "tokenBalance",
permissions
};

export default tokenBalanceModule;
20 changes: 20 additions & 0 deletions src/api/modules/token_balance/token_balance.background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { ModuleFunction } from "~api/background";
import { ExtensionStorage } from "~utils/storage";
import { getAoTokenBalance, getNativeTokenBalance } from "~tokens/aoTokens/ao";
import { AO_NATIVE_TOKEN } from "~utils/ao_import";
import { isAddress } from "~utils/assertions";

const background: ModuleFunction<string> = async (_, id?: string) => {
// validate input
isAddress(id);
const address = await ExtensionStorage.get("active_address");

const balance =
id === AO_NATIVE_TOKEN
? await getNativeTokenBalance(address)
: (await getAoTokenBalance(address, id)).toString();

return balance;
};

export default background;
6 changes: 6 additions & 0 deletions src/api/modules/token_balance/token_balance.foreground.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { ModuleFunction } from "~api/module";

// no need to transform anything in the foreground
const foreground: ModuleFunction<void> = () => {};

export default foreground;
9 changes: 9 additions & 0 deletions src/background.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getCapabilities, getPrinters, handlePrintRequest } from "~lib/printer";
import { addressChangeListener, walletsChangeListener } from "~wallets/event";
import { keyRemoveAlarmListener, onWindowClose } from "~wallets/auth";
import { appConfigChangeListener } from "~applications/events";
Expand Down Expand Up @@ -83,4 +84,12 @@ browser.runtime.onInstalled.addListener(onInstalled);
// handle ar:// protocol
browser.webNavigation.onBeforeNavigate.addListener(protocolHandler);

// print to the permaweb (only on chrome)
if (typeof chrome !== "undefined") {
// @ts-expect-error
chrome.printerProvider.onGetCapabilityRequested.addListener(getCapabilities);
chrome.printerProvider.onGetPrintersRequested.addListener(getPrinters);
chrome.printerProvider.onPrintRequested.addListener(handlePrintRequest);
}

export {};
1 change: 1 addition & 0 deletions src/components/arlocal/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export default function Transaction({ arweave }: Props) {
content: browser.i18n.getMessage("invalidPassword"),
duration: 2400
});
setSendingTx(false);
return;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/popup/Token.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ export default function Token({ onClick, ...props }: Props) {
}, [fractBalance.toString()]);

// token price
const { price, currency } = usePrice(props.ticker);
const { price, currency } = usePrice(props.ticker, props.ao);

// fiat balance
const fiatBalance = useMemo(() => {
if (!price) return <div />;
if (!price || props.ao) return <div />;

const estimate = fractBalance.multipliedBy(price);

Expand Down
4 changes: 2 additions & 2 deletions src/components/popup/home/NoBalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ export default function NoBalance() {
<ButtonWrapper>
<PureBuyButton />
<ButtonV2
onClick={() => push("/receive")}
onClick={() => push("/tokens")}
secondary
fullWidth
className="normal-font-weight"
>
{browser.i18n.getMessage("receive_AR_button")}
{browser.i18n.getMessage("view_all_assets")}
<ArrowRight style={{ marginLeft: "5px" }} />
</ButtonV2>
</ButtonWrapper>
Expand Down
227 changes: 227 additions & 0 deletions src/lib/printer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { uploadDataToTurbo } from "~api/modules/dispatch/uploader";
import { getActiveKeyfile, type DecryptedWallet } from "~wallets";
import { freeDecryptedWallet } from "~wallets/encryption";
import { createData, ArweaveSigner } from "arbundles";
import { concatGatewayURL } from "~gateways/utils";
import { findGateway } from "~gateways/wayfinder";
import browser from "webextension-polyfill";
import Arweave from "arweave";
import { signAuth } from "~api/modules/sign/sign_auth";
import { getActiveTab } from "~applications";
import { sleep } from "~utils/sleep";

const ARCONNECT_PRINTER_ID = "arconnect-permaweb-printer";

/**
* Tells Chrome about the virtual printer's
* capabilities in CDD format
*/
export function getCapabilities(
printerId: string,
callback: PrinterCapabilitiesCallback
) {
// only return capabilities for the ArConnect printer
if (printerId !== ARCONNECT_PRINTER_ID) return;

// mimic a regular printer's capabilities
callback({
version: "1.0",
printer: {
supported_content_type: [
{ content_type: "application/pdf" },
{ content_type: "image/pwg-raster" }
],
color: {
option: [
{ type: "STANDARD_COLOR", is_default: true },
{ type: "STANDARD_MONOCHROME" }
]
},
copies: {
default_copies: 1,
max_copies: 100
},
media_size: {
option: [
{
name: "ISO_A4",
width_microns: 210000,
height_microns: 297000,
is_default: true
},
{
name: "NA_LETTER",
width_microns: 215900,
height_microns: 279400
}
]
},
page_orientation: {
option: [
{
type: "PORTRAIT",
is_default: true
},
{ type: "LANDSCAPE" },
{ type: "AUTO" }
]
},
duplex: {
option: [
{ type: "NO_DUPLEX", is_default: true },
{ type: "LONG_EDGE" },
{ type: "SHORT_EDGE" }
]
}
}
});
}

/**
* Printer capabilities request callback type
*/
type PrinterCapabilitiesCallback = (p: unknown) => void;

/**
* Returns a list of "virtual" printers,
* in our case "Print/Publish to Arweave"
*/
export function getPrinters(callback: PrinterInfoCallback) {
callback([
{
id: ARCONNECT_PRINTER_ID,
name: "Print to Arweave",
description:
"Publish the content you want to print on Arweave, permanently."
}
]);
}

/**
* Printer info request callback type
*/
type PrinterInfoCallback = (p: chrome.printerProvider.PrinterInfo[]) => void;

/**
* Handles the request from the user to print the page to Arweave
*/
export async function handlePrintRequest(
printJob: chrome.printerProvider.PrintJob,
resultCallback: PrintCallback
) {
// only print for the ArConnect printer
if (printJob.printerId !== ARCONNECT_PRINTER_ID) return;

// wallet
let decryptedWallet: DecryptedWallet;

try {
// build data blog
const data = new Blob([printJob.document], { type: printJob.contentType });

// get user wallet
decryptedWallet = await getActiveKeyfile();

if (decryptedWallet.type === "hardware")
throw new Error("Cannot print with a hardware wallet.");

// extension manifest
const manifest = browser.runtime.getManifest();

// setup tags
const tags = [
{ name: "App-Name", value: manifest.name },
{ name: "App-Version", value: manifest.version },
{ name: "Type", value: "Print-Archive" },
{ name: "Content-Type", value: printJob.contentType },
{ name: "print:title", value: printJob.title },
{ name: "print:timestamp", value: new Date().getTime().toString() }
];

let transactionId: string;

// find a gateway to upload and display the result
const gateway = await findGateway({});
const arweave = Arweave.init(gateway);

// create data item
const dataSigner = new ArweaveSigner(decryptedWallet.keyfile);
const transactionData = new Uint8Array(await data.arrayBuffer());
const dataEntry = createData(transactionData, dataSigner, { tags });

// calculate reward for the transaction
const reward = await arweave.transactions.getPrice(
transactionData.byteLength
);

// get active tab
const activeTab = await getActiveTab();

await signAuth(
activeTab.url,
// @ts-expect-error
{
...dataEntry.toJSON(),
reward,
sizeInBytes: transactionData.byteLength
},
decryptedWallet.address
);

try {
// sign an upload data
await dataEntry.sign(dataSigner);
await uploadDataToTurbo(dataEntry, "https://turbo.ardrive.io");

await sleep(2000);

// this has to be one of FAILED, INVALID_DATA, INVALID_TICKET, OK
resultCallback("OK");

transactionId = dataEntry.id;
} catch (error) {
// sign & post if there is something wrong with turbo

const transaction = await arweave.createTransaction(
{ data: transactionData },
decryptedWallet.keyfile
);

for (const tag of tags) {
transaction.addTag(tag.name, tag.value);
}

// sign and upload
await arweave.transactions.sign(transaction, decryptedWallet.keyfile);
const uploader = await arweave.transactions.getUploader(transaction);

while (!uploader.isComplete) {
await uploader.uploadChunk();
}

await sleep(2000);

// this has to be one of FAILED, INVALID_DATA, INVALID_TICKET, OK
resultCallback("OK");

transactionId = transaction.id;
}

// open in new tab
await chrome.tabs.create({
url: `${concatGatewayURL(gateway)}/${transactionId}`
});
} catch (e) {
console.log("Printing failed:\n", e);
resultCallback("FAILED");
}

// free wallet from memory
if (decryptedWallet?.type == "local")
freeDecryptedWallet(decryptedWallet.keyfile);
}

/**
* Print request (result) callback
*/
type PrintCallback = (result: string) => void;
Loading

0 comments on commit d6158b2

Please sign in to comment.