From e01b8465d536f1a17e5d112a9563ecc916b3d8ed Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Thu, 18 Apr 2024 10:24:13 -0500 Subject: [PATCH] web worker check if already initialized more progress, zap feed not loading? request send receive fix setup profile editing and show zaps wallet connections kitchen sink mutiny plus and misc get rid of swap backup / restore, nostr stuff get rid of gifts channels stuff manage federations and profile fixes cleanup fix build fix chrome android update to cap 6 bump to actual 6.0.0 update xcode version fix interpolation again (regression) move all static methods to the worker add doc strings get rid of window.nostr, make parse params async fight load flicker use a "-test" bundle for debug builds so they don't clobber add back swaps and do some cleanup fix activity flicker --- android/app/build.gradle | 2 +- android/app/capacitor.build.gradle | 1 + android/build.gradle | 4 +- android/capacitor.settings.gradle | 25 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/variables.gradle | 14 +- e2e/encrypt.spec.ts | 9 +- e2e/roundtrip.spec.ts | 4 +- e2e/routes.spec.ts | 8 - ios/App/App.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/App.xcscheme | 84 + ios/App/Podfile | 27 +- ios/App/Podfile.lock | 100 +- package.json | 63 +- pnpm-lock.yaml | 813 +++++---- public/i18n/en.json | 3 +- src/components/Activity.tsx | 151 +- src/components/ActivityDetailsModal.tsx | 49 +- src/components/Amount.tsx | 14 +- src/components/AmountEditable.tsx | 40 +- src/components/BalanceBox.tsx | 58 +- src/components/ContactForm.tsx | 11 +- src/components/ContactViewer.tsx | 8 +- src/components/DeleteEverything.tsx | 9 +- src/components/EditProfileForm.tsx | 5 +- src/components/FeeDisplay.tsx | 17 +- src/components/GenericItem.tsx | 5 +- src/components/GiftLink.tsx | 22 - src/components/HomeBalance.tsx | 24 +- src/components/HomePrompt.tsx | 7 +- src/components/HomeSubnav.tsx | 7 +- src/components/ImportExport.tsx | 20 +- src/components/ImportNsecForm.tsx | 10 +- src/components/KitchenSink.tsx | 48 +- src/components/LoadingIndicator.tsx | 2 +- src/components/Logs.tsx | 5 +- src/components/NWCEditor.tsx | 24 +- src/components/NostrActivity.tsx | 15 +- src/components/PendingNwc.tsx | 25 +- src/components/ReceiveWarnings.tsx | 24 +- src/components/Restart.tsx | 6 +- src/components/ResyncOnchain.tsx | 4 +- src/components/SetupErrorDisplay.tsx | 5 +- src/components/SocialActionRow.tsx | 34 +- src/components/SyncContactsForm.tsx | 77 - src/components/index.ts | 2 - src/components/layout/Misc.tsx | 13 +- src/logic/mutinyWalletSetup.ts | 168 +- src/logic/waila.ts | 12 +- src/router.tsx | 96 +- src/routes/Chat.tsx | 69 +- src/routes/EditProfile.tsx | 20 +- src/routes/Gift.tsx | 270 --- src/routes/Main.tsx | 56 +- src/routes/Profile.tsx | 29 +- src/routes/Receive.tsx | 22 +- src/routes/Redeem.tsx | 18 +- src/routes/Request.tsx | 10 +- src/routes/Search.tsx | 141 +- src/routes/Send.tsx | 72 +- src/routes/Swap.tsx | 88 +- src/routes/SwapLightning.tsx | 54 +- src/routes/index.ts | 5 +- src/routes/settings/Backup.tsx | 8 +- src/routes/settings/Channels.tsx | 38 +- src/routes/settings/Connections.tsx | 9 +- src/routes/settings/Encrypt.tsx | 4 +- src/routes/settings/Gift.tsx | 323 ---- src/routes/settings/ImportProfile.tsx | 37 +- src/routes/settings/LightningAddress.tsx | 23 +- src/routes/settings/ManageFederations.tsx | 34 +- src/routes/settings/NostrKeys.tsx | 39 +- src/routes/settings/Plus.tsx | 19 +- src/routes/settings/Restore.tsx | 10 +- src/routes/settings/index.ts | 1 - src/routes/setup/NewProfile.tsx | 6 +- src/routes/setup/Root.tsx | 3 +- src/state/megaStore.tsx | 348 ++-- src/utils/conversions.ts | 33 +- src/utils/fetchZaps.ts | 30 +- src/utils/nostr.ts | 5 +- src/vite-env.d.ts | 1 + src/workers/walletWorker.ts | 1574 +++++++++++++++++ vite.config.ts | 12 +- 84 files changed, 3210 insertions(+), 2379 deletions(-) create mode 100644 ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme delete mode 100644 src/components/GiftLink.tsx delete mode 100644 src/components/SyncContactsForm.tsx delete mode 100644 src/routes/Gift.tsx delete mode 100644 src/routes/settings/Gift.tsx create mode 100644 src/workers/walletWorker.ts diff --git a/android/app/build.gradle b/android/app/build.gradle index 586d00d..31764ff 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { namespace "com.mutinywallet.mutinywallet" - compileSdkVersion rootProject.ext.compileSdkVersion + compileSdk rootProject.ext.compileSdkVersion defaultConfig { applicationId "com.mutinywallet.mutinywallet" minSdkVersion rootProject.ext.minSdkVersion diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index f68271e..36a5e0b 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -15,6 +15,7 @@ dependencies { implementation project(':capacitor-clipboard') implementation project(':capacitor-filesystem') implementation project(':capacitor-haptics') + implementation project(':capacitor-network') implementation project(':capacitor-share') implementation project(':capacitor-status-bar') implementation project(':capacitor-toast') diff --git a/android/build.gradle b/android/build.gradle index e914ead..384a71f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,8 +7,8 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.0.2' - classpath 'com.google.gms:google-services:4.3.15' + classpath 'com.android.tools.build:gradle:8.2.2' + classpath 'com.google.gms:google-services:4.4.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index 576c64b..7ccb4f6 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -1,33 +1,36 @@ // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN include ':capacitor-android' -project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@5.7.4_@capacitor+core@5.7.4/node_modules/@capacitor/android/capacitor') +project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/android/capacitor') include ':capacitor-mlkit-barcode-scanning' -project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@5.4.0_@capacitor+core@5.7.4/node_modules/@capacitor-mlkit/barcode-scanning/android') +project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor-mlkit/barcode-scanning/android') include ':capacitor-app' -project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/app/android') +project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app/android') include ':capacitor-app-launcher' -project(':capacitor-app-launcher').projectDir = new File('../node_modules/.pnpm/@capacitor+app-launcher@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/app-launcher/android') +project(':capacitor-app-launcher').projectDir = new File('../node_modules/.pnpm/@capacitor+app-launcher@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app-launcher/android') include ':capacitor-clipboard' -project(':capacitor-clipboard').projectDir = new File('../node_modules/.pnpm/@capacitor+clipboard@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/clipboard/android') +project(':capacitor-clipboard').projectDir = new File('../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/clipboard/android') include ':capacitor-filesystem' -project(':capacitor-filesystem').projectDir = new File('../node_modules/.pnpm/@capacitor+filesystem@5.2.1_@capacitor+core@5.7.4/node_modules/@capacitor/filesystem/android') +project(':capacitor-filesystem').projectDir = new File('../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/filesystem/android') include ':capacitor-haptics' -project(':capacitor-haptics').projectDir = new File('../node_modules/.pnpm/@capacitor+haptics@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/haptics/android') +project(':capacitor-haptics').projectDir = new File('../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/haptics/android') + +include ':capacitor-network' +project(':capacitor-network').projectDir = new File('../node_modules/.pnpm/@capacitor+network@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/network/android') include ':capacitor-share' -project(':capacitor-share').projectDir = new File('../node_modules/.pnpm/@capacitor+share@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/share/android') +project(':capacitor-share').projectDir = new File('../node_modules/.pnpm/@capacitor+share@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/share/android') include ':capacitor-status-bar' -project(':capacitor-status-bar').projectDir = new File('../node_modules/.pnpm/@capacitor+status-bar@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/status-bar/android') +project(':capacitor-status-bar').projectDir = new File('../node_modules/.pnpm/@capacitor+status-bar@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/status-bar/android') include ':capacitor-toast' -project(':capacitor-toast').projectDir = new File('../node_modules/.pnpm/@capacitor+toast@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/toast/android') +project(':capacitor-toast').projectDir = new File('../node_modules/.pnpm/@capacitor+toast@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/toast/android') include ':capacitor-secure-storage-plugin' -project(':capacitor-secure-storage-plugin').projectDir = new File('../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.7.4/node_modules/capacitor-secure-storage-plugin/android') +project(':capacitor-secure-storage-plugin').projectDir = new File('../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.0.0/node_modules/capacitor-secure-storage-plugin/android') diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 761b8f0..9b0a13f 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/variables.gradle b/android/variables.gradle index 5946ada..8ef305d 100644 --- a/android/variables.gradle +++ b/android/variables.gradle @@ -1,14 +1,14 @@ ext { minSdkVersion = 22 - compileSdkVersion = 33 - targetSdkVersion = 33 - androidxActivityVersion = '1.7.0' + compileSdkVersion = 34 + targetSdkVersion = 34 + androidxActivityVersion = '1.8.0' androidxAppCompatVersion = '1.6.1' androidxCoordinatorLayoutVersion = '1.2.0' - androidxCoreVersion = '1.10.0' - androidxFragmentVersion = '1.5.6' - coreSplashScreenVersion = '1.0.0' - androidxWebkitVersion = '1.6.1' + androidxCoreVersion = '1.12.0' + androidxFragmentVersion = '1.6.2' + coreSplashScreenVersion = '1.0.1' + androidxWebkitVersion = '1.9.0' junitVersion = '4.13.2' androidxJunitVersion = '1.1.5' androidxEspressoCoreVersion = '3.5.1' diff --git a/e2e/encrypt.spec.ts b/e2e/encrypt.spec.ts index dce6061..5cee5e0 100644 --- a/e2e/encrypt.spec.ts +++ b/e2e/encrypt.spec.ts @@ -61,7 +61,7 @@ test("test local encrypt", async ({ page }) => { // The "Encrypt" button should not be disabled const encryptButton = await page.locator("button", { hasText: "Encrypt" }); - await expect(encryptButton).not.toBeDisabled(); + await expect(encryptButton).toBeEnabled(); // wait 5 seconds for no reason (SADLY THIS IS IMPORTANT FOR THE TEST TO PASS) await page.waitForTimeout(5000); @@ -69,11 +69,8 @@ test("test local encrypt", async ({ page }) => { // Click the "Encrypt" button await encryptButton.click(); - // wait for a while just to see what happens - // await page.waitForTimeout(10000); - // Wait for a modal with the text "Enter your password" - await page.waitForSelector("text=Enter your password"); + await page.getByText("Enter your password").waitFor(); // Find the input field with the name "password" const passwordInput2 = await page.locator(`input[name='password']`); @@ -85,5 +82,5 @@ test("test local encrypt", async ({ page }) => { await page.click("text=Decrypt Wallet"); // Wait for an element matching the selector to appear in DOM. - await page.locator(`text=0 sats`).first(); + await page.locator(`text=0 sats`).first().waitFor(); }); diff --git a/e2e/roundtrip.spec.ts b/e2e/roundtrip.spec.ts index 9ad5da0..7001f7e 100644 --- a/e2e/roundtrip.spec.ts +++ b/e2e/roundtrip.spec.ts @@ -124,8 +124,8 @@ test("rountrip receive and send", async ({ page }) => { await page.click("text=Online Channels"); - // Give it just a second to settle down - await page.waitForTimeout(2000); + // Idk why the node isn't ready to close channels right away + await page.waitForTimeout(5000); await page.click("text=Close"); diff --git a/e2e/routes.spec.ts b/e2e/routes.spec.ts index f2b8586..b10672a 100644 --- a/e2e/routes.spec.ts +++ b/e2e/routes.spec.ts @@ -9,7 +9,6 @@ const routes = [ "/scanner", "/search", "/send", - "/swap", "/settings" ]; @@ -156,13 +155,6 @@ test("visit each route", async ({ page }) => { "Add Connection" ); - // Swap - await page.goto("http://localhost:3420/swap"); - await expect( - page.getByRole("heading", { name: "Swap to Lightning" }) - ).toBeVisible(); - checklist.set("/swap", true); - // print how many routes we've visited checklist.forEach((value, key) => { console.log(`${key}: ${value}`); diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 3f9eeb8..8986c21 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -362,7 +362,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.6.8; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; - PRODUCT_BUNDLE_IDENTIFIER = com.mutinywallet.mutiny; + PRODUCT_BUNDLE_IDENTIFIER = "com.mutinywallet.mutiny-test"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; diff --git a/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme new file mode 100644 index 0000000..fed8b5d --- /dev/null +++ b/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/App/Podfile b/ios/App/Podfile index 20f4c00..c9eaa55 100644 --- a/ios/App/Podfile +++ b/ios/App/Podfile @@ -1,4 +1,4 @@ -require_relative '../../node_modules/.pnpm/@capacitor+ios@5.7.4_@capacitor+core@5.7.4/node_modules/@capacitor/ios/scripts/pods_helpers' +require_relative '../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios/scripts/pods_helpers' platform :ios, '13.0' use_frameworks! @@ -9,18 +9,19 @@ use_frameworks! install! 'cocoapods', :disable_input_output_paths => true def capacitor_pods - pod 'Capacitor', :path => '../../node_modules/.pnpm/@capacitor+ios@5.7.4_@capacitor+core@5.7.4/node_modules/@capacitor/ios' - pod 'CapacitorCordova', :path => '../../node_modules/.pnpm/@capacitor+ios@5.7.4_@capacitor+core@5.7.4/node_modules/@capacitor/ios' - pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@5.4.0_@capacitor+core@5.7.4/node_modules/@capacitor-mlkit/barcode-scanning' - pod 'CapacitorApp', :path => '../../node_modules/.pnpm/@capacitor+app@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/app' - pod 'CapacitorAppLauncher', :path => '../../node_modules/.pnpm/@capacitor+app-launcher@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/app-launcher' - pod 'CapacitorClipboard', :path => '../../node_modules/.pnpm/@capacitor+clipboard@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/clipboard' - pod 'CapacitorFilesystem', :path => '../../node_modules/.pnpm/@capacitor+filesystem@5.2.1_@capacitor+core@5.7.4/node_modules/@capacitor/filesystem' - pod 'CapacitorHaptics', :path => '../../node_modules/.pnpm/@capacitor+haptics@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/haptics' - pod 'CapacitorShare', :path => '../../node_modules/.pnpm/@capacitor+share@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/share' - pod 'CapacitorStatusBar', :path => '../../node_modules/.pnpm/@capacitor+status-bar@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/status-bar' - pod 'CapacitorToast', :path => '../../node_modules/.pnpm/@capacitor+toast@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/toast' - pod 'CapacitorSecureStoragePlugin', :path => '../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.7.4/node_modules/capacitor-secure-storage-plugin' + pod 'Capacitor', :path => '../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios' + pod 'CapacitorCordova', :path => '../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios' + pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor-mlkit/barcode-scanning' + pod 'CapacitorApp', :path => '../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app' + pod 'CapacitorAppLauncher', :path => '../../node_modules/.pnpm/@capacitor+app-launcher@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app-launcher' + pod 'CapacitorClipboard', :path => '../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/clipboard' + pod 'CapacitorFilesystem', :path => '../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/filesystem' + pod 'CapacitorHaptics', :path => '../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/haptics' + pod 'CapacitorNetwork', :path => '../../node_modules/.pnpm/@capacitor+network@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/network' + pod 'CapacitorShare', :path => '../../node_modules/.pnpm/@capacitor+share@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/share' + pod 'CapacitorStatusBar', :path => '../../node_modules/.pnpm/@capacitor+status-bar@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/status-bar' + pod 'CapacitorToast', :path => '../../node_modules/.pnpm/@capacitor+toast@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/toast' + pod 'CapacitorSecureStoragePlugin', :path => '../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.0.0/node_modules/capacitor-secure-storage-plugin' end target 'App' do diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock index d13ff7b..3e5e70d 100644 --- a/ios/App/Podfile.lock +++ b/ios/App/Podfile.lock @@ -1,28 +1,30 @@ PODS: - - Capacitor (5.7.4): + - Capacitor (6.0.0): - CapacitorCordova - - CapacitorApp (5.0.7): + - CapacitorApp (6.0.0): - Capacitor - - CapacitorAppLauncher (5.0.7): + - CapacitorAppLauncher (6.0.0): - Capacitor - - CapacitorClipboard (5.0.7): + - CapacitorClipboard (6.0.0): - Capacitor - - CapacitorCordova (5.7.4) - - CapacitorFilesystem (5.2.1): + - CapacitorCordova (6.0.0) + - CapacitorFilesystem (6.0.0): - Capacitor - - CapacitorHaptics (5.0.7): + - CapacitorHaptics (6.0.0): - Capacitor - - CapacitorMlkitBarcodeScanning (5.4.0): + - CapacitorMlkitBarcodeScanning (6.0.0): - Capacitor - GoogleMLKit/BarcodeScanning (= 4.0.0) + - CapacitorNetwork (6.0.0): + - Capacitor - CapacitorSecureStoragePlugin (0.9.0): - Capacitor - SwiftKeychainWrapper - - CapacitorShare (5.0.7): + - CapacitorShare (6.0.0): - Capacitor - - CapacitorStatusBar (5.0.7): + - CapacitorStatusBar (6.0.0): - Capacitor - - CapacitorToast (5.0.7): + - CapacitorToast (6.0.0): - Capacitor - GoogleDataTransport (9.2.5): - GoogleUtilities/Environment (~> 7.7) @@ -81,18 +83,19 @@ PODS: - SwiftKeychainWrapper (4.0.1) DEPENDENCIES: - - "Capacitor (from `../../node_modules/.pnpm/@capacitor+ios@5.7.4_@capacitor+core@5.7.4/node_modules/@capacitor/ios`)" - - "CapacitorApp (from `../../node_modules/.pnpm/@capacitor+app@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/app`)" - - "CapacitorAppLauncher (from `../../node_modules/.pnpm/@capacitor+app-launcher@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/app-launcher`)" - - "CapacitorClipboard (from `../../node_modules/.pnpm/@capacitor+clipboard@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/clipboard`)" - - "CapacitorCordova (from `../../node_modules/.pnpm/@capacitor+ios@5.7.4_@capacitor+core@5.7.4/node_modules/@capacitor/ios`)" - - "CapacitorFilesystem (from `../../node_modules/.pnpm/@capacitor+filesystem@5.2.1_@capacitor+core@5.7.4/node_modules/@capacitor/filesystem`)" - - "CapacitorHaptics (from `../../node_modules/.pnpm/@capacitor+haptics@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/haptics`)" - - "CapacitorMlkitBarcodeScanning (from `../../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@5.4.0_@capacitor+core@5.7.4/node_modules/@capacitor-mlkit/barcode-scanning`)" - - "CapacitorSecureStoragePlugin (from `../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.7.4/node_modules/capacitor-secure-storage-plugin`)" - - "CapacitorShare (from `../../node_modules/.pnpm/@capacitor+share@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/share`)" - - "CapacitorStatusBar (from `../../node_modules/.pnpm/@capacitor+status-bar@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/status-bar`)" - - "CapacitorToast (from `../../node_modules/.pnpm/@capacitor+toast@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/toast`)" + - "Capacitor (from `../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios`)" + - "CapacitorApp (from `../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app`)" + - "CapacitorAppLauncher (from `../../node_modules/.pnpm/@capacitor+app-launcher@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app-launcher`)" + - "CapacitorClipboard (from `../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/clipboard`)" + - "CapacitorCordova (from `../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios`)" + - "CapacitorFilesystem (from `../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/filesystem`)" + - "CapacitorHaptics (from `../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/haptics`)" + - "CapacitorMlkitBarcodeScanning (from `../../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor-mlkit/barcode-scanning`)" + - "CapacitorNetwork (from `../../node_modules/.pnpm/@capacitor+network@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/network`)" + - "CapacitorSecureStoragePlugin (from `../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.0.0/node_modules/capacitor-secure-storage-plugin`)" + - "CapacitorShare (from `../../node_modules/.pnpm/@capacitor+share@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/share`)" + - "CapacitorStatusBar (from `../../node_modules/.pnpm/@capacitor+status-bar@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/status-bar`)" + - "CapacitorToast (from `../../node_modules/.pnpm/@capacitor+toast@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/toast`)" SPEC REPOS: trunk: @@ -112,43 +115,46 @@ SPEC REPOS: EXTERNAL SOURCES: Capacitor: - :path: "../../node_modules/.pnpm/@capacitor+ios@5.7.4_@capacitor+core@5.7.4/node_modules/@capacitor/ios" + :path: "../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios" CapacitorApp: - :path: "../../node_modules/.pnpm/@capacitor+app@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/app" + :path: "../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app" CapacitorAppLauncher: - :path: "../../node_modules/.pnpm/@capacitor+app-launcher@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/app-launcher" + :path: "../../node_modules/.pnpm/@capacitor+app-launcher@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app-launcher" CapacitorClipboard: - :path: "../../node_modules/.pnpm/@capacitor+clipboard@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/clipboard" + :path: "../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/clipboard" CapacitorCordova: - :path: "../../node_modules/.pnpm/@capacitor+ios@5.7.4_@capacitor+core@5.7.4/node_modules/@capacitor/ios" + :path: "../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios" CapacitorFilesystem: - :path: "../../node_modules/.pnpm/@capacitor+filesystem@5.2.1_@capacitor+core@5.7.4/node_modules/@capacitor/filesystem" + :path: "../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/filesystem" CapacitorHaptics: - :path: "../../node_modules/.pnpm/@capacitor+haptics@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/haptics" + :path: "../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/haptics" CapacitorMlkitBarcodeScanning: - :path: "../../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@5.4.0_@capacitor+core@5.7.4/node_modules/@capacitor-mlkit/barcode-scanning" + :path: "../../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor-mlkit/barcode-scanning" + CapacitorNetwork: + :path: "../../node_modules/.pnpm/@capacitor+network@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/network" CapacitorSecureStoragePlugin: - :path: "../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@5.7.4/node_modules/capacitor-secure-storage-plugin" + :path: "../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.0.0/node_modules/capacitor-secure-storage-plugin" CapacitorShare: - :path: "../../node_modules/.pnpm/@capacitor+share@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/share" + :path: "../../node_modules/.pnpm/@capacitor+share@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/share" CapacitorStatusBar: - :path: "../../node_modules/.pnpm/@capacitor+status-bar@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/status-bar" + :path: "../../node_modules/.pnpm/@capacitor+status-bar@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/status-bar" CapacitorToast: - :path: "../../node_modules/.pnpm/@capacitor+toast@5.0.7_@capacitor+core@5.7.4/node_modules/@capacitor/toast" + :path: "../../node_modules/.pnpm/@capacitor+toast@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/toast" SPEC CHECKSUMS: - Capacitor: 4fe9adf012caceb4c71ffea2f1f4d005cdcbeea7 - CapacitorApp: 17fecd0e6cb23feafac7eb0939417389038b0979 - CapacitorAppLauncher: 7b2705481a74cbe322441bd374f56194de59ab1a - CapacitorClipboard: 45e5e25f2271f98712985d422776cdc5a779cca1 - CapacitorCordova: a6e87fccc0307dee7aec1560ec9398485f2b0ce7 - CapacitorFilesystem: 9f3e3c7fea2fff12f46dd5b07a2914f2103e4cfc - CapacitorHaptics: 7c7c206f0c96a628fed073830c96d28c4b2e772e - CapacitorMlkitBarcodeScanning: 8fb81cbef3c6ffe0c0e2dbd15ed6dca889a5a062 + Capacitor: 559d073c4ca6c27f8e7002c807eea94c3ba435a9 + CapacitorApp: 9d53aec7101f7b030a950c5bdc4df8612576b279 + CapacitorAppLauncher: 24ce4be152a84883378d22114ffe6c492056ced2 + CapacitorClipboard: 80282f684154124b9019ebf401235b70b0cf4994 + CapacitorCordova: 8c4bfdf69368512e85b1d8b724dd7546abeb30af + CapacitorFilesystem: 60e59ba274c234a979e7a3be2552feaadcee4263 + CapacitorHaptics: 9ebc9363f0e9b8eb4295088a0b474530acf1859b + CapacitorMlkitBarcodeScanning: 1cc47dc2163628b44be5400864de32696362045b + CapacitorNetwork: f15a94c16a33cba7c47a17814cb6bcfe3ea34ded CapacitorSecureStoragePlugin: e91d7df060f2495a1acff9583641a6953e3aacba - CapacitorShare: c6a1ebbf0114ff9e863b966cd6052678fa25d480 - CapacitorStatusBar: f390fbb49b82ffb754ea4b3cf71dc8b048baf3e7 - CapacitorToast: c8bb89eeb59a23c1fc298f138cc06c8ff4d90ac1 + CapacitorShare: a771200d3b924a5d7ad9d9fecbac517e4c0aa74f + CapacitorStatusBar: 2e4369f99166125435641b1908d05f561eaba6f6 + CapacitorToast: ba573a7bc5dfd622e78d5be126a84ee221da4180 GoogleDataTransport: 54dee9d48d14580407f8f5fbf2f496e92437a2f2 GoogleMLKit: 2bd0dc6253c4d4f227aad460f69215a504b2980e GoogleToolboxForMac: 8bef7c7c5cf7291c687cf5354f39f9db6399ad34 @@ -163,6 +169,6 @@ SPEC CHECKSUMS: PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 SwiftKeychainWrapper: 807ba1d63c33a7d0613288512399cd1eda1e470c -PODFILE CHECKSUM: 3864776b838da083701f08e4d80ef59ac02798db +PODFILE CHECKSUM: 7d4dccfabb68790e47370312014de5fc49a89668 COCOAPODS: 1.14.3 diff --git a/package.json b/package.json index c781f7d..ada0a91 100644 --- a/package.json +++ b/package.json @@ -15,43 +15,44 @@ }, "type": "module", "devDependencies": { - "@capacitor/assets": "^2.0.4", - "@capacitor/cli": "^5.5.1", - "@ianvs/prettier-plugin-sort-imports": "^4.1.1", - "@playwright/test": "^1.39.0", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", - "autoprefixer": "^10.4.16", - "eslint": "^8.56.0", + "@capacitor/assets": "^3.0.5", + "@capacitor/cli": "6.0.0", + "@ianvs/prettier-plugin-sort-imports": "^4.2.1", + "@playwright/test": "^1.42.1", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", "eslint-import-resolver-typescript": "3.6.1", "eslint-plugin-import": "2.29.1", "eslint-plugin-prettier": "5.1.3", "eslint-plugin-solid": "0.13.1", - "postcss": "^8.4.35", - "prettier": "^3.0.3", + "postcss": "^8.4.38", + "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.12", "tailwindcss": "^3.4.1", - "typescript": "^5.3.3", - "vite": "^5.1.1", - "vite-plugin-pwa": "^0.16.7", - "vite-plugin-solid": "^2.10.1", + "typescript": "^5.4.5", + "vite": "^5.2.10", + "vite-plugin-pwa": "^0.19.8", + "vite-plugin-solid": "^2.10.2", "vite-plugin-wasm": "^3.3.0", - "workbox-window": "^7.0.0" + "workbox-window": "^7.0.0", + "vite-plugin-comlink": "^4.0.3" }, "dependencies": { - "i18next-http-backend": "^2.5.0", - "@capacitor-mlkit/barcode-scanning": "^5.3.0", - "@capacitor/android": "^5.5.1", - "@capacitor/app": "^5.0.6", - "@capacitor/app-launcher": "^5.0.6", - "@capacitor/clipboard": "^5.0.6", - "@capacitor/core": "^5.5.1", - "@capacitor/filesystem": "^5.1.4", - "@capacitor/haptics": "^5.0.6", - "@capacitor/ios": "^5.5.1", - "@capacitor/share": "^5.0.6", - "@capacitor/status-bar": "^5.0.6", - "@capacitor/toast": "^5.0.6", + "@capacitor-mlkit/barcode-scanning": "^6.0.0", + "@capacitor/android": "^6.0.0", + "@capacitor/app": "^6.0.0", + "@capacitor/app-launcher": "^6.0.0", + "@capacitor/clipboard": "^6.0.0", + "@capacitor/core": "^6.0.0", + "@capacitor/filesystem": "^6.0.0", + "@capacitor/haptics": "^6.0.0", + "@capacitor/ios": "^6.0.0", + "@capacitor/network": "^6.0.0", + "@capacitor/share": "^6.0.0", + "@capacitor/status-bar": "^6.0.0", + "@capacitor/toast": "^6.0.0", "@kobalte/core": "^0.12.6", "@kobalte/tailwindcss": "^0.9.0", "@mutinywallet/mutiny-wasm": "0.6.7", @@ -61,12 +62,14 @@ "@solidjs/router": "^0.13.1", "capacitor-secure-storage-plugin": "^0.9.0", "i18next": "^23.10.1", - "i18next-browser-languagedetector": "^7.1.0", + "i18next-browser-languagedetector": "^7.2.0", + "i18next-http-backend": "^2.5.0", "lucide-solid": "^0.363.0", "qr-scanner": "^1.4.2", "solid-js": "^1.8.16", "solid-qr-code": "^0.0.8", - "solid-transition-group": "^0.2.3" + "solid-transition-group": "^0.2.3", + "comlink": "^4.4.1" }, "engines": { "node": ">=18" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8039f73..a440ee9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,41 +6,44 @@ settings: dependencies: '@capacitor-mlkit/barcode-scanning': - specifier: ^5.3.0 - version: 5.4.0(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/android': - specifier: ^5.5.1 - version: 5.7.4(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/app': - specifier: ^5.0.6 - version: 5.0.7(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/app-launcher': - specifier: ^5.0.6 - version: 5.0.7(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/clipboard': - specifier: ^5.0.6 - version: 5.0.7(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/core': - specifier: ^5.5.1 - version: 5.7.4 + specifier: ^6.0.0 + version: 6.0.0 '@capacitor/filesystem': - specifier: ^5.1.4 - version: 5.2.1(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/haptics': - specifier: ^5.0.6 - version: 5.0.7(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/ios': - specifier: ^5.5.1 - version: 5.7.4(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) + '@capacitor/network': + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/share': - specifier: ^5.0.6 - version: 5.0.7(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/status-bar': - specifier: ^5.0.6 - version: 5.0.7(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@capacitor/toast': - specifier: ^5.0.6 - version: 5.0.7(@capacitor/core@5.7.4) + specifier: ^6.0.0 + version: 6.0.0(@capacitor/core@6.0.0) '@kobalte/core': specifier: ^0.12.6 version: 0.12.6(solid-js@1.8.16) @@ -64,12 +67,15 @@ dependencies: version: 0.13.1(solid-js@1.8.16) capacitor-secure-storage-plugin: specifier: ^0.9.0 - version: 0.9.0(@capacitor/core@5.7.4) + version: 0.9.0(@capacitor/core@6.0.0) + comlink: + specifier: ^4.4.1 + version: 4.4.1 i18next: specifier: ^23.10.1 version: 23.10.1 i18next-browser-languagedetector: - specifier: ^7.1.0 + specifier: ^7.2.0 version: 7.2.0 i18next-http-backend: specifier: ^2.5.0 @@ -92,46 +98,46 @@ dependencies: devDependencies: '@capacitor/assets': - specifier: ^2.0.4 - version: 2.0.4(@types/node@20.11.30)(typescript@5.4.3) + specifier: ^3.0.5 + version: 3.0.5(@types/node@20.11.30)(typescript@5.4.5) '@capacitor/cli': - specifier: ^5.5.1 - version: 5.7.4 + specifier: 6.0.0 + version: 6.0.0 '@ianvs/prettier-plugin-sort-imports': - specifier: ^4.1.1 + specifier: ^4.2.1 version: 4.2.1(prettier@3.2.5) '@playwright/test': - specifier: ^1.39.0 + specifier: ^1.42.1 version: 1.42.1 '@typescript-eslint/eslint-plugin': - specifier: ^7.0.1 - version: 7.3.1(@typescript-eslint/parser@7.3.1)(eslint@8.57.0)(typescript@5.4.3) + specifier: ^7.4.0 + version: 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/parser': - specifier: ^7.0.1 - version: 7.3.1(eslint@8.57.0)(typescript@5.4.3) + specifier: ^7.4.0 + version: 7.7.1(eslint@8.57.0)(typescript@5.4.5) autoprefixer: - specifier: ^10.4.16 + specifier: ^10.4.19 version: 10.4.19(postcss@8.4.38) eslint: - specifier: ^8.56.0 + specifier: ^8.57.0 version: 8.57.0 eslint-import-resolver-typescript: specifier: 3.6.1 - version: 3.6.1(@typescript-eslint/parser@7.3.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + version: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-plugin-import: specifier: 2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.3.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-prettier: specifier: 5.1.3 version: 5.1.3(eslint@8.57.0)(prettier@3.2.5) eslint-plugin-solid: specifier: 0.13.1 - version: 0.13.1(eslint@8.57.0)(typescript@5.4.3) + version: 0.13.1(eslint@8.57.0)(typescript@5.4.5) postcss: - specifier: ^8.4.35 + specifier: ^8.4.38 version: 8.4.38 prettier: - specifier: ^3.0.3 + specifier: ^3.2.5 version: 3.2.5 prettier-plugin-tailwindcss: specifier: ^0.5.12 @@ -140,20 +146,23 @@ devDependencies: specifier: ^3.4.1 version: 3.4.1 typescript: - specifier: ^5.3.3 - version: 5.4.3 + specifier: ^5.4.5 + version: 5.4.5 vite: - specifier: ^5.1.1 - version: 5.2.3(@types/node@20.11.30) + specifier: ^5.2.10 + version: 5.2.10(@types/node@20.11.30) + vite-plugin-comlink: + specifier: ^4.0.3 + version: 4.0.3(comlink@4.4.1)(vite@5.2.10) vite-plugin-pwa: - specifier: ^0.16.7 - version: 0.16.7(vite@5.2.3)(workbox-build@7.0.0)(workbox-window@7.0.0) + specifier: ^0.19.8 + version: 0.19.8(vite@5.2.10)(workbox-build@7.0.0)(workbox-window@7.0.0) vite-plugin-solid: - specifier: ^2.10.1 - version: 2.10.2(solid-js@1.8.16)(vite@5.2.3) + specifier: ^2.10.2 + version: 2.10.2(solid-js@1.8.16)(vite@5.2.10) vite-plugin-wasm: specifier: ^3.3.0 - version: 3.3.0(vite@5.2.3) + version: 3.3.0(vite@5.2.10) workbox-window: specifier: ^7.0.0 version: 7.0.0 @@ -1370,53 +1379,53 @@ packages: to-fast-properties: 2.0.0 dev: true - /@capacitor-mlkit/barcode-scanning@5.4.0(@capacitor/core@5.7.4): - resolution: {integrity: sha512-6vmnUm66suMbYnLKlUlBrEX5/xOQYg4laPTGvOL7gmfzcdxvSJA++oHY9L4m3RpDQk3FzmQELSpbIHzvcVYPuw==} + /@capacitor-mlkit/barcode-scanning@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-zW5UKgFtUui9qFV8PBxPzX+07gntyt5owW4S/GhOt4xWGaoxDZWERaA+XJnXgmMxS/MnPIHX2mL+mhMd1A0enQ==} peerDependencies: - '@capacitor/core': ^5.0.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/android@5.7.4(@capacitor/core@5.7.4): - resolution: {integrity: sha512-9ETwrCaLaimLHbwGpgfsPS9cHcPMFLmKFdlYsFsYIusMO6aOrcQTA9Q4xVAkr55ava4Wk+pVRniRYsekrbOLdw==} + /@capacitor/android@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-NwL87VO9F1WY/EgvJZN9pIhjejq688k2fRW6XWNLVe3cgGE6nUb9J34KI68fhx3139cf2LVGPUYs+mwZC8esiQ==} peerDependencies: - '@capacitor/core': ^5.7.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/app-launcher@5.0.7(@capacitor/core@5.7.4): - resolution: {integrity: sha512-FJacB/mK/iwWUKNf1PJz1d7sGyVNRLvEYyYSwbmn9F9+6onj/T2lCL7TU40qhsVwFt8oDmdvygjOnoIoqU9Bpg==} + /@capacitor/app-launcher@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-4srAYXv+NiAe1vHHYfVfjCc7bRytHiqUlCNTUqL4duRPLZlmsoyJ6DH2JnHXvPKoLIx+4fppG7DquXTosUGasg==} peerDependencies: - '@capacitor/core': ^5.0.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/app@5.0.7(@capacitor/core@5.7.4): - resolution: {integrity: sha512-oad0jwQu+vgQDukeS9UV56yG10dlxkAGGl26IQpZlTmg3dTI9qSJtvhmlLfkF0nEtoj5IsVQUPE+NLH1oZkgGQ==} + /@capacitor/app@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-X5UGd90Jh5p9rmoPyMqFyFWqOypdJgVJhYcM5X1YyDVJJGzmJ5MuYv1+ajj5DW9Qyh+5a3th9WYptdGby8jidA==} peerDependencies: - '@capacitor/core': ^5.0.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/assets@2.0.4(@types/node@20.11.30)(typescript@5.4.3): - resolution: {integrity: sha512-1zYuwJujihsCLavojpZQQEjQjxG8BgD2/QWaX1Je2TQgN8UVbmnQG7gO+6gaHqP+hgZu7SOcJ+BlMG+tarBXLA==} + /@capacitor/assets@3.0.5(@types/node@20.11.30)(typescript@5.4.5): + resolution: {integrity: sha512-ohz/OUq61Y1Fc6aVSt0uDrUdeOA7oTH4pkWDbv/8I3UrPjH7oPkzYhShuDRUjekNp9RBi198VSFdt0CetpEOzw==} engines: {node: '>=10.3.0'} hasBin: true dependencies: - '@capacitor/cli': 3.9.0 + '@capacitor/cli': 5.7.4 '@ionic/utils-array': 2.1.6 '@ionic/utils-fs': 3.1.7 - '@trapezedev/project': 5.0.10(@types/node@20.11.30)(typescript@5.4.3) + '@trapezedev/project': 7.0.10(@types/node@20.11.30)(typescript@5.4.5) commander: 8.3.0 debug: 4.3.4 fs-extra: 10.1.0 node-fetch: 2.7.0 node-html-parser: 5.4.2 - sharp: 0.30.7 + sharp: 0.32.6 tslib: 2.6.2 yargs: 17.7.2 transitivePeerDependencies: @@ -1428,31 +1437,6 @@ packages: - typescript dev: true - /@capacitor/cli@3.9.0: - resolution: {integrity: sha512-NkbVZhYb0oPdh/XArE2ZmOwPFJbla5meShGhv3DxKCXeKn1rt92ile+2xOgtB/j+mL7f9cqQzTQM/11sGQzMAg==} - engines: {node: '>=12.4.0'} - hasBin: true - dependencies: - '@ionic/cli-framework-output': 2.2.8 - '@ionic/utils-fs': 3.1.7 - '@ionic/utils-subprocess': 2.1.14 - '@ionic/utils-terminal': 2.3.5 - commander: 6.2.1 - debug: 4.3.4 - env-paths: 2.2.1 - kleur: 4.1.5 - native-run: 1.7.4 - open: 7.4.2 - plist: 3.1.0 - prompts: 2.4.2 - semver: 7.6.0 - tar: 6.2.1 - tslib: 2.6.2 - xml2js: 0.4.23 - transitivePeerDependencies: - - supports-color - dev: true - /@capacitor/cli@5.7.4: resolution: {integrity: sha512-Cn7ndfMHWfMT+A/wRoeac4/mAxZWNTlZoD1Mn7UQyVV+iGxZB2JpS9omqha7gBN4xrAuTi/X9FqkzrmR+1V96A==} engines: {node: '>=16.0.0'} @@ -1479,66 +1463,101 @@ packages: - supports-color dev: true - /@capacitor/clipboard@5.0.7(@capacitor/core@5.7.4): - resolution: {integrity: sha512-N9JJruFB1mKNmA/+HPXyhcSugvEmYYfM4BibYB8lPqGc9QUY8WirU1+/5Keo8+lKJ6Sv9FVyHr2Sf8XFpwK7Tw==} - peerDependencies: - '@capacitor/core': ^5.0.0 + /@capacitor/cli@6.0.0: + resolution: {integrity: sha512-6z30P0mr53l0VXPwFjzDVuKIt1991bqUSSfShTT2efWN+rBSGSAH2bPID6qSZornH1n5R5Lh/UHq/aGuW523MQ==} + engines: {node: '>=18.0.0'} + hasBin: true dependencies: - '@capacitor/core': 5.7.4 + '@ionic/cli-framework-output': 2.2.8 + '@ionic/utils-fs': 3.1.7 + '@ionic/utils-process': 2.1.11 + '@ionic/utils-subprocess': 2.1.11 + '@ionic/utils-terminal': 2.3.5 + commander: 9.5.0 + debug: 4.3.4 + env-paths: 2.2.1 + kleur: 4.1.5 + native-run: 2.0.1 + open: 8.4.2 + plist: 3.1.0 + prompts: 2.4.2 + rimraf: 4.4.1 + semver: 7.6.0 + tar: 6.2.1 + tslib: 2.6.2 + xml2js: 0.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@capacitor/clipboard@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-NjcGr2jncSZy9NM9grePo8zz0hhBYYBYZV71xXjLvJhVY4PSdZD/Sj+4jm0pvlOKSK8vAXYyF6VcWT4/Kh/xmw==} + peerDependencies: + '@capacitor/core': ^6.0.0 + dependencies: + '@capacitor/core': 6.0.0 dev: false - /@capacitor/core@5.7.4: - resolution: {integrity: sha512-iZBgvx3o4amzKv5ttA+QHB6i7cxK+/mYpCQd1tnSdipg6ZkvfBhg1HkzhEqHk+I7MNur+QwgYDZho9+ycHRwOw==} + /@capacitor/core@6.0.0: + resolution: {integrity: sha512-NvxIQsJcMiIV+Le1DilR2GGyQQbDInfXK1UywGROQ5mycdFlW5XoAPZ+MKnFGB123RoEgE3uhDGgwTXUmSlX9A==} dependencies: tslib: 2.6.2 dev: false - /@capacitor/filesystem@5.2.1(@capacitor/core@5.7.4): - resolution: {integrity: sha512-mexkJHUbNhydzfJywEUTIGARlB/HRogrtbjCZdA+mQxOi07T2887a70CwFaYRu1nKLMoKoliTM5UsLSsdl0PlA==} + /@capacitor/filesystem@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-GnC4CBfky7fvG9zSV/aQnZaGs6ZJ90AaQorr53z81ArTCqcrSUeBMuCxWmvti9HrdXLhBavyA1UOjvRGObOFjg==} peerDependencies: - '@capacitor/core': ^5.1.1 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/haptics@5.0.7(@capacitor/core@5.7.4): - resolution: {integrity: sha512-/j+7Qa4BxQA5aOU43cwXuiudfSXfoHFsAVfcehH5DkSjxLykZKWHEuE4uFJXqdkSIbAHjS37D0Sde6ENP6G/MA==} + /@capacitor/haptics@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-z2TmB+pTbd98Il2I1xpZGClCFwORxxP2m9f1a0rh70c2ubj2atVyZctgxrx1fuoExZTNGSxHaylWfrmjmtelVg==} peerDependencies: - '@capacitor/core': ^5.0.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/ios@5.7.4(@capacitor/core@5.7.4): - resolution: {integrity: sha512-tQH24WMSYVKYr/Jl1gFImooQmu8OdXUFHoDaPV1WpZIiwbwxbTdwOXeLlGes5U8B8t7xuxTWMWMDt3IwRlDbhQ==} + /@capacitor/ios@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-7mAs3gjWiE5DPM4AGPduqFSDGXCPwwqQRMzbohVway7/cTWnHomHV8mIojMZE4GILeWO2fILbyu3C8q9pHg2vg==} peerDependencies: - '@capacitor/core': ^5.7.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/share@5.0.7(@capacitor/core@5.7.4): - resolution: {integrity: sha512-4GraggRRxwhstxIdF9JyOEBq4QTufqFOekdB4P9GeiQYWJoA5VraSR1mwy4Trke1VFfaBjz/nGi4WQOJdHIAgg==} + /@capacitor/network@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-igGbz3VZuchnfQ/kSFxMsQjLGvKYmEKYzK5RD5S7leLaoBox0B3ueW04yRI2HcGC+PSIo9F52qDL8J6qb1N0vg==} peerDependencies: - '@capacitor/core': ^5.0.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/status-bar@5.0.7(@capacitor/core@5.7.4): - resolution: {integrity: sha512-KblB3gV2LDMEjx3fQoNBAzxb+Tr+2mv68SfFLLDCMiMUD3Eile2TAWRWd1yxy496pDFTOs2BJtup8++iuuuJ/w==} + /@capacitor/share@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-Doqvtxe9tejkjq+P9hZzDslR6VetM02R6N61GBavVBWkQj4N+xWNDA8zISG3PzuGBffGtTfHC6L0W+XySFY5nw==} peerDependencies: - '@capacitor/core': ^5.0.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false - /@capacitor/toast@5.0.7(@capacitor/core@5.7.4): - resolution: {integrity: sha512-E2AuYXUoPPFrfwv3iC5FOeSAkVwlJpIM0w0Sw4uqLM7jvSsOEZh3z9I4/Gdz0ltY0JsQmmnfvoFYiZ1TVsbhWw==} + /@capacitor/status-bar@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-Wo0ILugYlmENegKDgTzVCPjbvP8h1ObgHslLdgeVG643ViMS/diausHIq8e104WIKCXtKIELmQeYVp9mX7932g==} peerDependencies: - '@capacitor/core': ^5.0.0 + '@capacitor/core': ^6.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 + dev: false + + /@capacitor/toast@6.0.0(@capacitor/core@6.0.0): + resolution: {integrity: sha512-aDtNMHi0we5G2AFHGzJmcFuJJYIK1rt68x4gguz50UaC1rL5tM1SizW3Z85BJpKKteRNzEU+0G9nYCx26YGmag==} + peerDependencies: + '@capacitor/core': ^6.0.0 + dependencies: + '@capacitor/core': 6.0.0 dev: false /@corvu/utils@0.1.1(solid-js@1.8.16): @@ -1885,6 +1904,16 @@ packages: - supports-color dev: true + /@ionic/utils-array@2.1.5: + resolution: {integrity: sha512-HD72a71IQVBmQckDwmA8RxNVMTbxnaLbgFOl+dO5tbvW9CkkSFCv41h6fUuNsSEVgngfkn0i98HDuZC8mk+lTA==} + engines: {node: '>=10.3.0'} + dependencies: + debug: 4.3.4 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /@ionic/utils-array@2.1.6: resolution: {integrity: sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==} engines: {node: '>=16.0.0'} @@ -1895,6 +1924,18 @@ packages: - supports-color dev: true + /@ionic/utils-fs@3.1.6: + resolution: {integrity: sha512-eikrNkK89CfGPmexjTfSWl4EYqsPSBh0Ka7by4F0PLc1hJZYtJxUZV3X4r5ecA8ikjicUmcbU7zJmAjmqutG/w==} + engines: {node: '>=10.3.0'} + dependencies: + '@types/fs-extra': 8.1.5 + debug: 4.3.4 + fs-extra: 9.1.0 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /@ionic/utils-fs@3.1.7: resolution: {integrity: sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==} engines: {node: '>=16.0.0'} @@ -1907,6 +1948,16 @@ packages: - supports-color dev: true + /@ionic/utils-object@2.1.5: + resolution: {integrity: sha512-XnYNSwfewUqxq+yjER1hxTKggftpNjFLJH0s37jcrNDwbzmbpFTQTVAp4ikNK4rd9DOebX/jbeZb8jfD86IYxw==} + engines: {node: '>=10.3.0'} + dependencies: + debug: 4.3.4 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /@ionic/utils-object@2.1.6: resolution: {integrity: sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==} engines: {node: '>=16.0.0'} @@ -1917,6 +1968,20 @@ packages: - supports-color dev: true + /@ionic/utils-process@2.1.10: + resolution: {integrity: sha512-mZ7JEowcuGQK+SKsJXi0liYTcXd2bNMR3nE0CyTROpMECUpJeAvvaBaPGZf5ERQUPeWBVuwqAqjUmIdxhz5bxw==} + engines: {node: '>=10.3.0'} + dependencies: + '@ionic/utils-object': 2.1.5 + '@ionic/utils-terminal': 2.3.3 + debug: 4.3.4 + signal-exit: 3.0.7 + tree-kill: 1.2.2 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /@ionic/utils-process@2.1.11: resolution: {integrity: sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA==} engines: {node: '>=16.0.0'} @@ -1931,6 +1996,16 @@ packages: - supports-color dev: true + /@ionic/utils-stream@3.1.5: + resolution: {integrity: sha512-hkm46uHvEC05X/8PHgdJi4l4zv9VQDELZTM+Kz69odtO9zZYfnt8DkfXHJqJ+PxmtiE5mk/ehJWLnn/XAczTUw==} + engines: {node: '>=10.3.0'} + dependencies: + debug: 4.3.4 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /@ionic/utils-stream@3.1.6: resolution: {integrity: sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA==} engines: {node: '>=16.0.0'} @@ -1941,6 +2016,22 @@ packages: - supports-color dev: true + /@ionic/utils-subprocess@2.1.11: + resolution: {integrity: sha512-6zCDixNmZCbMCy5np8klSxOZF85kuDyzZSTTQKQP90ZtYNCcPYmuFSzaqDwApJT4r5L3MY3JrqK1gLkc6xiUPw==} + engines: {node: '>=10.3.0'} + dependencies: + '@ionic/utils-array': 2.1.5 + '@ionic/utils-fs': 3.1.6 + '@ionic/utils-process': 2.1.10 + '@ionic/utils-stream': 3.1.5 + '@ionic/utils-terminal': 2.3.3 + cross-spawn: 7.0.3 + debug: 4.3.4 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + /@ionic/utils-subprocess@2.1.14: resolution: {integrity: sha512-nGYvyGVjU0kjPUcSRFr4ROTraT3w/7r502f5QJEsMRKTqa4eEzCshtwRk+/mpASm0kgBN5rrjYA5A/OZg8ahqg==} engines: {node: '>=16.0.0'} @@ -1957,6 +2048,23 @@ packages: - supports-color dev: true + /@ionic/utils-terminal@2.3.3: + resolution: {integrity: sha512-RnuSfNZ5fLEyX3R5mtcMY97cGD1A0NVBbarsSQ6yMMfRJ5YHU7hHVyUfvZeClbqkBC/pAqI/rYJuXKCT9YeMCQ==} + engines: {node: '>=10.3.0'} + dependencies: + '@types/slice-ansi': 4.0.0 + debug: 4.3.4 + signal-exit: 3.0.7 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + tslib: 2.6.2 + untildify: 4.0.0 + wrap-ansi: 7.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /@ionic/utils-terminal@2.3.4: resolution: {integrity: sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==} engines: {node: '>=16.0.0'} @@ -2430,25 +2538,17 @@ packages: tslib: 2.6.2 dev: false - /@trapezedev/gradle-parse@5.0.10: - resolution: {integrity: sha512-yriBEyOkJ8K4mHCgoyUKQCyVI8tP4S513Wp6/9SCx6Ub8ZvSQUonqU3/OZB2G8FRfL4aijpFfMWtiVFJbX6V/w==} + /@trapezedev/gradle-parse@7.0.10: + resolution: {integrity: sha512-k822Is3jGroqOTKF0gAFm80LmhFJWBAyZvNtyuXq6uQUzDDe2fj/gHwixP6VFzlpaWKLP7IuR609Xv8gwJCXyg==} dev: true - /@trapezedev/project@5.0.10(@types/node@20.11.30)(typescript@5.4.3): - resolution: {integrity: sha512-TJs8vF2qDVq/cjPYKKjbji4b+d2oy1kT4trw++OzZrEPh58JqGzjvvol94E88AHzeGkEKWFO8JR/ypESYxmf6A==} + /@trapezedev/project@7.0.10(@types/node@20.11.30)(typescript@5.4.5): + resolution: {integrity: sha512-UjwsStjhHq/+D1bWREmFDoyKql+qFIgJX93zQLg7R6CyWZUdtlGP2hU3l7tsVRtjJBVXpVu5mj8tdwJJoABO3A==} dependencies: '@ionic/utils-fs': 3.1.7 '@ionic/utils-subprocess': 2.1.14 '@prettier/plugin-xml': 2.2.0 - '@trapezedev/gradle-parse': 5.0.10 - '@types/cross-spawn': 6.0.6 - '@types/diff': 5.0.9 - '@types/fs-extra': 9.0.13 - '@types/ini': 1.3.34 - '@types/jest': 27.5.2 - '@types/lodash': 4.17.0 - '@types/plist': 3.0.5 - '@types/slice-ansi': 5.0.2 + '@trapezedev/gradle-parse': 7.0.10 '@xmldom/xmldom': 0.7.13 conventional-changelog: 3.1.25 cross-fetch: 3.1.8 @@ -2457,6 +2557,7 @@ packages: env-paths: 3.0.0 gradle-to-js: 2.0.1 ini: 2.0.0 + kleur: 4.1.5 lodash: 4.17.21 mergexml: 1.2.3 npm-watch: 0.9.0 @@ -2466,7 +2567,7 @@ packages: replace: 1.2.2 tempy: 1.0.1 tmp: 0.2.3 - ts-node: 10.9.2(@types/node@20.11.30)(typescript@5.4.3) + ts-node: 10.9.2(@types/node@20.11.30)(typescript@5.4.5) xcode: 3.0.1 xml-js: 1.6.11 xpath: 0.0.32 @@ -2525,16 +2626,6 @@ packages: '@babel/types': 7.24.0 dev: true - /@types/cross-spawn@6.0.6: - resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==} - dependencies: - '@types/node': 20.11.30 - dev: true - - /@types/diff@5.0.9: - resolution: {integrity: sha512-RWVEhh/zGXpAVF/ZChwNnv7r4rvqzJ7lYNSmZSVTxjV0PBLf6Qu7RNg+SUtkpzxmiNkjCx0Xn2tPp7FIkshJwQ==} - dev: true - /@types/estree@0.0.39: resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} dev: true @@ -2549,23 +2640,6 @@ packages: '@types/node': 20.11.30 dev: true - /@types/fs-extra@9.0.13: - resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} - dependencies: - '@types/node': 20.11.30 - dev: true - - /@types/ini@1.3.34: - resolution: {integrity: sha512-FafeLhwmWucTi31ZYg/6aHBZNyrogQ35aDvSW7zMAz3HMhUqQ4G/NBya8c5pe2jwoYsDFwra8O9/yZotong76g==} - dev: true - - /@types/jest@27.5.2: - resolution: {integrity: sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==} - dependencies: - jest-matcher-utils: 27.5.1 - pretty-format: 27.5.1 - dev: true - /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true @@ -2574,10 +2648,6 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/lodash@4.17.0: - resolution: {integrity: sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==} - dev: true - /@types/minimist@1.2.5: resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} dev: true @@ -2596,13 +2666,6 @@ packages: resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==} dev: false - /@types/plist@3.0.5: - resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} - dependencies: - '@types/node': 20.11.30 - xmlbuilder: 15.1.1 - dev: true - /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: @@ -2617,16 +2680,12 @@ packages: resolution: {integrity: sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==} dev: true - /@types/slice-ansi@5.0.2: - resolution: {integrity: sha512-IfxMqRjS0zNRHlbDgfMnYRW4LgYMjb+0XeVuMrC0H7/krAGeaQFQ1A4ijuEpmXT+KUspgzjQ2wFUO+xuCZao4A==} - dev: true - /@types/trusted-types@2.0.7: resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} dev: true - /@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.3.1)(eslint@8.57.0)(typescript@5.4.3): - resolution: {integrity: sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==} + /@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -2637,25 +2696,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/scope-manager': 7.3.1 - '@typescript-eslint/type-utils': 7.3.1(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/utils': 7.3.1(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/visitor-keys': 7.3.1 + '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 7.7.1 + '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.7.1 debug: 4.3.4 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3): - resolution: {integrity: sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==} + /@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -2664,13 +2723,13 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 7.3.1 - '@typescript-eslint/types': 7.3.1 - '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.4.3) - '@typescript-eslint/visitor-keys': 7.3.1 + '@typescript-eslint/scope-manager': 7.7.1 + '@typescript-eslint/types': 7.7.1 + '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.7.1 debug: 4.3.4 eslint: 8.57.0 - typescript: 5.4.3 + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -2683,16 +2742,16 @@ packages: '@typescript-eslint/visitor-keys': 6.21.0 dev: true - /@typescript-eslint/scope-manager@7.3.1: - resolution: {integrity: sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==} + /@typescript-eslint/scope-manager@7.7.1: + resolution: {integrity: sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==} engines: {node: ^18.18.0 || >=20.0.0} dependencies: - '@typescript-eslint/types': 7.3.1 - '@typescript-eslint/visitor-keys': 7.3.1 + '@typescript-eslint/types': 7.7.1 + '@typescript-eslint/visitor-keys': 7.7.1 dev: true - /@typescript-eslint/type-utils@7.3.1(eslint@8.57.0)(typescript@5.4.3): - resolution: {integrity: sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==} + /@typescript-eslint/type-utils@7.7.1(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -2701,12 +2760,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.4.3) - '@typescript-eslint/utils': 7.3.1(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5) + '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) debug: 4.3.4 eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -2716,12 +2775,12 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@7.3.1: - resolution: {integrity: sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==} + /@typescript-eslint/types@7.7.1: + resolution: {integrity: sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==} engines: {node: ^18.18.0 || >=20.0.0} dev: true - /@typescript-eslint/typescript-estree@6.21.0(typescript@5.4.3): + /@typescript-eslint/typescript-estree@6.21.0(typescript@5.4.5): resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -2737,14 +2796,14 @@ packages: is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@7.3.1(typescript@5.4.3): - resolution: {integrity: sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==} + /@typescript-eslint/typescript-estree@7.7.1(typescript@5.4.5): + resolution: {integrity: sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' @@ -2752,20 +2811,20 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 7.3.1 - '@typescript-eslint/visitor-keys': 7.3.1 + '@typescript-eslint/types': 7.7.1 + '@typescript-eslint/visitor-keys': 7.7.1 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.3 + minimatch: 9.0.4 semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.4.3): + /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -2776,7 +2835,7 @@ packages: '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.3) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: @@ -2784,8 +2843,8 @@ packages: - typescript dev: true - /@typescript-eslint/utils@7.3.1(eslint@8.57.0)(typescript@5.4.3): - resolution: {integrity: sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==} + /@typescript-eslint/utils@7.7.1(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -2793,9 +2852,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.3.1 - '@typescript-eslint/types': 7.3.1 - '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.4.3) + '@typescript-eslint/scope-manager': 7.7.1 + '@typescript-eslint/types': 7.7.1 + '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5) eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: @@ -2811,11 +2870,11 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@7.3.1: - resolution: {integrity: sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==} + /@typescript-eslint/visitor-keys@7.7.1: + resolution: {integrity: sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==} engines: {node: ^18.18.0 || >=20.0.0} dependencies: - '@typescript-eslint/types': 7.3.1 + '@typescript-eslint/types': 7.7.1 eslint-visitor-keys: 3.4.3 dev: true @@ -2921,11 +2980,6 @@ packages: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true - /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -3068,6 +3122,10 @@ packages: possible-typed-array-names: 1.0.0 dev: true + /b4a@1.6.6: + resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + dev: true + /babel-plugin-jsx-dom-expressions@0.37.19(@babel/core@7.24.3): resolution: {integrity: sha512-nef2eLpWBgFggwrYwN6O3dNKn3RnlX6n4DIamNEAeHwp03kVQUaKUiLaEPnHPJHwxie1KwPelyIY9QikU03vUA==} peerDependencies: @@ -3129,6 +3187,44 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + /bare-events@2.2.2: + resolution: {integrity: sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==} + requiresBuild: true + dev: true + optional: true + + /bare-fs@2.3.0: + resolution: {integrity: sha512-TNFqa1B4N99pds2a5NYHR15o0ZpdNKbAeKTE/+G6ED/UeOavv8RY3dr/Fu99HW3zU3pXpo2kDNO8Sjsm2esfOw==} + requiresBuild: true + dependencies: + bare-events: 2.2.2 + bare-path: 2.1.1 + bare-stream: 1.0.0 + dev: true + optional: true + + /bare-os@2.3.0: + resolution: {integrity: sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==} + requiresBuild: true + dev: true + optional: true + + /bare-path@2.1.1: + resolution: {integrity: sha512-OHM+iwRDRMDBsSW7kl3dO62JyHdBKO3B25FB9vNQBPcGHMo4+eA8Yj41Lfbk3pS/seDY+siNge0LdRTulAau/A==} + requiresBuild: true + dependencies: + bare-os: 2.3.0 + dev: true + optional: true + + /bare-stream@1.0.0: + resolution: {integrity: sha512-KhNUoDL40iP4gFaLSsoGE479t0jHijfYdIcxRn/XtezA2BaUD0NRf/JGRpsMq6dMNM+SrCrB0YSSo/5wBY4rOQ==} + requiresBuild: true + dependencies: + streamx: 2.16.1 + dev: true + optional: true + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true @@ -3261,12 +3357,12 @@ packages: resolution: {integrity: sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==} dev: true - /capacitor-secure-storage-plugin@0.9.0(@capacitor/core@5.7.4): + /capacitor-secure-storage-plugin@0.9.0(@capacitor/core@6.0.0): resolution: {integrity: sha512-P5fiC94opcLHu41vceo9weXH+20g0SPYKkeAx+qm9eKNcVFqpcuI4dqwivXlGXYNMDygyjSQuAaFwZ4gW0Y91Q==} peerDependencies: '@capacitor/core': ^5.0.0 dependencies: - '@capacitor/core': 5.7.4 + '@capacitor/core': 6.0.0 dev: false /chalk@2.4.2: @@ -3379,6 +3475,9 @@ packages: color-string: 1.9.1 dev: true + /comlink@4.4.1: + resolution: {integrity: sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==} + /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true @@ -3387,11 +3486,6 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - /commander@6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - dev: true - /commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} @@ -3773,11 +3867,6 @@ packages: /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - /diff-sequences@27.5.1: - resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dev: true - /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -4059,7 +4148,7 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0): resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -4069,8 +4158,8 @@ packages: debug: 4.3.4 enhanced-resolve: 5.16.0 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.3.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.3.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.3 is-core-module: 2.13.1 @@ -4082,7 +4171,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.3.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} peerDependencies: @@ -4103,16 +4192,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5) debug: 3.2.7(supports-color@5.5.0) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -4122,7 +4211,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -4131,7 +4220,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.3.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -4167,13 +4256,13 @@ packages: synckit: 0.8.8 dev: true - /eslint-plugin-solid@0.13.1(eslint@8.57.0)(typescript@5.4.3): + /eslint-plugin-solid@0.13.1(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-PdNrAylFzeh/SbnLc2pQ432l+bXFGzXj/qNqkh5QNVZCoWIdSs0CJA2D7hqW0DloztwUrzkVZCDWFWc3iRAm/Q==} engines: {node: '>=12.0.0'} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 is-html: 2.0.0 kebab-case: 1.0.2 @@ -4294,6 +4383,10 @@ packages: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} dev: true + /fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + dev: true + /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -4813,11 +4906,6 @@ packages: engines: {node: '>=10'} dev: true - /ini@3.0.1: - resolution: {integrity: sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dev: true - /ini@4.1.2: resolution: {integrity: sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -5069,31 +5157,6 @@ packages: minimatch: 3.1.2 dev: true - /jest-diff@27.5.1: - resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: true - - /jest-get-type@27.5.1: - resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dev: true - - /jest-matcher-utils@27.5.1: - resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: true - /jest-worker@26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} @@ -5328,6 +5391,13 @@ packages: sourcemap-codec: 1.4.8 dev: true + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true @@ -5431,6 +5501,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -5512,26 +5589,6 @@ packages: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} dev: true - /native-run@1.7.4: - resolution: {integrity: sha512-yDEwTp66vmXpqFiSQzz4sVQgyq5U58gGRovglY4GHh12ITyWa6mh6Lbpm2gViVOVD1JYFtYnwcgr7GTFBinXNA==} - engines: {node: '>=12.13.0'} - hasBin: true - dependencies: - '@ionic/utils-fs': 3.1.7 - '@ionic/utils-terminal': 2.3.5 - bplist-parser: 0.3.2 - debug: 4.3.4 - elementtree: 0.1.7 - ini: 3.0.1 - plist: 3.1.0 - split2: 4.2.0 - through2: 4.0.2 - tslib: 2.6.2 - yauzl: 2.10.0 - transitivePeerDependencies: - - supports-color - dev: true - /native-run@2.0.1: resolution: {integrity: sha512-XfG1FBZLM50J10xH9361whJRC9SHZ0Bub4iNRhhI61C8Jv0e1ud19muex6sNKB51ibQNUJNuYn25MuYET/rE6w==} engines: {node: '>=16.0.0'} @@ -5567,8 +5624,8 @@ packages: semver: 7.6.0 dev: true - /node-addon-api@5.1.0: - resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + /node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} dev: true /node-fetch@2.7.0: @@ -5720,14 +5777,6 @@ packages: wrappy: 1.0.2 dev: true - /open@7.4.2: - resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} - engines: {node: '>=8'} - dependencies: - is-docker: 2.2.1 - is-wsl: 2.2.0 - dev: true - /open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -6100,15 +6149,6 @@ packages: engines: {node: ^14.13.1 || >=16.0.0} dev: true - /pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 - dev: true - /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: true @@ -6155,6 +6195,10 @@ packages: /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + /queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + dev: true + /quick-lru@4.0.1: resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} engines: {node: '>=8'} @@ -6176,10 +6220,6 @@ packages: strip-json-comments: 2.0.1 dev: true - /react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - dev: true - /read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} dependencies: @@ -6520,18 +6560,18 @@ packages: has-property-descriptors: 1.0.2 dev: true - /sharp@0.30.7: - resolution: {integrity: sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==} - engines: {node: '>=12.13.0'} + /sharp@0.32.6: + resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} + engines: {node: '>=14.15.0'} requiresBuild: true dependencies: color: 4.2.3 detect-libc: 2.0.3 - node-addon-api: 5.1.0 + node-addon-api: 6.1.0 prebuild-install: 7.1.2 semver: 7.6.0 simple-get: 4.0.1 - tar-fs: 2.1.1 + tar-fs: 3.0.5 tunnel-agent: 0.6.0 dev: true @@ -6679,6 +6719,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + /source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -6735,6 +6780,15 @@ packages: engines: {node: '>= 0.10.0'} dev: true + /streamx@2.16.1: + resolution: {integrity: sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==} + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + optionalDependencies: + bare-events: 2.2.2 + dev: true + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -6945,6 +6999,16 @@ packages: tar-stream: 2.2.0 dev: true + /tar-fs@3.0.5: + resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} + dependencies: + pump: 3.0.0 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 2.3.0 + bare-path: 2.1.1 + dev: true + /tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -6956,6 +7020,14 @@ packages: readable-stream: 3.6.2 dev: true + /tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + dependencies: + b4a: 1.6.6 + fast-fifo: 1.3.2 + streamx: 2.16.1 + dev: true + /tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -7084,19 +7156,19 @@ packages: engines: {node: '>=8'} dev: true - /ts-api-utils@1.3.0(typescript@5.4.3): + /ts-api-utils@1.3.0(typescript@5.4.5): resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.4.3 + typescript: 5.4.5 dev: true /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - /ts-node@10.9.2(@types/node@20.11.30)(typescript@5.4.3): + /ts-node@10.9.2(@types/node@20.11.30)(typescript@5.4.5): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -7122,7 +7194,7 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.4.3 + typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -7221,8 +7293,8 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /typescript@5.4.3: - resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==} + /typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} hasBin: true dev: true @@ -7337,25 +7409,42 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /vite-plugin-pwa@0.16.7(vite@5.2.3)(workbox-build@7.0.0)(workbox-window@7.0.0): - resolution: {integrity: sha512-4WMA5unuKlHs+koNoykeuCfTcqEGbiTRr8sVYUQMhc6tWxZpSRnv9Ojk4LKmqVhoPGHfBVCdGaMo8t9Qidkc1Q==} + /vite-plugin-comlink@4.0.3(comlink@4.4.1)(vite@5.2.10): + resolution: {integrity: sha512-CJVVd4h6YkeIxXEbRTbkvlbDwAHa1eOSl20AtNux9jd9aPe3AVKkYSxyTjGI9331Rg3wzAYsCUBSsnWHl1Ph0g==} + peerDependencies: + comlink: ^4.3.1 + vite: '>=2.9.6' + dependencies: + comlink: 4.4.1 + json5: 2.2.3 + magic-string: 0.30.5 + source-map: 0.7.4 + vite: 5.2.10(@types/node@20.11.30) + dev: true + + /vite-plugin-pwa@0.19.8(vite@5.2.10)(workbox-build@7.0.0)(workbox-window@7.0.0): + resolution: {integrity: sha512-e1oK0dfhzhDhY3VBuML6c0h8Xfx6EkOVYqolj7g+u8eRfdauZe5RLteCIA/c5gH0CBQ0CNFAuv/AFTx4Z7IXTw==} engines: {node: '>=16.0.0'} peerDependencies: - vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 + '@vite-pwa/assets-generator': ^0.2.4 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 workbox-build: ^7.0.0 workbox-window: ^7.0.0 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true dependencies: debug: 4.3.4 fast-glob: 3.3.2 pretty-bytes: 6.1.1 - vite: 5.2.3(@types/node@20.11.30) + vite: 5.2.10(@types/node@20.11.30) workbox-build: 7.0.0 workbox-window: 7.0.0 transitivePeerDependencies: - supports-color dev: true - /vite-plugin-solid@2.10.2(solid-js@1.8.16)(vite@5.2.3): + /vite-plugin-solid@2.10.2(solid-js@1.8.16)(vite@5.2.10): resolution: {integrity: sha512-AOEtwMe2baBSXMXdo+BUwECC8IFHcKS6WQV/1NEd+Q7vHPap5fmIhLcAzr+DUJ04/KHx/1UBU0l1/GWP+rMAPQ==} peerDependencies: '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* @@ -7371,22 +7460,22 @@ packages: merge-anything: 5.1.7 solid-js: 1.8.16 solid-refresh: 0.6.3(solid-js@1.8.16) - vite: 5.2.3(@types/node@20.11.30) - vitefu: 0.2.5(vite@5.2.3) + vite: 5.2.10(@types/node@20.11.30) + vitefu: 0.2.5(vite@5.2.10) transitivePeerDependencies: - supports-color dev: true - /vite-plugin-wasm@3.3.0(vite@5.2.3): + /vite-plugin-wasm@3.3.0(vite@5.2.10): resolution: {integrity: sha512-tVhz6w+W9MVsOCHzxo6SSMSswCeIw4HTrXEi6qL3IRzATl83jl09JVO1djBqPSwfjgnpVHNLYcaMbaDX5WB/pg==} peerDependencies: vite: ^2 || ^3 || ^4 || ^5 dependencies: - vite: 5.2.3(@types/node@20.11.30) + vite: 5.2.10(@types/node@20.11.30) dev: true - /vite@5.2.3(@types/node@20.11.30): - resolution: {integrity: sha512-+i1oagbvkVIhEy9TnEV+fgXsng13nZM90JQbrcPrf6DvW2mXARlz+DK7DLiDP+qeKoD1FCVx/1SpFL1CLq9Mhw==} + /vite@5.2.10(@types/node@20.11.30): + resolution: {integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -7421,7 +7510,7 @@ packages: fsevents: 2.3.3 dev: true - /vitefu@0.2.5(vite@5.2.3): + /vitefu@0.2.5(vite@5.2.10): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -7429,7 +7518,7 @@ packages: vite: optional: true dependencies: - vite: 5.2.3(@types/node@20.11.30) + vite: 5.2.10(@types/node@20.11.30) dev: true /webidl-conversions@3.0.1: @@ -7679,14 +7768,6 @@ packages: sax: 1.3.0 dev: true - /xml2js@0.4.23: - resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==} - engines: {node: '>=4.0.0'} - dependencies: - sax: 1.3.0 - xmlbuilder: 11.0.1 - dev: true - /xml2js@0.5.0: resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} engines: {node: '>=4.0.0'} diff --git a/public/i18n/en.json b/public/i18n/en.json index 57ec35d..101da1a 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -400,7 +400,8 @@ "import_state": "Import State From File", "confirm_replace": "Do you want to replace your state with", "password": "Enter your password to decrypt", - "decrypt_wallet": "Decrypt Wallet" + "decrypt_wallet": "Decrypt Wallet", + "decrypt_export": "Enter your password to save" }, "logs": { "title": "Download debug logs", diff --git a/src/components/Activity.tsx b/src/components/Activity.tsx index e9c51bd..3fa1154 100644 --- a/src/components/Activity.tsx +++ b/src/components/Activity.tsx @@ -18,12 +18,13 @@ import { Button, ButtonCard, ContactButton, + LoadingShimmer, NiceP, SimpleDialog } from "~/components"; import { useI18n } from "~/i18n/context"; import { PrivacyLevel } from "~/routes"; -import { useMegaStore } from "~/state/megaStore"; +import { useMegaStore, WalletWorker } from "~/state/megaStore"; import { actuallyFetchNostrProfile, createDeepSignal, @@ -56,9 +57,11 @@ export interface IActivityItem { } async function fetchContactForNpub( + sw: WalletWorker, npub: string ): Promise { - const hexpub = await hexpubFromNpub(npub); + const hexpub = await hexpubFromNpub(sw, npub); + console.log("fetchContactForNpub", hexpub); if (!hexpub) { return undefined; } @@ -75,6 +78,7 @@ export function UnifiedActivityItem(props: { onClick: (id: string, kind: HackActivityType) => void; onNewContactClick: (profile: PseudoContact) => void; }) { + const [_state, _actions, sw] = useMegaStore(); const navigate = useNavigate(); const click = () => { @@ -97,10 +101,12 @@ export function UnifiedActivityItem(props: { }); const getContact = cache(async (npub) => { - return await fetchContactForNpub(npub); + return await fetchContactForNpub(sw, npub); }, "profile"); - const profileFromNostr = createAsync(async () => { + // Complaining about a "tracked scope" but I think we're good + // eslint-disable-next-line solid/reactivity + const getProfileFromNostr = cache(async () => { if (props.item.contacts.length === 0) { if (props.item.labels) { const npub = props.item.labels.find((l) => @@ -137,6 +143,10 @@ export function UnifiedActivityItem(props: { } } return undefined; + }, "profileFromNostr"); + + const profileFromNostr = createAsync(async () => { + return await getProfileFromNostr(); }); // TODO: figure out what other shit we should filter out @@ -300,21 +310,20 @@ function NewContactModal(props: { profile: PseudoContact; close: () => void }) { const i18n = useI18n(); const navigate = useNavigate(); - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); async function createContact() { try { - const existingContact = - await state.mutiny_wallet?.get_contact_for_npub( - props.profile.hexpub - ); + const existingContact = await sw.get_contact_for_npub( + props.profile.hexpub + ); if (existingContact) { navigate(`/chat/${existingContact.id}`); return; } - const contactId = await state.mutiny_wallet?.create_new_contact( + const contactId = await sw.create_new_contact( props.profile.name, props.profile.hexpub, props.profile.ln_address, @@ -326,7 +335,7 @@ function NewContactModal(props: { profile: PseudoContact; close: () => void }) { throw new Error("no contact id returned"); } - const tagItem = await state.mutiny_wallet?.get_tag_item(contactId); + const tagItem = await sw.get_tag_item(contactId); if (!tagItem) { throw new Error("no contact returned"); @@ -365,7 +374,7 @@ function NewContactModal(props: { profile: PseudoContact; close: () => void }) { } export function CombinedActivity() { - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const i18n = useI18n(); const [detailsOpen, setDetailsOpen] = createSignal(false); @@ -386,25 +395,16 @@ export function CombinedActivity() { setDetailsOpen(true); } - async function getActivity() { + async function fetchActivity() { try { - console.log("refetching activity"); - const activity = await state.mutiny_wallet?.get_activity( - 50, - undefined - ); - - if (!activity) return []; - - return activity as IActivityItem[]; + return await sw.get_activity(50, undefined); } catch (e) { console.error(e); return [] as IActivityItem[]; } } - const [activity, { refetch }] = createResource(getActivity, { - initialValue: [], + const [activity, { refetch }] = createResource(fetchActivity, { storage: createDeepSignal }); @@ -433,65 +433,62 @@ export function CombinedActivity() { close={() => setNewContact(undefined)} /> - - - - navigate("/settings/federations")} - > + }> + + + + + navigate("/settings/federations") + } + > +
+ + {i18n.t("home.federation")} +
+
+
+ navigate("/receive")}>
- - {i18n.t("home.federation")} + + {i18n.t("home.receive")}
-
- navigate("/receive")}> + navigate("/search")}> +
+ + {i18n.t("home.find")} +
+
+
+ = 0} + > +
+ + {(activityItem) => ( + + )} + +
+
+
+ + navigate("/settings/backup")} + >
- - {i18n.t("home.receive")} + + {i18n.t("home.backup")}
- navigate("/search")}> -
- - {i18n.t("home.find")} -
-
- - navigate("/settings/backup")} - > -
- - {i18n.t("home.backup")} -
-
-
- - = 0}> - - navigate("/settings/backup")} - > -
- - {i18n.t("home.backup")} -
-
-
-
- - {(activityItem) => ( - - )} - -
-
- +
+ ); } diff --git a/src/components/ActivityDetailsModal.tsx b/src/components/ActivityDetailsModal.tsx index d966acb..a64e516 100644 --- a/src/components/ActivityDetailsModal.tsx +++ b/src/components/ActivityDetailsModal.tsx @@ -1,13 +1,13 @@ import { Dialog } from "@kobalte/core"; import { - MutinyChannel, + ActivityItem, MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm"; +import { createAsync } from "@solidjs/router"; import { Copy, Link, Shuffle, Zap } from "lucide-solid"; import { createEffect, - createMemo, createResource, Match, ParentComponent, @@ -30,7 +30,6 @@ import { VStack } from "~/components"; import { useI18n } from "~/i18n/context"; -import { Network } from "~/logic/mutinyWalletSetup"; import { BalanceBar } from "~/routes/settings/Channels"; import { useMegaStore } from "~/state/megaStore"; import { mempoolTxUrl, prettyPrintTime, useCopy } from "~/utils"; @@ -301,24 +300,21 @@ function OnchainDetails(props: { tags?: TagItem; }) { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const [copy, copied] = useCopy({ copiedTimeout: 1000 }); const confirmationTime = () => { return props.info.confirmation_time?.Confirmed?.time; }; - const network = state.mutiny_wallet?.get_network() as Network; + const network = state.network || "signet"; // Can return nothing if the channel is already closed const [channelInfo] = createResource(async () => { if (props.kind === "ChannelOpen") { try { - const channels = - await (state.mutiny_wallet?.list_channels() as Promise< - MutinyChannel[] - >); - const channel = channels.find((channel) => + const channels = await sw.list_channels(); + const channel = channels?.find((channel) => channel.outpoint?.startsWith(props.info.txid) ); return channel; @@ -500,7 +496,7 @@ export function ActivityDetailsModal(props: { id: string; setOpen: (open: boolean) => void; }) { - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const id = () => props.id; const kind = () => props.kind; @@ -508,18 +504,16 @@ export function ActivityDetailsModal(props: { try { if (kind() === "Lightning") { console.debug("reading invoice: ", id()); - const invoice = - await state.mutiny_wallet?.get_invoice_by_hash(id()); + const invoice = await sw.get_invoice_by_hash(id()); return invoice; } else if (kind() === "ChannelClose") { console.debug("reading channel close: ", id()); - const closeItem = - await state.mutiny_wallet?.get_channel_closure(id()); + const closeItem = await sw.get_channel_closure(id()); return closeItem; } else { console.debug("reading tx: ", id()); - const tx = await state.mutiny_wallet?.get_transaction(id()); + const tx = await sw.get_transaction(id()); return tx; } @@ -528,17 +522,18 @@ export function ActivityDetailsModal(props: { return undefined; } }); - const tags = createMemo(() => { + const tags = createAsync(async () => { if ( !!data() && + // @ts-expect-error we're narrowing the type here data()?.labels !== undefined && + // @ts-expect-error we're narrowing the type here typeof data()?.labels[0] === "string" ) { + const typedData = data() as MutinyInvoice | ActivityItem; try { // find if there's just one for now - const tags = state.mutiny_wallet?.get_tag_item( - data().labels[0] - ); + const tags = await sw.get_tag_item(typedData.labels[0]); if (tags) { return tags; } else { @@ -598,7 +593,7 @@ export function ActivityDetailsModal(props: { > @@ -612,7 +607,9 @@ export function ActivityDetailsModal(props: { @@ -623,14 +620,18 @@ export function ActivityDetailsModal(props: { } > diff --git a/src/components/Amount.tsx b/src/components/Amount.tsx index 9ace448..3463125 100644 --- a/src/components/Amount.tsx +++ b/src/components/Amount.tsx @@ -1,3 +1,4 @@ +import { createAsync } from "@solidjs/router"; import { Link, Users, Zap } from "lucide-solid"; import { Show } from "solid-js"; @@ -76,16 +77,19 @@ export function AmountFiat(props: { amountSats: bigint | number | undefined; denominationSize?: "sm" | "lg" | "xl"; }) { - const [state, _] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); - const amountInFiat = () => - (state.fiat.value === "BTC" ? "" : "~") + - satsToFormattedFiat( + const amountInFiat = createAsync(async () => { + const formattedFiat = await satsToFormattedFiat( state.price, Number(props.amountSats) || 0, - state.fiat + state.fiat, + sw ); + return (state.fiat.value === "BTC" ? "" : "~") + formattedFiat; + }); + return (

{amountInFiat()} diff --git a/src/components/AmountEditable.tsx b/src/components/AmountEditable.tsx index 2ea426d..ce67357 100644 --- a/src/components/AmountEditable.tsx +++ b/src/components/AmountEditable.tsx @@ -35,25 +35,35 @@ function methodToIcon(method: MethodChoice["method"]) { export const AmountEditable: ParentComponent<{ initialAmountSats: string | bigint; setAmountSats: (s: bigint) => void; - fee?: string; frozenAmount?: boolean; onSubmit?: () => void; activeMethod?: MethodChoice; methods?: MethodChoice[]; setChosenMethod?: (method: MethodChoice) => void; }> = (props) => { - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const [mode, setMode] = createSignal<"fiat" | "sats">("sats"); const [localSats, setLocalSats] = createSignal( props.initialAmountSats.toString() || "0" ); - const [localFiat, setLocalFiat] = createSignal( - satsToFiat( - state.price, - parseInt(props.initialAmountSats.toString() || "0") || 0, - state.fiat - ) + const [rawFiatAmount, setRawFiatAmount] = createSignal( + props.initialAmountSats.toString() || "0" ); + const [localFiat, setLocalFiat] = createSignal("0"); + + createEffect(() => { + if (rawFiatAmount()) { + satsToFiat( + state.price, + Number(rawFiatAmount()) || 0, + state.fiat, + sw + ).then((sats) => { + console.log("sats", sats); + setLocalFiat(sats); + }); + } + }); const displaySats = () => toDisplayHandleNaN(localSats()); const displayFiat = () => @@ -72,7 +82,7 @@ export const AmountEditable: ParentComponent<{ const { value } = e.target as HTMLInputElement; const sane = satsInputSanitizer(value); setLocalSats(sane); - setLocalFiat(satsToFiat(state.price, Number(sane) || 0, state.fiat)); + setRawFiatAmount(Number(sane).toString() || "0"); } /** This behaves the same as handleCharacterInput but allows for the keyboard to be used instead of the virtual keypad @@ -88,7 +98,7 @@ export const AmountEditable: ParentComponent<{ * result - 123.45 */ - function handleFiatInput(e: InputEvent) { + async function handleFiatInput(e: InputEvent) { const { value } = e.currentTarget as HTMLInputElement; let sane; @@ -101,7 +111,7 @@ export const AmountEditable: ParentComponent<{ } else { sane = fiatInputSanitizer( // This allows us to handle the backspace key and fight float rounding - btcFloatRounding(localFiat()), + btcFloatRounding(localFiat() || "0"), state.fiat.maxFractionalDigits ); } @@ -112,9 +122,13 @@ export const AmountEditable: ParentComponent<{ ); } setLocalFiat(sane); - setLocalSats( - fiatToSats(state.price, parseFloat(sane || "0") || 0, false) + const sats = await fiatToSats( + state.price, + Number(sane) || 0, + false, + sw ); + setLocalSats(sats); } function toggle(disabled: boolean) { diff --git a/src/components/BalanceBox.tsx b/src/components/BalanceBox.tsx index 5c46dfe..e091442 100644 --- a/src/components/BalanceBox.tsx +++ b/src/components/BalanceBox.tsx @@ -1,6 +1,6 @@ import { A, useNavigate } from "@solidjs/router"; import { Shuffle, Users } from "lucide-solid"; -import { Match, Show, Switch } from "solid-js"; +import { createMemo, Match, Show, Suspense, Switch } from "solid-js"; import { AmountFiat, @@ -51,13 +51,18 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) { const navigate = useNavigate(); const i18n = useI18n(); - const totalOnchain = () => - (state.balance?.confirmed || 0n) + - (state.balance?.unconfirmed || 0n) + - (state.balance?.force_close || 0n); + const totalOnchain = createMemo( + () => + (state.balance?.confirmed || 0n) + + (state.balance?.unconfirmed || 0n) + + (state.balance?.force_close || 0n) + ); - const usableOnchain = () => - (state.balance?.confirmed || 0n) + (state.balance?.unconfirmed || 0n); + const usableOnchain = createMemo( + () => + (state.balance?.confirmed || 0n) + + (state.balance?.unconfirmed || 0n) + ); return ( @@ -82,12 +87,15 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) { />
- + + +
- + + +
@@ -168,10 +178,12 @@ export function BalanceBox(props: { loading?: boolean; small?: boolean }) { />
- + + +
diff --git a/src/components/ContactForm.tsx b/src/components/ContactForm.tsx index b06fc9a..feb1938 100644 --- a/src/components/ContactForm.tsx +++ b/src/components/ContactForm.tsx @@ -8,14 +8,15 @@ import { import { Button, ContactFormValues, TextField, VStack } from "~/components"; import { useI18n } from "~/i18n/context"; +import { useMegaStore, WalletWorker } from "~/state/megaStore"; import { hexpubFromNpub } from "~/utils"; -const validateNpub = async (value?: string) => { +const validateNpub = async (sw: WalletWorker, value?: string) => { if (!value) { return false; } try { - const hexpub = await hexpubFromNpub(value); + const hexpub = await hexpubFromNpub(sw, value); if (!hexpub) { return false; } @@ -30,6 +31,7 @@ export function ContactForm(props: { initialValues?: ContactFormValues; cta: string; }) { + const [_state, _actions, sw] = useMegaStore(); const i18n = useI18n(); const [_contactForm, { Form, Field }] = createForm({ initialValues: props.initialValues @@ -78,7 +80,10 @@ export function ContactForm(props: { validateNpub(sw, v), + i18n.t("contacts.npub_error") + ) ]} > {(field, props) => ( diff --git a/src/components/ContactViewer.tsx b/src/components/ContactViewer.tsx index 36a6b66..92ddf2e 100644 --- a/src/components/ContactViewer.tsx +++ b/src/components/ContactViewer.tsx @@ -32,7 +32,7 @@ export function ContactViewer(props: { const i18n = useI18n(); const [isOpen, setIsOpen] = createSignal(false); const [isEditing, setIsEditing] = createSignal(false); - const [state, actions] = useMegaStore(); + const [state, actions, sw] = useMegaStore(); const navigate = useNavigate(); const [confirmOpen, setConfirmOpen] = createSignal(false); @@ -52,13 +52,13 @@ export function ContactViewer(props: { setIsOpen(false); }; - const handlePay = () => { - const network = state.mutiny_wallet?.get_network() || "signet"; + const handlePay = async () => { + const network = state.network || "signet"; const lnurl = props.contact.lnurl || props.contact.ln_address || ""; if (lnurl) { - const result = toParsedParams(lnurl, network); + const result = await toParsedParams(lnurl, network, sw); if (!result.ok) { showToast(result.error); return; diff --git a/src/components/DeleteEverything.tsx b/src/components/DeleteEverything.tsx index 879cc9b..efbe525 100644 --- a/src/components/DeleteEverything.tsx +++ b/src/components/DeleteEverything.tsx @@ -1,4 +1,3 @@ -import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { SecureStoragePlugin } from "capacitor-secure-storage-plugin"; import { createSignal } from "solid-js"; @@ -9,7 +8,7 @@ import { eify } from "~/utils"; export function DeleteEverything(props: { emergency?: boolean }) { const i18n = useI18n(); - const [state, actions] = useMegaStore(); + const [state, actions, sw] = useMegaStore(); async function confirmReset() { setConfirmOpen(true); @@ -29,7 +28,7 @@ export function DeleteEverything(props: { emergency?: boolean }) { // If we're in a context where the wallet is loaded we want to use the regular action to delete it // Otherwise we just call the import_json method directly - if (state.mutiny_wallet && !props.emergency) { + if (state.load_stage === "done" && !props.emergency) { try { await actions.deleteMutinyWallet(); } catch (e) { @@ -37,9 +36,7 @@ export function DeleteEverything(props: { emergency?: boolean }) { console.error(e); } } else { - // If there's no mutiny_wallet loaded we might need to initialize WASM - await initMutinyWallet(); - await MutinyWallet.import_json("{}"); + await sw.import_json("{}"); } showToast({ diff --git a/src/components/EditProfileForm.tsx b/src/components/EditProfileForm.tsx index b616b98..e74d294 100644 --- a/src/components/EditProfileForm.tsx +++ b/src/components/EditProfileForm.tsx @@ -20,7 +20,7 @@ export function EditProfileForm(props: { saving: boolean; cta: string; }) { - const [state] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const [uploading, setUploading] = createSignal(false); const [uploadError, setUploadError] = createSignal(); @@ -51,8 +51,7 @@ export function EditProfileForm(props: { if (files() && files().length) { const base64 = await blobToBase64(files()[0].file); if (base64) { - imageUrl = - await state.mutiny_wallet?.upload_profile_pic(base64); + imageUrl = await sw.upload_profile_pic(base64); } } await props.onSave({ diff --git a/src/components/FeeDisplay.tsx b/src/components/FeeDisplay.tsx index 3f9c312..53c8495 100644 --- a/src/components/FeeDisplay.tsx +++ b/src/components/FeeDisplay.tsx @@ -1,4 +1,5 @@ -import { createMemo, ParentComponent, Show } from "solid-js"; +import { createAsync } from "@solidjs/router"; +import { createMemo, ParentComponent, Show, Suspense } from "solid-js"; import { VStack } from "~/components"; import { useI18n } from "~/i18n/context"; @@ -20,20 +21,22 @@ const AmountKeyValue: ParentComponent<{ key: string; gray?: boolean }> = ( }; function USDShower(props: { amountSats: string; fee?: string }) { - const [state, _] = useMegaStore(); - const amountInFiat = () => - (state.fiat.value === "BTC" ? "" : "~") + - satsToFormattedFiat( + const [state, _actions, sw] = useMegaStore(); + const amountInFiat = createAsync(async () => { + const formattedFiat = await satsToFormattedFiat( state.price, add(props.amountSats, props.fee), - state.fiat + state.fiat, + sw ); + return (state.fiat.value === "BTC" ? "" : "~") + formattedFiat; + }); return (
- {`${amountInFiat()} `} + {`${amountInFiat()} `} {state.fiat.value}
diff --git a/src/components/GenericItem.tsx b/src/components/GenericItem.tsx index 8f55c08..4816a99 100644 --- a/src/components/GenericItem.tsx +++ b/src/components/GenericItem.tsx @@ -114,7 +114,10 @@ export function GenericItem(props: {
- {i18n.t("common.expires", { time: props.due })} + {i18n.t("common.expires", { + time: props.due, + interpolation: { escapeValue: false } + })}
diff --git a/src/components/GiftLink.tsx b/src/components/GiftLink.tsx deleted file mode 100644 index b589196..0000000 --- a/src/components/GiftLink.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { A, useLocation } from "@solidjs/router"; -import { Gift } from "lucide-solid"; - -import { useI18n } from "~/i18n/context"; - -export function GiftLink() { - const i18n = useI18n(); - const location = useLocation(); - - return ( - - {i18n.t("settings.gift.give_sats_link")} - - - ); -} diff --git a/src/components/HomeBalance.tsx b/src/components/HomeBalance.tsx index 93271d0..a066ebb 100644 --- a/src/components/HomeBalance.tsx +++ b/src/components/HomeBalance.tsx @@ -1,4 +1,4 @@ -import { Match, Switch } from "solid-js"; +import { createMemo, Match, Suspense, Switch } from "solid-js"; import { AmountFiat, AmountSats } from "~/components/Amount"; import { useMegaStore } from "~/state/megaStore"; @@ -6,11 +6,13 @@ import { useMegaStore } from "~/state/megaStore"; export function HomeBalance() { const [state, actions] = useMegaStore(); - const combinedBalance = () => - (state.balance?.federation || 0n) + - (state.balance?.lightning || 0n) + - (state.balance?.confirmed || 0n) + - (state.balance?.unconfirmed || 0n); + const combinedBalance = createMemo( + () => + (state.balance?.federation || 0n) + + (state.balance?.lightning || 0n) + + (state.balance?.confirmed || 0n) + + (state.balance?.unconfirmed || 0n) + ); // TODO: do some sort of status indicator // const fullyReady = () => state.load_stage === "done" && state.price !== 0; @@ -29,10 +31,12 @@ export function HomeBalance() { /> - + + +
diff --git a/src/components/HomePrompt.tsx b/src/components/HomePrompt.tsx index 32c47a2..b118ae8 100644 --- a/src/components/HomePrompt.tsx +++ b/src/components/HomePrompt.tsx @@ -35,7 +35,7 @@ const ImageWithFallback = (props: { src: string; alt: string }) => { }; export function HomePrompt() { - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const i18n = useI18n(); const [params, setParams] = useSearchParams(); @@ -68,7 +68,7 @@ export function HomePrompt() { lsps_token: params.token }; try { - await state.mutiny_wallet?.change_lsp( + await sw.change_lsp( values.lsp ? values.lsp : undefined, values.lsps_connection_string ? values.lsps_connection_string @@ -100,10 +100,9 @@ export function HomePrompt() { async function handleLnurlAuth() { setAuthLoading(true); try { - await state.mutiny_wallet?.lnurl_auth(lnurlauthResult()!); + await sw.lnurl_auth(lnurlauthResult()!); setIsAuthenticated(true); } catch (e) { - // lnurl1dp68gurn8ghj7um5v93kketj9ehx2amn9ashq6f0d3hxzat5dqlhgct884kx7emfdcnxkvfavvurwdtrvgmkyd3489skgcfexqckxd3svg6xgwr98q6nsd3c893kzcfkvc6nsdr9xpjxvc3jvejrxwpevyurqvfev3nxvvnxx5ergdc8g6gzl console.error(e); setAuthError(eify(e)); } finally { diff --git a/src/components/HomeSubnav.tsx b/src/components/HomeSubnav.tsx index 10a2ae4..054519c 100644 --- a/src/components/HomeSubnav.tsx +++ b/src/components/HomeSubnav.tsx @@ -18,7 +18,7 @@ import { useI18n } from "~/i18n/context"; import { useMegaStore } from "~/state/megaStore"; export function HomeSubnav() { - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const i18n = useI18n(); @@ -32,8 +32,7 @@ export function HomeSubnav() { const [pending, { refetch }] = createResource(async () => { try { - const pending = - await state.mutiny_wallet?.get_pending_nwc_invoices(); + const pending = await sw.get_pending_nwc_invoices(); return pending?.length || 0; } catch (e) { console.error(e); @@ -90,7 +89,7 @@ export function HomeSubnav() { "text-white": !!((pending.latest || 0) > 0) }} > - {pending()} + {pending.latest} diff --git a/src/components/ImportExport.tsx b/src/components/ImportExport.tsx index b84f363..0411465 100644 --- a/src/components/ImportExport.tsx +++ b/src/components/ImportExport.tsx @@ -1,4 +1,3 @@ -import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { createFileUploader } from "@solid-primitives/upload"; import { createSignal, Show } from "solid-js"; @@ -19,7 +18,7 @@ import { downloadTextFile, eify } from "~/utils"; export function ImportExport(props: { emergency?: boolean }) { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const [error, setError] = createSignal(); const [exportDecrypt, setExportDecrypt] = createSignal(false); @@ -28,7 +27,7 @@ export function ImportExport(props: { emergency?: boolean }) { async function handleSave() { try { setError(undefined); - const json = await MutinyWallet.export_json(); + const json = await sw.export_json(); await downloadTextFile(json || "", "mutiny-state.json"); } catch (e) { console.error(e); @@ -52,7 +51,7 @@ export function ImportExport(props: { emergency?: boolean }) { ) ); } - const json = await MutinyWallet.export_json(password()); + const json = await sw.export_json(password()); await downloadTextFile(json || "", "mutiny-state.json"); } catch (e) { console.error(e); @@ -103,24 +102,22 @@ export function ImportExport(props: { emergency?: boolean }) { fileReader.readAsText(file, "UTF-8"); }); - if (state.mutiny_wallet && !props.emergency) { + if (state.load_stage === "done" && !props.emergency) { console.log("Mutiny wallet loaded, stopping"); try { - await state.mutiny_wallet.stop(); + await sw.stop(); } catch (e) { console.error(e); setError(eify(e)); } } else { - // If there's no mutiny wallet loaded we need to initialize WASM - console.log("Initializing WASM"); - await initMutinyWallet(); + await sw.initializeWasm(); } // This should throw if there's a parse error, so we won't end up clearing if (text) { JSON.parse(text); - await MutinyWallet.import_json(text); + await sw.import_json(text); } setTimeout(() => { @@ -191,9 +188,10 @@ export function ImportExport(props: { emergency?: boolean }) { {/* TODO: this is pretty redundant with the DecryptDialog, could make a shared component */} setExportDecrypt(false)} >
diff --git a/src/components/ImportNsecForm.tsx b/src/components/ImportNsecForm.tsx index 45a60e1..e0041ae 100644 --- a/src/components/ImportNsecForm.tsx +++ b/src/components/ImportNsecForm.tsx @@ -1,4 +1,3 @@ -import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { useNavigate } from "@solidjs/router"; import { SecureStoragePlugin } from "capacitor-secure-storage-plugin"; import { createSignal, Show } from "solid-js"; @@ -7,7 +6,7 @@ import { Button, InfoBox, SimpleInput } from "~/components"; import { useMegaStore } from "~/state/megaStore"; export function ImportNsecForm(props: { setup?: boolean }) { - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const navigate = useNavigate(); const [nsec, setNsec] = createSignal(""); const [saving, setSaving] = createSignal(false); @@ -18,16 +17,13 @@ export function ImportNsecForm(props: { setup?: boolean }) { setError(undefined); const trimmedNsec = nsec().trim(); try { - const npub = await MutinyWallet.nsec_to_npub(trimmedNsec); + const npub = await sw.nsec_to_npub(trimmedNsec); if (!npub) { throw new Error("Invalid nsec"); } await SecureStoragePlugin.set({ key: "nsec", value: trimmedNsec }); - const new_npub = await state.mutiny_wallet?.change_nostr_keys( - trimmedNsec, - undefined - ); + const new_npub = await sw.change_nostr_keys(trimmedNsec, undefined); console.log("Changed to new npub: ", new_npub); if (props.setup) { navigate("/addfederation"); diff --git a/src/components/KitchenSink.tsx b/src/components/KitchenSink.tsx index 29ebe03..88833d6 100644 --- a/src/components/KitchenSink.tsx +++ b/src/components/KitchenSink.tsx @@ -43,13 +43,13 @@ type RefetchPeersType = ( function PeerItem(props: { peer: MutinyPeer; refetchPeers: RefetchPeersType }) { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const handleDisconnectPeer = async () => { if (props.peer.is_connected) { - await state.mutiny_wallet?.disconnect_peer(props.peer.pubkey); + await sw.disconnect_peer(props.peer.pubkey); } else { - await state.mutiny_wallet?.delete_peer(props.peer.pubkey); + await sw.delete_peer(props.peer.pubkey); } await props.refetchPeers(); }; @@ -82,12 +82,10 @@ function PeerItem(props: { peer: MutinyPeer; refetchPeers: RefetchPeersType }) { function PeersList() { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const getPeers = async () => { - return (await state.mutiny_wallet?.list_peers()) as Promise< - MutinyPeer[] - >; + return await sw.list_peers(); }; const [peers, { refetch }] = createResource(getPeers); @@ -125,7 +123,7 @@ function PeersList() { function ConnectPeer(props: { refetchPeers: RefetchPeersType }) { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const [value, setValue] = createSignal(""); @@ -134,7 +132,7 @@ function ConnectPeer(props: { refetchPeers: RefetchPeersType }) { const peerConnectString = value().trim(); - await state.mutiny_wallet?.connect_to_peer(peerConnectString); + await sw.connect_to_peer(peerConnectString); await props.refetchPeers(); @@ -176,7 +174,7 @@ type PendingChannelAction = "close" | "force_close" | "abandon"; function ChannelItem(props: { channel: MutinyChannel; network?: Network }) { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const [pendingChannelAction, setPendingChannelAction] = createSignal(); @@ -187,7 +185,7 @@ function ChannelItem(props: { channel: MutinyChannel; network?: Network }) { if (!action) return; setConfirmLoading(true); try { - await state.mutiny_wallet?.close_channel( + await sw.close_channel( props.channel.outpoint as string, action === "force_close", action === "abandon" @@ -279,17 +277,15 @@ function ChannelItem(props: { channel: MutinyChannel; network?: Network }) { function ChannelsList() { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const getChannels = async () => { - return (await state.mutiny_wallet?.list_channels()) as Promise< - MutinyChannel[] - >; + return sw.list_channels(); }; const [channels, { refetch }] = createResource(getChannels); - const network = state.mutiny_wallet?.get_network() as Network; + const network = state.network; return ( <> @@ -329,7 +325,7 @@ function ChannelsList() { function OpenChannel(props: { refetchChannels: RefetchChannelsListType }) { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const [creationError, setCreationError] = createSignal(); @@ -347,11 +343,11 @@ function OpenChannel(props: { refetchChannels: RefetchChannelsListType }) { try { const pubkey = peerPubkey().trim(); const bigAmount = BigInt(amount()); + console.log("pubkey", pubkey); + console.log("bigAmount", bigAmount); - const new_channel = await state.mutiny_wallet?.open_channel( - pubkey, - bigAmount - ); + const new_channel = await sw.open_channel(pubkey, bigAmount); + console.log("new_channel", new_channel); setNewChannel(new_channel); @@ -364,7 +360,7 @@ function OpenChannel(props: { refetchChannels: RefetchChannelsListType }) { } }; - const network = state.mutiny_wallet?.get_network() as Network; + const network = state.network; return ( <> @@ -421,10 +417,10 @@ function OpenChannel(props: { refetchChannels: RefetchChannelsListType }) { function ListNodes() { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const getNodeIds = async () => { - const nodes = await state.mutiny_wallet?.list_nodes(); + const nodes = await sw.list_nodes(); return nodes as string[]; }; @@ -450,7 +446,7 @@ function ListNodes() { function LSPS(props: { initialSettings: MutinyWalletSettingStrings }) { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const [lspSettingsForm, { Form, Field }] = createForm({ validate: (values) => { @@ -469,7 +465,7 @@ function LSPS(props: { initialSettings: MutinyWalletSettingStrings }) { async function handleSubmit(values: MutinyWalletSettingStrings) { console.log("values", values); try { - await state.mutiny_wallet?.change_lsp( + await sw.change_lsp( values.lsp ? values.lsp : undefined, values.lsps_connection_string ? values.lsps_connection_string diff --git a/src/components/LoadingIndicator.tsx b/src/components/LoadingIndicator.tsx index 214ec6b..8db4e57 100644 --- a/src/components/LoadingIndicator.tsx +++ b/src/components/LoadingIndicator.tsx @@ -43,7 +43,7 @@ function LoadingBar(props: { value: number; max: number }) { } export function LoadingIndicator() { - const [state, _actions] = useMegaStore(); + const [state] = useMegaStore(); const loadStageValue = () => { switch (state.load_stage) { diff --git a/src/components/Logs.tsx b/src/components/Logs.tsx index bd53cee..9c7577a 100644 --- a/src/components/Logs.tsx +++ b/src/components/Logs.tsx @@ -1,12 +1,13 @@ -import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { createSignal, Show } from "solid-js"; import { Button, InfoBox, InnerCard, NiceP, VStack } from "~/components"; import { useI18n } from "~/i18n/context"; +import { useMegaStore } from "~/state/megaStore"; import { downloadTextFile, eify } from "~/utils"; export function Logs() { const i18n = useI18n(); + const [_state, _actions, sw] = useMegaStore(); // Create state for errors, password and dialog visibility const [error, setError] = createSignal(); @@ -14,7 +15,7 @@ export function Logs() { async function handleSave() { try { setError(undefined); - const logs = await MutinyWallet.get_logs(); + const logs = await sw.get_logs(); await downloadTextFile( logs.join("") || "", "mutiny-logs.txt", diff --git a/src/components/NWCEditor.tsx b/src/components/NWCEditor.tsx index e5579ec..df34a59 100644 --- a/src/components/NWCEditor.tsx +++ b/src/components/NWCEditor.tsx @@ -104,7 +104,7 @@ export function NWCEditor(props: { initialNWA?: string; onSave: (indexToOpen?: number, nwcUriForCallback?: string) => Promise; }) { - const [state] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const i18n = useI18n(); const nwa = createMemo(() => parseNWA(props.initialNWA)); @@ -152,7 +152,7 @@ export function NWCEditor(props: { async function createNwa(f: BudgetForm) { if (!f.nwaString) throw new Error("We lost the NWA string!"); try { - await state.mutiny_wallet?.approve_nostr_wallet_auth( + await sw.approve_nostr_wallet_auth( f.connection_name || "Nostr Wallet Auth", // can we do better than ! here? f.nwaString @@ -177,7 +177,7 @@ export function NWCEditor(props: { try { const profile: NwcProfile | undefined = - await state.mutiny_wallet?.get_nwc_profile(index); + await sw.get_nwc_profile(index); console.log(profile); return profile; } catch (e) { @@ -200,8 +200,7 @@ export function NWCEditor(props: { if (!label) return undefined; try { - const contact: TagItem | undefined = - await state.mutiny_wallet?.get_tag_item(label); + const contact: TagItem | undefined = await sw.get_tag_item(label); return contact; } catch (e) { console.error(e); @@ -215,12 +214,11 @@ export function NWCEditor(props: { let newProfile: NwcProfile | undefined = undefined; if (!f.profileIndex) throw new Error("No profile index!"); if (!f.auto_approve || f.budget_amount === "0") { - newProfile = - await state.mutiny_wallet?.set_nwc_profile_require_approval( - f.profileIndex - ); + newProfile = await sw.set_nwc_profile_require_approval( + f.profileIndex + ); } else { - newProfile = await state.mutiny_wallet?.set_nwc_profile_budget( + newProfile = await sw.set_nwc_profile_budget( f.profileIndex, BigInt(f.budget_amount), mapIntervalToBudgetPeriod(f.interval) @@ -240,11 +238,9 @@ export function NWCEditor(props: { let newProfile: NwcProfile | undefined = undefined; if (!f.auto_approve || f.budget_amount === "0") { - newProfile = await state.mutiny_wallet?.create_nwc_profile( - f.connection_name - ); + newProfile = await sw.create_nwc_profile(f.connection_name); } else { - newProfile = await state.mutiny_wallet?.create_budget_nwc_profile( + newProfile = await sw.create_budget_nwc_profile( f.connection_name, BigInt(f.budget_amount), mapIntervalToBudgetPeriod(f.interval), diff --git a/src/components/NostrActivity.tsx b/src/components/NostrActivity.tsx index cf86e13..0880984 100644 --- a/src/components/NostrActivity.tsx +++ b/src/components/NostrActivity.tsx @@ -1,4 +1,3 @@ -import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { createAsync, useNavigate } from "@solidjs/router"; import { Search } from "lucide-solid"; import { @@ -38,9 +37,9 @@ export function Avatar(props: { image_url?: string; large?: boolean }) { export function NostrActivity() { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); - const npub = createAsync(async () => state.mutiny_wallet?.get_npub()); + const npub = createAsync(async () => await sw.get_npub()); const [data, { refetch }] = createResource(npub, fetchZaps); @@ -72,14 +71,14 @@ export function NostrActivity() { // TODO: can this be part of mutiny wallet? async function newContactFromHexpub(hexpub: string) { try { - const npub = await MutinyWallet.hexpub_to_npub(hexpub); + const npub = await sw.hexpub_to_npub(hexpub); + console.log("newContactFromHexpub", npub); if (!npub) { throw new Error("No npub for that hexpub"); } - const existingContact = - await state.mutiny_wallet?.get_contact_for_npub(npub); + const existingContact = await sw.get_contact_for_npub(npub); if (existingContact) { navigate(`/chat/${existingContact.id}`); @@ -94,7 +93,7 @@ export function NostrActivity() { const ln_address = parsed.lud16 || undefined; const lnurl = parsed.lud06 || undefined; - const contactId = await state.mutiny_wallet?.create_new_contact( + const contactId = await sw.create_new_contact( name, npub, ln_address, @@ -106,7 +105,7 @@ export function NostrActivity() { throw new Error("no contact id returned"); } - const tagItem = await state.mutiny_wallet?.get_tag_item(contactId); + const tagItem = await sw.get_tag_item(contactId); if (!tagItem) { throw new Error("no contact returned"); diff --git a/src/components/PendingNwc.tsx b/src/components/PendingNwc.tsx index a595186..cc13421 100644 --- a/src/components/PendingNwc.tsx +++ b/src/components/PendingNwc.tsx @@ -31,21 +31,20 @@ type PendingItem = { export function PendingNwc() { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const [error, setError] = createSignal(); const navigate = useNavigate(); async function fetchPendingRequests() { - const profiles = await state.mutiny_wallet?.get_nwc_profiles(); + const profiles = await sw.get_nwc_profiles(); if (!profiles) return []; - const contacts: TagItem[] | undefined = - await state.mutiny_wallet?.get_contacts_sorted(); + const contacts: TagItem[] | undefined = await sw.get_contacts_sorted(); if (!contacts) return []; - const pending = await state.mutiny_wallet?.get_pending_nwc_invoices(); + const pending = await sw.get_pending_nwc_invoices(); if (!pending) return []; const pendingItems: PendingItem[] = []; @@ -88,7 +87,7 @@ export function PendingNwc() { try { // setPaying(item.id); setPayList([...payList(), item.id]); - await state.mutiny_wallet?.approve_invoice(item.id); + await sw.approve_invoice(item.id); await vibrateSuccess(); } catch (e) { const err = eify(e); @@ -97,7 +96,7 @@ export function PendingNwc() { if (err.message === "An invoice must not get payed twice.") { // wrap in try/catch so we don't crash if the invoice is already gone try { - await state.mutiny_wallet?.deny_invoice(item.id); + await sw.deny_invoice(item.id); } catch (_e) { // do nothing } @@ -121,7 +120,7 @@ export function PendingNwc() { async function denyAll() { try { - await state.mutiny_wallet?.deny_all_pending_nwc(); + await sw.deny_all_pending_nwc(); } catch (e) { setError(eify(e)); console.error(e); @@ -133,7 +132,7 @@ export function PendingNwc() { async function rejectItem(item: PendingItem) { try { setPayList([...payList(), item.id]); - await state.mutiny_wallet?.deny_invoice(item.id); + await sw.deny_invoice(item.id); } catch (e) { setError(eify(e)); console.error(e); @@ -161,7 +160,11 @@ export function PendingNwc() { return ( - 0}> + 0 + } + > navigate("/settings/connections")}>
@@ -197,7 +200,7 @@ export function PendingNwc() { {error()?.message} - + {(pendingItem) => ( { try { - const channels = await state.mutiny_wallet?.list_channels(); - let inbound = 0; + const channels = await sw.list_channels(); + if (!channels) return 0n; + let inbound = 0n; + + // PAIN: mutiny-wasm types say these are bigints, but they're actually numbers for (const channel of channels) { - inbound += channel.size - (channel.balance + channel.reserve); + inbound += + BigInt(channel.size) - + BigInt(channel.balance + channel.reserve); } return inbound; } catch (e) { console.error(e); - return 0; + return 0n; } }); @@ -41,12 +46,7 @@ export function ReceiveWarnings(props: { }); } - const parsed = Number(props.amountSats); - if (isNaN(parsed)) { - return undefined; - } - - if (parsed > (inboundCapacity() || 0)) { + if (props.amountSats > (inboundCapacity() || 0n)) { return i18n.t("receive.amount_editable.setup_fee_lightning"); } diff --git a/src/components/Restart.tsx b/src/components/Restart.tsx index f9f4b78..8d9564d 100644 --- a/src/components/Restart.tsx +++ b/src/components/Restart.tsx @@ -6,16 +6,16 @@ import { useMegaStore } from "~/state/megaStore"; export function Restart() { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const [hasStopped, setHasStopped] = createSignal(false); async function toggle() { try { if (hasStopped()) { - await state.mutiny_wallet?.start(); + await sw.start(); setHasStopped(false); } else { - await state.mutiny_wallet?.stop(); + await sw.stop(); setHasStopped(true); } } catch (e) { diff --git a/src/components/ResyncOnchain.tsx b/src/components/ResyncOnchain.tsx index 2a0d6e2..45559cd 100644 --- a/src/components/ResyncOnchain.tsx +++ b/src/components/ResyncOnchain.tsx @@ -4,11 +4,11 @@ import { useMegaStore } from "~/state/megaStore"; export function ResyncOnchain() { const i18n = useI18n(); - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); async function reset() { try { - await state.mutiny_wallet?.reset_onchain_tracker(); + await sw.reset_onchain_tracker(); } catch (e) { console.error(e); } diff --git a/src/components/SetupErrorDisplay.tsx b/src/components/SetupErrorDisplay.tsx index 29d0982..f00c737 100644 --- a/src/components/SetupErrorDisplay.tsx +++ b/src/components/SetupErrorDisplay.tsx @@ -1,4 +1,3 @@ -import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { Title } from "@solidjs/meta"; import { MonitorSmartphone } from "lucide-solid"; import { createResource, Match, Switch } from "solid-js"; @@ -21,6 +20,7 @@ import { MutinyWalletSettingStrings } from "~/logic/mutinyWalletSetup"; import { FeedbackLink } from "~/routes/Feedback"; +import { useMegaStore } from "~/state/megaStore"; function ErrorFooter() { return ( @@ -38,6 +38,7 @@ export function SetupErrorDisplay(props: { password?: string; }) { // Error shouldn't be reactive, so we assign to it so it just gets rendered with the first value + const [_state, _actions, sw] = useMegaStore(); const i18n = useI18n(); const error = props.initialError; @@ -45,7 +46,7 @@ export function SetupErrorDisplay(props: { if (error.message.startsWith("Mutiny is already running")) { const settings: MutinyWalletSettingStrings = await getSettings(); try { - const secs = await MutinyWallet.get_device_lock_remaining_secs( + const secs = await sw.get_device_lock_remaining_secs( props.password, settings.auth, settings.storage diff --git a/src/components/SocialActionRow.tsx b/src/components/SocialActionRow.tsx index c7f1d41..5e323fa 100644 --- a/src/components/SocialActionRow.tsx +++ b/src/components/SocialActionRow.tsx @@ -1,7 +1,7 @@ import { TagItem } from "@mutinywallet/mutiny-wasm"; import { cache, createAsync, useNavigate } from "@solidjs/router"; import { Scan, Search } from "lucide-solid"; -import { createMemo, For, Suspense } from "solid-js"; +import { For, Suspense } from "solid-js"; import { Circle, LabelCircle, showToast } from "~/components"; import { useMegaStore } from "~/state/megaStore"; @@ -10,13 +10,12 @@ export function SocialActionRow(props: { onSearch: () => void; onScan: () => void; }) { - const [state, actions] = useMegaStore(); + const [_state, actions, sw] = useMegaStore(); const navigate = useNavigate(); const getContacts = cache(async () => { try { - const contacts: TagItem[] = - (await state.mutiny_wallet?.get_contacts_sorted()) || []; + const contacts: TagItem[] = (await sw.get_contacts_sorted()) || []; // contact must have a npub, ln_address, or lnurl return contacts.filter( @@ -33,16 +32,17 @@ export function SocialActionRow(props: { const contacts = createAsync(() => getContacts(), { initialValue: [] }); - const profileDeleted = createMemo( - () => state.mutiny_wallet?.get_nostr_profile().deleted - ); + const profileDeleted = createAsync(async () => { + const profile = await sw.get_nostr_profile(); + return profile?.deleted; + }); // TODO this is mostly copy pasted from chat, could be a shared util maybe - function sendToContact(contact?: TagItem) { + async function sendToContact(contact?: TagItem) { if (!contact) return; const address = contact.ln_address || contact.lnurl; if (address) { - actions.handleIncomingString( + await actions.handleIncomingString( (address || "").trim(), (error) => { showToast(error); @@ -60,6 +60,14 @@ export function SocialActionRow(props: { } } + async function handleClick(contact: TagItem) { + if (profileDeleted() || !contact.npub) { + sendToContact(contact); + } else { + navigate(`/chat/${contact.id}`); + } + } + return (
@@ -76,13 +84,7 @@ export function SocialActionRow(props: { label={false} name={contact.name} image_url={contact.primal_image_url} - onClick={() => { - if (profileDeleted() || !contact.npub) { - sendToContact(contact); - } else { - navigate(`/chat/${contact.id}`); - } - }} + onClick={() => handleClick(contact)} /> )} diff --git a/src/components/SyncContactsForm.tsx b/src/components/SyncContactsForm.tsx deleted file mode 100644 index 313c775..0000000 --- a/src/components/SyncContactsForm.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { createForm, required, SubmitHandler } from "@modular-forms/solid"; -import { createSignal, Show } from "solid-js"; - -import { Button, VStack } from "~/components"; -import { InfoBox } from "~/components/InfoBox"; -import { TextField } from "~/components/layout/TextField"; -import { useI18n } from "~/i18n/context"; -import { useMegaStore } from "~/state/megaStore"; -import { eify } from "~/utils"; - -type NostrContactsForm = { - npub: string; -}; - -export function SyncContactsForm() { - const i18n = useI18n(); - const [state, _actions] = useMegaStore(); - const [error, setError] = createSignal(); - - const [feedbackForm, { Form, Field }] = createForm({ - initialValues: { - npub: "" - } - }); - - const handleSubmit: SubmitHandler = async ( - f: NostrContactsForm - ) => { - try { - const npub = f.npub.trim(); - await state.mutiny_wallet?.sync_nostr_contacts(npub); - } catch (e) { - console.error(e); - setError(eify(e)); - } - }; - - return ( - - - - {(field, props) => ( - - )} - - - {error()?.message} - - - - - ); -} diff --git a/src/components/index.ts b/src/components/index.ts index b2c9e40..d3b2160 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -37,8 +37,6 @@ export * from "./Failure"; export * from "./ShareCard"; export * from "./Toaster"; export * from "./NostrActivity"; -export * from "./SyncContactsForm"; -export * from "./GiftLink"; export * from "./MutinyPlusCta"; export * from "./ToggleHodl"; export * from "./IOSbanner"; diff --git a/src/components/layout/Misc.tsx b/src/components/layout/Misc.tsx index 59e5bb7..f9025d6 100644 --- a/src/components/layout/Misc.tsx +++ b/src/components/layout/Misc.tsx @@ -51,11 +51,16 @@ export const Card: ParentComponent<{ export const ButtonCard: ParentComponent<{ onClick: () => void; + red?: boolean; }> = (props) => { return ( @@ -172,12 +177,14 @@ const FullscreenLoader = () => { }; export const MutinyWalletGuard: ParentComponent = (props) => { - const [state, _] = useMegaStore(); + const [state] = useMegaStore(); return ( }> - + {props.children} diff --git a/src/logic/mutinyWalletSetup.ts b/src/logic/mutinyWalletSetup.ts index 0e9d207..ff59532 100644 --- a/src/logic/mutinyWalletSetup.ts +++ b/src/logic/mutinyWalletSetup.ts @@ -1,6 +1,3 @@ -import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm"; -import { SecureStoragePlugin } from "capacitor-secure-storage-plugin"; - export type Network = "bitcoin" | "testnet" | "regtest" | "signet"; export type MutinyWalletSettingStrings = { @@ -139,7 +136,7 @@ export async function getSettings() { // Expect urls like /_services/proxy and /_services/storage if (selfhosted) { - let base = window.location.origin; + let base = location.origin; console.log("Self-hosted mode enabled, using base URL", base); const storage = settings.storage; if (storage && storage.startsWith("/")) { @@ -178,26 +175,6 @@ export async function setSettings(newSettings: MutinyWalletSettingStrings) { }); } -export async function checkForWasm() { - try { - if ( - typeof WebAssembly === "object" && - typeof WebAssembly.instantiate === "function" - ) { - const module = new WebAssembly.Module( - Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00) - ); - if (!(module instanceof WebAssembly.Module)) { - throw new Error("Couldn't instantiate WASM Module"); - } - } else { - throw new Error("No WebAssembly global object found"); - } - } catch (e) { - console.error(e); - } -} - export async function doubleInitDefense() { console.log("Starting init..."); // Ultimate defense against getting multiple instances of the wallet running. @@ -213,146 +190,3 @@ export async function doubleInitDefense() { window.location.reload(); } } - -export async function initializeWasm() { - // Actually intialize the WASM, this should be the first thing that requires the WASM blob to be downloaded - - // If WASM is already initialized, don't init twice - try { - const _sats_the_standard = MutinyWallet.convert_btc_to_sats(1); - console.debug("MutinyWallet WASM already initialized, skipping init"); - return; - } catch (e) { - console.debug("MutinyWallet WASM about to be initialized"); - await initMutinyWallet(); - } -} - -export async function setupMutinyWallet( - settings: MutinyWalletSettingStrings, - password?: string, - safeMode?: boolean, - shouldZapHodl?: boolean -): Promise { - console.log("Starting setup..."); - - // https://developer.mozilla.org/en-US/docs/Web/API/Storage_API - // Ask the browser to not clear storage - if (navigator.storage && navigator.storage.persist) { - navigator.storage.persist().then((persistent) => { - if (persistent) { - console.log( - "Storage will not be cleared except by explicit user action" - ); - } else { - console.log( - "Storage may be cleared by the UA under storage pressure." - ); - } - }); - } - - const { - network, - proxy, - esplora, - rgs, - lsp, - lsps_connection_string, - lsps_token, - auth, - subscriptions, - storage, - scorer, - primal_api, - blind_auth, - hermes - } = settings; - - let nsec; - // get nsec from secure storage - try { - const value = await SecureStoragePlugin.get({ key: "nsec" }); - nsec = value.value; - } catch (e) { - console.log("No nsec stored"); - } - - // if we didn't get an nsec from storage, try to use extension - let extension_key; - - if (!nsec && Object.prototype.hasOwnProperty.call(window, "nostr")) { - try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore ignore nostr not existing, only does if they have extension - extension_key = await window.nostr.getPublicKey(); - } catch (_) { - console.log("No NIP-07 extension"); - } - } - - console.log("Initializing Mutiny Manager"); - console.log("Using network", network); - console.log("Using proxy", proxy); - console.log("Using esplora address", esplora); - console.log("Using rgs address", rgs); - console.log("Using lsp address", lsp); - console.log("Using lsp connection string", lsps_connection_string); - console.log("Using lsp token", lsps_token); - console.log("Using auth address", auth); - console.log("Using subscriptions address", subscriptions); - console.log("Using storage address", storage); - console.log("Using scorer address", scorer); - console.log("Using primal api", primal_api); - console.log("Using blind auth", blind_auth); - console.log("Using hermes", hermes); - console.log(safeMode ? "Safe mode enabled" : "Safe mode disabled"); - console.log(shouldZapHodl ? "Hodl zaps enabled" : "Hodl zaps disabled"); - - // Only use lsps if there's no lsp set - const shouldUseLSPS = !lsp && lsps_connection_string && lsps_token; - - const mutinyWallet = await new MutinyWallet( - // Password - password ? password : undefined, - // Mnemonic - undefined, - proxy, - network, - esplora, - rgs, - shouldUseLSPS ? undefined : lsp, - shouldUseLSPS ? lsps_connection_string : undefined, - shouldUseLSPS ? lsps_token : undefined, - auth, - subscriptions, - storage, - scorer, - // Do not connect peers - undefined, - // Do not skip device lock - undefined, - // Safe mode - safeMode || undefined, - // Skip hodl invoices? (defaults to true, so if shouldZapHodl is true that's when we pass false) - shouldZapHodl ? false : undefined, - // Nsec override - nsec, - // Nip7 - extension_key ? extension_key : undefined, - // primal URL - primal_api || "https://primal-cache.mutinywallet.com/api", - /// blind auth url - blind_auth, - /// hermes url - hermes - ); - - sessionStorage.setItem("MUTINY_WALLET_INITIALIZED", Date.now().toString()); - - if (mutinyWallet) { - return mutinyWallet; - } else { - return undefined; - } -} diff --git a/src/logic/waila.ts b/src/logic/waila.ts index 59e66be..41ee03f 100644 --- a/src/logic/waila.ts +++ b/src/logic/waila.ts @@ -1,5 +1,4 @@ -import { PaymentParams } from "@mutinywallet/mutiny-wasm"; - +import { WalletWorker } from "~/state/megaStore"; import { Result } from "~/utils"; export type ParsedParams = { @@ -20,13 +19,14 @@ export type ParsedParams = { contact_id?: string; }; -export function toParsedParams( +export async function toParsedParams( str: string, - ourNetwork: string -): Result { + ourNetwork: string, + sw: WalletWorker +): Promise> { let params; try { - params = new PaymentParams(str || ""); + params = await sw.parse_params(str || ""); } catch (e) { return { ok: false, error: new Error("Invalid payment request") }; } diff --git a/src/router.tsx b/src/router.tsx index 1c78239..474dffb 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -8,6 +8,7 @@ import { JSX, Match, onCleanup, + onMount, Suspense, Switch } from "solid-js"; @@ -22,7 +23,6 @@ import { Chat, EditProfile, Feedback, - Gift as GiftReceive, Main, NotFound, Profile, @@ -43,7 +43,6 @@ import { Currency, EmergencyKit, Encrypt, - Gift, ImportProfileSettings, Language, LightningAddress, @@ -63,49 +62,6 @@ import { } from "~/routes/setup"; import { Provider as MegaStoreProvider, useMegaStore } from "~/state/megaStore"; -function GlobalListeners() { - // listeners for native navigation handling - // Check if the platform is Android to handle back - if (Capacitor.getPlatform() === "android") { - const { remove } = CapacitorApp.addListener( - "backButton", - ({ canGoBack }) => { - if (!canGoBack) { - CapacitorApp.exitApp(); - } else { - window.history.back(); - } - } - ); - - // Ensure the listener is cleaned up when the component is destroyed - onCleanup(() => { - console.debug("cleaning up backButton listener"); - remove(); - }); - } - - // Handle app links on native platforms - if (Capacitor.isNativePlatform()) { - const navigate = useNavigate(); - const { remove } = CapacitorApp.addListener("appUrlOpen", (data) => { - const url = new URL(data.url); - const path = url.pathname; - const urlParams = new URLSearchParams(url.search); - - console.log(`Navigating to ${path}?${urlParams.toString()}`); - navigate(`${path}?${urlParams.toString()}`); - }); - - onCleanup(() => { - console.debug("cleaning up appUrlOpen listener"); - remove(); - }); - } - - return null; -} - const setStatusBarStyleDark = async () => { await StatusBar.setStyle({ style: Style.Dark }); }; @@ -115,7 +71,7 @@ if (Capacitor.isNativePlatform()) { } function ChildrenOrError(props: { children: JSX.Element }) { - const [state, _] = useMegaStore(); + const [state] = useMegaStore(); return ( @@ -131,13 +87,57 @@ function ChildrenOrError(props: { children: JSX.Element }) { } export function Router() { + // listeners for native navigation handling + // Check if the platform is Android to handle back + onMount(async () => { + if (Capacitor.getPlatform() === "android") { + const { remove } = await CapacitorApp.addListener( + "backButton", + ({ canGoBack }) => { + if (!canGoBack) { + CapacitorApp.exitApp(); + } else { + window.history.back(); + } + } + ); + + // Ensure the listener is cleaned up when the component is destroyed + onCleanup(() => { + console.debug("cleaning up backButton listener"); + remove(); + }); + } + + // Handle app links on native platforms + if (Capacitor.isNativePlatform()) { + const navigate = useNavigate(); + const { remove } = await CapacitorApp.addListener( + "appUrlOpen", + (data) => { + const url = new URL(data.url); + const path = url.pathname; + const urlParams = new URLSearchParams(url.search); + + console.log( + `Navigating to ${path}?${urlParams.toString()}` + ); + navigate(`${path}?${urlParams.toString()}`); + } + ); + + onCleanup(() => { + console.debug("cleaning up appUrlOpen listener"); + remove(); + }); + } + }); return ( ( Mutiny Wallet }> - } @@ -172,7 +172,6 @@ export function Router() { - @@ -191,7 +190,6 @@ export function Router() { - diff --git a/src/routes/Chat.tsx b/src/routes/Chat.tsx index aab1b87..de8e2a5 100644 --- a/src/routes/Chat.tsx +++ b/src/routes/Chat.tsx @@ -43,14 +43,14 @@ import { MiniFab } from "~/components/Fab"; import { useI18n } from "~/i18n/context"; import { ParsedParams, toParsedParams } from "~/logic/waila"; import { useMegaStore } from "~/state/megaStore"; -import { eify, hexpubFromNpub, timeAgo } from "~/utils"; +import { createDeepSignal, eify, hexpubFromNpub, timeAgo } from "~/utils"; type CombinedMessagesAndActivity = | { kind: "message"; content: FakeDirectMessage } | { kind: "activity"; content: IActivityItem }; // TODO: Use the actual type from MutinyWallet -type FakeDirectMessage = { +export type FakeDirectMessage = { from: string; to: string; message: string; @@ -70,8 +70,8 @@ function SingleMessage(props: { counterPartyNpub: string; counterPartyContactId: string; }) { - const [state, actions] = useMegaStore(); - const network = state.mutiny_wallet?.get_network() || "signet"; + const [state, actions, sw] = useMegaStore(); + const network = state.network || "signet"; const navigate = useNavigate(); const parsed = createAsync( @@ -82,7 +82,7 @@ function SingleMessage(props: { const split_message_by_whitespace = props.dm.message.split(/\s+/g); for (const word of split_message_by_whitespace) { if (word.length > 15) { - result = toParsedParams(word, network); + result = await toParsedParams(word, network, sw); if (result.ok) { break; } @@ -94,9 +94,8 @@ function SingleMessage(props: { } if (result.value?.invoice) { - console.log("about to get invoice"); try { - const alreadyPaid = await state.mutiny_wallet?.get_invoice( + const alreadyPaid = await sw.get_invoice( result.value.invoice ); if (alreadyPaid?.paid) { @@ -149,9 +148,10 @@ function SingleMessage(props: { navWithContactId(); } - function handlePay(invoice: string) { - actions.handleIncomingString( - invoice, + async function handlePay() { + if (!parsed()) return; + await actions.handleIncomingString( + parsed()!.value, (error) => { showToast(error); }, @@ -191,7 +191,7 @@ function SingleMessage(props: { @@ -308,17 +308,17 @@ function FixedChatHeader(props: { sendToContact: (contact: TagItem) => void; requestFromContact: (contact: TagItem) => void; }) { - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const navigate = useNavigate(); async function saveContact(id: string, contact: ContactFormValues) { console.log("saving contact", id, contact); - const hexpub = await hexpubFromNpub(contact.npub?.trim()); + const hexpub = await hexpubFromNpub(sw, contact.npub?.trim()); try { - const existing = state.mutiny_wallet?.get_tag_item(id); + const existing = await sw.get_tag_item(id); // This shouldn't happen if (!existing) throw new Error("No existing contact"); - await state.mutiny_wallet?.edit_contact( + await sw.edit_contact( id, contact.name, hexpub ? hexpub : undefined, @@ -337,7 +337,7 @@ function FixedChatHeader(props: { async function deleteContact(id: string) { try { - await state.mutiny_wallet?.delete_contact(id); + await sw.delete_contact(id); } catch (e) { console.error(e); showToast(eify(e)); @@ -352,7 +352,7 @@ function FixedChatHeader(props: { try { if (!props.contact.npub) throw new Error("No npub"); - await state.mutiny_wallet?.follow_npub(props.contact.npub); + await sw.follow_npub(props.contact.npub); props.refetch(); } catch (e) { console.error(e); @@ -366,7 +366,7 @@ function FixedChatHeader(props: { try { if (!props.contact.npub) throw new Error("No npub"); - await state.mutiny_wallet?.unfollow_npub(props.contact.npub); + await sw.unfollow_npub(props.contact.npub); props.refetch(); } catch (e) { console.error(e); @@ -447,26 +447,16 @@ function FixedChatHeader(props: { export function Chat() { const params = useParams(); - const [state, actions] = useMegaStore(); + const [_state, actions, sw] = useMegaStore(); const [messageValue, setMessageValue] = createSignal(""); const [sending, setSending] = createSignal(false); const i18n = useI18n(); - // const contact = createAsync(async () => { - // try { - // return state.mutiny_wallet?.get_tag_item(params.id); - // } catch (e) { - // console.error("couldn't find contact"); - // console.error(e); - // return undefined; - // } - // }); - const [contact, { refetch: refetchContact }] = createResource(async () => { try { - return state.mutiny_wallet?.get_tag_item(params.id); + return await sw.get_tag_item(params.id); } catch (e) { console.error("couldn't find contact"); console.error(e); @@ -474,6 +464,7 @@ export function Chat() { } }); + // TODO: could probably move this to the web worker and make it even snappier const [convo, { refetch }] = createResource( contact, async (contact?: TagItem) => { @@ -484,7 +475,7 @@ export function Chat() { let dms = [] as FakeDirectMessage[]; try { - acts = (await state.mutiny_wallet?.get_label_activity( + acts = (await sw.get_label_activity( params.id )) as IActivityItem[]; } catch (e) { @@ -492,7 +483,7 @@ export function Chat() { } try { - dms = (await state.mutiny_wallet?.get_dm_conversation( + dms = (await sw.get_dm_conversation( contact.npub, 20n, undefined, @@ -529,13 +520,14 @@ export function Chat() { return b_time - a_time; // Descending order }); - console.log("combined activity", combined); - return combined as CombinedMessagesAndActivity[]; } catch (e) { console.error("error getting convo:", e); return [] as CombinedMessagesAndActivity[]; } + }, + { + storage: createDeepSignal } ); @@ -546,10 +538,7 @@ export function Chat() { const rememberedValue = messageValue(); setMessageValue(""); try { - const dmResult = await state.mutiny_wallet?.send_dm( - npub, - rememberedValue - ); + const dmResult = await sw.send_dm(npub, rememberedValue); console.log("dmResult:", dmResult); refetch(); } catch (e) { @@ -567,11 +556,11 @@ export function Chat() { }); }); - function sendToContact(contact?: TagItem) { + async function sendToContact(contact?: TagItem) { if (!contact) return; const address = contact.ln_address || contact.lnurl; if (address) { - actions.handleIncomingString( + await actions.handleIncomingString( (address || "").trim(), (error) => { showToast(error); diff --git a/src/routes/EditProfile.tsx b/src/routes/EditProfile.tsx index bc5e705..72c32d0 100644 --- a/src/routes/EditProfile.tsx +++ b/src/routes/EditProfile.tsx @@ -1,5 +1,5 @@ -import { useNavigate } from "@solidjs/router"; -import { createMemo, createSignal, Show } from "solid-js"; +import { createAsync, useNavigate } from "@solidjs/router"; +import { createSignal, Show } from "solid-js"; import { BackLink, @@ -14,14 +14,14 @@ import { useMegaStore } from "~/state/megaStore"; import { DEFAULT_NOSTR_NAME } from "~/utils"; export function EditProfile() { - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); // const i18n = useI18n(); const navigate = useNavigate(); const [saving, setSaving] = createSignal(false); - const originalProfile = createMemo(() => { - const profile = state.mutiny_wallet?.get_nostr_profile(); + const originalProfile = createAsync(async () => { + const profile = await sw.get_nostr_profile(); return { name: profile?.display_name || profile?.name || DEFAULT_NOSTR_NAME, @@ -37,11 +37,11 @@ export function EditProfile() { console.log("new profile", profile); - const newProfile = await state.mutiny_wallet?.edit_nostr_profile( + const newProfile = await sw.edit_nostr_profile( profile.nym ? profile.nym : undefined, profile.imageUrl ? profile.imageUrl : undefined, profile.lightningAddress ? profile.lightningAddress : undefined, - originalProfile().nip05 ? originalProfile().nip05 : undefined + originalProfile()?.nip05 ? originalProfile()?.nip05 : undefined ); console.log("newProfile", newProfile); @@ -62,9 +62,9 @@ export function EditProfile() { { - try { - const channels = await state.mutiny_wallet?.list_channels(); - let inbound = 0n; - - for (const channel of channels) { - inbound = - inbound + - BigInt(channel.size) - - BigInt(channel.balance + channel.reserve); - } - - return inbound; - } catch (e) { - console.error(e); - return 0n; - } - }); - - const warningText = createMemo(() => { - if (isNaN(Number(searchParams.amount))) { - return undefined; - } - - const amountNumber = Number(searchParams.amount); - - const amount = BigInt(amountNumber); - - const network = state.mutiny_wallet?.get_network() as Network; - - const threshold = network === "bitcoin" ? 100000 : 10000; - const balance = - (state.balance?.lightning || 0n) + - (state.balance?.federation || 0n); - - if (balance === 0n && amount < threshold) { - return i18n.t("settings.gift.receive_too_small", { - amount: network === "bitcoin" ? "100,000" : "10,000" - }); - } - - if (inboundCapacity() && inboundCapacity()! > amount) { - return undefined; - } else { - return i18n.t("settings.gift.setup_fee_lightning"); - } - }); - - return ( - - - {warningText()} - - - ); -} - -export function Gift() { - const [state, _] = useMegaStore(); - const i18n = useI18n(); - - const [claimSuccess, setClaimSuccess] = createSignal(false); - const [error, setError] = createSignal(); - const [loading, setLoading] = createSignal(false); - - const [searchParams] = useSearchParams(); - - async function claim() { - const amount = Number(searchParams.amount); - const nwc = searchParams.nwc_uri; - setLoading(true); - if (!nwc) { - throw new Error(i18n.t("settings.gift.something_went_wrong")); - } - try { - const claimResult = await state.mutiny_wallet?.claim_single_use_nwc( - BigInt(amount), - nwc - ); - if (claimResult === "Already Claimed") { - throw new Error(i18n.t("settings.gift.already_claimed")); - } - if ( - claimResult === - "Failed to pay invoice: We do not have enough balance to pay the given amount." - ) { - throw new Error(i18n.t("settings.gift.sender_is_poor")); - } - // Fallback for any other errors - if (claimResult) { - throw new Error( - i18n.t("settings.gift.sender_generic_error", { - error: claimResult - }) - ); - } - setClaimSuccess(true); - } catch (e) { - console.error(e); - const err = eify(e); - if (err.message === "Payment timed out.") { - setError(new Error(i18n.t("settings.gift.sender_timed_out"))); - } else { - setError(err); - } - } finally { - setLoading(false); - } - } - - async function tryAgain() { - setError(undefined); - await claim(); - } - - return ( - - - - - - - -
- - - - - - - - - -
- -
- - -
-

- {i18n.t("settings.gift.receive_header")} -

- - {i18n.t( - "settings.gift.receive_description" - )} - - - - - - - - - - {error()?.message} - - - {i18n.t("common.dangit")} - - - - - - {i18n.t( - "settings.gift.receive_claimed" - )} - - - {i18n.t("common.nice")} - - - - - - -
-
-
-
-
- -
- ); -} diff --git a/src/routes/Main.tsx b/src/routes/Main.tsx index 8fd9a58..30668ad 100644 --- a/src/routes/Main.tsx +++ b/src/routes/Main.tsx @@ -1,5 +1,5 @@ import { createAsync, useNavigate } from "@solidjs/router"; -import { createMemo, Show, Suspense } from "solid-js"; +import { Show, Suspense } from "solid-js"; import { Circle, @@ -16,45 +16,37 @@ import { } from "~/components"; import { Fab } from "~/components/Fab"; import { useMegaStore } from "~/state/megaStore"; -import { DEFAULT_NOSTR_NAME } from "~/utils"; export function WalletHeader(props: { loading: boolean }) { const navigate = useNavigate(); - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); - async function getProfile() { - const profile = state.mutiny_wallet?.get_nostr_profile(); - - return { - name: profile?.display_name || profile?.name || DEFAULT_NOSTR_NAME, - picture: profile?.picture || undefined, - // TODO: this but for real - lud16: profile?.lud16 || undefined - }; - } - - const profile = createAsync(() => getProfile()); - - const profileImage = createMemo(() => { + const profile = createAsync(async () => { if (props.loading) { return undefined; } - - if (profile() && profile()!.picture) { - return profile()!.picture; - } - - return undefined; + return await sw.get_nostr_profile(); }); return (
- navigate("/profile")} - /> + navigate("/profile")} + /> + } + > + navigate("/profile")} + /> + navigate("/settings")}> - - - + { - const profile = state.mutiny_wallet?.get_nostr_profile(); + const profile = createAsync(async () => { + const profile = await sw.get_nostr_profile(); const userProfile: UserProfile = { name: profile?.display_name || profile?.name || DEFAULT_NOSTR_NAME, @@ -43,17 +44,17 @@ export function Profile() { }); const profileDeleted = createMemo(() => { - return profile().deleted === true || profile().deleted === "true"; + return profile()?.deleted === true || profile()?.deleted === "true"; }); const hasMutinyAddress = createMemo(() => { - if (profile().lud16) { + if (profile()?.lud16) { const hermes = import.meta.env.VITE_HERMES; if (!hermes) { return false; } const hermesDomain = new URL(hermes).hostname; - const afterAt = profile().lud16!.split("@")[1]; + const afterAt = profile()?.lud16!.split("@")[1]; if (afterAt && afterAt.includes(hermesDomain)) { return true; } @@ -70,11 +71,13 @@ export function Profile() {

- {profile().name} + + {profile()?.name} +

@@ -107,7 +110,7 @@ export function Profile() {
- + navigate("/settings/importprofile")} > @@ -119,7 +122,9 @@ export function Profile() {
- + }> + + diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index 13174b8..38c3579 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -47,7 +47,7 @@ import { useI18n } from "~/i18n/context"; import { useMegaStore } from "~/state/megaStore"; import { eify, objectToSearchParams, vibrateSuccess } from "~/utils"; -type OnChainTx = { +export type OnChainTx = { transaction: { version: number; lock_time: number; @@ -123,7 +123,7 @@ function ReceiveMethodHelp() { } export function Receive() { - const [state, actions] = useMegaStore(); + const [state, actions, sw] = useMegaStore(); const navigate = useNavigate(); const i18n = useI18n(); @@ -242,10 +242,7 @@ export function Receive() { // First we try to get both an invoice and an address try { console.log("big amount", bigAmount); - const raw = await state.mutiny_wallet?.create_bip21( - bigAmount, - tags - ); + const raw = await sw.create_bip21(bigAmount, tags); // Save the raw info so we can watch the address and invoice setBip21Raw(raw); @@ -270,7 +267,7 @@ export function Receive() { // If we didn't return before this, that means create_bip21 failed // So now we'll just try and get an address without the invoice try { - const raw = await state.mutiny_wallet?.get_new_address(tags); + const raw = await sw.get_new_address(tags); // Save the raw info so we can watch the address setBip21Raw(raw); @@ -314,8 +311,7 @@ export function Receive() { try { // Lightning invoice might be blank if (lightning) { - const invoice = - await state.mutiny_wallet?.get_invoice(lightning); + const invoice = await sw.get_invoice(lightning); // If the invoice has a fees amount that's probably the LSP fee if (invoice?.fees_paid) { @@ -330,9 +326,9 @@ export function Receive() { } } - const tx = (await state.mutiny_wallet?.check_address( - address - )) as OnChainTx | undefined; + const tx = (await sw.check_address(address)) as + | OnChainTx + | undefined; if (tx) { setReceiveState("paid"); @@ -396,7 +392,7 @@ export function Receive() { onSubmit={getQr} /> diff --git a/src/routes/Redeem.tsx b/src/routes/Redeem.tsx index 65c8872..d166499 100644 --- a/src/routes/Redeem.tsx +++ b/src/routes/Redeem.tsx @@ -36,7 +36,7 @@ import { eify, vibrateSuccess } from "~/utils"; type RedeemState = "edit" | "paid"; export function Redeem() { - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const navigate = useNavigate(); const i18n = useI18n(); @@ -52,8 +52,9 @@ export function Redeem() { const [loading, setLoading] = createSignal(false); const [error, setError] = createSignal(""); - function mSatsToSats(mSats: bigint) { - return mSats / 1000n; + function mSatsToSats(mSats: bigint | number) { + const bigMsats = BigInt(mSats); + return bigMsats / 1000n; } function clearAll() { @@ -69,9 +70,7 @@ export function Redeem() { const [decodedLnurl] = createResource(async () => { if (state.scan_result) { if (state.scan_result.lnurl) { - const decoded = await state.mutiny_wallet?.decode_lnurl( - state.scan_result.lnurl - ); + const decoded = await sw.decode_lnurl(state.scan_result.lnurl); return decoded; } } @@ -126,10 +125,7 @@ export function Redeem() { setLoading(true); try { - const success = await state.mutiny_wallet?.lnurl_withdraw( - lnurlString(), - amount() - ); + const success = await sw.lnurl_withdraw(lnurlString(), amount()); if (!success) { setError(i18n.t("redeem.lnurl_redeem_failed")); } else { @@ -170,7 +166,7 @@ export function Redeem() { diff --git a/src/routes/Request.tsx b/src/routes/Request.tsx index 8d101bc..e8654c6 100644 --- a/src/routes/Request.tsx +++ b/src/routes/Request.tsx @@ -22,7 +22,7 @@ import { eify } from "~/utils"; import { DestinationItem } from "./Send"; export function RequestRoute() { - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const navigate = useNavigate(); const i18n = useI18n(); @@ -46,12 +46,12 @@ export function RequestRoute() { tags.push(whatForInput().trim()); } - const raw = await state.mutiny_wallet?.create_bip21(amount(), tags); + const raw = await sw.create_bip21(amount(), tags); if (!raw || !raw.invoice) throw new Error("Invoice creation failed"); - await state.mutiny_wallet?.send_dm(npub, raw.invoice); + await sw.send_dm(npub, raw.invoice); navigate("/chat/" + params.id); } catch (e) { @@ -64,7 +64,7 @@ export function RequestRoute() { async function getContact(id: string) { console.log("fetching contact", id); try { - const contact = state.mutiny_wallet?.get_tag_item(id); + const contact = await sw.get_tag_item(id); console.log("fetching contact", contact); // This shouldn't happen if (!contact) throw new Error("Contact not found"); @@ -103,7 +103,7 @@ export function RequestRoute() { setAmountSats={setAmount} onSubmit={handleSubmit} /> - +
diff --git a/src/routes/Search.tsx b/src/routes/Search.tsx index 7810cfd..8003bed 100644 --- a/src/routes/Search.tsx +++ b/src/routes/Search.tsx @@ -74,7 +74,7 @@ export function Search() { function ActualSearch(props: { initialValue?: string }) { const [searchValue, setSearchValue] = createSignal(""); const [debouncedSearchValue, setDebouncedSearchValue] = createSignal(""); - const [state, actions] = useMegaStore(); + const [_state, actions, sw] = useMegaStore(); const navigate = useNavigate(); const i18n = useI18n(); @@ -88,7 +88,7 @@ function ActualSearch(props: { initialValue?: string }) { const getContacts = cache(async () => { try { - const contacts = await state.mutiny_wallet?.get_contacts_sorted(); + const contacts = await sw.get_contacts_sorted(); return contacts || ([] as TagItem[]); } catch (e) { console.error(e); @@ -124,7 +124,7 @@ function ActualSearch(props: { initialValue?: string }) { type SearchState = "notsendable" | "sendable" | "sendableWithContact"; - const searchState = createMemo(() => { + const searchState = createAsync(async () => { if (debouncedSearchValue() === "") { return "notsendable"; } @@ -134,7 +134,7 @@ function ActualSearch(props: { initialValue?: string }) { return "notsendable"; } let state: SearchState = "notsendable"; - actions.handleIncomingString( + await actions.handleIncomingString( text, (_error) => { // noop @@ -147,6 +147,7 @@ function ActualSearch(props: { initialValue?: string }) { } } ); + console.log("params searchState", state); return state; }); @@ -160,8 +161,8 @@ function ActualSearch(props: { initialValue?: string }) { }); } - function handleContinue() { - actions.handleIncomingString( + async function handleContinue() { + await actions.handleIncomingString( debouncedSearchValue().trim(), (error) => { showToast(error); @@ -181,16 +182,17 @@ function ActualSearch(props: { initialValue?: string }) { ); } - const profileDeleted = createMemo( - () => state.mutiny_wallet?.get_nostr_profile().deleted - ); + const profileDeleted = createAsync(async () => { + const profile = await sw.get_nostr_profile(); + return profile?.deleted; + }); // TODO this is mostly copy pasted from chat, could be a shared util maybe - function navToSend(contact?: TagItem) { + async function navToSend(contact?: TagItem) { if (!contact) return; const address = contact.ln_address || contact.lnurl; if (address) { - actions.handleIncomingString( + await actions.handleIncomingString( (address || "").trim(), (error) => { showToast(error); @@ -208,9 +210,9 @@ function ActualSearch(props: { initialValue?: string }) { } } - function sendToContact(contact: TagItem) { + async function sendToContact(contact: TagItem) { if (profileDeleted()) { - navToSend(contact); + await navToSend(contact); } else { navWithSearchValue(`/chat/${contact.id}`); } @@ -224,11 +226,11 @@ function ActualSearch(props: { initialValue?: string }) { ); if (existingContact) { - sendToContact(existingContact); + await sendToContact(existingContact); return; } - const contactId = await state.mutiny_wallet?.create_new_contact( + const contactId = await sw.create_new_contact( contact.name, contact.npub ? contact.npub.trim() : undefined, contact.ln_address ? contact.ln_address.trim() : undefined, @@ -240,7 +242,7 @@ function ActualSearch(props: { initialValue?: string }) { throw new Error("no contact id returned"); } - const tagItem = await state.mutiny_wallet?.get_tag_item(contactId); + const tagItem = await sw.get_tag_item(contactId); if (!tagItem) { throw new Error("no contact returned"); @@ -249,9 +251,9 @@ function ActualSearch(props: { initialValue?: string }) { // if the new contact has an npub, send to chat // otherwise, send to send page if (tagItem.npub) { - sendToContact(tagItem); + await sendToContact(tagItem); } else if (tagItem.ln_address) { - actions.handleIncomingString( + await actions.handleIncomingString( tagItem.ln_address, () => {}, () => { @@ -259,7 +261,7 @@ function ActualSearch(props: { initialValue?: string }) { } ); } else if (tagItem.lnurl) { - actions.handleIncomingString( + await actions.handleIncomingString( tagItem.lnurl, () => {}, () => { @@ -293,14 +295,14 @@ function ActualSearch(props: { initialValue?: string }) { const trimText = text.trim(); setSearchValue(trimText); - parsePaste(trimText); + await parsePaste(trimText); } catch (e) { console.error(e); } } - function parsePaste(text: string) { - actions.handleIncomingString( + async function parsePaste(text: string) { + await actions.handleIncomingString( text, (error) => { showToast(error); @@ -354,45 +356,49 @@ function ActualSearch(props: { initialValue?: string }) {
-
- - - -
- - - -

Contacts

- 0}> - - {(contact) => ( - sendToContact(contact)} - /> - )} - - -
- + +
+ + + +
+ + + +

Contacts

+ 0}> + + {(contact) => ( + + sendToContact(contact) + } + /> + )} + + +
+ - }> - -

- Global Search -

- -
-
-
- - + }> + +

+ Global Search +

+ +
+
+
+ + + ); } @@ -402,10 +408,11 @@ function GlobalSearch(props: { sendToContact: (contact: TagItem) => void; foundNpubs: (string | undefined)[]; }) { + const [_state, _actions, sw] = useMegaStore(); const hexpubs = createMemo(() => { const hexpubs: Set = new Set(); for (const npub of props.foundNpubs) { - hexpubFromNpub(npub) + hexpubFromNpub(sw, npub) .then((h) => { if (h) { hexpubs.add(h); @@ -425,7 +432,7 @@ function GlobalSearch(props: { try { // Handling case when value starts with "npub" if (args.value?.toLowerCase().startsWith("npub")) { - const hexpub = await hexpubFromNpub(args.value); + const hexpub = await hexpubFromNpub(sw, args.value); if (!hexpub) return []; const profile = await actuallyFetchNostrProfile(hexpub); @@ -491,23 +498,25 @@ function SingleContact(props: { contact: PseudoContact; sendToContact: (contact: TagItem) => void; }) { - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); async function createContactFromSearchResult(contact: PseudoContact) { try { - const contactId = await state.mutiny_wallet?.create_new_contact( + const contactId = await sw.create_new_contact( contact.name, - contact.hexpub ? contact.hexpub : undefined, + contact.hexpub ? contact.hexpub : "", contact.ln_address ? contact.ln_address : undefined, undefined, contact.image_url ? contact.image_url : undefined ); + console.log("contactId", contactId); + if (!contactId) { throw new Error("no contact id returned"); } - const tagItem = await state.mutiny_wallet?.get_tag_item(contactId); + const tagItem = await sw.get_tag_item(contactId); if (!tagItem) { throw new Error("no contact returned"); diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index e73f4c4..efa7400 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -1,5 +1,10 @@ import { MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm"; -import { useLocation, useNavigate, useSearchParams } from "@solidjs/router"; +import { + createAsync, + useLocation, + useNavigate, + useSearchParams +} from "@solidjs/router"; import { Eye, EyeOff, Link, X, Zap } from "lucide-solid"; import { createEffect, @@ -158,7 +163,7 @@ export function DestinationItem(props: { } export function Send() { - const [state, actions] = useMegaStore(); + const [state, actions, sw] = useMegaStore(); const navigate = useNavigate(); const [params, setParams] = useSearchParams(); const i18n = useI18n(); @@ -220,8 +225,8 @@ export function Send() { } // TODO: can I dedupe this from the search page? - function parsePaste(text: string) { - actions.handleIncomingString( + async function parsePaste(text: string) { + await actions.handleIncomingString( text, (error) => { showToast(error); @@ -233,9 +238,10 @@ export function Send() { ); } + // TODO: do we actually use this anywhere? // send?invoice=... need to check for wallet because we can't parse until we have the wallet createEffect(() => { - if (params.invoice && state.mutiny_wallet) { + if (params.invoice && state.load_stage === "done") { parsePaste(params.invoice); setParams({ invoice: undefined }); } @@ -300,7 +306,7 @@ export function Send() { }); // Rerun every time the amount changes if we're onchain - const feeEstimate = createMemo(() => { + const feeEstimate = createAsync(async () => { if ( source() === "onchain" && amountSats() && @@ -310,16 +316,16 @@ export function Send() { try { // If max we want to use the sweep fee estimator if (isMax()) { - return state.mutiny_wallet?.estimate_sweep_tx_fee( - address()! - ); + return await sw.estimate_sweep_tx_fee(address()!); } - return state.mutiny_wallet?.estimate_tx_fee( + const estimate = await sw.estimate_tx_fee( address()!, amountSats(), undefined ); + console.log("estimate", estimate); + return estimate; } catch (e) { setError(eify(e).message); } @@ -367,15 +373,15 @@ export function Send() { // A ParsedParams with an invoice in it function processInvoice(source: ParsedParams & { invoice: string }) { - state.mutiny_wallet - ?.decode_invoice(source.invoice!) + sw.decode_invoice(source.invoice!) .then((invoice) => { + if (!invoice) return; if (invoice.expire <= Date.now() / 1000) { navigate("/search"); throw new Error(i18n.t("send.error_expired")); } - if (invoice?.amount_sats) { + if (invoice.amount_sats) { setAmountSats(invoice.amount_sats); setIsAmtEditable(false); } @@ -396,8 +402,7 @@ export function Send() { // A ParsedParams with an lnurl in it function processLnurl(source: ParsedParams & { lnurl: string }) { setDecodingLnUrl(true); - state.mutiny_wallet - ?.decode_lnurl(source.lnurl) + sw.decode_lnurl(source.lnurl) .then((lnurlParams) => { setDecodingLnUrl(false); if (lnurlParams.tag === "payRequest") { @@ -470,7 +475,7 @@ export function Send() { sentDetails.destination = bolt11; // If the invoice has sats use that, otherwise we pass the user-defined amount if (invoice()?.amount_sats) { - const payment = await state.mutiny_wallet?.pay_invoice( + const payment = await sw.pay_invoice( bolt11, undefined, tags @@ -479,7 +484,7 @@ export function Send() { sentDetails.payment_hash = payment?.payment_hash; sentDetails.fee_estimate = payment?.fees_paid || 0; } else { - const payment = await state.mutiny_wallet?.pay_invoice( + const payment = await sw.pay_invoice( bolt11, amountSats(), tags @@ -489,7 +494,7 @@ export function Send() { sentDetails.fee_estimate = payment?.fees_paid || 0; } } else if (source() === "lightning" && nodePubkey()) { - const payment = await state.mutiny_wallet?.keysend( + const payment = await sw.keysend( nodePubkey()!, amountSats(), undefined, // todo add optional keysend message @@ -509,7 +514,7 @@ export function Send() { visibility() !== "Not Available" && contact()?.npub ? contact()?.npub : undefined; - const payment = await state.mutiny_wallet?.lnurl_pay( + const payment = await sw.lnurl_pay( lnurlp()!, amountSats(), zapNpub, // zap_npub @@ -530,17 +535,14 @@ export function Send() { if (isMax()) { // If we're trying to send the max amount, use the sweep method instead of regular send // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const txid = await state.mutiny_wallet?.sweep_wallet( - address()!, - tags - ); + const txid = await sw.sweep_wallet(address()!, tags); sentDetails.amount = amountSats(); sentDetails.destination = address(); sentDetails.txid = txid; sentDetails.fee_estimate = feeEstimate() ?? 0; } else if (payjoinEnabled()) { - const txid = await state.mutiny_wallet?.send_payjoin( + const txid = await sw.send_payjoin( originalScan()!, amountSats(), tags @@ -551,7 +553,7 @@ export function Send() { sentDetails.fee_estimate = feeEstimate() ?? 0; } else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const txid = await state.mutiny_wallet?.send_to_address( + const txid = await sw.send_to_address( address()!, amountSats(), tags @@ -665,7 +667,7 @@ export function Send() { async function getContact(id: string) { console.log("fetching contact", id); try { - const contact = state.mutiny_wallet?.get_tag_item(id); + const contact = await sw.get_tag_item(id); console.log("fetching contact", contact); // This shouldn't happen if (!contact) throw new Error("Contact not found"); @@ -773,7 +775,6 @@ export function Send() { sendButtonDisabled() ? undefined : handleSend() } @@ -791,7 +792,6 @@ export function Send() { sendButtonDisabled() ? undefined : handleSend() @@ -801,13 +801,15 @@ export function Send() { setChosenMethod={setSourceFromMethod} /> - - - + + + + +

{i18n.t("send.hodl_invoice_warning")}

diff --git a/src/routes/Swap.tsx b/src/routes/Swap.tsx index a56a291..b38f876 100644 --- a/src/routes/Swap.tsx +++ b/src/routes/Swap.tsx @@ -1,6 +1,6 @@ import { createForm, required } from "@modular-forms/solid"; -import { MutinyChannel, MutinyPeer } from "@mutinywallet/mutiny-wasm"; -import { useNavigate } from "@solidjs/router"; +import { MutinyChannel } from "@mutinywallet/mutiny-wasm"; +import { createAsync, useNavigate } from "@solidjs/router"; import { createMemo, createResource, @@ -8,6 +8,7 @@ import { For, Match, Show, + Suspense, Switch } from "solid-js"; @@ -33,7 +34,6 @@ import { VStack } from "~/components"; import { useI18n } from "~/i18n/context"; -import { Network } from "~/logic/mutinyWalletSetup"; import { useMegaStore } from "~/state/megaStore"; import { eify, vibrateSuccess } from "~/utils"; @@ -50,7 +50,7 @@ type ChannelOpenDetails = { }; export function Swap() { - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const navigate = useNavigate(); const i18n = useI18n(); @@ -100,9 +100,7 @@ export function Swap() { }; const getPeers = async () => { - return (await state.mutiny_wallet?.list_peers()) as Promise< - MutinyPeer[] - >; + return await sw?.list_peers(); }; const [peers, { refetch }] = createResource(getPeers); @@ -114,7 +112,7 @@ export function Swap() { try { const peerConnectString = values.peer.trim(); - await state.mutiny_wallet?.connect_to_peer(peerConnectString); + await sw.connect_to_peer(peerConnectString); await refetch(); @@ -156,12 +154,11 @@ export function Swap() { } if (isMax()) { - const new_channel = - await state.mutiny_wallet?.sweep_all_to_channel(peer); + const new_channel = await sw.sweep_all_to_channel(peer); setChannelOpenResult({ channel: new_channel }); } else { - const new_channel = await state.mutiny_wallet?.open_channel( + const new_channel = await sw.open_channel( peer, amountSats() ); @@ -182,7 +179,7 @@ export function Swap() { const balance = (state.balance?.confirmed || 0n) + (state.balance?.unconfirmed || 0n); - const network = state.mutiny_wallet?.get_network() as Network; + const network = state.network || "signet"; if (network === "bitcoin") { return ( @@ -199,12 +196,12 @@ export function Swap() { } }; - const amountWarning = () => { + const amountWarning = createAsync(async () => { if (amountSats() === 0n || !!channelOpenResult()) { return undefined; } - const network = state.mutiny_wallet?.get_network() as Network; + const network = state.network || "signet"; if (network === "bitcoin" && amountSats() < 100000n) { return i18n.t("swap.channel_too_small", { amount: "100,000" }); @@ -224,7 +221,7 @@ export function Swap() { } return undefined; - }; + }); function calculateMaxOnchain() { return ( @@ -241,12 +238,12 @@ export function Swap() { return amountSats() === calculateMaxOnchain(); }); - const feeEstimate = createMemo(() => { - const max = calculateMaxOnchain(); + const feeEstimate = createAsync(async () => { + const max = maxOnchain(); // If max we want to use the sweep fee estimator if (amountSats() > 0n && amountSats() === max) { try { - return state.mutiny_wallet?.estimate_sweep_channel_open_fee(); + return await sw.estimate_sweep_channel_open_fee(); } catch (e) { console.error(e); return undefined; @@ -255,7 +252,7 @@ export function Swap() { if (amountSats() > 0n) { try { - return state.mutiny_wallet?.estimate_tx_fee( + return await sw.estimate_tx_fee( CHANNEL_FEE_ESTIMATE_ADDRESS, amountSats(), undefined @@ -328,18 +325,20 @@ export function Swap() { })}

- + + +

@@ -425,7 +424,6 @@ export function Swap() { - 0n}> - - - 0n}> - {amountWarning()} - + + 0n}> + + + + + 0n}> + + {amountWarning()} + + +
diff --git a/src/routes/SwapLightning.tsx b/src/routes/SwapLightning.tsx index 2250256..5fb94ac 100644 --- a/src/routes/SwapLightning.tsx +++ b/src/routes/SwapLightning.tsx @@ -1,6 +1,13 @@ import { FedimintSweepResult } from "@mutinywallet/mutiny-wasm"; import { useNavigate } from "@solidjs/router"; -import { createMemo, createSignal, Match, Show, Switch } from "solid-js"; +import { + createMemo, + createSignal, + Match, + Show, + Suspense, + Switch +} from "solid-js"; import { AmountEditable, @@ -31,7 +38,7 @@ type SweepResultDetails = { }; export function SwapLightning() { - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const navigate = useNavigate(); const i18n = useI18n(); @@ -63,17 +70,11 @@ export function SwapLightning() { setFeeEstimateWarning(undefined); if (isMax()) { - const result = - await state.mutiny_wallet?.sweep_federation_balance( - undefined - ); + const result = await sw.sweep_federation_balance(undefined); setSweepResult({ result: result }); } else { - const result = - await state.mutiny_wallet?.sweep_federation_balance( - amountSats() - ); + const result = await sw.sweep_federation_balance(amountSats()); setSweepResult({ result: result }); } @@ -128,10 +129,9 @@ export function SwapLightning() { setLoading(true); setFeeEstimateWarning(undefined); - const fee = - await state.mutiny_wallet?.estimate_sweep_federation_fee( - isMax() ? undefined : amountSats() - ); + const fee = await sw.estimate_sweep_federation_fee( + isMax() ? undefined : amountSats() + ); if (fee) { setFeeSats(fee); @@ -201,11 +201,13 @@ export function SwapLightning() { })}

- + + +

@@ -236,7 +238,7 @@ export function SwapLightning() { ]} /> - - - {feeEstimateWarning()} - - + + + + {feeEstimateWarning()} + + +
diff --git a/src/routes/index.ts b/src/routes/index.ts index 860fab1..cd7fa32 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,15 +1,14 @@ export * from "./[...404]"; export * from "./Feedback"; -export * from "./Gift"; export * from "./Main"; export * from "./Receive"; export * from "./Scanner"; export * from "./Send"; -export * from "./Swap"; -export * from "./SwapLightning"; export * from "./Search"; export * from "./Redeem"; export * from "./Profile"; export * from "./Chat"; export * from "./Request"; export * from "./EditProfile"; +export * from "./Swap"; +export * from "./SwapLightning"; diff --git a/src/routes/settings/Backup.tsx b/src/routes/settings/Backup.tsx index bf0ca4b..1571f74 100644 --- a/src/routes/settings/Backup.tsx +++ b/src/routes/settings/Backup.tsx @@ -1,4 +1,4 @@ -import { useNavigate } from "@solidjs/router"; +import { createAsync, useNavigate } from "@solidjs/router"; import { createEffect, createSignal, Show } from "solid-js"; import { @@ -53,7 +53,7 @@ function Quiz(props: { setHasCheckedAll: (hasChecked: boolean) => void }) { export function Backup() { const i18n = useI18n(); - const [store, actions] = useMegaStore(); + const [_store, actions, sw] = useMegaStore(); const navigate = useNavigate(); const [hasSeenBackup, setHasSeenBackup] = createSignal(false); @@ -67,6 +67,8 @@ export function Backup() { setLoading(false); } + const words = createAsync(async () => await sw.show_seed()); + return ( @@ -79,7 +81,7 @@ export function Backup() { {i18n.t("settings.backup.warning_one")} {i18n.t("settings.backup.warning_two")} diff --git a/src/routes/settings/Channels.tsx b/src/routes/settings/Channels.tsx index 6e67408..8b58c1d 100644 --- a/src/routes/settings/Channels.tsx +++ b/src/routes/settings/Channels.tsx @@ -30,7 +30,6 @@ import { VStack } from "~/components"; import { useI18n } from "~/i18n/context"; -import { Network } from "~/logic/mutinyWalletSetup"; import { useMegaStore } from "~/state/megaStore"; import { createDeepSignal, eify, mempoolTxUrl } from "~/utils"; @@ -100,8 +99,8 @@ function splitChannelNumbers(channel: MutinyChannel): { function SingleChannelItem(props: { channel: MutinyChannel; online: boolean }) { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); - const network = state.mutiny_wallet?.get_network() as Network; + const [state, _actions, sw] = useMegaStore(); + const network = state.network; const [confirmOpen, setConfirmOpen] = createSignal(false); const [confirmLoading, setConfirmLoading] = createSignal(false); @@ -115,11 +114,7 @@ function SingleChannelItem(props: { channel: MutinyChannel; online: boolean }) { if (!props.channel.outpoint) return; setConfirmLoading(true); const forceClose = !props.online; - await state.mutiny_wallet?.close_channel( - props.channel.outpoint, - forceClose, - false - ); + await sw.close_channel(props.channel.outpoint, forceClose, false); } catch (e) { console.error(e); showToast(eify(e)); @@ -180,12 +175,11 @@ function SingleChannelItem(props: { channel: MutinyChannel; online: boolean }) { function LiquidityMonitor() { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); async function listChannels() { try { - const channels: MutinyChannel[] | undefined = - await state.mutiny_wallet?.list_channels(); + const channels = await sw.list_channels(); if (!channels) return { @@ -232,22 +226,24 @@ function LiquidityMonitor() { return ( - + {i18n.t("settings.channels.have_channels")}{" "} - {channelInfo()?.channelCount}{" "} - {channelInfo()?.channelCount === 1 + {channelInfo.latest?.channelCount}{" "} + {channelInfo.latest?.channelCount === 1 ? i18n.t("settings.channels.have_channels_one") : i18n.t( "settings.channels.have_channels_many" )} {" "} {i18n.t("settings.channels.inbound_outbound_tip")} @@ -256,7 +252,7 @@ function LiquidityMonitor() { {i18n.t("settings.channels.reserve_tip")} - + - + {(channel) => ( - + - + {(channel) => ( void; }) { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const [confirmOpen, setConfirmOpen] = createSignal(false); @@ -87,7 +87,7 @@ function NwcDetails(props: { async function deleteProfile() { try { - await state.mutiny_wallet?.delete_nwc_profile(props.profile.index); + await sw.delete_nwc_profile(props.profile.index); setConfirmOpen(false); props.refetch(); } catch (e) { @@ -187,11 +187,12 @@ function NwcDetails(props: { function Nwc() { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); async function fetchNwcProfiles() { try { - const profiles = await state.mutiny_wallet?.get_nwc_profiles(); + const profiles = await sw.get_nwc_profiles(); + console.log("profiles", profiles); if (!profiles) return []; return profiles; diff --git a/src/routes/settings/Encrypt.tsx b/src/routes/settings/Encrypt.tsx index 63f91f3..e02de57 100644 --- a/src/routes/settings/Encrypt.tsx +++ b/src/routes/settings/Encrypt.tsx @@ -26,7 +26,7 @@ type EncryptPasswordForm = { export function Encrypt() { const i18n = useI18n(); - const [store, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const [error, setError] = createSignal(); const [loading, setLoading] = createSignal(false); @@ -56,7 +56,7 @@ export function Encrypt() { const handleFormSubmit = async (f: EncryptPasswordForm) => { setLoading(true); try { - await store.mutiny_wallet?.change_password( + await sw.change_password( f.existingPassword === "" ? undefined : f.existingPassword, f.password === "" ? undefined : f.password ); diff --git a/src/routes/settings/Gift.tsx b/src/routes/settings/Gift.tsx deleted file mode 100644 index bb3ab1e..0000000 --- a/src/routes/settings/Gift.tsx +++ /dev/null @@ -1,323 +0,0 @@ -import { - createForm, - getValue, - required, - reset, - setValue, - SubmitHandler -} from "@modular-forms/solid"; -import { NwcProfile } from "@mutinywallet/mutiny-wasm"; -import { - createEffect, - createResource, - createSignal, - For, - Match, - Show, - Suspense, - Switch -} from "solid-js"; - -import { - AmountEditable, - BackPop, - Button, - Collapser, - ConfirmDialog, - DefaultMain, - InfoBox, - IntegratedQr, - LargeHeader, - LoadingSpinner, - MutinyPlusCta, - MutinyWalletGuard, - NavBar, - NiceP, - SettingsCard, - TextField, - VStack -} from "~/components"; -import { useI18n } from "~/i18n/context"; -import { useMegaStore } from "~/state/megaStore"; -import { eify, isFreeGiftingDay } from "~/utils"; -import { baseUrlAccountingForNative } from "~/utils/baseUrl"; -import { createDeepSignal } from "~/utils/deepSignal"; - -type CreateGiftForm = { - name: string; - amount: string; -}; - -function SingleGift(props: { profile: NwcProfile; onDelete?: () => void }) { - const i18n = useI18n(); - const [state, _actions] = useMegaStore(); - - const network = state.mutiny_wallet?.get_network(); - - const baseUrl = baseUrlAccountingForNative(network); - - const sharableUrl = () => baseUrl.concat(props.profile.url_suffix || ""); - const amount = () => props.profile.budget_amount?.toString() || "0"; - - const [confirmOpen, setConfirmOpen] = createSignal(false); - - const handleConfirmDelete = async () => { - try { - await state.mutiny_wallet?.delete_nwc_profile(props.profile.index); - setConfirmOpen(false); - props.onDelete && props.onDelete(); - } catch (e) { - console.error(e); - } - }; - - return ( - - - - - - setConfirmOpen(false)} - > - {i18n.t("settings.gift.send_delete_confirm")} - - - ); -} - -function ExistingGifts() { - const [state, _actions] = useMegaStore(); - - const [giftNWCProfiles, { refetch }] = createResource(async () => { - try { - const profiles = await state.mutiny_wallet?.get_nwc_profiles(); - if (!profiles) return []; - - const filteredForGifts = profiles.filter((p) => p.tag === "Gift"); - - return filteredForGifts; - } catch (e) { - console.error(e); - } - }); - - return ( - 0}> - - - {(profile) => ( - - - - )} - - - - ); -} - -export function Gift() { - const i18n = useI18n(); - const [state, _actions] = useMegaStore(); - - const [_error, setError] = createSignal(); - - const [giftResult, setGiftResult] = createSignal(); - - const [giftForm, { Form, Field }] = createForm({ - initialValues: { - name: "", - amount: "100000" - } - }); - - function resetGifting() { - reset(giftForm); - setGiftResult(undefined); - } - - const handleSubmit: SubmitHandler = async ( - f: CreateGiftForm - ) => { - const nwc_name = f.name.trim(); - const amount = Number(f.amount); - - try { - const profile = await state.mutiny_wallet?.create_single_use_nwc( - nwc_name, - BigInt(amount) - ); - - setGiftResult(profile); - } catch (e) { - console.error(e); - setError(eify(e)); - } - }; - - async function fetchProfile(gift?: NwcProfile) { - if (!gift) return; - try { - const fresh = await state.mutiny_wallet?.get_nwc_profile( - gift.index - ); - return fresh; - } catch (e) { - console.error(e); - // If the gift is not found it means it's been deleted because it was redeemed - return undefined; - } - } - - const [freshProfile, { refetch }] = createResource( - () => giftResult(), - fetchProfile, - { - storage: createDeepSignal - } - ); - - createEffect(() => { - // Should re-run after every sync - if (!state.is_syncing) { - refetch(); - } - }); - - const lessThanMinChannelSize = () => { - return Number(getValue(giftForm, "amount")) < 100000; - }; - - const selfHosted = state.settings?.selfhosted === "true"; - const freeDay = isFreeGiftingDay(); - - const canGift = state.mutiny_plus || selfHosted || freeDay; - - return ( - - - - - - - {i18n.t("settings.gift.send_header")} - - {i18n.t("settings.gift.need_plus")} - - - - - - - - - {i18n.t( - "settings.gift.send_header_claimed" - )} - - - {i18n.t("settings.gift.send_claimed")} - - - - - {i18n.t( - "settings.gift.send_sharable_header" - )} - - - {i18n.t("settings.gift.send_instructions")} - - - {i18n.t("settings.gift.send_tip")} - - - - - - - - - - {i18n.t("settings.gift.send_header")} - -
- - - {i18n.t("settings.gift.send_explainer")} - - - {(field) => ( - - setValue( - giftForm, - "amount", - newAmount.toString() - ) - } - /> - )} - - - {(field, props) => ( - - )} - - - - - {i18n.t("settings.gift.send_small_warning")} - - - - -
- }> - - -
-
- -
- ); -} diff --git a/src/routes/settings/ImportProfile.tsx b/src/routes/settings/ImportProfile.tsx index ef658a9..210e67e 100644 --- a/src/routes/settings/ImportProfile.tsx +++ b/src/routes/settings/ImportProfile.tsx @@ -1,20 +1,27 @@ -import { BackLink, DefaultMain, ImportNsecForm } from "~/components"; +import { + BackLink, + DefaultMain, + ImportNsecForm, + MutinyWalletGuard +} from "~/components"; export function ImportProfileSettings() { return ( - - -
-
-

Import nostr profile

-

- Login with an existing nostr account. -
-

-
- -
-
- + + + +
+
+

Import nostr profile

+

+ Login with an existing nostr account. +
+

+
+ +
+
+ + ); } diff --git a/src/routes/settings/LightningAddress.tsx b/src/routes/settings/LightningAddress.tsx index d86b18f..9a22db5 100644 --- a/src/routes/settings/LightningAddress.tsx +++ b/src/routes/settings/LightningAddress.tsx @@ -6,7 +6,7 @@ import { reset, SubmitHandler } from "@modular-forms/solid"; -import { useNavigate } from "@solidjs/router"; +import { createAsync, useNavigate } from "@solidjs/router"; import { Users } from "lucide-solid"; import { createMemo, @@ -49,7 +49,7 @@ const validateLowerCase = (value?: string) => { // todo(paul) put this somewhere else function HermesForm(props: { onSubmit: (name: string) => void }) { - const [state, _] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const [error, setError] = createSignal(); const [success, setSuccess] = createSignal(""); @@ -70,17 +70,16 @@ function HermesForm(props: { onSubmit: (name: string) => void }) { setError(undefined); try { const name = f.name.trim().toLowerCase(); - const available = - await state.mutiny_wallet?.check_available_lnurl_name(name); + const available = await sw.check_available_lnurl_name(name); if (!available) { throw new Error("Name already taken"); } - await state.mutiny_wallet?.reserve_lnurl_name(name); + await sw.reserve_lnurl_name(name); console.log("lnurl name reserved:", name); const formattedName = `${name}@${hermesDomain}`; - const _ = await state.mutiny_wallet?.edit_nostr_profile( + const _ = await sw.edit_nostr_profile( undefined, undefined, // lnurl @@ -144,14 +143,14 @@ function HermesForm(props: { onSubmit: (name: string) => void }) { export function LightningAddress() { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const navigate = useNavigate(); const [error, setError] = createSignal(); const [settingLnAddress, setSettingLnAddress] = createSignal(false); const [lnurlName] = createResource(async () => { try { - const name = await state.mutiny_wallet?.check_lnurl_name(); + const name = await sw.check_lnurl_name(); return name; } catch (e) { setError(eify(e)); @@ -172,10 +171,10 @@ export function LightningAddress() { } }); - const profileLnAddress = createMemo(() => { + const profileLnAddress = createAsync(async () => { if (lnurlName()) { - const profile = state.mutiny_wallet?.get_nostr_profile(); - if (profile?.lud16) { + const profile = await sw.get_nostr_profile(); + if (profile && profile.lud16) { return profile.lud16; } } @@ -187,7 +186,7 @@ export function LightningAddress() { setSettingLnAddress(true); setError(undefined); - const _ = await state.mutiny_wallet?.edit_nostr_profile( + const _ = await sw.edit_nostr_profile( undefined, undefined, newAddress, diff --git a/src/routes/settings/ManageFederations.tsx b/src/routes/settings/ManageFederations.tsx index cf3133e..fe33961 100644 --- a/src/routes/settings/ManageFederations.tsx +++ b/src/routes/settings/ManageFederations.tsx @@ -89,7 +89,7 @@ export function AddFederationForm(props: { browseOnly?: boolean; }) { const i18n = useI18n(); - const [state, actions] = useMegaStore(); + const [_state, actions, sw] = useMegaStore(); const navigate = useNavigate(); const [error, setError] = createSignal(); const [success, setSuccess] = createSignal(""); @@ -122,8 +122,7 @@ export function AddFederationForm(props: { const [federations] = createResource(async () => { try { - const federations: DiscoveredFederation[] = - await state.mutiny_wallet?.discover_federations(); + const federations = await sw.discover_federations(); return federations; } catch (e) { console.error(e); @@ -139,8 +138,7 @@ export function AddFederationForm(props: { try { console.log("Adding federation:", inviteCode); setLoadingFederation(inviteCode); - const newFederation = - await state.mutiny_wallet?.new_federation(inviteCode); + const newFederation = await sw.new_federation(inviteCode); console.log("New federation added:", newFederation); break; } catch (e) { @@ -345,7 +343,7 @@ export function AddFederationForm(props: { } function RecommendButton(props: { fed: MutinyFederationIdentity }) { - const [state] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const i18n = useI18n(); const [recommendLoading, setRecommendLoading] = createSignal(false); // This is just some local state that makes it feel like they've recommended it @@ -354,10 +352,9 @@ function RecommendButton(props: { fed: MutinyFederationIdentity }) { const [recommendedByMe, { refetch }] = createResource(async () => { try { - const hasRecommended = - await state.mutiny_wallet?.has_recommended_federation( - props.fed.federation_id - ); + const hasRecommended = await sw.has_recommended_federation( + props.fed.federation_id + ); return hasRecommended; } catch (e) { console.error(e); @@ -368,7 +365,7 @@ function RecommendButton(props: { fed: MutinyFederationIdentity }) { async function recommendFederation() { setRecommendLoading(true); try { - const event_id = await state.mutiny_wallet?.recommend_federation( + const event_id = await sw.recommend_federation( props.fed.invite_code ); console.log("Recommended federation: ", event_id); @@ -383,9 +380,7 @@ function RecommendButton(props: { fed: MutinyFederationIdentity }) { async function deleteRecommendation() { setRecommendLoading(true); try { - await state.mutiny_wallet?.delete_federation_recommendation( - props.fed.federation_id - ); + await sw.delete_federation_recommendation(props.fed.federation_id); setRecommended(false); refetch(); } catch (e) { @@ -430,14 +425,12 @@ function FederationListItem(props: { balance?: bigint; }) { const i18n = useI18n(); - const [state, actions] = useMegaStore(); + const [_state, actions, sw] = useMegaStore(); async function removeFederation() { setConfirmLoading(true); try { - await state.mutiny_wallet?.remove_federation( - props.fed.federation_id - ); + await sw.remove_federation(props.fed.federation_id); await actions.refreshFederations(); } catch (e) { console.error(e); @@ -522,12 +515,11 @@ function FederationListItem(props: { export function ManageFederations() { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); const [balances, { refetch }] = createResource(async () => { try { - const balances = - await state.mutiny_wallet?.get_federation_balances(); + const balances = await sw.get_federation_balances(); return balances?.balances || []; } catch (e) { console.error(e); diff --git a/src/routes/settings/NostrKeys.tsx b/src/routes/settings/NostrKeys.tsx index cf0a946..6fb4c95 100644 --- a/src/routes/settings/NostrKeys.tsx +++ b/src/routes/settings/NostrKeys.tsx @@ -23,7 +23,7 @@ import { useMegaStore } from "~/state/megaStore"; function DeleteAccount() { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); async function confirmDelete() { setConfirmOpen(true); @@ -35,7 +35,7 @@ function DeleteAccount() { async function deleteNostrAccount() { setConfirmLoading(true); try { - await state.mutiny_wallet?.delete_profile(); + await sw.delete_profile(); // Remove the nsec from secure storage if it exists await SecureStoragePlugin.clear(); window.location.href = "/"; @@ -113,14 +113,11 @@ function UnlinkAccount() { export function NostrKeys() { const i18n = useI18n(); - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); - const npub = createAsync(async () => state.mutiny_wallet?.get_npub()); - const nsec = createAsync(async () => state.mutiny_wallet?.export_nsec()); - const profile = () => state.mutiny_wallet?.get_nostr_profile(); - - // @ts-expect-error we're checking for an extension - const windowHasNostr = window.nostr && window.nostr.getPublicKey; + const npub = createAsync(async () => await sw.get_npub()); + const nsec = createAsync(async () => await sw.export_nsec()); + const profile = createAsync(async () => await sw.get_nostr_profile()); const [nsecInSecureStorage] = createResource(async () => { try { @@ -144,7 +141,7 @@ export function NostrKeys() { - +
@@ -169,27 +166,23 @@ export function NostrKeys() { - - - - {i18n.t("settings.nostr_keys.import_profile")} - - + + + {i18n.t("settings.nostr_keys.import_profile")} + - + - + { try { - const plans = await state.mutiny_wallet?.get_subscription_plans(); + const plans = await sw.get_subscription_plans(); console.log("plans:", plans); if (!plans) return undefined; return plans[0]; @@ -82,17 +82,12 @@ function PlusCTA() { if (planDetails()?.id === undefined || planDetails()?.id === null) throw new Error(i18n.t("settings.plus.error_no_plan")); - const invoice = await state.mutiny_wallet?.subscribe_to_plan( - planDetails().id - ); + const invoice = await sw.subscribe_to_plan(planDetails()!.id); if (!invoice?.bolt11) throw new Error(i18n.t("settings.plus.error_failure")); - await state.mutiny_wallet?.pay_subscription_invoice( - invoice?.bolt11, - true - ); + await sw.pay_subscription_invoice(invoice?.bolt11, true); await vibrateSuccess(); @@ -112,7 +107,7 @@ function PlusCTA() { return ( (state.balance?.lightning || 0n) + (state.balance?.federation || 0n) > - planDetails().amount_sat + planDetails()!.amount_sat ); }; @@ -126,7 +121,7 @@ function PlusCTA() { {" "} {i18n.t("settings.plus.sats_per_month", { amount: Number( - planDetails().amount_sat + planDetails()!.amount_sat ).toLocaleString() })} @@ -137,7 +132,7 @@ function PlusCTA() { {i18n.t("settings.plus.lightning_balance", { amount: Number( - planDetails().amount_sat + planDetails()!.amount_sat ).toLocaleString() })} diff --git a/src/routes/settings/Restore.tsx b/src/routes/settings/Restore.tsx index 874d42d..a5267d5 100644 --- a/src/routes/settings/Restore.tsx +++ b/src/routes/settings/Restore.tsx @@ -9,7 +9,6 @@ import { SubmitHandler, validate } from "@modular-forms/solid"; -import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { LucideClipboard } from "lucide-solid"; import { createSignal, For, Show, splitProps } from "solid-js"; @@ -79,7 +78,7 @@ function SeedTextField(props: TextFieldProps) { export function TwelveWordsEntry() { const i18n = useI18n(); - const [state, actions] = useMegaStore(); + const [state, actions, sw] = useMegaStore(); const [error, setError] = createSignal(); const [mnemnoic, setMnemonic] = createSignal(); @@ -129,17 +128,16 @@ export function TwelveWordsEntry() { try { setConfirmLoading(true); - if (state.mutiny_wallet) { + if (state.load_stage === "done") { console.log("Mutiny wallet loaded, stopping"); try { - await state.mutiny_wallet.stop(); - actions.dropMutinyWallet(); + await sw.stop(); } catch (e) { console.error(e); } } - await MutinyWallet.restore_mnemonic(mnemnoic() || "", ""); + await sw.restore_mnemonic(mnemnoic() || "", ""); actions.setHasBackedUp(); diff --git a/src/routes/settings/index.ts b/src/routes/settings/index.ts index b04a476..606e445 100644 --- a/src/routes/settings/index.ts +++ b/src/routes/settings/index.ts @@ -7,7 +7,6 @@ export * from "./Currency"; export * from "./Language"; export * from "./EmergencyKit"; export * from "./Encrypt"; -export * from "./Gift"; export * from "./Plus"; export * from "./Restore"; export * from "./Servers"; diff --git a/src/routes/setup/NewProfile.tsx b/src/routes/setup/NewProfile.tsx index 060a8b0..091d533 100644 --- a/src/routes/setup/NewProfile.tsx +++ b/src/routes/setup/NewProfile.tsx @@ -12,7 +12,7 @@ import { useMegaStore } from "~/state/megaStore"; import { DEFAULT_NOSTR_NAME } from "~/utils"; export function NewProfile() { - const [state, _actions] = useMegaStore(); + const [_state, _actions, sw] = useMegaStore(); const i18n = useI18n(); const [creating, setCreating] = createSignal(false); @@ -23,7 +23,7 @@ export function NewProfile() { async function handleSkip() { setSkipping(true); // set up an empty profile so we at least have some kind0 event - const profile = await state.mutiny_wallet?.setup_new_profile( + const profile = await sw.setup_new_profile( DEFAULT_NOSTR_NAME, undefined, undefined, @@ -38,7 +38,7 @@ export function NewProfile() { async function createProfile(p: EditableProfile) { setCreating(true); try { - const profile = await state.mutiny_wallet?.setup_new_profile( + const profile = await sw.setup_new_profile( p.nym ? p.nym : DEFAULT_NOSTR_NAME, p.imageUrl ? p.imageUrl : undefined, undefined, diff --git a/src/routes/setup/Root.tsx b/src/routes/setup/Root.tsx index 3da5a8f..8e9a79d 100644 --- a/src/routes/setup/Root.tsx +++ b/src/routes/setup/Root.tsx @@ -39,9 +39,8 @@ export function Setup() { return ( - {/* Setup */}
-
+
(); - type LoadStage = | "fresh" | "checking_double_init" @@ -45,69 +39,22 @@ type LoadStage = | "setup" | "done"; -type MegaStore = [ - { - mutiny_wallet?: MutinyWallet; - deleting: boolean; - scan_result?: ParsedParams; - balance?: MutinyBalance; - is_syncing?: boolean; - last_sync?: number; - price_sync_backoff_multiple?: number; - price: number; - fiat: Currency; - lang?: string; - has_backed_up: boolean; - wallet_loading: boolean; - setup_error?: Error; - is_pwa: boolean; - existing_tab_detected: boolean; - subscription_timestamp?: number; - readonly mutiny_plus: boolean; - needs_password: boolean; - password?: string; - load_stage: LoadStage; - settings?: MutinyWalletSettingStrings; - safe_mode?: boolean; - preferredInvoiceType: "unified" | "lightning" | "onchain"; - testflightPromptDismissed: boolean; - should_zap_hodl: boolean; - federations?: MutinyFederationIdentity[]; - balanceView: "sats" | "fiat" | "hidden"; - }, - { - setup(password?: string): Promise; - deleteMutinyWallet(): Promise; - setScanResult(scan_result: ParsedParams | undefined): void; - sync(): Promise; - setHasBackedUp(): void; - listTags(): Promise; - checkForSubscription(justPaid?: boolean): Promise; - fetchPrice(fiat: Currency): Promise; - saveFiat(fiat: Currency): void; - saveLanguage(lang: string): void; - setPreferredInvoiceType( - type: "unified" | "lightning" | "onchain" - ): void; - handleIncomingString( - str: string, - onError: (e: Error) => void, - onSuccess: (value: ParsedParams) => void - ): void; - setTestFlightPromptDismissed(): void; - toggleHodl(): void; - dropMutinyWallet(): void; - refreshFederations(): Promise; - cycleBalanceView(): void; - } -]; +export type WalletWorker = Remote; -export const Provider: ParentComponent = (props) => { +export const makeMegaStoreContext = () => { const [searchParams] = useSearchParams(); const navigate = useNavigate(); + // Not actually a shared worker, but it's the same code + const sw = new ComlinkWorker( + new URL("../workers/walletWorker", import.meta.url), + { + type: "module" + } + ); + const [state, setState] = createStore({ - mutiny_wallet: undefined as MutinyWallet | undefined, + network: undefined as Network | undefined, deleting: false, scan_result: undefined as ParsedParams | undefined, price: 0, @@ -115,7 +62,7 @@ export const Provider: ParentComponent = (props) => { ? (JSON.parse(localStorage.getItem("fiat_currency")!) as Currency) : USD_OPTION, has_backed_up: localStorage.getItem("has_backed_up") === "true", - balance: undefined as MutinyBalance | undefined, + balance: undefined as Partial | undefined, last_sync: undefined as number | undefined, price_sync_backoff_multiple: 1, is_syncing: false, @@ -146,7 +93,7 @@ export const Provider: ParentComponent = (props) => { const actions = { async checkForSubscription(justPaid?: boolean): Promise { try { - const timestamp = await state.mutiny_wallet?.check_subscribed(); + const timestamp = await sw.check_subscribed(); // Check that timestamp is a number if (timestamp && !isNaN(Number(timestamp))) { @@ -160,21 +107,27 @@ export const Provider: ParentComponent = (props) => { console.error(e); } }, - async preSetup(): Promise { + async preSetup(): Promise { try { // If we're already in an error state there should be no reason to continue if (state.setup_error) { throw state.setup_error; } - // If there's already a mutiny wallet in state abort! - if (state.mutiny_wallet) { - setState({ - setup_error: new Error( - "Existing Mutiny Wallet already running, aborting setup" - ) - }); - return; + setState({ wallet_loading: true }); + + await this.checkForExistingTab(); + + if (state.existing_tab_detected) { + return false; + } + + console.log("checking for browser compatibility"); + try { + await checkBrowserCompatibility(); + } catch (e) { + setState({ setup_error: eify(e) }); + return false; } setState({ @@ -183,11 +136,22 @@ export const Provider: ParentComponent = (props) => { }); await doubleInitDefense(); + setState({ load_stage: "downloading" }); - await initializeWasm(); + await sw.initializeWasm(); + + setState({ load_stage: "checking_for_existing_wallet" }); + const existing = await sw.has_node_manager(); + + if (!existing && !searchParams.skip_setup) { + navigate("/setup"); + return false; + } + return true; } catch (e) { console.error(e); setState({ setup_error: eify(e) }); + return false; } }, async setup(password?: string): Promise { @@ -224,24 +188,29 @@ export const Provider: ParentComponent = (props) => { } }, 1000); - const mutinyWallet = await setupMutinyWallet( + let nsec; + // get nsec from secure storage + try { + const value = await SecureStoragePlugin.get({ + key: "nsec" + }); + nsec = value.value; + } catch (e) { + console.log("No nsec stored"); + } + + const success = await sw.setupMutinyWallet( settings, password, state.safe_mode, - state.should_zap_hodl + state.should_zap_hodl, + nsec ); - // Done with the timeout shenanigans clearInterval(interval); - // I've never managed to trigger this but it's just some extra safety I guess - if (!mutinyWallet) { - setState({ - setup_error: new Error( - "Failed to initialize Mutiny Wallet" - ) - }); - return; + if (!success) { + throw new Error("Failed to initialize mutiny wallet"); } // Give other components access to settings via the store @@ -250,21 +219,32 @@ export const Provider: ParentComponent = (props) => { // If we get this far then we don't need the password anymore setState({ needs_password: false }); + // Get network + const network = await sw.get_network(); + // Get balance - const balance = await mutinyWallet.get_balance(); + const balance = await sw.get_balance(); // Get federations const federations = - (await mutinyWallet.list_federations()) as MutinyFederationIdentity[]; + (await sw.list_federations()) as MutinyFederationIdentity[]; setState({ - mutiny_wallet: mutinyWallet, wallet_loading: false, load_stage: "done", balance, - federations + federations, + network: network as Network }); + // Timestamp our initialization for double init defense + sessionStorage.setItem( + "MUTINY_WALLET_INITIALIZED", + Date.now().toString() + ); + + console.log("Wallet initialized"); + await actions.postSetup(); } catch (e) { console.error(e); @@ -277,7 +257,7 @@ export const Provider: ParentComponent = (props) => { } }, async postSetup(): Promise { - if (!state.mutiny_wallet) { + if (!sw) { console.error( "Unable to run post setup, no mutiny_wallet is set" ); @@ -286,8 +266,7 @@ export const Provider: ParentComponent = (props) => { // Check if we're subscribed and update the timestamp try { - const timestamp = await state.mutiny_wallet.check_subscribed(); - + const timestamp = await sw.check_subscribed(); // Check that timestamp is a number if (timestamp && !isNaN(Number(timestamp))) { setState({ subscription_timestamp: Number(timestamp) }); @@ -319,9 +298,9 @@ export const Provider: ParentComponent = (props) => { ...prevState, deleting: true })); - if (state.mutiny_wallet) { - await state.mutiny_wallet?.stop(); - await state.mutiny_wallet?.delete_all(); + if (sw) { + await sw.stop(); + await sw.delete_all(); } } catch (e) { console.error(e); @@ -346,9 +325,9 @@ export const Provider: ParentComponent = (props) => { }, async sync(): Promise { try { - if (state.mutiny_wallet && !state.is_syncing) { + if (sw && !state.is_syncing) { setState({ is_syncing: true }); - const newBalance = await state.mutiny_wallet?.get_balance(); + const newBalance = await sw.get_balance(); try { setState({ balance: newBalance, @@ -376,7 +355,7 @@ export const Provider: ParentComponent = (props) => { return price; } else { try { - price = await state.mutiny_wallet?.get_bitcoin_price( + price = await sw.get_bitcoin_price( fiat.value.toLowerCase() || "usd" ); return price; @@ -395,7 +374,7 @@ export const Provider: ParentComponent = (props) => { }, async listTags(): Promise { try { - return state.mutiny_wallet?.get_tag_items(); + return sw.get_tag_items(); } catch (e) { console.error(e); return []; @@ -416,18 +395,13 @@ export const Provider: ParentComponent = (props) => { setPreferredInvoiceType(type: "unified" | "lightning" | "onchain") { setState({ preferredInvoiceType: type }); }, - handleIncomingString( + async handleIncomingString( str: string, onError: (e: Error) => void, onSuccess: (value: ParsedParams) => void - ): void { + ): Promise { try { const url = new URL(str); - if (url && url.pathname.startsWith("/gift")) { - navigate(url.pathname + url.search); - return; - } - if (url && url.pathname.startsWith("/settings/plus")) { navigate(url.pathname + url.search); return; @@ -436,9 +410,10 @@ export const Provider: ParentComponent = (props) => { // If it's not a URL, we'll just continue with normal parsing } - const network = state.mutiny_wallet?.get_network() || "signet"; - const result = toParsedParams(str || "", network); - if (!result.ok) { + const network = state.network || "signet"; + const result = await toParsedParams(str || "", network, sw); + + if (!result || !result.ok) { if (onError) { onError(result.error); } @@ -489,12 +464,8 @@ export const Provider: ParentComponent = (props) => { localStorage.setItem("should_zap_hodl", should_zap_hodl.toString()); setState({ should_zap_hodl }); }, - dropMutinyWallet() { - setState({ mutiny_wallet: undefined }); - }, async refreshFederations() { - const federations = - (await state.mutiny_wallet?.list_federations()) as MutinyFederationIdentity[]; + const federations = await sw.list_federations(); setState({ federations }); }, cycleBalanceView() { @@ -508,110 +479,71 @@ export const Provider: ParentComponent = (props) => { localStorage.setItem("balanceView", "sats"); setState({ balanceView: "sats" }); } + }, + async checkForExistingTab() { + // Set up existing tab detector + const channel = new BroadcastChannel("tab-detector"); + + // First we let everyone know we exist + channel.postMessage({ type: "NEW_TAB" }); + + channel.onmessage = (e) => { + // If any tabs reply, we know there's an existing tab so abort setup + if (e.data.type === "EXISTING_TAB") { + console.debug("there's an existing tab"); + setState({ + existing_tab_detected: true, + setup_error: new Error( + "Existing tab detected, aborting setup" + ) + }); + return; + } + + // If we get notified of a new tab, we let it know we exist + if (e.data.type === "NEW_TAB") { + console.debug("a new tab just came online"); + channel.postMessage({ type: "EXISTING_TAB" }); + } + }; } }; - onCleanup(() => { - console.warn("Parent Component is being unmounted!!!"); - state.mutiny_wallet - ?.stop() - .then(() => { - console.warn("Successfully stopped mutiny wallet"); - sessionStorage.removeItem("MUTINY_WALLET_INITIALIZED"); - }) - .catch((e) => { - console.error("Error stopping mutiny wallet", e); - }); - }); + return [state, actions, sw] as const; +}; - async function checkForExistingTab() { - // Set up existing tab detector - const channel = new BroadcastChannel("tab-detector"); +type MegaStoreContextType = ReturnType; - // First we let everyone know we exist - channel.postMessage({ type: "NEW_TAB" }); +export const MegaStoreContext = createContext(); +export const useMegaStore = () => useContext(MegaStoreContext)!; - channel.onmessage = (e) => { - // If any tabs reply, we know there's an existing tab so abort setup - if (e.data.type === "EXISTING_TAB") { - console.debug("there's an existing tab"); - setState({ - existing_tab_detected: true, - setup_error: new Error( - "Existing tab detected, aborting setup" - ) - }); - return; - } - - // If we get notified of a new tab, we let it know we exist - if (e.data.type === "NEW_TAB") { - console.debug("a new tab just came online"); - channel.postMessage({ type: "EXISTING_TAB" }); - } - }; - } - - const [params, _] = useSearchParams(); +export const Provider: ParentComponent = (props) => { + const [state, actions, sw] = makeMegaStoreContext(); onMount(async () => { - await checkForExistingTab(); - if (state.existing_tab_detected) { - return; - } - - console.log("checking for browser compatibility"); - try { - await checkBrowserCompatibility(); - } catch (e) { - setState({ setup_error: eify(e) }); - return; - } - - await actions.preSetup(); - - setState({ load_stage: "checking_for_existing_wallet" }); - const existing = await MutinyWallet.has_node_manager(); - - if (!existing && !params.skip_setup) { - navigate("/setup"); - return; - } - - // Setup catches its own errors and sets state itself - console.log("running setup node manager"); + const shouldSetup = await actions.preSetup(); + console.log("Should run setup?", shouldSetup); if ( - !state.mutiny_wallet && + shouldSetup && + sw && + !state.existing_tab_detected && !state.deleting && - !state.setup_error && - !state.existing_tab_detected + !state.setup_error ) { await actions.setup(); - } else { - console.warn("setup aborted"); - return; } - - // After we have the mutiny wallet we still need to check for subscription and sync nostr - // await actions.postSetup(); - - console.log("node manager setup done"); }); - const store = [state, actions] as MegaStore; + onCleanup(async () => { + console.warn("Parent Component is being unmounted!!!"); + await sw.stop(); + console.warn("Successfully stopped mutiny wallet"); + sessionStorage.removeItem("MUTINY_WALLET_INITIALIZED"); + }); return ( - + {props.children} ); }; - -export function useMegaStore() { - // This is a trick to narrow the typescript types: https://docs.solidjs.com/references/api-reference/component-apis/createContext - const context = useContext(MegaStoreContext); - if (!context) { - throw new Error("useMegaStore: cannot find a MegaStoreContext"); - } - return context; -} diff --git a/src/utils/conversions.ts b/src/utils/conversions.ts index 7830fbf..ebb4e73 100644 --- a/src/utils/conversions.ts +++ b/src/utils/conversions.ts @@ -1,4 +1,4 @@ -import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; +import { WalletWorker } from "~/state/megaStore"; import { Currency } from "./currencies"; @@ -9,18 +9,17 @@ import { Currency } from "./currencies"; * @param {Currency} fiat - Takes {@link Currency} object options to determine how to format the amount input */ -export function satsToFiat( +export async function satsToFiat( amount: number | undefined, price: number, - fiat: Currency -): string { + fiat: Currency, + sw: WalletWorker +): Promise { if (typeof amount !== "number" || isNaN(amount)) { return ""; } try { - const btc = MutinyWallet.convert_sats_to_btc( - BigInt(Math.floor(amount)) - ); + const btc = await sw.convert_sats_to_btc(BigInt(Math.floor(amount))); const fiatPrice = btc * price; const roundedFiat = Math.round(fiatPrice); if ( @@ -45,18 +44,17 @@ export function satsToFiat( * @param {Currency} fiat - Takes {@link Currency} object options to determine how to format the amount input */ -export function satsToFormattedFiat( +export async function satsToFormattedFiat( amount: number | undefined, price: number, - fiat: Currency -): string { + fiat: Currency, + sw: WalletWorker +): Promise { if (typeof amount !== "number" || isNaN(amount)) { return ""; } try { - const btc = MutinyWallet.convert_sats_to_btc( - BigInt(Math.floor(amount)) - ); + const btc = await sw.convert_sats_to_btc(BigInt(Math.floor(amount))); const fiatPrice = btc * price; //Handles currencies not supported by .toLocaleString() like BTC //Returns a string with a currency symbol and a number with decimals equal to the maxFractionalDigits @@ -82,17 +80,18 @@ export function satsToFormattedFiat( } } -export function fiatToSats( +export async function fiatToSats( amount: number | undefined, price: number, - formatted: boolean -): string { + formatted: boolean, + sw: WalletWorker +): Promise { if (typeof amount !== "number" || isNaN(amount)) { return ""; } try { const btc = price / amount; - const sats = MutinyWallet.convert_btc_to_sats(btc); + const sats = await sw.convert_btc_to_sats(btc); if (formatted) { return parseInt(sats.toString()).toLocaleString(); } else { diff --git a/src/utils/fetchZaps.ts b/src/utils/fetchZaps.ts index 267e500..2d68cf7 100644 --- a/src/utils/fetchZaps.ts +++ b/src/utils/fetchZaps.ts @@ -1,7 +1,6 @@ -import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; import { ResourceFetcher } from "solid-js"; -import { useMegaStore } from "~/state/megaStore"; +import { useMegaStore, WalletWorker } from "~/state/megaStore"; import { getPrimalImageUrl, hexpubFromNpub, @@ -71,7 +70,7 @@ function getZapKind(event: NostrEvent): "public" | "private" | "anonymous" { async function simpleZapFromEvent( event: NostrEvent, - wallet: MutinyWallet + sw: WalletWorker ): Promise { if (event.kind === 9735 && event.tags?.length > 0) { const to = findByTag(event.tags, "p") || ""; @@ -94,8 +93,8 @@ async function simpleZapFromEvent( if (bolt11) { try { // We hardcode the "bitcoin" network because we don't have a good source of mutinynet zaps - const decoded = await wallet.decode_invoice(bolt11, "bitcoin"); - if (decoded.amount_sats) { + const decoded = await sw.decode_invoice(bolt11, "bitcoin"); + if (decoded?.amount_sats) { amount = decoded.amount_sats; } else { console.log("no amount in decoded invoice"); @@ -181,7 +180,7 @@ export const fetchZaps: ResourceFetcher< until?: number; } > = async (npub, info) => { - const [state, _actions] = useMegaStore(); + const [state, _actions, sw] = useMegaStore(); try { console.log("fetching zaps for:", npub); @@ -197,13 +196,15 @@ export const fetchZaps: ResourceFetcher< // Only have to ask the relays for follows one time if (follows.length === 0) { - const contacts = await state.mutiny_wallet?.get_contacts_sorted(); + const contacts = await sw.get_contacts_sorted(); const hexpubs = []; - for (const contact of contacts) { - if (contact.npub) { - const hexpub = await hexpubFromNpub(contact.npub); - if (hexpub) { - hexpubs.push(hexpub); + if (contacts) { + for (const contact of contacts) { + if (contact.npub) { + const hexpub = await hexpubFromNpub(sw, contact.npub); + if (hexpub) { + hexpubs.push(hexpub); + } } } } @@ -239,10 +240,7 @@ export const fetchZaps: ResourceFetcher< if (object.kind === 9735) { // console.log("got a 9735 object", object); try { - const event = await simpleZapFromEvent( - object, - state.mutiny_wallet! - ); + const event = await simpleZapFromEvent(object, sw); // Only add it if it's a valid zap (not undefined) if (event) { diff --git a/src/utils/nostr.ts b/src/utils/nostr.ts index 79bf527..b9bee0a 100644 --- a/src/utils/nostr.ts +++ b/src/utils/nostr.ts @@ -1,4 +1,4 @@ -import { MutinyWallet } from "@mutinywallet/mutiny-wasm"; +import { WalletWorker } from "~/state/megaStore"; export type NostrTag = string[]; export declare enum NostrKind { @@ -45,6 +45,7 @@ export declare enum NostrKind { } export async function hexpubFromNpub( + sw: WalletWorker, npub?: string ): Promise { if (!npub) { @@ -55,7 +56,7 @@ export async function hexpubFromNpub( } try { - const hexpub = await MutinyWallet.npub_to_hexpub(npub); + const hexpub = await sw?.npub_to_hexpub(npub); return hexpub; } catch (err) { console.error(err); diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe..bcf2c0d 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1,2 @@ /// +/// diff --git a/src/workers/walletWorker.ts b/src/workers/walletWorker.ts new file mode 100644 index 0000000..8c6ec0b --- /dev/null +++ b/src/workers/walletWorker.ts @@ -0,0 +1,1574 @@ +import initMutinyWallet, { + ActivityItem, + BudgetPeriod, + ChannelClosure, + FederationBalance, + FederationBalances, + FedimintSweepResult, + LnUrlParams, + MutinyBalance, + MutinyBip21RawMaterials, + MutinyChannel, + MutinyInvoice, + MutinyPeer, + MutinyWallet, + NwcProfile, + PaymentParams, + PendingNwcInvoice, + TagItem +} from "@mutinywallet/mutiny-wasm"; + +import { IActivityItem } from "~/components"; +import { MutinyWalletSettingStrings } from "~/logic/mutinyWalletSetup"; +import { FakeDirectMessage, OnChainTx } from "~/routes"; +import { + DiscoveredFederation, + MutinyFederationIdentity +} from "~/routes/settings"; + +// For some reason {...invoice } doesn't bring across the paid field +function destructureInvoice(invoice: MutinyInvoice): MutinyInvoice { + return { + amount_sats: invoice.amount_sats, + bolt11: invoice.bolt11, + description: invoice.description, + expire: invoice.expire, + expired: invoice.expired, + fees_paid: invoice.fees_paid, + inbound: invoice.inbound, + labels: invoice.labels, + last_updated: invoice.last_updated, + paid: invoice.paid, + payee_pubkey: invoice.payee_pubkey, + payment_hash: invoice.payment_hash, + potential_hodl_invoice: invoice.potential_hodl_invoice, + preimage: invoice.preimage, + privacy_level: invoice.privacy_level, + status: invoice.status + } as MutinyInvoice; +} + +let wallet: MutinyWallet | undefined; +export let wasm_initialized = false; +export let wallet_initialized = false; + +export async function checkForWasm() { + try { + if ( + typeof WebAssembly === "object" && + typeof WebAssembly.instantiate === "function" + ) { + const module = new WebAssembly.Module( + Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00) + ); + if (!(module instanceof WebAssembly.Module)) { + throw new Error("Couldn't instantiate WASM Module"); + } + } else { + throw new Error("No WebAssembly global object found"); + } + } catch (e) { + console.error(e); + } +} + +export async function initializeWasm() { + // Actually intialize the WASM, this should be the first thing that requires the WASM blob to be downloaded + + // If WASM is already initialized, don't init twice + try { + const _sats_the_standard = MutinyWallet.convert_btc_to_sats(1); + console.debug("MutinyWallet WASM already initialized, skipping init"); + wasm_initialized = true; + return; + } catch (e) { + console.debug("MutinyWallet WASM about to be initialized"); + await initMutinyWallet(); + console.debug("MutinyWallet WASM initialized"); + } +} + +export async function setupMutinyWallet( + settings: MutinyWalletSettingStrings, + password?: string, + safeMode?: boolean, + shouldZapHodl?: boolean, + nsec?: string +): Promise { + console.log("Starting setup..."); + + // https://developer.mozilla.org/en-US/docs/Web/API/Storage_API + // Ask the browser to not clear storage + if (navigator.storage && navigator.storage.persist) { + navigator.storage.persist().then((persistent) => { + if (persistent) { + console.log( + "Storage will not be cleared except by explicit user action" + ); + } else { + console.log( + "Storage may be cleared by the UA under storage pressure." + ); + } + }); + } + + const { + network, + proxy, + esplora, + rgs, + lsp, + lsps_connection_string, + lsps_token, + auth, + subscriptions, + storage, + scorer, + primal_api, + blind_auth, + hermes + } = settings; + + console.log("Initializing Mutiny Manager"); + console.log("Using network", network); + console.log("Using proxy", proxy); + console.log("Using esplora address", esplora); + console.log("Using rgs address", rgs); + console.log("Using lsp address", lsp); + console.log("Using lsp connection string", lsps_connection_string); + console.log("Using lsp token", lsps_token); + console.log("Using auth address", auth); + console.log("Using subscriptions address", subscriptions); + console.log("Using storage address", storage); + console.log("Using scorer address", scorer); + console.log("Using primal api", primal_api); + console.log("Using blind auth", blind_auth); + console.log("Using hermes", hermes); + console.log(safeMode ? "Safe mode enabled" : "Safe mode disabled"); + console.log(shouldZapHodl ? "Hodl zaps enabled" : "Hodl zaps disabled"); + + // Only use lsps if there's no lsp set + const shouldUseLSPS = !lsp && lsps_connection_string && lsps_token; + + const mutinyWallet = await new MutinyWallet( + // Password + password ? password : undefined, + // Mnemonic + undefined, + proxy, + network, + esplora, + rgs, + shouldUseLSPS ? undefined : lsp, + shouldUseLSPS ? lsps_connection_string : undefined, + shouldUseLSPS ? lsps_token : undefined, + auth, + subscriptions, + storage, + scorer, + // Do not connect peers + undefined, + // Do not skip device lock + undefined, + // Safe mode + safeMode || undefined, + // Skip hodl invoices? (defaults to true, so if shouldZapHodl is true that's when we pass false) + shouldZapHodl ? false : undefined, + // Nsec override + nsec, + // Nip7 (not supported in web worker) + undefined, + // primal URL + primal_api || "https://primal-cache.mutinywallet.com/api", + /// blind auth url + blind_auth, + /// hermes url + hermes + ); + + wallet = mutinyWallet; + wallet_initialized = true; + + return true; +} + +/** + * Gets the current balance of the wallet. + * This includes both on-chain and lightning funds. + * + * This will not include any funds in an unconfirmed lightning channel. + * @returns {Promise} + */ +export async function get_balance(): Promise { + const balance = await wallet!.get_balance(); + return { + federation: balance.federation, + lightning: balance.lightning, + confirmed: balance.confirmed, + unconfirmed: balance.unconfirmed, + force_close: balance.force_close + } as MutinyBalance; +} + +/** + * Lists the federation id's of the federation clients in the manager. + * @returns {Promise} + */ +export async function list_federations(): Promise { + const federations = await wallet!.list_federations(); + return federations as MutinyFederationIdentity[]; +} + +/** + * Checks whether or not the user is subscribed to Mutiny+. + * Submits a NWC string to keep the subscription active if not expired. + * + * Returns None if there's no subscription at all. + * Returns Some(u64) for their unix expiration timestamp, which may be in the + * past or in the future, depending on whether or not it is currently active. + * @returns {Promise} + */ +export async function check_subscribed(): Promise { + const subscribed = await wallet!.check_subscribed(); + return subscribed; +} + +/** + * Stops all of the nodes and background processes. + * Returns after node has been stopped. + * @returns {Promise} + */ +export async function stop(): Promise { + await wallet!.stop(); +} + +/** + * Clears storage and deletes all data. + * + * All data in VSS persists but the device lock is cleared. + * @returns {Promise} + */ +export async function delete_all(): Promise { + await wallet!.delete_all(); +} + +/** + * Gets the current bitcoin price in chosen Fiat. + * @param {string | undefined} [fiat] + * @returns {Promise} + */ +export async function get_bitcoin_price(fiat: string): Promise { + const price = await wallet!.get_bitcoin_price(fiat); + return price; +} + +export async function get_tag_items(): Promise { + const tagItems = await wallet!.get_tag_items(); + return tagItems; +} + +/** + * Returns the network of the wallet. + * @returns {string} + */ +export async function get_network(): Promise { + const network = await wallet!.get_network(); + return network || "signet"; +} + +/** + * Returns the user's nostr profile data + * @returns {any} + */ +export async function get_nostr_profile(): Promise { + if (wallet) { + const profile = wallet.get_nostr_profile(); + return { ...profile }; + } else { + return undefined; + } +} + +/** + * Returns all the on-chain and lightning activity from the wallet. + * @param {number | undefined} [limit] + * @param {number | undefined} [offset] + */ +export async function get_activity( + limit: number, + offset?: number +): Promise { + const activity = await wallet!.get_activity(limit, offset); + return activity; +} + +export async function get_contact_for_npub( + npub: string +): Promise { + const contact = await wallet!.get_contact_for_npub(npub); + if (!contact) return undefined; + return { ...contact?.value }; +} + +export async function create_new_contact( + name: string, + npub?: string, + ln_address?: string, + lnurl?: string, + image_url?: string +): Promise { + const contactId = await wallet!.create_new_contact( + name, + npub, + ln_address, + lnurl, + image_url + ); + return contactId; +} + +export async function get_tag_item(id: string): Promise { + const tagItem = await wallet!.get_tag_item(id); + if (!tagItem) return undefined; + return { ...tagItem?.value }; +} + +/** + * Returns all the on-chain and lightning activity for a given label + * @param {string} label + * @returns {Promise} + */ +export async function get_label_activity( + label: string +): Promise { + const activity = await wallet!.get_label_activity(label); + return activity; +} + +/** + * Get dm conversation between us and given npub + * Returns a vector of messages sorted by newest first + * @param {string} npub + * @param {bigint} limit + * @param {bigint | undefined} [until] + * @param {bigint | undefined} [since] + * @returns {Promise} + */ +export async function get_dm_conversation( + npub: string, + limit: bigint, + until?: bigint, + since?: bigint +): Promise { + const dms = await wallet!.get_dm_conversation(npub, limit, until, since); + return [...dms]; +} + +/** + * Gets an invoice from the node manager. + * This includes sent and received invoices. + * @param {string} invoice + * @returns {Promise} + */ +export async function get_invoice( + bolt11: string +): Promise { + const invoice = await wallet!.get_invoice(bolt11); + if (!invoice) return undefined; + // For some reason {...invoice } doesn't bring across the paid field + return destructureInvoice(invoice); +} + +/** + * Gets all contacts sorted by last used + * @returns {Promise} + */ +export async function get_contacts_sorted(): Promise { + const contacts = await wallet!.get_contacts_sorted(); + return contacts; +} + +export async function edit_contact( + id: string, + name: string, + npub?: string, + ln_address?: string, + lnurl?: string, + image_url?: string +): Promise { + await wallet!.edit_contact(id, name, npub, ln_address, lnurl, image_url); +} + +export async function delete_contact(id: string): Promise { + await wallet!.delete_contact(id); +} + +/** + * Follows the npub on nostr if we're not already following + * @param {string} npub + * @returns {Promise} + */ +export async function follow_npub(npub: string): Promise { + await wallet!.follow_npub(npub); +} + +/** + * Unfollows the npub on nostr if we're following them + * + * Returns true if we were following them before + * @param {string} npub + * @returns {Promise} + */ +export async function unfollow_npub(npub: string): Promise { + await wallet!.unfollow_npub(npub); +} + +/** + * Sends a DM to the given npub + * @param {string} npub + * @param {string} message + * @returns {Promise} + */ +export async function send_dm( + npub: string, + message: string +): Promise { + const result = await wallet!.send_dm(npub, message); + return result; +} + +/** + * Returns the user's npub + * @returns {Promise} + */ +export async function get_npub(): Promise { + const npub = await wallet!.get_npub(); + return npub; +} + +/** + * Decodes a lightning invoice into useful information. + * Will return an error if the invoice is for a different network. + * @param {string} invoice + * @param {string | undefined} [network] + * @returns {Promise} + */ +export async function decode_invoice( + invoice: string, + network?: string +): Promise { + const decoded = await wallet!.decode_invoice(invoice, network); + if (!decoded) return undefined; + return destructureInvoice(decoded); +} + +/** + * Creates a BIP 21 invoice. This creates a new address and a lightning invoice. + * The lightning invoice may return errors related to the LSP. Check the error and + * fallback to `get_new_address` and warn the user that Lightning is not available. + * + * + * Errors that might be returned include: + * + * - [`MutinyJsError::LspGenericError`]: This is returned for various reasons, including if a + * request to the LSP server fails for any reason, or if the server returns + * a status other than 500 that can't be parsed into a `ProposalResponse`. + * + * - [`MutinyJsError::LspFundingError`]: Returned if the LSP server returns an error with + * a status of 500, indicating an "Internal Server Error", and a message + * stating "Cannot fund new channel at this time". This means that the LSP cannot support + * a new channel at this time. + * + * - [`MutinyJsError::LspAmountTooHighError`]: Returned if the LSP server returns an error with + * a status of 500, indicating an "Internal Server Error", and a message stating "Invoice + * amount is too high". This means that the LSP cannot support the amount that the user + * requested. The user should request a smaller amount from the LSP. + * + * - [`MutinyJsError::LspConnectionError`]: Returned if the LSP server returns an error with + * a status of 500, indicating an "Internal Server Error", and a message that starts with + * "Failed to connect to peer". This means that the LSP is not connected to our node. + * + * If the server returns a status of 500 with a different error message, + * a [`MutinyJsError::LspGenericError`] is returned. + * @param {bigint | undefined} amount + * @param {(string)[]} labels + * @returns {Promise} + */ +export async function create_bip21( + amount: bigint | undefined, + labels: string[] +): Promise { + const mbrw = await wallet!.create_bip21(amount, labels); + return { + ...mbrw?.value + } as MutinyBip21RawMaterials; +} + +/** + * Estimates the onchain fee for a transaction sweep our on-chain balance + * to the given address. + * + * The fee rate is in sat/vbyte. + * @param {string} destination_address + * @param {number | undefined} [fee_rate] + * @returns {bigint} + */ +export async function estimate_sweep_tx_fee( + address: string +): Promise { + const fee = await wallet!.estimate_sweep_tx_fee(address); + return fee; +} + +/** + * Estimates the onchain fee for a transaction sending to the given address. + * The amount is in satoshis and the fee rate is in sat/vbyte. + * @param {string} destination_address + * @param {bigint} amount + * @param {number | undefined} [fee_rate] + * @returns {bigint} + */ +export async function estimate_tx_fee( + address: string, + amount: bigint, + feeRate?: number +): Promise { + const fee = await wallet!.estimate_tx_fee(address, amount, feeRate); + return fee; +} +/** + * Calls upon a LNURL to get the parameters for it. + * This contains what kind of LNURL it is (pay, withdrawal, auth, etc). + * @param {string} lnurl + * @returns {Promise} + */ +export async function decode_lnurl(lnurl: string): Promise { + const lnurlParams = await wallet!.decode_lnurl(lnurl); + // PAIN: this is supposed to be returning bigints, but it returns numbers instead + return { + ...lnurlParams?.value + } as LnUrlParams; +} + +/** + * Pays a lightning invoice from the selected node. + * An amount should only be provided if the invoice does not have an amount. + * The amount should be in satoshis. + * @param {string} invoice_str + * @param {bigint | undefined} amt_sats + * @param {(string)[]} labels + * @returns {Promise} + */ +export async function pay_invoice( + invoice_str: string, + amt_sats: bigint | undefined, + labels: string[] +): Promise { + const invoice = await wallet!.pay_invoice(invoice_str, amt_sats, labels); + if (!invoice) return undefined; + return destructureInvoice(invoice); +} + +/** + * Calls upon a LNURL and pays it. + * This will fail if the LNURL is not a LNURL pay. + * @param {string} lnurl + * @param {bigint} amount_sats + * @param {string | undefined} zap_npub + * @param {(string)[]} labels + * @param {string | undefined} [comment] + * @param {string | undefined} [privacy_level] + * @returns {Promise} + */ +export async function lnurl_pay( + lnurl: string, + amount_sats: bigint, + zap_npub: string | undefined, + labels: string[], + comment?: string, + privacy_level?: string +): Promise { + const invoice = await wallet!.lnurl_pay( + lnurl, + amount_sats, + zap_npub, + labels, + comment, + privacy_level + ); + if (!invoice) return undefined; + return destructureInvoice(invoice); +} + +/** + * Sweeps all the funds from the wallet to the given address. + * The fee rate is in sat/vbyte. + * + * If a fee rate is not provided, one will be used from the fee estimator. + * @param {string} destination_address + * @param {(string)[]} labels + * @param {number | undefined} [fee_rate] + * @returns {Promise} + */ +export async function sweep_wallet( + destination_address: string, + labels: string[], + fee_rate?: number +): Promise { + const payment = await wallet!.sweep_wallet( + destination_address, + labels, + fee_rate + ); + return payment; +} + +export async function send_payjoin( + payjoin_uri: string, + amount: bigint, + labels: string[], + fee_rate?: number +): Promise { + const payment = await wallet!.send_payjoin( + payjoin_uri, + amount, + labels, + fee_rate + ); + return payment; +} + +/** + * Sends an on-chain transaction to the given address. + * The amount is in satoshis and the fee rate is in sat/vbyte. + * + * If a fee rate is not provided, one will be used from the fee estimator. + * @param {string} destination_address + * @param {bigint} amount + * @param {(string)[]} labels + * @param {number | undefined} [fee_rate] + * @returns {Promise} + */ +export async function send_to_address( + destination_address: string, + amount: bigint, + labels: string[], + fee_rate?: number +): Promise { + const payment = await wallet!.send_to_address( + destination_address, + amount, + labels, + fee_rate + ); + return payment; +} + +/** + * Sends a spontaneous payment to a node from the selected node. + * The amount should be in satoshis. + * @param {string} to_node + * @param {bigint} amt_sats + * @param {string | undefined} message + * @param {(string)[]} labels + * @returns {Promise} + */ +export async function keysend( + to_node: string, + amt_sats: bigint, + message: string | undefined, + labels: string[] +): Promise { + const invoice = await wallet!.keysend(to_node, amt_sats, message, labels); + if (!invoice) return undefined; + return destructureInvoice(invoice); +} + +/** + * Gets an invoice from the node manager. + * This includes sent and received invoices. + * @param {string} hash + * @returns {Promise} + */ +export async function get_invoice_by_hash( + hash: string +): Promise { + const invoice = await wallet!.get_invoice_by_hash(hash); + return destructureInvoice(invoice); +} + +/** + * Gets an channel closure from the node manager. + * @param {string} user_channel_id + * @returns {Promise} + */ +export async function get_channel_closure( + user_channel_id: string +): Promise { + const channel_closure = await wallet!.get_channel_closure(user_channel_id); + return { + channel_id: channel_closure.channel_id, + node_id: channel_closure.node_id, + reason: channel_closure.reason, + timestamp: channel_closure.timestamp + } as ChannelClosure; +} + +/** + * Gets the details of a specific on-chain transaction. + * @param {string} txid + * @returns {any} + */ +export async function get_transaction(txid: string): Promise { + // TODO: this is an ActivityItem right? + const transaction = await wallet!.get_transaction(txid); + return transaction as ActivityItem; +} + +/** + * Gets a new bitcoin address from the wallet. + * Will generate a new address on every call. + * + * It is recommended to create a new address for every transaction. + * @param {(string)[]} labels + * @returns {MutinyBip21RawMaterials} + */ +export async function get_new_address( + labels: string[] +): Promise { + const mbrw = await wallet!.get_new_address(labels); + return { + ...mbrw?.value + } as MutinyBip21RawMaterials; +} + +/** + * Checks if the given address has any transactions. + * If it does, it returns the details of the first transaction. + * + * This should be used to check if a payment has been made to an address. + * @param {string} address + * @returns {Promise} + */ +export async function check_address(address: string): Promise { + const tx = await wallet!.check_address(address); + return tx as OnChainTx; +} + +/** + * Lists all the channels for all the nodes in the node manager. + * @returns {Promise} + */ +export async function list_channels(): Promise { + const channels = await wallet!.list_channels(); + return channels; +} + +/** + * This should only be called when the user is setting up a new profile + * never for an existing profile + * @param {string | undefined} [name] + * @param {string | undefined} [img_url] + * @param {string | undefined} [lnurl] + * @param {string | undefined} [nip05] + * @returns {Promise} + */ +export async function setup_new_profile( + name?: string, + img_url?: string, + lnurl?: string, + nip05?: string +): Promise { + const profile = await wallet!.setup_new_profile( + name, + img_url, + lnurl, + nip05 + ); + return profile; +} + +/** + * Queries our relays for federation announcements + * @returns {Promise} + */ +export async function discover_federations(): Promise< + DiscoveredFederation[] | undefined +> { + const federations = await wallet!.discover_federations(); + return federations; +} + +/** + * Checks if we have recommended the given federation + * @param {string} federation_id + * @returns {Promise} + */ +export async function has_recommended_federation( + federation_id: string +): Promise { + const hasRecommended = + await wallet!.has_recommended_federation(federation_id); + return hasRecommended; +} + +/** + * Adds a new federation based on its federation code + * @param {string} federation_code + * @returns {Promise} + */ +export async function new_federation(inviteCode: string): Promise { + const newFederation = await wallet!.new_federation(inviteCode); + return newFederation; +} + +export type NostrMetadata = { + name?: string; + display_name?: string; + picture?: string; + lud16?: string; + nip05?: string; + deleted?: boolean; +}; + +/** + * Sets the user's nostr profile data + * @param {string | undefined} [name] + * @param {string | undefined} [img_url] + * @param {string | undefined} [lnurl] + * @param {string | undefined} [nip05] + * @returns {Promise} + */ +export async function edit_nostr_profile( + name?: string, + img_url?: string, + lnurl?: string, + nip05?: string +): Promise { + const profile = await wallet!.edit_nostr_profile( + name, + img_url, + lnurl, + nip05 + ); + return { + ...profile + }; +} + +/** + * Uploads a profile pic to nostr.build and returns the uploaded file's URL + * @param {string} img_base64 + * @returns {Promise} + */ +export async function upload_profile_pic(data: string): Promise { + const url = await wallet!.upload_profile_pic(data); + return url; +} + +/** + * Lists all pending NWC invoices + * @returns {(PendingNwcInvoice)[]} + */ +export async function get_pending_nwc_invoices(): Promise< + PendingNwcInvoice[] | undefined +> { + const pending = await wallet!.get_pending_nwc_invoices(); + + // PAIN + // Have to rebuild the array because it's an array of pointers + const newPending: PendingNwcInvoice[] = []; + for (const pendingItem of pending) { + newPending.push({ + amount_sats: pendingItem.amount_sats, + expiry: pendingItem.expiry, + id: pendingItem.id, + index: pendingItem.index, + invoice: pendingItem.invoice, + invoice_description: pendingItem.invoice_description, + npub: pendingItem.npub, + profile_name: pendingItem.profile_name + } as PendingNwcInvoice); + } + return newPending; +} + +/** + * Deletes a nostr wallet connect profile + * @param {number} profile_index + * @returns {Promise} + */ +export async function delete_nwc_profile(index: number): Promise { + await wallet!.delete_nwc_profile(index); +} + +/** + * Get nostr wallet connect profiles + * @returns {(NwcProfile)[]} + */ +export async function get_nwc_profiles(): Promise { + const profiles = await wallet!.get_nwc_profiles(); + + // PAIN + // Have to rebuild the array because it's an array of pointers + const newProfiles: NwcProfile[] = []; + for (const profile of profiles) { + newProfiles.push({ + ...profile.value + } as NwcProfile); + } + return newProfiles; +} + +/** + * Approves a nostr wallet auth request. + * Creates a new NWC profile and saves to storage. + * This will also broadcast the info event to the relay. + * @param {string} name + * @param {string} uri + * @param {bigint} budget + * @param {BudgetPeriod} period + * @returns {Promise} + */ +export async function approve_nostr_wallet_auth( + name: string, + uri: string +): Promise { + const profile = await wallet!.approve_nostr_wallet_auth(name, uri); + return profile; +} + +/** + * Finds a nostr wallet connect profile by index + * @param {number} index + * @returns {Promise} + */ +export async function get_nwc_profile(index: number): Promise { + const profile = await wallet!.get_nwc_profile(index); + console.log("get_nwc_profile", profile); + return { + ...profile.value + } as NwcProfile; +} + +/** + * Approves an invoice and sends the payment + * @param {string} hash + * @returns {Promise} + */ +export async function approve_invoice(hash: string): Promise { + await wallet!.approve_invoice(hash); +} + +/** + * Removes an invoice from the pending list, will also remove expired invoices + * @param {string} hash + * @returns {Promise} + */ +export async function deny_invoice(hash: string): Promise { + await wallet!.deny_invoice(hash); +} + +/** + * Removes all invoices from the pending list + * @returns {Promise} + */ +export async function deny_all_pending_nwc(): Promise { + await wallet!.deny_all_pending_nwc(); +} + +/** + * Set budget for a NWC Profile + * @param {number} profile_index + * @param {bigint} budget_sats + * @param {BudgetPeriod} period + * @param {bigint | undefined} [single_max_sats] + * @returns {Promise} + */ +export async function set_nwc_profile_budget( + profile_index: number, + budget_sats: bigint, + period: BudgetPeriod, + single_max_sats?: bigint +): Promise { + const profile = await wallet!.set_nwc_profile_budget( + profile_index, + budget_sats, + period, + single_max_sats + ); + return profile; +} + +/** + * Require approval for a NWC Profile + * @param {number} profile_index + * @returns {Promise} + */ +export async function set_nwc_profile_require_approval( + profile_index: number +): Promise { + const profile = + await wallet!.set_nwc_profile_require_approval(profile_index); + return profile; +} + +/** + * Create a nostr wallet connect profile + * @param {string} name + * @param {(string)[] | undefined} [commands] + * @returns {Promise} + */ +export async function create_nwc_profile( + name: string, + commands?: string[] +): Promise { + const profile = await wallet!.create_nwc_profile(name, commands); + return profile; +} + +/** + * Create a budgeted nostr wallet connect profile + * @param {string} name + * @param {bigint} budget + * @param {BudgetPeriod} period + * @param {bigint | undefined} [single_max] + * @param {(string)[] | undefined} [commands] + * @returns {Promise} + */ +export async function create_budget_nwc_profile( + name: string, + budget: bigint, + period: BudgetPeriod, + single_max?: bigint, + commands?: string[] +): Promise { + const profile = await wallet!.create_budget_nwc_profile( + name, + budget, + period, + single_max, + commands + ); + return profile; +} + +/** + * Disconnects from a peer from the selected node. + * @param {string} peer + * @returns {Promise} + */ +export async function disconnect_peer(pubkey: string): Promise { + await wallet!.disconnect_peer(pubkey); +} + +/** + * Deletes a peer from the selected node. + * This will make it so that the node will not attempt to + * reconnect to the peer. + * @param {string} peer + * @returns {Promise} + */ +export async function delete_peer(pubkey: string): Promise { + await wallet!.delete_peer(pubkey); +} + +/** + * Lists all the peers for all the nodes in the node manager. + * @returns {Promise} + */ +export async function list_peers(): Promise { + return wallet!.list_peers(); +} + +/** + * Attempts to connect to a peer from the selected node. + * @param {string} connection_string + * @param {string | undefined} [label] + * @returns {Promise} + */ +export async function connect_to_peer( + connection_string: string +): Promise { + await wallet!.connect_to_peer(connection_string); +} + +/** + * Closes a channel with the given outpoint. + * + * If force is true, the channel will be force closed. + * + * If abandon is true, the channel will be abandoned. + * This will force close without broadcasting the latest transaction. + * This should only be used if the channel will never actually be opened. + * + * If both force and abandon are true, an error will be returned. + * @param {string} outpoint + * @param {boolean} force + * @param {boolean} abandon + * @returns {Promise} + */ +export async function close_channel( + outpoint: string, + force: boolean, + abandon: boolean +): Promise { + await wallet!.close_channel(outpoint, force, abandon); +} + +/** + * Removes a federation by setting its archived status to true, based on the FederationId. + * @param {string} federation_id + * @returns {Promise} + */ +export async function remove_federation(federation_id: string): Promise { + await wallet!.remove_federation(federation_id); +} + +/** + * Opens a channel from our selected node to the given pubkey. + * The amount is in satoshis. + * + * The node must be online and have a connection to the peer. + * The wallet much have enough funds to open the channel. + * @param {string | undefined} to_pubkey + * @param {bigint} amount + * @param {number | undefined} [fee_rate] + * @returns {Promise} + */ +export async function open_channel( + to_pubkey: string | undefined, + amount: bigint +): Promise { + const channel = await wallet!.open_channel(to_pubkey, amount); + return { ...channel.value } as MutinyChannel; +} + +/** + * Lists the pubkeys of the lightning node in the manager. + * @returns {Promise} + */ +export async function list_nodes(): Promise { + return await wallet!.list_nodes(); +} + +/** + * Changes all the node's LSPs to the given config. If any of the nodes have an active channel with the + * current LSP, it will fail to change the LSP. + * + * Requires a restart of the node manager to take effect. + * @param {string | undefined} [lsp_url] + * @param {string | undefined} [lsp_connection_string] + * @param {string | undefined} [lsp_token] + * @returns {Promise} + */ +export async function change_lsp( + lsp_url?: string, + lsp_connection_string?: string, + lsps_token?: string +): Promise { + await wallet!.change_lsp(lsp_url, lsp_connection_string, lsps_token); +} + +/** + * Resets BDK's keychain tracker. This will require a re-sync of the blockchain. + * + * This can be useful if you get stuck in a bad state. + * @returns {Promise} + */ +export async function reset_onchain_tracker(): Promise { + await wallet!.reset_onchain_tracker(); +} + +/** + * Starts up all the nodes again. + * Not needed after [NodeManager]'s `new()` function. + * @returns {Promise} + */ +export async function start(): Promise { + await wallet!.start(); +} + +/** + * Authenticates with a LNURL-auth for the given profile. + * @param {string} lnurl + * @returns {Promise} + */ +export async function lnurl_auth(lnurl: string): Promise { + // TODO: test auth + await wallet!.lnurl_auth(lnurl); +} + +type PlanDetails = { + id: number; + amount_sat: bigint; +}; + +/** + * Gets the subscription plans for Mutiny+ subscriptions + * @returns {Promise} + */ +export async function get_subscription_plans(): Promise { + return await wallet!.get_subscription_plans(); +} + +/** + * Subscribes to a Mutiny+ plan with a specific plan id. + * + * Returns a lightning invoice so that the plan can be paid for to start it. + * @param {number} id + * @returns {Promise} + */ +export async function subscribe_to_plan(id: number): Promise { + const invoice = await wallet!.subscribe_to_plan(id); + return destructureInvoice(invoice); +} + +/** + * Pay the subscription invoice. This will post a NWC automatically afterwards. + * @param {string} invoice_str + * @param {boolean} autopay + * @returns {Promise} + */ +export async function pay_subscription_invoice( + invoice_str: string, + autopay: boolean +): Promise { + await wallet!.pay_subscription_invoice(invoice_str, autopay); +} + +/** + * Change our active nostr keys to the given nsec + * @param {string | undefined} [nsec] + * @param {string | undefined} [extension_pk] + * @returns {Promise} + */ +export async function change_nostr_keys( + nsec?: string, + extension_pk?: string +): Promise { + return await wallet!.change_nostr_keys(nsec, extension_pk); +} + +/** + * Sets the user's nostr profile data to a "deleted" state + * @returns {Promise} + */ +export async function delete_profile(): Promise { + await wallet!.delete_profile(); +} + +/** + * Export the user's nostr secret key if available + * @returns {Promise} + */ +export async function export_nsec(): Promise { + // TODO: is this the right nsec? + return await wallet!.export_nsec(); +} + +/** + * Returns the mnemonic seed phrase for the wallet. + * @returns {string} + */ +export async function show_seed(): Promise { + return await wallet!.show_seed(); +} + +/** + * Create a single use nostr wallet connect profile + * @param {bigint} amount_sats + * @param {string} nwc_uri + * @returns {Promise} + */ +export async function claim_single_use_nwc( + amount_sats: bigint, + nwc_uri: string +): Promise { + return await wallet!.claim_single_use_nwc(amount_sats, nwc_uri); +} + +/** + * Calls upon a LNURL and withdraws from it. + * This will fail if the LNURL is not a LNURL withdrawal. + * @param {string} lnurl + * @param {bigint} amount_sats + * @returns {Promise} + */ +export async function lnurl_withdraw( + lnurl: string, + amount_sats: bigint +): Promise { + return await wallet!.lnurl_withdraw(lnurl, amount_sats); +} + +/** + * Checks the registered username for the user + * @returns {Promise} + */ +export async function check_lnurl_name(): Promise { + return await wallet!.check_lnurl_name(); +} + +/** + * Checks if a given LNURL name is available + * @param {string} name + * @returns {Promise} + */ +export async function check_available_lnurl_name( + name: string +): Promise { + return await wallet!.check_available_lnurl_name(name); +} + +/** + * Reserves a given LNURL name for the user + * @param {string} name + * @returns {Promise} + */ +export async function reserve_lnurl_name(name: string): Promise { + return await wallet!.reserve_lnurl_name(name); +} + +/** + * Creates a recommendation event for a federation + * @param {string} invite_code + * @param {string | undefined} [review] + * @returns {Promise} + */ +export async function recommend_federation( + invite_code: string, + review?: string +): Promise { + return await wallet!.recommend_federation(invite_code, review); +} + +/** + * Creates a delete event for a federation recommendation + * @param {string} federation_id + * @returns {Promise} + */ +export async function delete_federation_recommendation( + federation_id: string +): Promise { + await wallet!.delete_federation_recommendation(federation_id); +} + +/** + * Gets the current balances of each federation. + * @returns {Promise} + */ +export async function get_federation_balances(): Promise { + const balances = await wallet!.get_federation_balances(); + // PAIN + // Have to rebuild the balances from the raw data, which is a bit of a pain + const newBalances: FederationBalance[] = []; + for (const balance of balances.balances) { + const newBalance: FederationBalance = { + balance: balance.balance, + identity_federation_id: balance.identity_federation_id, + identity_uuid: balance.identity_uuid + } as FederationBalance; + newBalances.push(newBalance); + } + return { + balances: newBalances + } as FederationBalances; +} + +export async function change_password( + old_password?: string, + new_password?: string +): Promise { + await wallet!.change_password(old_password, new_password); +} + +/** + * Converts a satoshi amount to BTC. + * @param {bigint} sats + * @returns {number} + */ +export async function convert_sats_to_btc(sats: bigint): Promise { + return await MutinyWallet.convert_sats_to_btc(sats); +} + +/** + * Converts a bitcoin amount in BTC to satoshis. + * @param {number} btc + * @returns {bigint} + */ +export async function convert_btc_to_sats(btc: number): Promise { + return await MutinyWallet.convert_btc_to_sats(btc); +} + +/** + * Returns if there is a saved wallet in storage. + * This is checked by seeing if a mnemonic seed exists in storage. + * @returns {Promise} + */ +export async function has_node_manager(): Promise { + return await MutinyWallet.has_node_manager(); +} + +/** + * Convert an npub string to a hex string + * @param {string} npub + * @returns {Promise} + */ +export async function npub_to_hexpub(npub: string): Promise { + return await MutinyWallet.npub_to_hexpub(npub); +} + +/** + * Convert an npub string to a hex string + * @param {string} nsec + * @returns {Promise} + */ +export async function nsec_to_npub(nsec: string): Promise { + return await MutinyWallet.nsec_to_npub(nsec); +} + +/** + * Convert an hex string to a npub string + * @param {string} npub + * @returns {Promise} + */ +export async function hexpub_to_npub(hexpub: string): Promise { + // TODO: the argument is called "npub" but it's actually a hexpub? + return await MutinyWallet.hexpub_to_npub(hexpub); +} + +/** + * Restore's the mnemonic after deleting the previous state. + * + * Backup the state beforehand. Does not restore lightning data. + * Should refresh or restart afterwards. Wallet should be stopped. + * @param {string} m + * @param {string | undefined} [password] + * @returns {Promise} + */ +export async function restore_mnemonic( + mnemonic: string, + password?: string +): Promise { + await MutinyWallet.restore_mnemonic(mnemonic, password); +} + +/** + * Restore a node manager from a json object. + * @param {string} json + * @returns {Promise} + */ +export async function import_json(json: string): Promise { + await MutinyWallet.import_json(json); +} + +/** + * Exports the current state of the node manager to a json object. + * @param {string | undefined} [password] + * @returns {Promise} + */ +export async function export_json(password?: string): Promise { + return await MutinyWallet.export_json(password); +} + +/** + * Exports the current state of the node manager to a json object. + * @returns {Promise} + */ +export async function get_logs(): Promise { + return await MutinyWallet.get_logs(); +} + +/** + * Returns the number of remaining seconds until the device lock expires. + */ +export async function get_device_lock_remaining_secs( + password?: string, + auth_url?: string, + storage_url?: string +): Promise { + return await MutinyWallet.get_device_lock_remaining_secs( + password, + auth_url, + storage_url + ); +} + +/** + * Opens a channel from our selected node to the given pubkey. + * It will spend the all the on-chain utxo in full to fund the channel. + * + * The node must be online and have a connection to the peer. + * @param {string | undefined} [to_pubkey] + * @returns {Promise} + */ +export async function sweep_all_to_channel( + to_pubkey?: string +): Promise { + return await wallet!.sweep_all_to_channel(to_pubkey); +} + +/** + * Estimates the onchain fee for sweeping our on-chain balance to open a lightning channel. + * The fee rate is in sat/vbyte. + * @param {number | undefined} [fee_rate] + * @returns {bigint} + */ +export async function estimate_sweep_channel_open_fee( + fee_rate?: number | undefined +): Promise { + return await wallet!.estimate_sweep_channel_open_fee(fee_rate); +} + +/** + * Sweep the federation balance into a lightning channel + * @param {bigint | undefined} [amount] + * @returns {Promise} + */ +export async function sweep_federation_balance( + amount?: bigint +): Promise { + const result = await wallet!.sweep_federation_balance(amount); + return { ...result.value } as FedimintSweepResult; +} + +/** + * Estimate the fee before trying to sweep from federation + * @param {bigint | undefined} [amount] + * @returns {Promise} + */ +export async function estimate_sweep_federation_fee( + amount?: bigint +): Promise { + return await wallet!.estimate_sweep_federation_fee(amount); +} + +export async function parse_params(params: string): Promise { + const paramsResult = await new PaymentParams(params); + // PAIN just another object rebuild + return { + address: paramsResult.address, + amount_msats: paramsResult.amount_msats, + amount_sats: paramsResult.amount_sats, + cashu_token: paramsResult.cashu_token, + disable_output_substitution: paramsResult.disable_output_substitution, + fedimint_invite_code: paramsResult.fedimint_invite_code, + fedimint_oob_notes: paramsResult.fedimint_oob_notes, + invoice: paramsResult.invoice, + is_lnurl_auth: paramsResult.is_lnurl_auth, + lightning_address: paramsResult.lightning_address, + lnurl: paramsResult.lnurl, + memo: paramsResult.memo, + network: paramsResult.network, + node_pubkey: paramsResult.node_pubkey, + nostr_pubkey: paramsResult.nostr_pubkey, + nostr_wallet_auth: paramsResult.nostr_wallet_auth, + offer: paramsResult.offer, + payjoin_endpoint: paramsResult.payjoin_endpoint, + payjoin_supported: paramsResult.payjoin_supported, + refund: paramsResult.refund, + string: paramsResult.string + } as PaymentParams; +} diff --git a/vite.config.ts b/vite.config.ts index 0ec332d..4ebd3b1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,7 @@ import child from "node:child_process"; import path from "node:path"; import { defineConfig } from "vite"; +import { comlink } from "vite-plugin-comlink"; import { VitePWA, VitePWAOptions } from "vite-plugin-pwa"; import solid from "vite-plugin-solid"; import wasm from "vite-plugin-wasm"; @@ -40,7 +41,11 @@ export default defineConfig({ allow: [".."] } }, - plugins: [wasm(), solid(), VitePWA(pwaOptions)], + plugins: [comlink(), wasm(), solid(), VitePWA(pwaOptions)], + worker: { + plugins: () => [comlink(), wasm()], + format: "es" + }, define: { "import.meta.env.__COMMIT_HASH__": JSON.stringify(commitHash), "import.meta.env.__RELEASE_VERSION__": JSON.stringify( @@ -69,6 +74,9 @@ export default defineConfig({ "@capacitor/toast" ], // This is necessary because otherwise `vite dev` can't find the wasm - exclude: ["@mutinywallet/mutiny-wasm"] + exclude: ["@mutinywallet/mutiny-wasm"], + esbuildOptions: { + target: "esnext" + } } });