From 85b541402a3f392d199419f80fcfb465d05e20be Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 26 Jun 2025 16:14:15 -0700 Subject: add intents --- android/app/src/main/AndroidManifest.xml | 82 +++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 28 deletions(-) (limited to 'android') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 86de46b2..97c0e655 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,34 +7,60 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Thu, 26 Jun 2025 16:46:39 -0700 Subject: v1.0.7 --- android/app/build.gradle | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'android') diff --git a/android/app/build.gradle b/android/app/build.gradle index 88077520..4d3dacf9 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "io.coinos" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 6 - versionName "1.0.6" + versionCode 7 + versionName "1.0.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { @@ -24,9 +24,9 @@ android { signingConfigs { release { keyAlias = System.getenv("KEY_ALIAS") ?: "coinos" - keyPassword = System.getenv("KEY_PASSWORD") ?: "default-password" + keyPassword = System.getenv("KEY_PASSWORD") ?: "MPJzfq97" storeFile = file(System.getenv("STORE_FILE") ?: "coinos.keystore") - storePassword = System.getenv("STORE_PASSWORD") ?: "default-password" + storePassword = System.getenv("STORE_PASSWORD") ?: "MPJzfq97" } } diff --git a/package.json b/package.json index 6d0e2474..cec2b275 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "coinos", - "version": "1.0.4", + "version": "1.0.7", "devDependencies": { "@capacitor/android": "^6.2.0", "@capacitor/assets": "^3.0.5", -- cgit v1.2.3 From 8af38f7f4736beadf23b4a2c68b0b918a6cfeb0d Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 6 Jul 2025 23:10:37 -0700 Subject: nfc intents --- android/app/build.gradle | 4 +- android/app/src/main/AndroidManifest.xml | 119 +++++++------ .../app/src/main/java/io/coinos/MainActivity.java | 195 +++++++++++++-------- package.json | 2 +- 4 files changed, 198 insertions(+), 122 deletions(-) (limited to 'android') diff --git a/android/app/build.gradle b/android/app/build.gradle index 4d3dacf9..e419e70f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "io.coinos" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7 - versionName "1.0.7" + versionCode 8 + versionName "1.0.8" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 97c0e655..72d7ccc0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,60 +7,80 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - - - - - - - + + + + + + + - - - - - - - + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + - - - - - - - - + + + + + + + + - diff --git a/android/app/src/main/java/io/coinos/MainActivity.java b/android/app/src/main/java/io/coinos/MainActivity.java index 7f710952..aaf59abe 100644 --- a/android/app/src/main/java/io/coinos/MainActivity.java +++ b/android/app/src/main/java/io/coinos/MainActivity.java @@ -2,80 +2,137 @@ package io.coinos; import com.getcapacitor.BridgeActivity; import com.getcapacitor.Bridge; -import com.getcapacitor.PluginCall; -import com.getcapacitor.PluginHandle; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.Parcelable; import android.webkit.CookieManager; import android.webkit.WebView; +import android.nfc.NdefMessage; +import android.nfc.NdefRecord; +import android.nfc.NfcAdapter; + +import java.nio.charset.Charset; +import java.util.Arrays; + public class MainActivity extends BridgeActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - WebView webView = bridge.getWebView(); - CookieManager cookieManager = CookieManager.getInstance(); - cookieManager.setAcceptCookie(true); - webView.getSettings().setJavaScriptEnabled(true); - webView.getSettings().setDomStorageEnabled(true); - - Intent intent = getIntent(); - if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { - Uri data = intent.getData(); - if (data != null) { - String url = data.toString(); - Bridge bridge = getBridge(); - - if (bridge != null) { - String encodedUrl = Uri.encode(url); - String destinationUrl = "https://coinos.io/send/" + encodedUrl; - - bridge.getWebView().post(() -> { - bridge.getWebView().loadUrl(destinationUrl); - }); - } - } - } - } - - @Override - public void onNewIntent(Intent intent) { - super.onNewIntent(intent); - setIntent(intent); - if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { - Uri data = intent.getData(); - if (data != null) { - String url = data.toString(); - - // Dispatch deep link when app is already open - Bridge bridge = getBridge(); - if (bridge != null) { - String encodedUrl = Uri.encode(url); - String destinationUrl = "https://coinos.io/send/" + encodedUrl; - - bridge.getWebView().post(() -> { - bridge.getWebView().loadUrl(destinationUrl); - }); - } - } - } - } - - @Override - public void onPause() { - super.onPause(); - CookieManager.getInstance().flush(); - } - - @Override - public void onBackPressed() { - WebView webView = getBridge().getWebView(); - if (webView.canGoBack()) { - webView.goBack(); - } else { - super.onBackPressed(); // Default behavior, which minimizes the app - } - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + WebView webView = bridge.getWebView(); + CookieManager cookieManager = CookieManager.getInstance(); + cookieManager.setAcceptCookie(true); + webView.getSettings().setJavaScriptEnabled(true); + webView.getSettings().setDomStorageEnabled(true); + + handleIntent(getIntent()); + } + + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + setIntent(intent); + handleIntent(intent); + } + + private void handleIntent(Intent intent) { + if (intent == null) return; + + String action = intent.getAction(); + Uri data = null; + + if (Intent.ACTION_VIEW.equals(action)) { + data = intent.getData(); + } else if ("android.nfc.action.NDEF_DISCOVERED".equals(action)) { + Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); + if (rawMsgs != null && rawMsgs.length > 0) { + NdefMessage ndefMessage = (NdefMessage) rawMsgs[0]; + for (NdefRecord record : ndefMessage.getRecords()) { + if (record.getTnf() == NdefRecord.TNF_WELL_KNOWN && + Arrays.equals(record.getType(), NdefRecord.RTD_URI)) { + + byte[] payload = record.getPayload(); + byte prefix = payload[0]; + String uriPrefix = resolveUriPrefix(prefix); + String uriBody = new String(Arrays.copyOfRange(payload, 1, payload.length), Charset.forName("UTF-8")); + data = Uri.parse(uriPrefix + uriBody); + break; + } + } + } + } + + if (data != null) { + String url = data.toString(); + Bridge bridge = getBridge(); + if (bridge != null) { + String encodedUrl = Uri.encode(url); + String destinationUrl = "https://coinos.io/send/" + encodedUrl; + + bridge.getWebView().post(() -> { + bridge.getWebView().loadUrl(destinationUrl); + }); + } + } + } + + private String resolveUriPrefix(byte prefix) { + switch (prefix) { + case 0x00: return ""; + case 0x01: return "http://www."; + case 0x02: return "https://www."; + case 0x03: return "http://"; + case 0x04: return "https://"; + case 0x05: return "tel:"; + case 0x06: return "mailto:"; + case 0x07: return "ftp://anonymous:anonymous@"; + case 0x08: return "ftp://ftp."; + case 0x09: return "ftps://"; + case 0x0A: return "sftp://"; + case 0x0B: return "smb://"; + case 0x0C: return "nfs://"; + case 0x0D: return "ftp://"; + case 0x0E: return "dav://"; + case 0x0F: return "news:"; + case 0x10: return "telnet://"; + case 0x11: return "imap:"; + case 0x12: return "rtsp://"; + case 0x13: return "urn:"; + case 0x14: return "pop:"; + case 0x15: return "sip:"; + case 0x16: return "sips:"; + case 0x17: return "tftp:"; + case 0x18: return "btspp://"; + case 0x19: return "btl2cap://"; + case 0x1A: return "btgoep://"; + case 0x1B: return "tcpobex://"; + case 0x1C: return "irdaobex://"; + case 0x1D: return "file://"; + case 0x1E: return "urn:epc:id:"; + case 0x1F: return "urn:epc:tag:"; + case 0x20: return "urn:epc:pat:"; + case 0x21: return "urn:epc:raw:"; + case 0x22: return "urn:epc:"; + case 0x23: return "urn:nfc:"; + default: return ""; + } + } + + @Override + public void onPause() { + super.onPause(); + CookieManager.getInstance().flush(); + } + + @Override + public void onBackPressed() { + WebView webView = getBridge().getWebView(); + if (webView.canGoBack()) { + webView.goBack(); + } else { + super.onBackPressed(); + } + } } diff --git a/package.json b/package.json index cec2b275..e7410f23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "coinos", - "version": "1.0.7", + "version": "1.0.8", "devDependencies": { "@capacitor/android": "^6.2.0", "@capacitor/assets": "^3.0.5", -- cgit v1.2.3 From 1b1718a4b64275bac8e1fe519d33c67e3a95f2c5 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 7 Jul 2025 07:39:35 -0700 Subject: v1.0.9 --- android/app/build.gradle | 4 ++-- android/variables.gradle | 6 +++--- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'android') diff --git a/android/app/build.gradle b/android/app/build.gradle index e419e70f..212a6987 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "io.coinos" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 8 - versionName "1.0.8" + versionCode 9 + versionName "1.0.9" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { diff --git a/android/variables.gradle b/android/variables.gradle index 8ef305d0..b0e92003 100644 --- a/android/variables.gradle +++ b/android/variables.gradle @@ -1,7 +1,7 @@ ext { minSdkVersion = 22 - compileSdkVersion = 34 - targetSdkVersion = 34 + compileSdkVersion = 35 + targetSdkVersion = 35 androidxActivityVersion = '1.8.0' androidxAppCompatVersion = '1.6.1' androidxCoordinatorLayoutVersion = '1.2.0' @@ -13,4 +13,4 @@ ext { androidxJunitVersion = '1.1.5' androidxEspressoCoreVersion = '3.5.1' cordovaAndroidVersion = '10.1.1' -} \ No newline at end of file +} diff --git a/package.json b/package.json index e7410f23..685cb7bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "coinos", - "version": "1.0.8", + "version": "1.0.9", "devDependencies": { "@capacitor/android": "^6.2.0", "@capacitor/assets": "^3.0.5", -- cgit v1.2.3 From c0efcd94f37793348bc0cbe99a47e5fe8df4282a Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 8 Jul 2025 03:05:47 -0700 Subject: inset --- .../app/src/main/java/io/coinos/MainActivity.java | 17 ++++++++++++++++ capacitor.config.json | 23 +++++++++++----------- src/routes/(static)/register/+page.server.ts | 1 - src/routes/+layout.svelte | 11 +++++++++++ 4 files changed, 40 insertions(+), 12 deletions(-) (limited to 'android') diff --git a/android/app/src/main/java/io/coinos/MainActivity.java b/android/app/src/main/java/io/coinos/MainActivity.java index aaf59abe..791c89a9 100644 --- a/android/app/src/main/java/io/coinos/MainActivity.java +++ b/android/app/src/main/java/io/coinos/MainActivity.java @@ -1,5 +1,11 @@ package io.coinos; +import android.os.Build; +import android.view.View; +import android.view.WindowInsets; +import android.view.WindowManager; +import androidx.core.view.ViewCompat; + import com.getcapacitor.BridgeActivity; import com.getcapacitor.Bridge; import android.content.Intent; @@ -21,12 +27,23 @@ public class MainActivity extends BridgeActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // Ensure the WebView doesn't overlap the system status bar + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + } + WebView webView = bridge.getWebView(); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setDomStorageEnabled(true); + // Ensure layout respects system window insets + ViewCompat.setOnApplyWindowInsetsListener(webView, (v, insets) -> { + v.setPadding(0, insets.getSystemWindowInsetTop(), 0, 0); + return insets; + }); + handleIntent(getIntent()); } diff --git a/capacitor.config.json b/capacitor.config.json index 585ff21c..a31c19db 100644 --- a/capacitor.config.json +++ b/capacitor.config.json @@ -1,13 +1,14 @@ { - "appId": "io.coinos", - "appName": "Coinos", - "server": { - "hostname": "coinos.io", - "url": "https://coinos.io/start" - }, - "android": { - "webContentsDebuggingEnabled": true, - "allowMixedContent": true, - "useBrowserHistory": true - } + "appId": "io.coinos", + "appName": "Coinos", + "server": { + "hostname": "coinos.io", + "url": "https://coinos.io/start" + }, + "android": { + "webContentsDebuggingEnabled": true, + "allowMixedContent": true, + "useBrowserHistory": true, + "immersive": false + } } diff --git a/src/routes/(static)/register/+page.server.ts b/src/routes/(static)/register/+page.server.ts index 8c6d6a8d..44aa69d3 100644 --- a/src/routes/(static)/register/+page.server.ts +++ b/src/routes/(static)/register/+page.server.ts @@ -21,6 +21,5 @@ export const actions = { const user = { picture, username, password }; await register(user, ip, cookies, loginRedirect); - redirect(303, loginRedirect || `/${user.username}`); }, }; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index d25f570a..f2c567fa 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -61,4 +61,15 @@ --toastContainerLeft: calc(50vw - 8rem); --toastBackground: #292929; } + + main { + padding-top: constant(safe-area-inset-top); + padding-top: env(safe-area-inset-top); + } + + @supports not (padding-top: env(safe-area-inset-top)) { + main { + padding-top: 24px; + } + } -- cgit v1.2.3 From 503fa32f2453d76ed34d4d15f3321c981fc8f952 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 8 Jul 2025 03:35:34 -0700 Subject: inset --- .../app/src/main/java/io/coinos/MainActivity.java | 23 ++++++++----- android/app/src/main/res/layout/activity_main.xml | 13 ++++---- android/app/src/main/res/values/styles.xml | 13 +++++--- bun.lockb | Bin 289631 -> 290051 bytes package.json | 5 ++- src/routes/+layout.svelte | 37 +++++++++------------ 6 files changed, 50 insertions(+), 41 deletions(-) (limited to 'android') diff --git a/android/app/src/main/java/io/coinos/MainActivity.java b/android/app/src/main/java/io/coinos/MainActivity.java index 791c89a9..501d2e0f 100644 --- a/android/app/src/main/java/io/coinos/MainActivity.java +++ b/android/app/src/main/java/io/coinos/MainActivity.java @@ -8,6 +8,7 @@ import androidx.core.view.ViewCompat; import com.getcapacitor.BridgeActivity; import com.getcapacitor.Bridge; + import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -25,25 +26,31 @@ import java.util.Arrays; public class MainActivity extends BridgeActivity { @Override protected void onCreate(Bundle savedInstanceState) { + // ✅ Let Capacitor inflate its layout and wire everything super.onCreate(savedInstanceState); - // Ensure the WebView doesn't overlap the system status bar + WebView webView = bridge.getWebView(); // This works *after* super.onCreate() + + // ✅ Ensure layout respects system bars if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); } - WebView webView = bridge.getWebView(); + // ✅ Apply top inset padding if needed + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + webView.setOnApplyWindowInsetsListener((v, insets) -> { + v.setPadding(0, insets.getSystemWindowInsetTop(), 0, 0); + return insets.consumeSystemWindowInsets(); + }); + } + + // Other app setup CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setDomStorageEnabled(true); - // Ensure layout respects system window insets - ViewCompat.setOnApplyWindowInsetsListener(webView, (v, insets) -> { - v.setPadding(0, insets.getSystemWindowInsetTop(), 0, 0); - return insets; - }); - handleIntent(getIntent()); } diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml index b5ad1387..3710e23b 100644 --- a/android/app/src/main/res/layout/activity_main.xml +++ b/android/app/src/main/res/layout/activity_main.xml @@ -1,12 +1,13 @@ - + android:fitsSystemWindows="true"> - + android:layout_height="match_parent" + android:fitsSystemWindows="true" /> + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index be874e54..07128ac1 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,9 +1,7 @@ - - + - \ No newline at end of file + + diff --git a/bun.lockb b/bun.lockb index bcf4f106..2a601932 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 685cb7bb..a2241d3c 100644 --- a/package.json +++ b/package.json @@ -68,5 +68,8 @@ "lint": "prettier --check .", "format": "prettier --write ." }, - "type": "module" + "type": "module", + "dependencies": { + "cordova-plugin-android-notch": "^1.0.3" + } } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 76db8abe..4317a6a2 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -16,16 +16,6 @@ ? `http://${PUBLIC_DOMAIN}` : `https://${PUBLIC_DOMAIN}`; - function getSafeInsetTop() { - const div = document.createElement("div"); - div.style.cssText = "position:absolute;top:0;padding-top:env(safe-area-inset-top);"; - document.body.appendChild(div); - const computed = getComputedStyle(div).paddingTop; - document.body.removeChild(div); - const px = parseInt(computed, 10); - return isNaN(px) ? 24 : px; - } - onMount(() => { if (!browser) return; @@ -35,11 +25,18 @@ $installPrompt = event; }); - // Fix: Apply top padding manually for Android WebView - const insetTop = getSafeInsetTop(); - const main = document.querySelector("main"); - if (main && insetTop > 0) { - main.style.paddingTop = `${insetTop}px`; + if (typeof window.AndroidNotch !== "undefined") { + window.AndroidNotch.getInsetTop( + (px) => { + document.documentElement.style.setProperty( + "--safe-area-inset-top", + px + "px", + ); + }, + (err) => { + console.error("Failed to get notch inset:", err); + }, + ); } }); @@ -75,15 +72,11 @@ --toastContainerBottom: 8rem; --toastContainerLeft: calc(50vw - 8rem); --toastBackground: #292929; - } - main { - padding-top: env(safe-area-inset-top, 24px); + --safe-area-inset-top: 24px; } - @supports not (padding-top: env(safe-area-inset-top)) { - main { - padding-top: 24px; - } + main { + padding-top: var(--safe-area-inset-top); } -- cgit v1.2.3 From 5b458624745c7ea80d1e63c9a6ef77075763ff5f Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 10 Jul 2025 12:36:50 -0700 Subject: user onboarding --- .../app/src/main/java/io/coinos/MainActivity.java | 5 ++ .../java/io/coinos/nfcwriter/NfcWriterPlugin.java | 62 ++++++++++++++++++++++ capacitor.config.json | 7 ++- src/lib/utils.ts | 2 +- src/locales/en.json | 7 ++- src/routes/(app)/+layout.svelte | 45 ++++++++++------ src/routes/(app)/[username]/(profile)/+page.svelte | 22 +++++++- src/routes/(app)/fund/[id]/sweep/+page.server.ts | 10 ++-- src/routes/(app)/fund/[id]/sweep/+page.svelte | 6 ++- src/routes/(app)/funder/+page.svelte | 55 +++++++++++++++++++ src/routes/(app)/invoice/[id]/tip/+page.server.ts | 28 +++++----- src/routes/(app)/settings/profile/+page.svelte | 16 +++++- src/routes/(app)/settings/security/+page.svelte | 12 ----- 13 files changed, 225 insertions(+), 52 deletions(-) create mode 100644 android/app/src/main/java/io/coinos/nfcwriter/NfcWriterPlugin.java create mode 100644 src/routes/(app)/funder/+page.svelte (limited to 'android') diff --git a/android/app/src/main/java/io/coinos/MainActivity.java b/android/app/src/main/java/io/coinos/MainActivity.java index 501d2e0f..271ee363 100644 --- a/android/app/src/main/java/io/coinos/MainActivity.java +++ b/android/app/src/main/java/io/coinos/MainActivity.java @@ -23,11 +23,16 @@ import android.nfc.NfcAdapter; import java.nio.charset.Charset; import java.util.Arrays; +import io.coinos.nfcwriter.NfcWriterPlugin; + public class MainActivity extends BridgeActivity { + private NfcAdapter nfcAdapter; + @Override protected void onCreate(Bundle savedInstanceState) { // ✅ Let Capacitor inflate its layout and wire everything super.onCreate(savedInstanceState); + registerPlugin(NfcWriterPlugin.class); WebView webView = bridge.getWebView(); // This works *after* super.onCreate() diff --git a/android/app/src/main/java/io/coinos/nfcwriter/NfcWriterPlugin.java b/android/app/src/main/java/io/coinos/nfcwriter/NfcWriterPlugin.java new file mode 100644 index 00000000..d2954e9b --- /dev/null +++ b/android/app/src/main/java/io/coinos/nfcwriter/NfcWriterPlugin.java @@ -0,0 +1,62 @@ +// src/android/NfcWriterPlugin.java +package io.coinos.nfcwriter; + +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.NfcAdapter.ReaderCallback; +import android.os.Bundle; +import android.util.Log; + +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; + +@CapacitorPlugin(name = "NfcWriter") +public class NfcWriterPlugin extends Plugin { + private NfcAdapter nfcAdapter; + + @Override + public void load() { + super.load(); + nfcAdapter = NfcAdapter.getDefaultAdapter(getContext()); + } + + @PluginMethod + public void enableExclusiveNfcMode(PluginCall call) { + if (nfcAdapter == null) { + call.reject("NFC adapter not available"); + return; + } + + nfcAdapter.enableReaderMode( + getActivity(), + new ReaderCallback() { + @Override + public void onTagDiscovered(Tag tag) { + Log.d("NfcWriter", "Tag discovered"); + bridge.getWebView().post(() -> { + bridge.getWebView().evaluateJavascript( + "window.dispatchEvent(new CustomEvent('nativeNfcTagDiscovered'))", + null + ); + }); + } + }, + NfcAdapter.FLAG_READER_NFC_A | + NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK | + NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS, + null + ); + + call.resolve(); + } + + @PluginMethod + public void disableExclusiveNfcMode(PluginCall call) { + if (nfcAdapter != null) { + nfcAdapter.disableReaderMode(getActivity()); + } + call.resolve(); + } +} diff --git a/capacitor.config.json b/capacitor.config.json index a31c19db..99458def 100644 --- a/capacitor.config.json +++ b/capacitor.config.json @@ -2,13 +2,16 @@ "appId": "io.coinos", "appName": "Coinos", "server": { - "hostname": "coinos.io", - "url": "https://coinos.io/start" + "hostname": "dev.coinos.io", + "url": "https://dev.coinos.io/start" }, "android": { "webContentsDebuggingEnabled": true, "allowMixedContent": true, "useBrowserHistory": true, "immersive": false + }, + "plugins": { + "NfcWriter": {} } } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 31d93fde..28a219e1 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -486,5 +486,5 @@ export const register = async (user, ip, cookies, loginRedirect) => { }); if (error) return svelteFail(400, { error }); - redirect(303, loginRedirect || `/${user.username}`); + redirect(303, loginRedirect || `/${user.username}`); }; diff --git a/src/locales/en.json b/src/locales/en.json index 922d24b3..88de69da 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -350,7 +350,12 @@ "locale": "Language", "username": "Username", "email": "Email", - "newPassword": "Change Password", + "newPassword": "New Password", + "newPasswordDesc": "(Leave blank to keep unchanged)", + "yourUsername": "Your username is", + "yourPassword": "and your password is", + "welcome": "Welcome!", + "clickBelow": "Click below to finish setting up your account", "about": "About", "aboutPlaceholder": "A little something about you or your business address and hours", "emailNotifications": "Email Notifications", diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index af4dbf2c..830e5291 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -46,22 +46,35 @@ checkSocket(); $pin = getCookie("pin"); - if (window.NDEFReader && user.nfc) { - try { - $ndef = new NDEFReader(); - await $ndef.scan(); - - $ndef.addEventListener("readingerror", (e) => { - console.log("nfc error", e); - }); - - $ndef.addEventListener("reading", ({ message, serialNumber }) => { - goto(`/send/${message}`); - }); - } catch (error) { - console.log("Argh! " + error); - } - } + // if (window.NDEFReader) { + // try { + // $ndef = new NDEFReader(); + // await $ndef.scan(); + // + // $ndef.addEventListener("readingerror", (e) => { + // console.log("nfc error", e); + // }); + // + // $ndef.addEventListener("reading", ({ message, serialNumber }) => { + // console.log("MESSAGE", message); + // + // const record = message.records[0]; + // + // if (record.recordType === "url") { + // const textDecoder = new TextDecoder(); + // const url = textDecoder.decode(record.data); + // console.log("URL:", url); + // + // // Navigate to the URL or use it however you want + // goto(`/send/${encodeURIComponent(url)}`); + // } else { + // console.error("Unsupported record type:", record.recordType); + // } + // }); + // } catch (error) { + // console.log("Argh! " + error); + // } + // } } }); diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index d064c3a9..31320197 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -6,7 +6,7 @@ import Account from "$comp/Account.svelte"; import Balance from "$comp/Balance.svelte"; import { t } from "$lib/translations"; - import { installPrompt } from "$lib/store"; + import { installPrompt, password } from "$lib/store"; import { afterNavigate, preloadData } from "$app/navigation"; import { page } from "$app/stores"; @@ -32,6 +32,24 @@
+ {#if user?.fresh} +
+

{$t("user.settings.welcome")}

+
+ {$t("user.settings.yourUsername")} {user?.username} + {#if $password} + {$t("user.settings.yourPassword")} + {$password}{/if} +
+ +
{$t("user.settings.clickBelow")}
+ + + + Settings +
+ {/if} {#if user?.id && user.id === subject.id}
{#each accounts as account, i} @@ -55,6 +73,8 @@ {/if}
+ +
{#if user?.username !== subject.username && (!subject.anon || subject.lud16)} + import { onMount } from "svelte"; + import { password } from "$lib/store"; import Amount from "$comp/Amount.svelte"; import Success from "$comp/Success.svelte"; import { scale } from "svelte/transition"; @@ -9,9 +11,11 @@ let { data } = $props(); - let { amount, id, user, rate, rates } = data; + let { amount, id, user, rate, rates, password: pw } = data; let { currency } = user; let locale = $derived(loc(user)); + + onMount(() => ($password = pw));
diff --git a/src/routes/(app)/funder/+page.svelte b/src/routes/(app)/funder/+page.svelte new file mode 100644 index 00000000..e6549360 --- /dev/null +++ b/src/routes/(app)/funder/+page.svelte @@ -0,0 +1,55 @@ + + +HI + +{#if here}HERE{/if} +{#if ready}go{/if} +{#if success}done{/if} diff --git a/src/routes/(app)/invoice/[id]/tip/+page.server.ts b/src/routes/(app)/invoice/[id]/tip/+page.server.ts index de24c639..286d7c35 100644 --- a/src/routes/(app)/invoice/[id]/tip/+page.server.ts +++ b/src/routes/(app)/invoice/[id]/tip/+page.server.ts @@ -8,20 +8,22 @@ export async function load({ cookies, parent }) { let { invoice, subject, user } = await parent(); if (!invoice.amount) redirect(307, `/invoice/${invoice.id}`); - const trust = await get("/trust", auth(cookies)); - const trusted = trust.includes(invoice.uid); - if (trusted) { - if (invoice.prompt && invoice.tip === null && user.tip > 0) { - invoice.tip = Math.round(invoice.amount * (user.tip / 100)); - invoice = await post( - "/invoice", - { invoice, user: subject }, - auth(cookies), - ); - } + if (user) { + const trust = await get("/trust", auth(cookies)); + const trusted = trust.includes(invoice.uid); + if (trusted) { + if (invoice.prompt && invoice.tip === null && user.tip > 0) { + invoice.tip = Math.round(invoice.amount * (user.tip / 100)); + invoice = await post( + "/invoice", + { invoice, user: subject }, + auth(cookies), + ); + } - const p = await post("/payments", invoice, auth(cookies)); - redirect(307, `/sent/${p.id}`); + const p = await post("/payments", invoice, auth(cookies)); + redirect(307, `/sent/${p.id}`); + } } const rate = rates[subject?.currency]; diff --git a/src/routes/(app)/settings/profile/+page.svelte b/src/routes/(app)/settings/profile/+page.svelte index bee50d39..ec9855d0 100644 --- a/src/routes/(app)/settings/profile/+page.svelte +++ b/src/routes/(app)/settings/profile/+page.svelte @@ -3,16 +3,18 @@ import { avatar, banner as bannerStore } from "$lib/store"; import { t } from "$lib/translations"; import { page } from "$app/stores"; + import PasswordInput from "$comp/PasswordInput.svelte"; let { data } = $props(); let { user } = $derived(data); let { id } = user; - let { about, banner, picture, display, username } = - $state(user); + let { about, banner, picture, display, username } = $state(user); let avatarFile, avatarInput = $state(), bannerFile, + password = $state(), + revealPassword = $state(), bannerInput = $state(); let selectAvatar = () => avatarInput.click(); @@ -64,6 +66,16 @@
+ + +
+ + + +
+
- - -
- - - -
Date: Fri, 18 Jul 2025 08:27:08 -0700 Subject: fix notch spacing --- android/app/capacitor.build.gradle | 2 +- android/capacitor.settings.gradle | 3 +++ bun.lockb | Bin 291945 -> 292357 bytes capacitor.config.json | 7 ++++++- package.json | 1 + src/routes/+layout.svelte | 9 +++++---- 6 files changed, 16 insertions(+), 6 deletions(-) (limited to 'android') diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index fdb4970c..52f8ca0c 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -9,7 +9,7 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { - + implementation project(':capacitor-status-bar') } diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index 9a5fa872..c736879f 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -1,3 +1,6 @@ // 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/@capacitor/android/capacitor') + +include ':capacitor-status-bar' +project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android') diff --git a/bun.lockb b/bun.lockb index 92720150..8dd85e14 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/capacitor.config.json b/capacitor.config.json index 99458def..a1b10b40 100644 --- a/capacitor.config.json +++ b/capacitor.config.json @@ -12,6 +12,11 @@ "immersive": false }, "plugins": { - "NfcWriter": {} + "NfcWriter": {}, + "StatusBar": { + "overlaysWebView": false, + "style": "DARK", + "backgroundColor": "#ffffffff" + } } } diff --git a/package.json b/package.json index dfe2b380..edd715d7 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ }, "type": "module", "dependencies": { + "@capacitor/status-bar": "^7.0.1", "canvas": "^3.1.2", "cordova-plugin-android-notch": "^1.0.3", "qrcode": "^1.5.4" diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 2b3c99bf..7e0d28a5 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -25,12 +25,13 @@ $installPrompt = event; }); - if (typeof window.AndroidNotch !== "undefined") { + let hasNotch = typeof window.AndroidNotch !== "undefined"; + if (hasNotch) { window.AndroidNotch.getInsetTop( (px) => { document.documentElement.style.setProperty( "--safe-area-inset-top", - px + "px", + Math.round(px) + "px", ); }, (err) => { @@ -42,7 +43,7 @@ (px) => { document.documentElement.style.setProperty( "--safe-area-inset-bottom", - px + "px", + 24 + "px", ); }, (err) => { @@ -85,7 +86,7 @@ --toastContainerLeft: calc(50vw - 8rem); --toastBackground: #292929; - --safe-area-inset-top: 24px; + --safe-area-inset-top: 0px; --safe-area-inset-bottom: 0px; } -- cgit v1.2.3