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" + } } });