mirror of
https://github.com/tompro/sattle.git
synced 2026-08-26 23:05:58 +00:00
feat: deep link routing for lightning and lnurl schemes plus association files
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"applinks": {
|
||||
"apps": [],
|
||||
"details": [
|
||||
{
|
||||
"appID": "REPLACE_WITH_APPLE_TEAM_ID.eu.protom.sattle",
|
||||
"paths": ["*"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"webcredentials": {
|
||||
"apps": ["REPLACE_WITH_APPLE_TEAM_ID.eu.protom.sattle"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "eu.protom.sattle",
|
||||
"sha256_cert_fingerprints": [
|
||||
"REPLACE_WITH_RELEASE_KEYSTORE_SHA256_CERT_FINGERPRINT"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
+1
-1
@@ -11,7 +11,7 @@ export default defineConfig((ctx) => {
|
||||
// app boot file (/src/boot)
|
||||
// --> boot files are part of "main.js"
|
||||
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||
boot: ['i18n', 'kit-fetch', 'wallet'],
|
||||
boot: ['i18n', 'kit-fetch', 'wallet', 'deeplinks'],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#css
|
||||
css: ['app.scss'],
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
"id": "sattle",
|
||||
"display_override": ["fullscreen", "minimal-ui"],
|
||||
"display": "standalone",
|
||||
"protocol_handlers": [
|
||||
{
|
||||
"protocol": "web+lightning",
|
||||
"url": "/?uri=%s"
|
||||
}
|
||||
],
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/icon-128x128.png",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { defineBoot } from '#q-app';
|
||||
|
||||
import { initDeepLinks } from '@/capabilities/deepLinks';
|
||||
|
||||
// Deep-link bootstrap (capabilities/deepLinks.ts): native appUrlOpen /
|
||||
// cold-start launch URLs and the PWA protocol-handler ?uri= landing all
|
||||
// funnel into pendingExternalInput; the home screen consumes it and opens
|
||||
// the matching receive/pay dialog. Every accepted link routes home first -
|
||||
// the dialogs live there.
|
||||
export default defineBoot(({ router }) => {
|
||||
void initDeepLinks(() => {
|
||||
void router.push('/').catch(() => {
|
||||
// already on '/' - a redundant navigation is not an error
|
||||
});
|
||||
});
|
||||
});
|
||||
+23
-7
@@ -6,9 +6,7 @@
|
||||
class="sattle-card full-width q-mt-xl q-pa-lg text-center welcome-card"
|
||||
>
|
||||
<div class="text-h5 text-primary q-mb-sm">Welcome to sattle</div>
|
||||
<div class="text-grey-5 q-mb-md">
|
||||
A simple wallet for Lightning bearer notes.
|
||||
</div>
|
||||
<div class="text-grey-5 q-mb-md">A simple wallet for Lightning bearer notes.</div>
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
@@ -43,9 +41,7 @@
|
||||
class="sattle-card q-pa-sm q-mt-md row items-center lock-warning"
|
||||
>
|
||||
<q-icon name="lock_clock" color="warning" class="q-mx-sm" />
|
||||
<div class="col text-grey-4">
|
||||
Locking in {{ wallet.lockWarningSecondsLeft }}s
|
||||
</div>
|
||||
<div class="col text-grey-4">Locking in {{ wallet.lockWarningSecondsLeft }}s</div>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
@@ -128,10 +124,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { isValidNoteInput } from 'lnurlcash-kit';
|
||||
import { consumePendingExternalInput, pendingExternalInput } from '@/capabilities/deepLinks';
|
||||
import { useWalletStore } from '@/stores/wallet';
|
||||
import UnlockForm from '@/components/UnlockForm.vue';
|
||||
import HistoryList from '@/components/HistoryList.vue';
|
||||
@@ -174,6 +171,25 @@ const onScanError = (message: string) => {
|
||||
showScan.value = false;
|
||||
$q.notify({ type: 'negative', message, position: 'top' });
|
||||
};
|
||||
|
||||
// Deep links (capabilities/deepLinks.ts): an inbound lightning:/lnurlw:
|
||||
// link waits as pendingExternalInput until the wallet is unlocked (the
|
||||
// dialogs need the keys), then opens the same dialogs a scan would.
|
||||
watch(
|
||||
[pendingExternalInput, () => wallet.state],
|
||||
() => {
|
||||
if (!pendingExternalInput.value || wallet.state !== 'unlocked') return;
|
||||
const input = consumePendingExternalInput();
|
||||
if (!input) return;
|
||||
scanned.value = input.value;
|
||||
if (input.kind === 'note') {
|
||||
showReceiveToken.value = true;
|
||||
} else {
|
||||
showPayInvoice.value = true;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in New Issue
Block a user