Skip to content

Commit 1674693

Browse files
committed
backend: open CSV automatically after export
SystemOpen() has a whitelist, but the whitelist does not cover exported CSV files - only the Downloads folder, but the user can save it somewhere else. Instead of adding CSV export paths dynamically to the whitelist, it is simpler to automatically open the CSV. This also avoids the clumsy hack of converting the button from 'export' to 'open'.
1 parent d83626f commit 1674693

File tree

7 files changed

+14
-42
lines changed

7 files changed

+14
-42
lines changed

backend/accounts.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ func (backend *Backend) createAndAddAccount(
403403
GetNotifier: func(configurations signing.Configurations) accounts.Notifier {
404404
return backend.notifier.ForAccount(code)
405405
},
406-
GetSaveFilename: backend.environment.GetSaveFilename,
406+
GetSaveFilename: backend.environment.GetSaveFilename,
407+
UnsafeSystemOpen: backend.environment.SystemOpen,
407408
}
408409

409410
switch specificCoin := coin.(type) {

backend/accounts/baseaccount.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ type AccountConfig struct {
5757
SigningConfigurations signing.Configurations
5858
GetNotifier func(signing.Configurations) Notifier
5959
GetSaveFilename func(suggestedFilename string) string
60+
// Opens a file in a default application. The filename is not checked.
61+
UnsafeSystemOpen func(filename string) error
6062
}
6163

6264
// BaseAccount is an account struct with common functionality to all coin accounts.

backend/backend.go

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"net/url"
2121
"os"
2222
"path/filepath"
23-
"runtime"
2423
"strings"
2524

2625
"github.com/btcsuite/btcd/chaincfg"
@@ -42,7 +41,6 @@ import (
4241
"github.com/digitalbitbox/bitbox-wallet-app/backend/keystore"
4342
"github.com/digitalbitbox/bitbox-wallet-app/backend/keystore/software"
4443
"github.com/digitalbitbox/bitbox-wallet-app/backend/rates"
45-
utilConfig "github.com/digitalbitbox/bitbox-wallet-app/util/config"
4644
"github.com/digitalbitbox/bitbox-wallet-app/util/errp"
4745
"github.com/digitalbitbox/bitbox-wallet-app/util/locker"
4846
"github.com/digitalbitbox/bitbox-wallet-app/util/logging"
@@ -633,8 +631,7 @@ func (backend *Backend) NotifyUser(text string) {
633631

634632
// SystemOpen opens the given URL using backend.environment.
635633
// It consults fixedURLWhitelist, matching the URL with each whitelist item.
636-
// If an item is a prefix of url, it is allowed to be openend. Otherwise, an ad-hoc
637-
// patter matching is performed for URLs like the CSV export download path.
634+
// If an item is a prefix of url, it is allowed to be openend.
638635
//
639636
// If none matched, an ad-hoc URL construction failed or opening a URL failed,
640637
// an error is returned.
@@ -646,17 +643,6 @@ func (backend *Backend) SystemOpen(url string) error {
646643
}
647644
}
648645

649-
if runtime.GOOS != "android" { // TODO: fix DownloadsDir() for android
650-
// Whitelist CSV export.
651-
downloadDir, err := utilConfig.DownloadsDir()
652-
if err != nil {
653-
return err
654-
}
655-
if strings.HasPrefix(url, downloadDir) {
656-
return backend.environment.SystemOpen(url)
657-
}
658-
}
659-
660646
return errp.Newf("Blocked /open with url: %s", url)
661647
}
662648

backend/coins/btc/handlers/handlers.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@ func (handlers *Handlers) getAccountTransactions(_ *http.Request) (interface{},
196196
func (handlers *Handlers) postExportTransactions(_ *http.Request) (interface{}, error) {
197197
type result struct {
198198
Success bool `json:"success"`
199-
Path string `json:"path"`
200199
ErrorMessage string `json:"errorMessage"`
201200
}
202201
name := fmt.Sprintf("%s-%s-export.csv", time.Now().Format("2006-01-02-at-15-04-05"), handlers.account.Config().Code)
@@ -232,7 +231,11 @@ func (handlers *Handlers) postExportTransactions(_ *http.Request) (interface{},
232231
handlers.log.WithError(err).Error("error exporting account")
233232
return result{Success: false, ErrorMessage: err.Error()}, nil
234233
}
235-
return result{Success: true, Path: path}, nil
234+
if err := handlers.account.Config().UnsafeSystemOpen(path); err != nil {
235+
handlers.log.WithError(err).Error("error exporting account")
236+
return result{Success: false, ErrorMessage: err.Error()}, nil
237+
}
238+
return result{Success: true}, nil
236239
}
237240

238241
func (handlers *Handlers) getAccountInfo(_ *http.Request) (interface{}, error) {

frontends/web/src/components/transactions/transactions.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,12 @@ import A from '../../components/anchor/anchor';
2121
import { translate, TranslateProps } from '../../decorators/translate';
2222
import { runningInAndroid } from '../../utils/env';
2323
import { Transaction } from './transaction';
24-
import { apiPost } from '../../utils/request';
2524
import style from './transactions.module.css';
2625

2726
interface TransactionsProps {
2827
accountCode: string;
2928
explorerURL: string;
3029
transactions?: ITransaction[];
31-
exported: string;
3230
handleExport: () => void;
3331
}
3432

@@ -41,7 +39,6 @@ class Transactions extends Component<Props> {
4139
accountCode,
4240
explorerURL,
4341
transactions,
44-
exported,
4542
handleExport,
4643
} = this.props;
4744
// We don't support CSV export on Android yet, as it's a tricky to deal with the Downloads
@@ -52,15 +49,8 @@ class Transactions extends Component<Props> {
5249
<div className="flex flex-row flex-between flex-items-center">
5350
<label className="labelXLarge">{t('accountSummary.transactionHistory')}</label>
5451
{ !csvExportDisabled && (
55-
exported ? (
56-
<A key="open" href="#" onClick={() => apiPost('open', exported)} className="labelXLarge labelLink">
57-
{t('account.openFile')}
58-
</A>
59-
) : (
60-
<A key="export" href="#" onClick={handleExport} className="labelXLarge labelLink" title={t('account.exportTransactions')}>{t('account.export')}</A>
61-
)
62-
)
63-
}
52+
<A key="export" href="#" onClick={handleExport} className="labelXLarge labelLink" title={t('account.exportTransactions')}>{t('account.export')}</A>
53+
) }
6454
</div>
6555
<div className={[style.columns, style.headers, style.showOnMedium].join(' ')}>
6656
<div className={style.type}>{t('transaction.details.type')}</div>

frontends/web/src/locales/en/app.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
},
1616
"initializing": "Getting information from the blockchain…",
1717
"maybeProxyError": "Tor proxy enabled. Ensure that your Tor proxy is running properly, or disable the proxy setting.",
18-
"openFile": "Open file",
1918
"reconnecting": "Lost connection, trying to reconnect…",
2019
"syncedAddressesCount": "Scanned {{count}} addresses"
2120
},

frontends/web/src/routes/account/account.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ interface State {
6565
transactions?: accountApi.ITransaction[];
6666
balance?: accountApi.IBalance;
6767
hasCard: boolean;
68-
exported: string;
6968
accountInfo?: accountApi.ISigningConfigurationList;
7069
syncedAddressesCount?: number;
7170
}
@@ -78,7 +77,6 @@ class Account extends Component<Props, State> {
7877
transactions: undefined,
7978
balance: undefined,
8079
hasCard: false,
81-
exported: '',
8280
accountInfo: undefined,
8381
syncedAddressesCount: undefined,
8482
};
@@ -218,7 +216,6 @@ class Account extends Component<Props, State> {
218216
transactions: undefined,
219217
});
220218
}
221-
this.setState({ exported: '' });
222219
}
223220

224221
private export = () => {
@@ -227,12 +224,8 @@ class Account extends Component<Props, State> {
227224
}
228225
accountApi.exportAccount(this.props.code)
229226
.then(result => {
230-
if (result !== null) {
231-
if (result.success) {
232-
this.setState({ exported: result.path });
233-
} else {
234-
alertUser(result.errorMessage);
235-
}
227+
if (result !== null && !result.success) {
228+
alertUser(result.errorMessage);
236229
}
237230
})
238231
.catch(console.error);
@@ -276,7 +269,6 @@ class Account extends Component<Props, State> {
276269
transactions,
277270
balance,
278271
hasCard,
279-
exported,
280272
accountInfo,
281273
syncedAddressesCount,
282274
} = this.state;
@@ -360,7 +352,6 @@ class Account extends Component<Props, State> {
360352
) : (
361353
<Transactions
362354
accountCode={code}
363-
exported={exported}
364355
handleExport={this.export}
365356
explorerURL={account.blockExplorerTxPrefix}
366357
transactions={transactions}

0 commit comments

Comments
 (0)