Compare commits

...

2 Commits

Author SHA1 Message Date
jwinterm
b6f6f3f4da Wire error reporting to hash.boats/report, surface Wownero primary address
Two unrelated wallet-UX fixes in one commit:

1) lib/utils/exception_handler.dart — switch the "Report Error" flow
   from "open prefilled GitHub issue URL" to "copy error trace to
   clipboard + open https://hash.boats/report/". The hash.boats backend
   (Node service + nginx proxy + Telegram relay) was already deployed
   to the suchwow server; the in-app handler just never got wired to
   use it. No GitHub account required, works on every platform.

2) lib/wownero/cw_wownero.dart — add 'primaryAddress' to the keys map
   returned by getKeys(). The lib/monero/cw_monero.dart equivalent
   already had this; without it, WalletKeysViewModel renders zero
   primary-address entry for Wownero wallets and users can't easily
   read off the main address for sharing (e.g. to set up a view-only
   wallet on another device). The underlying WowneroWalletBase.keys
   already populated primaryAddress — only the getKeys map projection
   was missing it.
2026-06-01 18:39:57 -04:00
jwinterm
e6195d1e98 iOS TestFlight: override signing in Release.xcconfig for CI
The committed project.pbxproj Release config uses CODE_SIGN_STYLE =
Automatic + CODE_SIGN_IDENTITY = "Apple Development" — both set up for
local Xcode-signed-into-an-Apple-ID development workflow. CI has only
the Distribution cert + provisioning profile we install into a temp
keychain, no Apple ID session, so Automatic signing fails at archive
time.

Append manual-signing overrides to ios/Flutter/Release.xcconfig in CI
(xcconfig values take precedence over pbxproj defaults, so this
overrides without modifying the committed project file):
  CODE_SIGN_IDENTITY = Apple Distribution
  CODE_SIGN_STYLE = Manual
  DEVELOPMENT_TEAM = ${APPLE_TEAM_ID}
  PROVISIONING_PROFILE_SPECIFIER = ${PROFILE_NAME}

PROFILE_NAME is parsed earlier from the .mobileprovision Name field.
APPLE_TEAM_ID comes from the existing Gitea secret. Local Debug/Profile
builds remain on Automatic signing — unchanged.
2026-06-01 18:30:34 -04:00
3 changed files with 46 additions and 34 deletions

View File

@@ -284,6 +284,28 @@ jobs:
cd ios cd ios
pod install --repo-update pod install --repo-update
# ---- Override signing in Release.xcconfig for the archive step ------
# The committed project.pbxproj Release config uses Automatic signing
# + "Apple Development" identity (set up for local dev with Xcode
# signed into an Apple ID). CI can't do Automatic signing — it has
# only the cert + provisioning profile we installed in the temp
# keychain. Append manual-signing overrides to Release.xcconfig;
# xcconfig values take precedence over pbxproj defaults.
- name: Override signing settings for archive
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
set -x
cat >> ios/Flutter/Release.xcconfig <<XCFG
// CI-only signing overrides (appended at build time, not committed).
CODE_SIGN_IDENTITY = Apple Distribution
CODE_SIGN_STYLE = Manual
DEVELOPMENT_TEAM = ${APPLE_TEAM_ID}
PROVISIONING_PROFILE_SPECIFIER = ${PROFILE_NAME}
XCFG
tail -8 ios/Flutter/Release.xcconfig
# ---- Generate ExportOptions.plist + build IPA ----------------------- # ---- Generate ExportOptions.plist + build IPA -----------------------
- name: Write ExportOptions.plist - name: Write ExportOptions.plist
env: env:

View File

@@ -70,20 +70,21 @@ class ExceptionHandler {
); );
} }
// Hash Bags: the original Cake implementation used flutter_mailer to // Hash Bags: error reports are submitted to our own form at
// hand the error.txt to the OS mail client. That plugin has no Linux // hash.boats/report. The receiving Node service at hash.boats relays
// implementation (throws MissingPluginException) and on other platforms // them to a private Telegram channel and writes them to disk on the
// many users have no mail client configured, so very few reports ever // suchwow server — see hash.boats/deploy/install.sh + server.js.
// reached anyone. Instead, we open a prefilled GitHub issue. Users
// without a GH account get nothing useful, but the trade-off beats
// pretending the mail flow works.
// //
// GitHub's "new issue" URL caps the body at ~8KB before truncating, so // The original Cake flow used flutter_mailer to hand error.txt to the
// we send the tail of error.txt (most recent exception is usually most // OS mail client; that plugin has no Linux backend and most users
// relevant) and prepend a hint to attach the full file. // don't have a mail client configured, so it rarely worked. We tried
static const _issueUrl = // a GitHub-issue prefill next, but that needs a GitHub account most
'https://github.com/Such-Software/hash-wallet/issues/new'; // wallet users won't have.
static const _maxBodyChars = 6500; //
// The flow now: copy error.txt to the clipboard, open the report
// page, user pastes + optionally adds context + submits. No accounts,
// no third-party (it's our own backend), works on every platform.
static const _reportUrl = 'https://hash.boats/report/';
static void _sendExceptionFile() async { static void _sendExceptionFile() async {
try { try {
@@ -95,34 +96,22 @@ class ExceptionHandler {
await _addDeviceInfo(_file!); await _addDeviceInfo(_file!);
final fullBody = await _file!.readAsString(); final fullBody = await _file!.readAsString();
final trimmed = fullBody.length > _maxBodyChars
? '...(truncated, full log at ${_file!.path})\n\n${fullBody.substring(fullBody.length - _maxBodyChars)}'
: fullBody;
final body = ''' // Put the full trace on the clipboard so the user can paste it into
<!-- Thanks for reporting. The trace below was captured automatically. // the form on hash.boats/report. The receiving server caps each
Please add a short description of what you were doing when the error // upload at 200KB — way more than a typical error.txt — so we
appeared (e.g. "creating wallet", "restoring from seed", "sending Tx"). --> // don't bother trimming here.
await Clipboard.setData(ClipboardData(text: fullBody));
---
$trimmed
''';
final uri = Uri.https('github.com', '/Such-Software/hash-wallet/issues/new', {
'title': 'App error report',
'labels': 'bug,from-app',
'body': body,
});
final uri = Uri.parse(_reportUrl);
final launched = await launchUrl(uri, mode: LaunchMode.externalApplication); final launched = await launchUrl(uri, mode: LaunchMode.externalApplication);
if (!launched) { if (!launched) {
printV('Could not open GitHub issue URL: $uri'); printV('Could not open report URL: $uri');
return; return;
} }
// Clear the file now that the user has been handed the trace. // Clear the local error file now that the user has the trace on
// their clipboard and the report page is open in their browser.
await _file!.writeAsString('', mode: FileMode.write); await _file!.writeAsString('', mode: FileMode.write);
} catch (e, s) { } catch (e, s) {
_saveException(e.toString(), s); _saveException(e.toString(), s);

View File

@@ -247,6 +247,7 @@ class CWWownero extends Wownero {
final wowneroWallet = wallet as WowneroWallet; final wowneroWallet = wallet as WowneroWallet;
final keys = wowneroWallet.keys; final keys = wowneroWallet.keys;
return <String, String>{ return <String, String>{
'primaryAddress': keys.primaryAddress,
'privateSpendKey': keys.privateSpendKey, 'privateSpendKey': keys.privateSpendKey,
'privateViewKey': keys.privateViewKey, 'privateViewKey': keys.privateViewKey,
'publicSpendKey': keys.publicSpendKey, 'publicSpendKey': keys.publicSpendKey,