Skip to content

Commit

Permalink
Merge pull request #239 from lollerfirst/nfc
Browse files Browse the repository at this point in the history
Adding NDEF capability
  • Loading branch information
callebtc authored Dec 15, 2024
2 parents 5b23d8c + 3e546ad commit e2dd0aa
Show file tree
Hide file tree
Showing 10 changed files with 428 additions and 33 deletions.
1 change: 1 addition & 0 deletions src/components/HistoryTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export default defineComponent({
this.sendData.tokens = token.getProofs(tokenObj);
this.sendData.tokensBase64 = _.clone(tokensBase64);
this.sendData.paymentRequest = historyToken.paymentRequest;
this.sendData.historyAmount = historyToken.amount;
this.showSendTokens = true;
},
},
Expand Down
36 changes: 31 additions & 5 deletions src/components/ReceiveTokenDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
<q-btn
unelevated
dense
class="q-mx-sm"
class="q-mr-sm"
v-if="hasCamera && !receiveData.tokensBase64.length"
@click="showCamera"
>
Expand All @@ -114,8 +114,9 @@
>
<q-icon name="lock_outline" class="q-pr-sm" />Lock
</q-btn>
<!-- does not require a second dow of buttons, close button here -->
<q-btn
v-if="!enablePaymentRequest"
v-if="!(enablePaymentRequest || ndefSupported)"
v-close-popup
rounded
flat
Expand All @@ -125,16 +126,38 @@
>
</div>
<div
v-if="!receiveData.tokensBase64.length && enablePaymentRequest"
v-if="
!receiveData.tokensBase64.length &&
(enablePaymentRequest || ndefSupported)
"
class="row q-mt-lg"
>
<!-- does require second row of buttons -->
<q-btn
unelevated
dense
class="q-mr-sm"
v-if="!receiveData.tokensBase64.length && ndefSupported"
:loading="scanningCard"
:disabled="scanningCard"
@click="toggleScanner"
>
<q-icon name="nfc" class="q-pr-sm" />
<q-tooltip>{{
ndefSupported ? "Read from NFC card" : "NDEF unsupported"
}}</q-tooltip>
<template v-slot:loading>
<q-spinner @click="toggleScanner"> </q-spinner>
</template>
NFC
</q-btn>
<q-btn
unelevated
dense
class="q-mr-sm"
@click="handlePaymentRequestBtn"
>
<q-icon name="move_to_inbox" class="q-pr-sm" />Payment Request
<q-icon name="move_to_inbox" class="q-pr-sm" />Request
</q-btn>
<q-btn
v-if="enablePaymentRequest"
Expand Down Expand Up @@ -168,6 +191,7 @@ import { mapActions, mapState, mapWritableState } from "pinia";
// import ChooseMint from "components/ChooseMint.vue";
import TokenInformation from "components/TokenInformation.vue";
import { map } from "underscore";
import { notifyError, notifySuccess, notify } from "../js/notify";
export default defineComponent({
name: "ReceiveTokenDialog",
Expand All @@ -181,12 +205,14 @@ export default defineComponent({
data: function () {
return {
showP2PKDialog: false,
ndefSupported: "NDEFReader" in globalThis,
};
},
computed: {
...mapWritableState(useReceiveTokensStore, [
"showReceiveTokens",
"receiveData",
"scanningCard",
]),
...mapState(useUiStore, ["tickerShort"]),
...mapState(useMintsStore, [
Expand Down Expand Up @@ -231,6 +257,7 @@ export default defineComponent({
"receiveIfDecodes",
"decodeToken",
"knowThisMintOfTokenJson",
"toggleScanner",
]),
// TOKEN METHODS
getProofs: function (decoded_token) {
Expand Down Expand Up @@ -289,6 +316,5 @@ export default defineComponent({
});
},
},
created: function () {},
});
</script>
169 changes: 162 additions & 7 deletions src/components/SendTokenDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,13 @@
</div>
<q-card-section class="q-pa-sm">
<div class="row justify-center">
<q-item-label overline class="q-mb-sm text-white"
>Ecash</q-item-label
<q-item-label overline class="q-mb-sm text-white">
{{
sendData.historyAmount && sendData.historyAmount < 0
? "Sent"
: "Received"
}}
Ecash</q-item-label
>
</div>
<div class="row justify-center q-py-md">
Expand All @@ -259,6 +264,11 @@
<strong>{{ displayUnit }}</strong></q-item-label
>
</div>
<div v-if="paidFees" class="row justify-center q-pb-md">
<q-item-label class="text-weight-bold">
Fees: {{ formatCurrency(paidFees, tokenUnit) }}
</q-item-label>
</div>
<div class="row justify-center q-pt-sm">
<TokenInformation
:encodedToken="sendData.tokensBase64"
Expand All @@ -284,11 +294,38 @@
unelevated
dense
class="q-mx-sm"
v-if="hasCamera && !sendData.paymentRequest"
v-if="
hasCamera &&
!sendData.paymentRequest &&
sendData.historyAmount < 0
"
@click="showCamera"
>
<q-icon name="qr_code_scanner" class="q-pr-sm" />
</q-btn>
<q-btn
unelevated
dense
v-if="
ndefSupported &&
!sendData.paymentRequest &&
sendData.historyAmount < 0
"
:disabled="scanningCard"
:loading="scanningCard"
class="q-mx-sm"
icon="nfc"
size="md"
@click="writeTokensToCard"
flat
>
<q-tooltip>{{
ndefSupported ? "Flash to NFC card" : "NDEF unsupported"
}}</q-tooltip>
<template v-slot:loading>
<q-spinner @click="closeCardScanner" />
</template>
</q-btn>
<q-btn
class="q-mx-none"
color="grey"
Expand All @@ -303,12 +340,20 @@
color="grey"
icon="delete"
size="md"
@click="showDeleteDialog = true"
@click="
showDeleteDialog = true;
closeCardScanner();
"
flat
>
<q-tooltip>Delete from history</q-tooltip>
</q-btn>
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
<q-btn
v-close-popup
@click="closeCardScanner"
flat
color="grey"
class="q-ml-auto"
>Close</q-btn
>
</div>
Expand Down Expand Up @@ -380,7 +425,12 @@ import { mapActions, mapState, mapWritableState } from "pinia";
import ChooseMint from "components/ChooseMint.vue";
import { UR, UREncoder } from "@gandlaf21/bc-ur";
import SendPaymentRequest from "./SendPaymentRequest.vue";
import {
notifyError,
notifySuccess,
notify,
notifyWarning,
} from "src/js/notify.ts";
export default defineComponent({
name: "SendTokenDialog",
mixins: [windowMixin],
Expand Down Expand Up @@ -414,6 +464,8 @@ export default defineComponent({
framentInervalSlow: 500,
fragmentSpeedLabel: "F",
isV4Token: false,
scanningCard: false,
ndefSupported: "NDEFReader" in globalThis,
};
},
computed: {
Expand All @@ -439,6 +491,7 @@ export default defineComponent({
...mapState(useSettingsStore, [
"checkSentTokens",
"includeFeesInSendAmount",
"nfcEncoding",
]),
...mapState(useWorkersStore, ["tokenWorkerRunning"]),
// TOKEN METHODS
Expand All @@ -458,6 +511,9 @@ export default defineComponent({
let mint = token.getMint(token.decode(this.sendData.tokensBase64));
return mint;
},
paidFees: function () {
return this.sumProofs - Math.abs(this.sendData.historyAmount);
},
displayMemo: function () {
return token.getMemo(token.decode(this.sendData.tokensBase64));
},
Expand Down Expand Up @@ -648,6 +704,104 @@ export default defineComponent({
this.showDeleteDialog = false;
this.clearAllWorkers();
},
writeTokensToCard: function () {
if (!this.scanningCard) {
try {
this.ndef = new NDEFReader();
this.controller = new AbortController();
const signal = this.controller.signal;
this.ndef
.scan({ signal })
.then(() => {
console.log("> Scan started");
this.ndef.onreadingerror = (error) => {
console.error(`Cannot read NDEF data! ${error}`);
notifyError("Cannot read data from the NFC tag");
this.controller.abort();
this.scanningCard = false;
};
this.ndef.onreading = ({ message, serialNumber }) => {
console.log(`Read card ${serialNumber}`);
this.controller.abort();
this.scanningCard = false;
try {
let records = [];
switch (this.nfcEncoding) {
case "text":
records = [
{
recordType: "text",
data: `${this.sendData.tokensBase64}`,
},
];
break;
case "weburl":
records = [
{
recordType: "url",
data: `${window.location}#token=${this.sendData.tokensBase64}`,
},
];
break;
case "binary":
throw new Error("Binary encoding not supported yet");
/*
const data = null;
records = [
{
recordType: "mime",
mediaType: "application/octet-stream",
data: data,
},
];
break;
*/
default:
throw new Error(
`Unknown NFC encoding: ${this.nfcEncoding}`
);
}
this.ndef
.write({ records: records }, { overwrite: true })
.then(() => {
console.log("Successfully flashed tokens to card!");
notifySuccess("Successfully flashed tokens to card!");
this.showSendTokens = false;
})
.catch((err) => {
console.error(
`NFC write failed: The card may not have enough capacity (needed ${records[0].data.length} bytes).`
);
notifyError(
`NFC write failed: The card may not have enough capacity (needed ${records[0].data.length} bytes).`
);
});
} catch (err) {
console.error(`NFC error: ${err.message}`);
notifyError(`NFC error: ${err.message}`);
}
};
this.scanningCard = true;
})
.catch((error) => {
console.error(`NFC error: ${error.message}`);
notifyError(`NFC error: ${error.message}`);
this.scanningCard = false;
});
notifyWarning("This will overwrite your card!");
} catch (error) {
console.error(`NFC error: ${error.message}`);
notifyError(`NFC error: ${error.message}`);
this.scanningCard = false;
}
}
},
closeCardScanner: function () {
this.controller.abort();
this.scanningCard = false;
},
lockTokens: async function () {
let sendAmount = this.sendData.amount;
// if unit is USD, multiply by 100
Expand Down Expand Up @@ -704,8 +858,9 @@ export default defineComponent({
// update UI
this.sendData.tokens = sendProofs;
this.sendData.tokensBase64 = this.serializeProofs(sendProofs);
this.sendData.historyAmount = -this.sendData.amount;
this.addPendingToken({
amount: -sendAmount,
serializedProofs: this.sendData.tokensBase64,
Expand Down
Loading

0 comments on commit e2dd0aa

Please sign in to comment.