Cashu: PWA install button (#1455)

This commit is contained in:
calle 2023-02-05 14:04:23 +01:00 committed by GitHub
parent 9f177dea98
commit 754db04d99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -370,7 +370,7 @@ page_container %}
<div class="col-4 q-pt-xs"> <div class="col-4 q-pt-xs">
<div align="center"> <div align="center">
<q-btn <q-btn
class="q-mx-xs q-px-none" class="q-mx-xs q-px-none q-my-sm"
size="0.5rem" size="0.5rem"
rectangle rectangle
color="warning" color="warning"
@ -380,7 +380,7 @@ page_container %}
><q-tooltip>Warning</q-tooltip></q-btn ><q-tooltip>Warning</q-tooltip></q-btn
> >
<q-btn <q-btn
class="q-mx-xs q-px-none" class="q-mx-xs q-px-none q-my-sm"
size="0.5rem" size="0.5rem"
outline outline
rectangle rectangle
@ -389,6 +389,15 @@ page_container %}
@click="getLocalstorageToFile" @click="getLocalstorageToFile"
><q-tooltip>Download wallet backup</q-tooltip></q-btn ><q-tooltip>Download wallet backup</q-tooltip></q-btn
> >
<q-btn
class="q-mx-xs q-px-none q-my-sm"
outline
size="0.5rem"
v-if="getPWADisplayMode()=='browser' && deferredPWAInstallPrompt != null"
color="primary"
@click="triggerPWAInstall()"
><b>Install</b><q-tooltip>Install Cashu</q-tooltip></q-btn
>
</div> </div>
</div> </div>
<div class="col-4 q-pt-none"> <div class="col-4 q-pt-none">
@ -708,8 +717,20 @@ page_container %}
Backup button to download a copy of your tokens. Backup button to download a copy of your tokens.
</p> </p>
<div class="row q-mt-lg"> <div class="row q-mt-lg">
<q-btn outline color="grey" @click="copyText(baseURL)" <q-btn
>Copy wallet URL</q-btn outline
class="q-mx-sm"
v-if="getPWADisplayMode()=='browser' && deferredPWAInstallPrompt != null"
color="primary"
@click="triggerPWAInstall()"
>Install Cashu</q-btn
>
<q-btn
outline
color="grey"
class="q-mx-sm"
@click="copyText(baseURL)"
>Copy URL</q-btn
> >
<q-btn <q-btn
v-close-popup v-close-popup
@ -1033,6 +1054,7 @@ page_container %}
keys: '', keys: '',
proofs: [], proofs: [],
activeProofs: [], activeProofs: [],
deferredPWAInstallPrompt: null,
invoicesCashu: [], invoicesCashu: [],
historyTokens: [], historyTokens: [],
invoiceData: { invoiceData: {
@ -1771,8 +1793,8 @@ page_container %}
requestMint: async function () { requestMint: async function () {
/* /*
gets an invoice from the mint to get new tokens gets an invoice from the mint to get new tokens
*/ */
try { try {
const {data} = await axios.get( const {data} = await axios.get(
`${this.activeMintURL}/mint?amount=${this.invoiceData.amount}` `${this.activeMintURL}/mint?amount=${this.invoiceData.amount}`
@ -1802,9 +1824,9 @@ page_container %}
mintApi: async function (amounts, payment_hash, verbose = true) { mintApi: async function (amounts, payment_hash, verbose = true) {
/* /*
asks the mint to check whether the invoice with payment_hash has been paid asks the mint to check whether the invoice with payment_hash has been paid
and requests signing of the attached outputs. and requests signing of the attached outputs.
*/ */
try { try {
let secrets = await this.generateSecrets(amounts) let secrets = await this.generateSecrets(amounts)
@ -1870,9 +1892,9 @@ page_container %}
split: async function (proofs, amount) { split: async function (proofs, amount) {
/* /*
supplies proofs and requests a split from the mint of these supplies proofs and requests a split from the mint of these
proofs at a specific amount proofs at a specific amount
*/ */
try { try {
if (proofs.length == 0) { if (proofs.length == 0) {
throw new Error('no proofs provided.') throw new Error('no proofs provided.')
@ -1945,11 +1967,11 @@ page_container %}
splitToSend: async function (proofs, amount, invlalidate = false) { splitToSend: async function (proofs, amount, invlalidate = false) {
/* /*
splits proofs so the user can keep firstProofs, send scndProofs. splits proofs so the user can keep firstProofs, send scndProofs.
then sets scndProofs as reserved. then sets scndProofs as reserved.
if invalidate, scndProofs (the one to send) are invalidated if invalidate, scndProofs (the one to send) are invalidated
*/ */
try { try {
const spendableProofs = proofs.filter(p => !p.reserved) const spendableProofs = proofs.filter(p => !p.reserved)
if (this.sumProofs(spendableProofs) < amount) { if (this.sumProofs(spendableProofs) < amount) {
@ -1992,8 +2014,8 @@ page_container %}
redeem: async function () { redeem: async function () {
/* /*
uses split to receive new tokens. uses split to receive new tokens.
*/ */
this.showReceiveTokens = false this.showReceiveTokens = false
console.log('### receive tokens', this.receiveData.tokensBase64) console.log('### receive tokens', this.receiveData.tokensBase64)
try { try {
@ -2055,8 +2077,8 @@ page_container %}
sendTokens: async function () { sendTokens: async function () {
/* /*
calls splitToSend, displays token and kicks off the spendableWorker calls splitToSend, displays token and kicks off the spendableWorker
*/ */
try { try {
// keep firstProofs, send scndProofs and delete them (invalidate=true) // keep firstProofs, send scndProofs and delete them (invalidate=true)
let {fristProofs, scndProofs} = await this.splitToSend( let {fristProofs, scndProofs} = await this.splitToSend(
@ -2165,9 +2187,9 @@ page_container %}
checkProofsSpendable: async function (proofs, update_history = false) { checkProofsSpendable: async function (proofs, update_history = false) {
/* /*
checks with the mint whether an array of proofs is still checks with the mint whether an array of proofs is still
spendable or already invalidated spendable or already invalidated
*/ */
if (proofs.length == 0) { if (proofs.length == 0) {
return return
} }
@ -2323,9 +2345,9 @@ page_container %}
checkTokenSpendable: async function (token, verbose = true) { checkTokenSpendable: async function (token, verbose = true) {
/* /*
checks whether a base64-encoded token (from the history table) has been spent already. checks whether a base64-encoded token (from the history table) has been spent already.
if it is spent, the appropraite entry in the history table is set to paid. if it is spent, the appropraite entry in the history table is set to paid.
*/ */
const tokenJson = JSON.parse(atob(token)) const tokenJson = JSON.parse(atob(token))
const proofs = tokenJson.proofs const proofs = tokenJson.proofs
@ -2443,7 +2465,6 @@ page_container %}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////// UI HELPERS /////////////
assertMintError: function (response, verbose = true) { assertMintError: function (response, verbose = true) {
if (response.error != null) { if (response.error != null) {
if (verbose) { if (verbose) {
@ -2452,6 +2473,32 @@ page_container %}
throw new Error(`Mint error: ${response.error}`) throw new Error(`Mint error: ${response.error}`)
} }
}, },
////////////// UI HELPERS /////////////
getPWADisplayMode: function () {
const isStandalone = window.matchMedia(
'(display-mode: standalone)'
).matches
if (document.referrer.startsWith('android-app://')) {
return 'twa'
} else if (navigator.standalone || isStandalone) {
return 'standalone'
}
return 'browser'
},
triggerPWAInstall: function () {
// Show the install prompt
this.deferredPWAInstallPrompt.prompt()
// Wait for the user to respond to the prompt
this.deferredPWAInstallPrompt.userChoice.then(choiceResult => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the install prompt')
this.setWelcomeDialogSeen()
} else {
console.log('User dismissed the install prompt')
}
})
},
showNoMintsWarning: function () { showNoMintsWarning: function () {
if (!this.activeMintURL) { if (!this.activeMintURL) {
this.walletURL = this.baseURL this.walletURL = this.baseURL
@ -2680,26 +2727,6 @@ page_container %}
await this.fetchMintKeys() await this.fetchMintKeys()
} }
// const keysJson = localStorage.getItem(this.mintKey(this.mintId, 'keys'))
// if (!keysJson) {
// if (this.activeMintURL.length) {
// this.fetchMintKeys()
// }
// } else {
// this.keys = JSON.parse(keysJson)
// }
// this.invoicesCashu = JSON.parse(
// localStorage.getItem(this.mintKey(this.mintId, 'invoicesCashu')) || '[]'
// )
// this.historyTokens = JSON.parse(
// localStorage.getItem(this.mintKey(this.mintId, 'historyTokens')) || '[]'
// )
// this.proofs = JSON.parse(
// localStorage.getItem(this.mintKey(this.mintId, 'proofs')) || '[]'
// )
this.invoicesCashu = JSON.parse( this.invoicesCashu = JSON.parse(
localStorage.getItem('cashu.invoicesCashu') || '[]' localStorage.getItem('cashu.invoicesCashu') || '[]'
) )
@ -2752,7 +2779,20 @@ page_container %}
this.activateMint(startupMintUrl) this.activateMint(startupMintUrl)
} }
// Initialize deferredPWAInstallPrompt for use later to show browser install prompt.
this.showWelcomeDialog() this.showWelcomeDialog()
// register event listener for PWA install prompt
window.addEventListener('beforeinstallprompt', e => {
// Prevent the mini-infobar from appearing on mobile
// e.preventDefault()
// Stash the event so it can be triggered later.
this.deferredPWAInstallPrompt = e
console.log(
`'beforeinstallprompt' event was fired.`,
this.getPWADisplayMode()
)
})
} }
}) })
</script> </script>