create component FileExporter

This commit is contained in:
btzr-io 2018-02-23 17:24:00 -07:00
parent 80f0b170d8
commit f29a947c09
17 changed files with 166 additions and 72 deletions

View file

@ -5,16 +5,16 @@ mixpanel.init('691723e855cabb9d27a7a79002216967');
type Analytics = { type Analytics = {
track: (string, ?Object) => void, track: (string, ?Object) => void,
setUser: (Object) => void, setUser: Object => void,
toggle: (boolean, ?boolean) => void toggle: (boolean, ?boolean) => void,
} };
let analyticsEnabled: boolean = false; let analyticsEnabled: boolean = false;
const analytics: Analytics = { const analytics: Analytics = {
track: (name: string, payload: ?Object): void => { track: (name: string, payload: ?Object): void => {
if(analyticsEnabled) { if (analyticsEnabled) {
if(payload) { if (payload) {
mixpanel.track(name, payload); mixpanel.track(name, payload);
} else { } else {
mixpanel.track(name); mixpanel.track(name);
@ -22,21 +22,21 @@ const analytics: Analytics = {
} }
}, },
setUser: (user: Object): void => { setUser: (user: Object): void => {
if(user.id) { if (user.id) {
mixpanel.identify(user.id); mixpanel.identify(user.id);
} }
if(user.primary_email) { if (user.primary_email) {
mixpanel.people.set({ mixpanel.people.set({
"$email": user.primary_email $email: user.primary_email,
}); });
} }
}, },
toggle: (enabled: boolean, logDisabled: ?boolean): void => { toggle: (enabled: boolean, logDisabled: ?boolean): void => {
if(!enabled && logDisabled) { if (!enabled && logDisabled) {
mixpanel.track('DISABLED'); mixpanel.track('DISABLED');
} }
analyticsEnabled = enabled; analyticsEnabled = enabled;
} },
} };
export default analytics; export default analytics;

View file

@ -0,0 +1,82 @@
import fs from 'fs';
import path from 'path';
import React from 'react';
import PropTypes from 'prop-types';
import Link from 'component/link';
import parseData from 'util/parseData';
import * as icons from 'constants/icons';
const { remote } = require('electron');
class FileExporter extends React.PureComponent {
static propTypes = {
data: PropTypes.array,
type: PropTypes.oneOf(['json', 'csv']),
title: PropTypes.string,
initPath: PropTypes.string,
onFileCreated: PropTypes.func,
};
static defaultProps = {
type: 'json',
};
constructor(props) {
super(props);
this._inputElem = null;
}
componentWillMount() {
this.setState({
path: this.props.initPath || null,
});
}
handleFileCreation(filename, data) {
const { onFileCreated } = this.props;
fs.writeFile(filename, data, err => {
if (err) throw err;
//Debug
console.log('The file has been saved!', filename);
// Do something after creation
onFileCreated && onFileCreated(filename);
});
}
handleButtonClick() {
const { title, defaultPath, data } = this.props;
const options = {
title,
defaultPath,
filters: [{ name: 'JSON', extensions: ['json'] }, { name: 'CSV', extensions: ['csv'] }],
};
remote.dialog.showSaveDialog(options, filename => {
// User hit cancel so do nothing:
if (!filename) return;
// Get extension and remove initial dot
const format = path.extname(filename).replace(/\./g, '');
// Parse data to string with the chosen format
const parsed = parseData(data, format);
// Debug
console.log(parsed, data, format);
// Write file
parsed && this.handleFileCreation(filename, parsed);
});
}
render() {
const { title, label } = this.props;
return (
<Link
button="alt"
icon={icons.DOWNLOAD}
title={title || __('Export')}
label={label || __('Export')}
onClick={() => this.handleButtonClick()}
/>
);
}
}
export default FileExporter;

View file

@ -2,6 +2,7 @@ import React from 'react';
import TransactionListItem from './internal/TransactionListItem'; import TransactionListItem from './internal/TransactionListItem';
import FormField from 'component/formField'; import FormField from 'component/formField';
import Link from 'component/link'; import Link from 'component/link';
//import FileExporter from 'component/file-exporter.js';
import * as icons from 'constants/icons'; import * as icons from 'constants/icons';
import * as modals from 'constants/modal_types'; import * as modals from 'constants/modal_types';

View file

@ -48,7 +48,6 @@ export const FETCH_BLOCK_SUCCESS = 'FETCH_BLOCK_SUCCESS';
export const SUPPORT_TRANSACTION_STARTED = 'SUPPORT_TRANSACTION_STARTED'; export const SUPPORT_TRANSACTION_STARTED = 'SUPPORT_TRANSACTION_STARTED';
export const SUPPORT_TRANSACTION_COMPLETED = 'SUPPORT_TRANSACTION_COMPLETED'; export const SUPPORT_TRANSACTION_COMPLETED = 'SUPPORT_TRANSACTION_COMPLETED';
export const SUPPORT_TRANSACTION_FAILED = 'SUPPORT_TRANSACTION_FAILED'; export const SUPPORT_TRANSACTION_FAILED = 'SUPPORT_TRANSACTION_FAILED';
export const EXPORT_TRANSACTIONS = 'EXPORT_TRANSACTIONS';
// Claims // Claims
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED'; export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';

View file

@ -3,3 +3,4 @@ export const LOCAL = 'folder';
export const FILE = 'file'; export const FILE = 'file';
export const HISTORY = 'history'; export const HISTORY = 'history';
export const HELP_CIRCLE = 'question-circle'; export const HELP_CIRCLE = 'question-circle';
export const DOWNLOAD = 'download';

View file

@ -2,7 +2,7 @@ export const CONFIRM_FILE_REMOVE = 'confirmFileRemove';
export const INCOMPATIBLE_DAEMON = 'incompatibleDaemon'; export const INCOMPATIBLE_DAEMON = 'incompatibleDaemon';
export const FILE_TIMEOUT = 'file_timeout'; export const FILE_TIMEOUT = 'file_timeout';
export const DOWNLOADING = 'downloading'; export const DOWNLOADING = 'downloading';
export const AUTO_UPDATE_DOWNLOADED = "auto_update_downloaded"; export const AUTO_UPDATE_DOWNLOADED = 'auto_update_downloaded';
export const AUTO_UPDATE_CONFIRM = 'auto_update_confirm'; export const AUTO_UPDATE_CONFIRM = 'auto_update_confirm';
export const ERROR = 'error'; export const ERROR = 'error';
export const INSUFFICIENT_CREDITS = 'insufficient_credits'; export const INSUFFICIENT_CREDITS = 'insufficient_credits';

View file

@ -8,7 +8,12 @@ import lbry from 'lbry';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { doConditionalAuthNavigate, doDaemonReady, doShowSnackBar, doAutoUpdate } from 'redux/actions/app'; import {
doConditionalAuthNavigate,
doDaemonReady,
doShowSnackBar,
doAutoUpdate,
} from 'redux/actions/app';
import { doUpdateIsNightAsync } from 'redux/actions/settings'; import { doUpdateIsNightAsync } from 'redux/actions/settings';
import { doNavigate } from 'redux/actions/navigation'; import { doNavigate } from 'redux/actions/navigation';
import { doDownloadLanguages } from 'redux/actions/settings'; import { doDownloadLanguages } from 'redux/actions/settings';
@ -20,7 +25,7 @@ import analytics from './analytics';
const { autoUpdater } = remote.require('electron-updater'); const { autoUpdater } = remote.require('electron-updater');
autoUpdater.logger = remote.require("electron-log"); autoUpdater.logger = remote.require('electron-log');
window.addEventListener('contextmenu', event => { window.addEventListener('contextmenu', event => {
contextMenu(remote.getCurrentWindow(), event.x, event.y, app.env === 'development'); contextMenu(remote.getCurrentWindow(), event.x, event.y, app.env === 'development');
@ -91,19 +96,19 @@ document.addEventListener('click', event => {
}); });
const init = () => { const init = () => {
autoUpdater.on("update-downloaded", () => { autoUpdater.on('update-downloaded', () => {
app.store.dispatch(doAutoUpdate()); app.store.dispatch(doAutoUpdate());
}); });
if (["win32", "darwin"].includes(process.platform)) { if (['win32', 'darwin'].includes(process.platform)) {
autoUpdater.on("update-available", () => { autoUpdater.on('update-available', () => {
console.log("Update available"); console.log('Update available');
}); });
autoUpdater.on("update-not-available", () => { autoUpdater.on('update-not-available', () => {
console.log("Update not available"); console.log('Update not available');
}); });
autoUpdater.on("update-downloaded", () => { autoUpdater.on('update-downloaded', () => {
console.log("Update downloaded"); console.log('Update downloaded');
app.store.dispatch(doAutoUpdate()); app.store.dispatch(doAutoUpdate());
}); });
} }

View file

@ -1,7 +1,7 @@
import React from "react"; import React from 'react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { doCloseModal, doAutoUpdateDeclined } from "redux/actions/app"; import { doCloseModal, doAutoUpdateDeclined } from 'redux/actions/app';
import ModalAutoUpdateConfirm from "./view"; import ModalAutoUpdateConfirm from './view';
const perform = dispatch => ({ const perform = dispatch => ({
closeModal: () => dispatch(doCloseModal()), closeModal: () => dispatch(doCloseModal()),

View file

@ -1,9 +1,9 @@
import React from "react"; import React from 'react';
import { Modal } from "modal/modal"; import { Modal } from 'modal/modal';
import { Line } from "rc-progress"; import { Line } from 'rc-progress';
import Link from "component/link/index"; import Link from 'component/link/index';
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require('electron');
class ModalAutoUpdateConfirm extends React.PureComponent { class ModalAutoUpdateConfirm extends React.PureComponent {
render() { render() {
@ -13,11 +13,11 @@ class ModalAutoUpdateConfirm extends React.PureComponent {
<Modal <Modal
isOpen={true} isOpen={true}
type="confirm" type="confirm"
contentLabel={__("Update Downloaded")} contentLabel={__('Update Downloaded')}
confirmButtonLabel={__("Upgrade")} confirmButtonLabel={__('Upgrade')}
abortButtonLabel={__("Not now")} abortButtonLabel={__('Not now')}
onConfirmed={() => { onConfirmed={() => {
ipcRenderer.send("autoUpdateAccepted"); ipcRenderer.send('autoUpdateAccepted');
}} }}
onAborted={() => { onAborted={() => {
declineAutoUpdate(); declineAutoUpdate();
@ -25,12 +25,8 @@ class ModalAutoUpdateConfirm extends React.PureComponent {
}} }}
> >
<section> <section>
<h3 className="text-center">{__("LBRY Update Ready")}</h3> <h3 className="text-center">{__('LBRY Update Ready')}</h3>
<p> <p>{__('Your LBRY update is ready. Restart LBRY now to use it!')}</p>
{__(
'Your LBRY update is ready. Restart LBRY now to use it!'
)}
</p>
<p className="meta text-center"> <p className="meta text-center">
{__('Want to know what has changed?')} See the{' '} {__('Want to know what has changed?')} See the{' '}
<Link label={__('release notes')} href="https://github.com/lbryio/lbry-app/releases" />. <Link label={__('release notes')} href="https://github.com/lbryio/lbry-app/releases" />.

View file

@ -1,7 +1,7 @@
import React from "react"; import React from 'react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { doCloseModal, doAutoUpdateDeclined } from "redux/actions/app"; import { doCloseModal, doAutoUpdateDeclined } from 'redux/actions/app';
import ModalAutoUpdateDownloaded from "./view"; import ModalAutoUpdateDownloaded from './view';
const perform = dispatch => ({ const perform = dispatch => ({
closeModal: () => dispatch(doCloseModal()), closeModal: () => dispatch(doCloseModal()),

View file

@ -1,9 +1,9 @@
import React from "react"; import React from 'react';
import { Modal } from "modal/modal"; import { Modal } from 'modal/modal';
import { Line } from "rc-progress"; import { Line } from 'rc-progress';
import Link from "component/link/index"; import Link from 'component/link/index';
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require('electron');
class ModalAutoUpdateDownloaded extends React.PureComponent { class ModalAutoUpdateDownloaded extends React.PureComponent {
render() { render() {
@ -13,20 +13,20 @@ class ModalAutoUpdateDownloaded extends React.PureComponent {
<Modal <Modal
isOpen={true} isOpen={true}
type="confirm" type="confirm"
contentLabel={__("Update Downloaded")} contentLabel={__('Update Downloaded')}
confirmButtonLabel={__("Use it Now")} confirmButtonLabel={__('Use it Now')}
abortButtonLabel={__("Upgrade on Close")} abortButtonLabel={__('Upgrade on Close')}
onConfirmed={() => { onConfirmed={() => {
ipcRenderer.send("autoUpdateAccepted"); ipcRenderer.send('autoUpdateAccepted');
}} }}
onAborted={() => { onAborted={() => {
declineAutoUpdate(); declineAutoUpdate();
ipcRenderer.send("autoUpdateDeclined"); ipcRenderer.send('autoUpdateDeclined');
closeModal(); closeModal();
}} }}
> >
<section> <section>
<h3 className="text-center">{__("LBRY Leveled Up")}</h3> <h3 className="text-center">{__('LBRY Leveled Up')}</h3>
<p> <p>
{__( {__(
'A new version of LBRY has been released, downloaded, and is ready for you to use pending a restart.' 'A new version of LBRY has been released, downloaded, and is ready for you to use pending a restart.'

View file

@ -334,7 +334,7 @@ class SettingsPage extends React.PureComponent {
<FormRow <FormRow
type="checkbox" type="checkbox"
disabled={theme === 'dark'} disabled={theme === 'dark'}
onChange={(e) => this.onAutomaticDarkModeChange(e.target.checked)} onChange={e => this.onAutomaticDarkModeChange(e.target.checked)}
checked={automaticDarkModeEnabled} checked={automaticDarkModeEnabled}
label={__('Automatic dark mode (9pm to 8am)')} label={__('Automatic dark mode (9pm to 8am)')}
/> />

View file

@ -10,7 +10,7 @@ import { doAuthNavigate } from 'redux/actions/navigation';
import { doFetchDaemonSettings } from 'redux/actions/settings'; import { doFetchDaemonSettings } from 'redux/actions/settings';
import { doAuthenticate } from 'redux/actions/user'; import { doAuthenticate } from 'redux/actions/user';
import { doBalanceSubscribe } from 'redux/actions/wallet'; import { doBalanceSubscribe } from 'redux/actions/wallet';
import { doPause } from "redux/actions/media"; import { doPause } from 'redux/actions/media';
import { import {
selectCurrentModal, selectCurrentModal,
@ -84,7 +84,8 @@ export function doDownloadUpgradeRequested() {
const autoUpdateDeclined = selectAutoUpdateDeclined(state); const autoUpdateDeclined = selectAutoUpdateDeclined(state);
if (['win32', 'darwin'].includes(process.platform)) { // electron-updater behavior if (['win32', 'darwin'].includes(process.platform)) {
// electron-updater behavior
if (autoUpdateDeclined) { if (autoUpdateDeclined) {
// The user declined an update before, so show the "confirm" dialog // The user declined an update before, so show the "confirm" dialog
dispatch({ dispatch({
@ -99,7 +100,8 @@ export function doDownloadUpgradeRequested() {
data: { modal: MODALS.AUTO_UPDATE_DOWNLOADED }, data: { modal: MODALS.AUTO_UPDATE_DOWNLOADED },
}); });
} }
} else { // Old behavior for Linux } else {
// Old behavior for Linux
dispatch(doDownloadUpgrade()); dispatch(doDownloadUpgrade());
} }
}; };
@ -164,7 +166,7 @@ export function doAutoUpdateDeclined() {
dispatch({ dispatch({
type: ACTIONS.AUTO_UPDATE_DECLINED, type: ACTIONS.AUTO_UPDATE_DECLINED,
}); });
} };
} }
export function doCancelUpgrade() { export function doCancelUpgrade() {
@ -197,7 +199,7 @@ export function doCheckUpgradeAvailable() {
type: ACTIONS.CHECK_UPGRADE_START, type: ACTIONS.CHECK_UPGRADE_START,
}); });
if (["win32", "darwin"].includes(process.platform)) { if (['win32', 'darwin'].includes(process.platform)) {
// On Windows and Mac, updates happen silently through // On Windows and Mac, updates happen silently through
// electron-updater. // electron-updater.
const autoUpdateDeclined = selectAutoUpdateDeclined(state); const autoUpdateDeclined = selectAutoUpdateDeclined(state);

View file

@ -70,11 +70,12 @@ export function doUpdateIsNight() {
const momentNow = moment(); const momentNow = moment();
return { return {
type: ACTIONS.UPDATE_IS_NIGHT, type: ACTIONS.UPDATE_IS_NIGHT,
data: { isNight: (() => { data: {
isNight: (() => {
const startNightMoment = moment('19:00', 'HH:mm'); const startNightMoment = moment('19:00', 'HH:mm');
const endNightMoment = moment('8:00', 'HH:mm'); const endNightMoment = moment('8:00', 'HH:mm');
return !(momentNow.isAfter(endNightMoment) && momentNow.isBefore(startNightMoment)); return !(momentNow.isAfter(endNightMoment) && momentNow.isBefore(startNightMoment));
})() })(),
}, },
}; };
} }

View file

@ -93,7 +93,7 @@ reducers[ACTIONS.AUTO_UPDATE_DECLINED] = state => {
return Object.assign({}, state, { return Object.assign({}, state, {
autoUpdateDeclined: true, autoUpdateDeclined: true,
}); });
} };
reducers[ACTIONS.UPGRADE_DOWNLOAD_COMPLETED] = (state, action) => reducers[ACTIONS.UPGRADE_DOWNLOAD_COMPLETED] = (state, action) =>
Object.assign({}, state, { Object.assign({}, state, {

View file

@ -56,9 +56,15 @@ export const selectUpgradeDownloadPath = createSelector(selectState, state => st
export const selectUpgradeDownloadItem = createSelector(selectState, state => state.downloadItem); export const selectUpgradeDownloadItem = createSelector(selectState, state => state.downloadItem);
export const selectAutoUpdateDownloaded = createSelector(selectState, state => state.autoUpdateDownloaded); export const selectAutoUpdateDownloaded = createSelector(
selectState,
state => state.autoUpdateDownloaded
);
export const selectAutoUpdateDeclined = createSelector(selectState, state => state.autoUpdateDeclined); export const selectAutoUpdateDeclined = createSelector(
selectState,
state => state.autoUpdateDeclined
);
export const selectModalsAllowed = createSelector(selectState, state => state.modalsAllowed); export const selectModalsAllowed = createSelector(selectState, state => state.modalsAllowed);

View file

@ -1,4 +1,3 @@
const parseJson = data => JSON.stringify(data); const parseJson = data => JSON.stringify(data);
// No need for an external module: // No need for an external module:
@ -23,9 +22,9 @@ const parseCsv = data => {
return `${getHeaders(data[0])} \n ${getData(data)}`; return `${getHeaders(data[0])} \n ${getData(data)}`;
}; };
export function parseData(data, format) { const parseData = (data, format) => {
// Check for validation // Check for validation
const valid = (data && data[0]) && format; const valid = data && data[0] && format;
// Pick a format // Pick a format
const formats = { const formats = {
csv: list => parseCsv(list), csv: list => parseCsv(list),
@ -33,4 +32,6 @@ export function parseData(data, format) {
}; };
// Return parsed data: JSON || CSV // Return parsed data: JSON || CSV
return valid && formats[format] ? formats[format](data) : undefined; return valid && formats[format] ? formats[format](data) : undefined;
} };
export default parseData;