diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 9d08a1a..0000000 --- a/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true diff --git a/.vscode/settings.json b/.vscode/settings.json index 746cf57..8dcb2a8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { - "editor.bracketPairColorization.enabled": true, - "editor.guides.bracketPairs": true, + // "editor.bracketPairColorization.enabled": true, + // "editor.guides.bracketPairs": true, "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": [ diff --git a/README.md b/README.md index c6a6e1d..4b604bc 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ Because of its minimalist design, Cashonize is a user-friendly wallet, even for Cashonize nicely displays your NFT collections by collapsing NFTs of the same tokenId, making it ideal for NFT collectors. Cashonize is a single-address wallet, for privacy-centered users is better to use a HD-wallet. -Cashonize does not have a display for your transaction history and for now it only supports USD for denomating BCH balances. ## Platforms diff --git a/package.json b/package.json index 79e1f96..8114cb0 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,12 @@ }, "dependencies": { "@bitjson/qr-code": "^1.0.2", + "@capacitor-community/barcode-scanner": "^4.0.1", "@capacitor/app": "^5.0.7", "@capacitor/core": "^5.7.4", "@download/blockies": "^1.0.3", "@mainnet-cash/indexeddb-storage": "^2.2.7", + "@mainnet-pat/json5-bigint": "^2.2.5", "@quasar/extras": "^1.16.4", "@types/blockies": "^0.0.4", "@vueform/toggle": "^2.1.4", @@ -29,10 +31,11 @@ "@walletconnect/core": "^2.11.1", "@walletconnect/web3wallet": "^1.10.1", "chota": "^0.9.2", - "mainnet-js": "^2.3.13", + "mainnet-js": "^2.4.1", "pinia": "^2.1.7", "quasar": "^2.14.3", "vue": "^3.3.4", + "vue-qrcode-reader": "^5.5.7", "vue-router": "^4.0.12" }, "devDependencies": { diff --git a/public/images/qrscan.svg b/public/images/qrscan.svg new file mode 100644 index 0000000..fecea08 --- /dev/null +++ b/public/images/qrscan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/quasar.config.js b/quasar.config.js index c32f94e..1fbcdb7 100644 --- a/quasar.config.js +++ b/quasar.config.js @@ -82,7 +82,14 @@ module.exports = configure(function (/* ctx */) { // extendViteConf (viteConf) {}, // viteVuePluginOptions: {}, - + viteVuePluginOptions: { + template: { + compilerOptions: { + isCustomElement: (tag) => tag === ('qr-code') + } + } + }, + vitePlugins: [ [ nodePolyfills diff --git a/src/components/bchWallet.vue b/src/components/bchWallet.vue index 514a855..413eaf1 100644 --- a/src/components/bchWallet.vue +++ b/src/components/bchWallet.vue @@ -4,9 +4,12 @@ import { decodeCashAddress } from "@bitauth/libauth" import { defineCustomElements } from '@bitjson/qr-code'; import alertDialog from 'src/components/alertDialog.vue' + import { CurrencySymbols, CurrencyShortNames, TokenDataFT, TokenDataNFT, TokenData } from 'src/interfaces/interfaces' import { useStore } from '../stores/store' import { useSettingsStore } from '../stores/settingsStore' import { useQuasar } from 'quasar' + import QrCodeDialog from './qr/qrCodeScanDialog.vue'; + const $q = useQuasar() const store = useStore() const settingsStore = useSettingsStore() @@ -14,7 +17,7 @@ const { width } = useWindowSize(); const isMobile = computed(() => width.value < 480) - const nrTokenCategories = computed(() => store.tokenList?.length) + const nrTokenCategories = computed(() => store.tokenList?.filter((token: TokenData) => (token as TokenDataFT).amount > 0n || (token as TokenDataNFT).nfts?.length > 0).length) const numberFormatter = new Intl.NumberFormat('en-US', {maximumFractionDigits: 8}); @@ -32,8 +35,9 @@ // reactive state const displayeBchQr = ref(true); const bchSendAmount = ref(undefined as (number | undefined)); - const usdSendAmount = ref(undefined as (number | undefined)); + const currencySendAmount = ref(undefined as (number | undefined)); const destinationAddr = ref(""); + const showQrCodeDialog = ref(false); function switchAddressTypeQr(){ displayeBchQr.value = !displayeBchQr.value; @@ -57,29 +61,29 @@ let bchAmount = Number(params.split("amount=")[1]); if(settingsStore.bchUnit == "sat") bchAmount = Math.round(bchAmount * 100_000_000); bchSendAmount.value = bchAmount; - setUsdAmount() + setCurrencyAmount() } } - async function setUsdAmount() { + async function setCurrencyAmount() { if(typeof bchSendAmount.value != 'number'){ - usdSendAmount.value = undefined + currencySendAmount.value = undefined return } - const newUsdValue = await convert(bchSendAmount.value, settingsStore.bchUnit, "usd"); - usdSendAmount.value = Number(newUsdValue.toFixed(2)); + const newCurrencyValue = await convert(bchSendAmount.value, settingsStore.bchUnit, settingsStore.currency); + currencySendAmount.value = Number(newCurrencyValue.toFixed(2)); } async function setBchAmount() { - if(typeof usdSendAmount.value != 'number'){ + if(typeof currencySendAmount.value != 'number'){ bchSendAmount.value = undefined return } - const newBchValue = await convert(usdSendAmount.value, "usd", settingsStore.bchUnit); + const newBchValue = await convert(currencySendAmount.value, settingsStore.currency, settingsStore.bchUnit); bchSendAmount.value = Number(newBchValue); } async function useMaxBchAmount(){ if(store.maxAmountToSend && store.maxAmountToSend[settingsStore.bchUnit]){ bchSendAmount.value = store.maxAmountToSend[settingsStore.bchUnit]; - setUsdAmount() + setCurrencyAmount() } else{ $q.notify({ @@ -124,7 +128,7 @@ console.log(alertMessage); // reset fields bchSendAmount.value = undefined; - usdSendAmount.value = undefined; + currencySendAmount.value = undefined; destinationAddr.value = ""; } catch(error){ console.log(error) @@ -136,15 +140,26 @@ }) } } + const qrDecode = (content: string) => { + destinationAddr.value = content; + } + const qrFilter = (content: string) => { + const decoded = decodeCashAddress(content); + if (typeof decoded === "string" || decoded.prefix !== store.wallet?.networkPrefix) { + return "Not a cashaddress on current network"; + } + + return true; + } \ No newline at end of file diff --git a/src/components/createTokens.vue b/src/components/createTokens.vue index 04f8eba..5e0587a 100644 --- a/src/components/createTokens.vue +++ b/src/components/createTokens.vue @@ -4,6 +4,7 @@ import alertDialog from 'src/components/alertDialog.vue' import { useStore } from '../stores/store' import { useQuasar } from 'quasar' +import { cachedFetch } from 'src/utils/utils'; const $q = useQuasar() const store = useStore() @@ -61,7 +62,10 @@ const fetchLocation = selectedUri.value != "IPFS" ? "https://" + inputField + bcmrLocation : inputField + inputField.slice(7); try{ console.log("fetching bcmr at "+fetchLocation); - const response = await fetch(fetchLocation); + const response = await cachedFetch(fetchLocation, { + storageType: localStorage, + duration: 1000 * 60 * 60 * 24, // 1 day + }); if(response?.status == 200) validitityCheck.value = true; const bcmrContent = await response.text(); const hashContent = sha256.hash(utf8ToBin(bcmrContent)); diff --git a/src/components/history/transactionDialog.vue b/src/components/history/transactionDialog.vue new file mode 100644 index 0000000..4400ab2 --- /dev/null +++ b/src/components/history/transactionDialog.vue @@ -0,0 +1,200 @@ + + + + + \ No newline at end of file diff --git a/src/components/history/walletHistory.vue b/src/components/history/walletHistory.vue new file mode 100644 index 0000000..d3b7a21 --- /dev/null +++ b/src/components/history/walletHistory.vue @@ -0,0 +1,219 @@ + + + + + \ No newline at end of file diff --git a/src/components/myTokens.vue b/src/components/myTokens.vue index 61e8429..f72b977 100644 --- a/src/components/myTokens.vue +++ b/src/components/myTokens.vue @@ -9,10 +9,10 @@ \ No newline at end of file diff --git a/src/components/qr/qrCodeScanDialog.vue b/src/components/qr/qrCodeScanDialog.vue new file mode 100644 index 0000000..7d68439 --- /dev/null +++ b/src/components/qr/qrCodeScanDialog.vue @@ -0,0 +1,107 @@ + + + + + \ No newline at end of file diff --git a/src/components/qr/qrScannerUi.vue b/src/components/qr/qrScannerUi.vue new file mode 100644 index 0000000..f6407c1 --- /dev/null +++ b/src/components/qr/qrScannerUi.vue @@ -0,0 +1,146 @@ + + + + + \ No newline at end of file diff --git a/src/components/settingsMenu.vue b/src/components/settingsMenu.vue index ee722da..27339ff 100644 --- a/src/components/settingsMenu.vue +++ b/src/components/settingsMenu.vue @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/src/components/tokenItems/tokenItemFT.vue b/src/components/tokenItems/tokenItemFT.vue index 3f89a14..e72fce1 100644 --- a/src/components/tokenItems/tokenItemFT.vue +++ b/src/components/tokenItems/tokenItemFT.vue @@ -1,15 +1,17 @@ @@ -303,15 +324,20 @@
-
Amount: - {{ numberFormatter.format(toAmountDecimals(tokenData?.amount)) }} {{ tokenMetaData?.token?.symbol }} +
+
Amount: + {{ numberFormatter.format(toAmountDecimals(tokenData?.amount)) }} {{ tokenMetaData?.token?.symbol }} +
+
Value: + {{ tokenPrice }} {{ CurrencySymbols[settingsStore.currency] }} +
- + send info @@ -324,6 +350,8 @@ auth transfer + + {{ settingsStore.featuredTokens.includes(tokenData.tokenId) ? "★" : "☆" }} favorite
@@ -363,7 +391,12 @@ Send these tokens to
- +
+ + +
@@ -408,4 +441,7 @@
+
+ +
\ No newline at end of file diff --git a/src/components/tokenItems/tokenItemNFT.vue b/src/components/tokenItems/tokenItemNFT.vue index ed0a925..654be9b 100644 --- a/src/components/tokenItems/tokenItemNFT.vue +++ b/src/components/tokenItems/tokenItemNFT.vue @@ -12,6 +12,8 @@ import { useStore } from 'src/stores/store' import { useSettingsStore } from 'src/stores/settingsStore' import { useQuasar } from 'quasar' + import QrCodeScanDialog from '../qr/qrCodeScanDialog.vue'; + const $q = useQuasar() const store = useStore() const settingsStore = useSettingsStore() @@ -40,10 +42,11 @@ const startingNumberNFTs = ref(undefined as string | undefined); const totalNumberNFTs = ref(undefined as number | undefined); const hasMintingNFT = ref(undefined as boolean | undefined); + const showQrCodeDialog = ref(false); let fetchedMetadataChildren = false - tokenMetaData.value = store.bcmrRegistries?.[tokenData.value.tokenId] ?? null; + tokenMetaData.value = store.bcmrRegistries?.[tokenData.value.tokenId] ?? undefined; const isSingleNft = computed(() => tokenData.value.nfts?.length == 1); const nftMetadata = computed(() => { @@ -385,6 +388,18 @@ color: "red" }) } + + const qrDecode = (content: string) => { + destinationAddr.value = content; + } + const qrFilter = (content: string) => { + const decoded = decodeCashAddress(content); + if (typeof decoded === "string" || decoded.prefix !== store.wallet?.networkPrefix || !['p2pkhWithTokens', 'p2shWithTokens'].includes(decoded.type)) { + return "Not a tokenaddress on current network"; + } + + return true; + } \ No newline at end of file diff --git a/src/components/walletConnect.vue b/src/components/walletConnect.vue index c7f7127..1e0445c 100644 --- a/src/components/walletConnect.vue +++ b/src/components/walletConnect.vue @@ -7,6 +7,7 @@ import { useStore } from 'src/stores/store' import { useWalletconnectStore } from 'src/stores/walletconnectStore' import { useQuasar } from 'quasar' + import QrCodeDialog from './qr/qrCodeScanDialog.vue'; const $q = useQuasar() const store = useStore() const walletconnectStore = useWalletconnectStore() @@ -19,6 +20,7 @@ const dappUriInput = ref(""); const sessionProposalWC = ref(undefined as any); const activeSessions = computed(() => walletconnectStore.activeSessions) + const showQrCodeDialog = ref(false); async function connectDappUriInput(){ try { @@ -94,6 +96,18 @@ const updatedSessions = web3wallet?.getActiveSessions(); walletconnectStore.activeSessions = updatedSessions; } + + const qrDecode = async (content: string) => { + await web3wallet?.core.pairing.pair({ uri: content}); + } + const qrFilter = (content: string) => { + const matchV2 = String(content).match(/^wc:([0-9a-fA-F]{64})@(\d+)\?([a-zA-Z0-9\-._~%!$&'()*+,;=:@/?=&]*)$/i); + if (!matchV2) { + return "Not a valid WalletConnect2 URI"; + } + + return true; +}