Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-add ability to export transactions #5899

Merged
merged 5 commits into from
Apr 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

- Japanese, Afrikaans, Filipino, Thai and Vietnamese language support ([#5684](https://github.com/lbryio/lbry-desktop/issues/5684))
- Highlight comments made by content owner _community pr!_ ([#5744](https://github.com/lbryio/lbry-desktop/pull/5744))
- Ability to report infringing content directly from the application ([#5808](https://github.com/lbryio/lbry-desktop/pull/5808))
- Ability to report infringing content directly from the application ([#5808](https://github.com/lbryio/lbry-desktop/pull/5808))
- Re-added ability to export wallet transactions ([#5899](https://github.com/lbryio/lbry-desktop/pull/5899))

### Changed

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
"imagesloaded": "^4.1.4",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#3ca0c8d20466a695acfd7a9c20f8f580afa02206",
"lbry-redux": "lbryio/lbry-redux#eb37009a987410a60e9f2ba79708049c9904687c",
"lbryinc": "lbryio/lbryinc#8f9a58bfc8312a65614fd7327661cdcc502c4e59",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",
Expand Down
5 changes: 5 additions & 0 deletions static/app-strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -1859,6 +1859,11 @@
"Learn more and sign petition": "Learn more and sign petition",
"Publishing...": "Publishing...",
"Collection": "Collection",
"Fetch transaction data for export": "Fetch transaction data for export",
"Fetching data": "Fetching data",
"Download fetched file": "Download fetched file",
"No data to export": "No data to export",
"Failed to process fetched data.": "Failed to process fetched data.",
"More from %claim_name%": "More from %claim_name%",
"Upload that unlabeled video you found behind the TV in 1991": "Upload that unlabeled video you found behind the TV in 1991",
"Select Replay": "Select Replay",
Expand Down
138 changes: 66 additions & 72 deletions ui/component/common/file-exporter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,92 +3,86 @@ import * as ICONS from 'constants/icons';

import React from 'react';
import Button from 'component/button';
import parseData from 'util/parse-data';
import { remote } from 'electron';
import path from 'path';
// @if TARGET='app'
import fs from 'fs';
// @endif
import Spinner from 'component/spinner';

type Props = {
data: Array<any>,
title: string,
data: any,
label: string,
defaultPath?: string,
filters: Array<string>,
onFileCreated?: string => void,
disabled: boolean,
tooltip?: string,
defaultFileName?: string,
filters?: Array<string>,
onFetch?: () => void,
progressMsg?: string,
disabled?: boolean,
};

class FileExporter extends React.PureComponent<Props> {
static defaultProps = {
filters: [],
};

constructor() {
super();
(this: any).handleButtonClick = this.handleButtonClick.bind(this);
}

handleFileCreation(filename: string, data: any) {
const { onFileCreated } = this.props;
// @if TARGET='app'
fs.writeFile(filename, data, err => {
if (err) throw err;
// Do something after creation

if (onFileCreated) {
onFileCreated(filename);
}
});
// @endif
(this: any).handleDownload = this.handleDownload.bind(this);
}

handleButtonClick() {
const { title, data, defaultPath, filters } = this.props;

const options = {
title,
defaultPath,
filters: [
{
name: 'CSV',
extensions: ['csv'],
},
{
name: 'JSON',
extensions: ['json'],
},
],
};
handleDownload() {
const { data, defaultFileName } = this.props;

remote.dialog.showSaveDialog(remote.getCurrentWindow(), options, filename => {
// User hit cancel so do nothing:
if (!filename) return;
// Get extension and remove initial dot
// @if TARGET='app'
const format = path.extname(filename).replace(/\./g, '');
// @endif
// Parse data to string with the chosen format
const parsed = parseData(data, format, filters);
// Write file
if (parsed) {
this.handleFileCreation(filename, parsed);
}
});
const element = document.createElement('a');
const file = new Blob([data], { type: 'text/plain' });
element.href = URL.createObjectURL(file);
element.download = defaultFileName || 'file.txt';
// $FlowFixMe
document.body.appendChild(element);
element.click();
// $FlowFixMe
document.body.removeChild(element);
}

render() {
const { label, disabled } = this.props;
return (
<Button
button="primary"
disabled={disabled}
icon={ICONS.DOWNLOAD}
label={label || __('Export')}
onClick={this.handleButtonClick}
/>
);
const { data, label, tooltip, disabled, onFetch, progressMsg } = this.props;

if (onFetch) {
return (
<>
{!progressMsg && (
<div className="button-group">
<Button
button="alt"
disabled={disabled}
icon={ICONS.FETCH}
label={label}
aria-label={tooltip}
onClick={() => onFetch()}
/>
{data && (
<Button
button="alt"
disabled={disabled}
icon={ICONS.DOWNLOAD}
aria-label={__('Download fetched file')}
onClick={this.handleDownload}
/>
)}
</div>
)}
{progressMsg && (
<>
{__(progressMsg)}
<Spinner type="small" />
</>
)}
</>
);
} else {
return (
<Button
button="primary"
disabled={disabled}
icon={ICONS.DOWNLOAD}
label={label || __('Export')}
aria-label={tooltip}
onClick={this.handleDownload}
/>
);
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions ui/component/common/icon-custom.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@ export const icons = {
/>
</g>
),
[ICONS.FETCH]: buildIcon(
<g fill="none" fillRule="evenodd" strokeLinecap="round">
<polyline points="8 17 12 21 16 17" />
<line x1="12" y1="12" x2="12" y2="21" />
<path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29" />
</g>
),
[ICONS.SUBSCRIBE]: buildIcon(
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
),
Expand Down
12 changes: 9 additions & 3 deletions ui/component/txoList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,34 @@ import { connect } from 'react-redux';
import { doOpenModal } from 'redux/actions/app';
import {
selectIsFetchingTxos,
selectIsFetchingTransactions,
selectFetchingTxosError,
selectTransactionsFile,
selectTxoPage,
selectTxoPageNumber,
selectTxoItemCount,
doFetchTxoPage,
doFetchTransactions,
doUpdateTxoPageParams,
} from 'lbry-redux';
import { withRouter } from 'react-router';
import TxoList from './view';

const select = state => ({
const select = (state) => ({
txoFetchError: selectFetchingTxosError(state),
txoPage: selectTxoPage(state),
txoPageNumber: selectTxoPageNumber(state),
txoItemCount: selectTxoItemCount(state),
loading: selectIsFetchingTxos(state),
isFetchingTransactions: selectIsFetchingTransactions(state),
transactionsFile: selectTransactionsFile(state),
});

const perform = dispatch => ({
const perform = (dispatch) => ({
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
fetchTxoPage: () => dispatch(doFetchTxoPage()),
updateTxoPageParams: params => dispatch(doUpdateTxoPageParams(params)),
fetchTransactions: () => dispatch(doFetchTransactions()),
updateTxoPageParams: (params) => dispatch(doUpdateTxoPageParams(params)),
});

export default withRouter(connect(select, perform)(TxoList));
48 changes: 38 additions & 10 deletions ui/component/txoList/view.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ import Card from 'component/common/card';
import { toCapitalCase } from 'util/string';
import classnames from 'classnames';
import HelpLink from 'component/common/help-link';
import FileExporter from 'component/common/file-exporter';

type Props = {
search: string,
history: { action: string, push: string => void, replace: string => void },
history: { action: string, push: (string) => void, replace: (string) => void },
txoPage: Array<Transaction>,
txoPageNumber: string,
txoItemCount: number,
fetchTxoPage: () => void,
updateTxoPageParams: any => void,
fetchTransactions: () => void,
isFetchingTransactions: boolean,
transactionsFile: string,
updateTxoPageParams: (any) => void,
toast: (string, boolean) => void,
};

type Delta = {
Expand All @@ -28,7 +33,17 @@ type Delta = {
};

function TxoList(props: Props) {
const { search, txoPage, txoItemCount, fetchTxoPage, updateTxoPageParams, history } = props;
const {
search,
txoPage,
txoItemCount,
fetchTxoPage,
fetchTransactions,
updateTxoPageParams,
history,
isFetchingTransactions,
transactionsFile,
} = props;

const urlParams = new URLSearchParams(search);
const page = urlParams.get(TXO.PAGE) || String(1);
Expand Down Expand Up @@ -176,6 +191,19 @@ function TxoList(props: Props) {
title={<div className="table__header-text">{__(`Transactions`)}</div>}
titleActions={
<div className="card__actions--inline">
{!isFetchingTransactions && transactionsFile === null && (
<label>{<span className="error__text">{__('Failed to process fetched data.')}</span>}</label>
)}
<div className="txo__export">
<FileExporter
data={transactionsFile}
label={__('Export')}
tooltip={__('Fetch transaction data for export')}
defaultFileName={'transactions-history.csv'}
onFetch={() => fetchTransactions()}
progressMsg={isFetchingTransactions ? __('Fetching data') : ''}
/>
</div>
<Button button="alt" icon={ICONS.REFRESH} label={__('Refresh')} onClick={() => fetchTxoPage()} />
</div>
}
Expand All @@ -195,9 +223,9 @@ function TxoList(props: Props) {
</>
}
value={type || 'all'}
onChange={e => handleChange({ dkey: TXO.TYPE, value: e.target.value })}
onChange={(e) => handleChange({ dkey: TXO.TYPE, value: e.target.value })}
>
{Object.values(TXO.DROPDOWN_TYPES).map(v => {
{Object.values(TXO.DROPDOWN_TYPES).map((v) => {
const stringV = String(v);
return (
<option key={stringV} value={stringV}>
Expand All @@ -214,9 +242,9 @@ function TxoList(props: Props) {
name="subtype"
label={__('Payment Type')}
value={subtype || 'all'}
onChange={e => handleChange({ dkey: TXO.SUB_TYPE, value: e.target.value })}
onChange={(e) => handleChange({ dkey: TXO.SUB_TYPE, value: e.target.value })}
>
{Object.values(TXO.DROPDOWN_SUBTYPES).map(v => {
{Object.values(TXO.DROPDOWN_SUBTYPES).map((v) => {
const stringV = String(v);
return (
<option key={stringV} value={stringV}>
Expand All @@ -234,23 +262,23 @@ function TxoList(props: Props) {
<div className={'txo__radios'}>
<Button
button="alt"
onClick={e => handleChange({ dkey: TXO.ACTIVE, value: 'active' })}
onClick={(e) => handleChange({ dkey: TXO.ACTIVE, value: 'active' })}
className={classnames(`button-toggle`, {
'button-toggle--active': active === TXO.ACTIVE,
})}
label={__('Active')}
/>
<Button
button="alt"
onClick={e => handleChange({ dkey: TXO.ACTIVE, value: 'spent' })}
onClick={(e) => handleChange({ dkey: TXO.ACTIVE, value: 'spent' })}
className={classnames(`button-toggle`, {
'button-toggle--active': active === 'spent',
})}
label={__('Historical')}
/>
<Button
button="alt"
onClick={e => handleChange({ dkey: TXO.ACTIVE, value: 'all' })}
onClick={(e) => handleChange({ dkey: TXO.ACTIVE, value: 'all' })}
className={classnames(`button-toggle`, {
'button-toggle--active': active === 'all',
})}
Expand Down
1 change: 1 addition & 0 deletions ui/constants/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const ARROW_LEFT = 'ChevronLeft';
export const ARROW_RIGHT = 'ChevronRight';
export const DOWNLOAD = 'Download';
export const PUBLISH = 'UploadCloud';
export const FETCH = 'Fetch';
export const REMOVE = 'X';
export const ADD = 'Plus';
export const SUBTRACT = 'Subtract';
Expand Down
8 changes: 8 additions & 0 deletions ui/scss/component/_txo-list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@
display: flex;
flex-direction: row;
}

.txo__export {
display: none;

@media (min-width: $breakpoint-small) {
display: block;
}
}
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6951,9 +6951,9 @@ lazy-val@^1.0.4:
yargs "^13.2.2"
zstd-codec "^0.1.1"

lbry-redux@lbryio/lbry-redux#3ca0c8d20466a695acfd7a9c20f8f580afa02206:
lbry-redux@lbryio/lbry-redux#eb37009a987410a60e9f2ba79708049c9904687c:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/3ca0c8d20466a695acfd7a9c20f8f580afa02206"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/eb37009a987410a60e9f2ba79708049c9904687c"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
Expand Down