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

[DDW-637] Add timestamp for Paper wallet #1385

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b189393
[DDW-637] Add timestamp for Paper wallet PDF name
thedanheller May 6, 2019
182b9cc
[DDW-637] CHANGELOG
thedanheller May 6, 2019
25585ce
[DDW-637] Printing timestamp on PDF - missing locale time
thedanheller May 7, 2019
0202bfd
[DDW-637] Locale date format
thedanheller May 8, 2019
f9b866f
[DDW-637] Correct locale date format
thedanheller May 9, 2019
b3aab27
[DDW-637] Test handling open PDF error
thedanheller May 10, 2019
c43e6de
Merge branch 'develop' into feature/ddw-637-add-timestamp-for-paper-w…
thedanheller May 10, 2019
664a75e
[DDW-637] Error debugging
thedanheller May 10, 2019
c4d5afb
[DDW-637] Error debugging 2
thedanheller May 10, 2019
137694d
[DDW-637] Error debugging 3
thedanheller May 13, 2019
ead9b5f
Merge remote-tracking branch 'origin/develop' into feature/ddw-637-ad…
thedanheller May 13, 2019
4dc5e41
[DDW-637] Handling open file error
thedanheller May 13, 2019
e8c4104
[DDW-637] Handling open file error - style adjustment
thedanheller May 13, 2019
b0c605e
[DDW-637] Handling open file error - adjustment
thedanheller May 14, 2019
553b66a
[DDW-637] Handling open file error - style adjustment
thedanheller May 14, 2019
3c31138
Merge remote-tracking branch 'origin/develop' into feature/ddw-637-ad…
thedanheller May 14, 2019
ce07727
Merge branch 'develop' into feature/ddw-637-add-timestamp-for-paper-w…
nikolaglumac May 15, 2019
739c11a
[DDW-637] Reset error when restarting node and remove UTXO error
thedanheller May 15, 2019
ad57e88
[DDW-637] Merge develop
thedanheller May 15, 2019
9c9c34e
Merge branch 'feature/ddw-637-add-timestamp-for-paper-wallet-pdf-name…
thedanheller May 15, 2019
f6fcaed
[DDW-637] Fix broken Download logs
thedanheller May 16, 2019
7daf3fe
[637] Unit tests for generateFileNameWithTimestamp
thedanheller May 16, 2019
80031ab
Merge branch 'develop' into feature/ddw-637-add-timestamp-for-paper-w…
thedanheller May 16, 2019
d4c9551
Merge branch 'develop' into feature/ddw-637-add-timestamp-for-paper-w…
nikolaglumac May 17, 2019
ec792ec
[DDW-637] Fix time never changing
thedanheller May 17, 2019
3115919
Merge branch 'feature/ddw-637-add-timestamp-for-paper-wallet-pdf-name…
thedanheller May 17, 2019
5453adb
Merge branch 'develop' into feature/ddw-637-add-timestamp-for-paper-w…
thedanheller May 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Changelog

### Features

- Implemented timestamp on paper wallet PDF's ([PR 1385](https://github.com/input-output-hk/daedalus/pull/1385))
- Implemented "Start of decentralisation notification" UI ([PR 1390](https://github.com/input-output-hk/daedalus/pull/1390))
- Implemented the wallet UTxO statistics screen ([PR 1353](https://github.com/input-output-hk/daedalus/pull/1353))
- Implemented scroll error message into view ([PR 1383](https://github.com/input-output-hk/daedalus/pull/1383))
Expand Down
21 changes: 21 additions & 0 deletions features/generate-filename-with-timestamp.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@unit
Feature: Generate fileName with timestamp

Scenario: I don't pass any props and use the default ones
Given I dont pass any props to the function
Then the prefix should be "logs"
And the extention should be "zip"
And the time should be converted into UTC

Scenario Outline: I pass the following props
Given I pass the following props to the function:
| prefix | extention | isUTC |
| <prefix> | <extention> | <isUTC> |
Then the prefix should be "<prefix>"
And the extention should be "<extention>"
And the time <isUTC> be converted into UTC

Examples:
| prefix | extention | isUTC |
| testPrefix | docx | should |
| | pdf | shouldn't |
7 changes: 4 additions & 3 deletions features/tests/e2e/helpers/screenshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import path from 'path';
import { generateFileNameWithTimestamp } from '../../../../source/common/utils/files';
import ensureDirectoryExists from '../../../../source/main/utils/ensureDirectoryExists';

export const generateScreenshotFilePath = testName => {
const filePath = path.resolve(__dirname, '../screenshots', testName);
const fileName = generateFileNameWithTimestamp(testName, 'png');
export const generateScreenshotFilePath = prefix => {
const filePath = path.resolve(__dirname, '../screenshots', prefix);
const extension = 'png';
const fileName = generateFileNameWithTimestamp({ prefix, extension });
ensureDirectoryExists(filePath);
return `${filePath}/${fileName}`;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Given, When, Then } from 'cucumber';
import { expect } from 'chai';
import { pickBy, identity } from 'lodash';
import {
generateFileNameWithTimestamp,
defaultProps,
} from '../../../../source/common/utils/files';

const getDataFromFunction = props => {
const filename = generateFileNameWithTimestamp(props);
const prefix = filename.match(/^[^-]*[^ -]/i)[0];
const extention = filename.match(/\.[0-9a-z]+$/i)[0].replace('.', '');
const isUTC = !!filename.match(`Z.${extention}`);
return {
filename,
prefix,
extention,
isUTC,
};
};

Given('I dont pass any props to the function', function() {
const { filename, extention, prefix, isUTC } = getDataFromFunction();
this.context.filename = filename;
this.context.extention = extention;
this.context.prefix = prefix;
this.context.isUTC = isUTC;
});

Given('I pass the following props to the function:', function(data) {
let [expectedProps] = data.hashes();
expectedProps = pickBy(expectedProps, identity);
if (expectedProps.isUTC)
expectedProps.isUTC = Boolean(expectedProps.isUTC === 'should');
const { filename, extention, prefix, isUTC } = getDataFromFunction(
expectedProps
);
this.context.filename = filename;
this.context.extention = extention;
this.context.prefix = prefix;
this.context.isUTC = isUTC;
});

Then('the prefix should be {string}', function(prefix) {
const expectedPrefix = prefix || defaultProps.prefix;
expect(this.context.prefix).to.equal(expectedPrefix);
});

Then('the extention should be {string}', function(extention) {
const expectedExtention = extention || defaultProps.extention;
expect(this.context.extention).to.equal(expectedExtention);
});

Then(/^the time (should|shouldn't) be converted into UTC/, function(state) {
const isUTC = state === 'should';
expect(isUTC).to.equal(this.context.isUTC);
});
1 change: 1 addition & 0 deletions source/common/types/paper-wallet-request.types.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type GeneratePaperWalletParams = {
mnemonics: Array<string>,
isMainnet: boolean,
buildLabel: string,
timestamp: string,
messages: {
walletAddressLabel: string,
recoveryPhraseLabel: string,
Expand Down
37 changes: 31 additions & 6 deletions source/common/utils/files.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
// @flow
import moment from 'moment';

export const generateFileNameWithTimestamp = (
prefix: string = 'logs',
fileType: string = 'zip'
) => `${prefix}-${moment.utc().format('YYYY-MM-DDTHHmmss.0SSS')}Z.${fileType}`;
export const defaultProps = {
prefix: 'logs',
extention: 'zip',
isUTC: true,
};

type Props = {
prefix?: string,
extention?: string,
date?: moment,
isUTC?: boolean,
};

export const generateFileNameWithTimestamp = (props?: Props = {}) => {
const { prefix, extention, isUTC } = {
...defaultProps,
...props,
};
let date = props.date || moment();
let z = '';
if (isUTC === true) {
if (!props || !Object.prototype.hasOwnProperty.call(props, 'date'))
date = date.utc();
z = 'Z';
}
return `${prefix}-${`${date.format('YYYY-MM-DDTHHmmss.0SSS')}${z}`}${
extention ? '.' : ''
}${extention}`;
};

export const isFileNameWithTimestamp = (
prefix: string = 'logs',
fileType: string = 'zip'
extention: string = 'zip'
) => (fileName: string) =>
fileName.match(
RegExp(
`(${prefix}-)([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{6}.0[0-9]{3}Z)(.${fileType})`
`(${prefix}-)([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{6}.0[0-9]{3}Z)(.${extention})`
)
);

Expand Down
7 changes: 6 additions & 1 deletion source/main/ipc/generatePaperWalletChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const handlePaperWalletRequests = () => {
filePath,
isMainnet,
messages,
timestamp,
} = request;

// Helpers
Expand Down Expand Up @@ -77,9 +78,13 @@ export const handlePaperWalletRequests = () => {
isMainnet ? paperWalletPage1Path : paperWalletPage1PathTestnet
);

doc.fillColor(textColor);

// Timestamp
doc.fontSize(8).text(timestamp, 119, 484);

doc.image(page1Uri, 0, 0, { fit: [width, height] });
doc.rotate(180, { origin: [width / 2, height / 2] });
doc.fillColor(textColor);
doc.fontSize(10).text(messages.walletAddressLabel, 0, 160, {
width: 595,
align: 'center',
Expand Down
1 change: 1 addition & 0 deletions source/renderer/app/actions/network-status-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ import Action from './lib/Action';
export default class NetworkStatusActions {
isSyncedAndReady: Action<any> = new Action();
tlsConfigIsReady: Action<any> = new Action();
restartNode: Action<any> = new Action();
}
2 changes: 1 addition & 1 deletion source/renderer/app/components/status/NetworkStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ export default class NetworkStatus extends Component<Props, State> {

restartNode = () => {
this.setState({ isNodeRestarting: true });
this.props.onRestartNode();
this.props.onRestartNode.trigger();
};

getClass = (isTrue: boolean) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { defineMessages, intlShape, FormattedMessage } from 'react-intl';
import Dialog from '../../widgets/Dialog';
import DialogCloseButton from '../../widgets/DialogCloseButton';
import { getNetworkExplorerUrl } from '../../../utils/network';
import LocalizableError from '../../../i18n/LocalizableError';
import styles from './InstructionsDialog.scss';
import { handleFormErrors } from '../../../utils/ReactToolboxMobxForm';
import {
PAPER_WALLET_RECOVERY_PHRASE_WORD_COUNT,
PAPER_WALLET_WRITTEN_WORDS_COUNT,
Expand Down Expand Up @@ -109,6 +111,7 @@ type Props = {
onClose: Function,
onOpenExternalLink: Function,
onPrint: Function,
error?: ?LocalizableError,
};

@observer
Expand All @@ -121,6 +124,12 @@ export default class InstructionsDialog extends Component<Props> {
network: DEVELOPMENT,
};

componentWillReceiveProps(newProps: Props) {
if (!this.props.error && newProps.error) {
handleFormErrors('.InstructionsDialog_error', { focusElement: true });
}
}

render() {
const { intl } = this.context;
const {
Expand All @@ -129,6 +138,7 @@ export default class InstructionsDialog extends Component<Props> {
inProgress,
onOpenExternalLink,
network,
error,
} = this.props;
const dialogClasses = classnames([styles.component, 'instructionsDialog']);

Expand Down Expand Up @@ -213,6 +223,8 @@ export default class InstructionsDialog extends Component<Props> {
<p className={styles.printingInstructions}>
<strong>{intl.formatMessage(messages.printingInstructions)}</strong>
</p>

{error && <p className={styles.error}>{intl.formatMessage(error)}</p>}
</div>
</Dialog>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import '../../../themes/mixins/loading-spinner';
@import '../../../themes/mixins/error-message';

.instructionsContentWrapper {
color: var(--theme-instructions-text-color);
Expand Down Expand Up @@ -59,6 +60,12 @@
}
}

.error {
@include error-message;
margin-top: 20px;
text-align: center;
}

.submitButtonSpinning {
box-shadow: none !important;
@include loading-spinner('../../../assets/images/spinner-light.svg');
Expand Down
2 changes: 0 additions & 2 deletions source/renderer/app/components/wallet/utxo/WalletUtxo.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// @flow
import React, { Component, Fragment } from 'react';
import { observer } from 'mobx-react';
import BigNumber from 'bignumber.js';
import classnames from 'classnames';
import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl';
Expand Down Expand Up @@ -62,7 +61,6 @@ type State = {
isHoveringChart: boolean,
};

@observer
export default class WalletUtxo extends Component<Props, State> {
static contextTypes = {
intl: intlShape.isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default class NetworkStatusDialog extends Component<Props> {
render() {
const { actions, stores } = this.props;
const { closeNetworkStatusDialog } = actions.app;
const { restartNode } = actions.networkStatus;
const { app, networkStatus } = stores;
const { openExternalLink } = app;
const {
Expand All @@ -40,7 +41,6 @@ export default class NetworkStatusDialog extends Component<Props> {
networkBlockHeight,
latestLocalBlockTimestamp,
latestNetworkBlockTimestamp,
restartNode,
isSystemTimeIgnored,
environment,
} = networkStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
// @flow
import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';
import { defineMessages, intlShape } from 'react-intl';
import moment from 'moment';
import InstructionsDialog from '../../../../components/wallet/paper-wallet-certificate/InstructionsDialog';
import type { InjectedDialogContainerProps } from '../../../../types/injectedPropsType';
import { generateFileNameWithTimestamp } from '../../../../../../common/utils/files';

const messages = defineMessages({
timestamp: {
id: 'paper.wallet.create.certificate.instructions.dialog.timestamp',
defaultMessage: '!!!MMMM D, YYYY - h:mm A',
description: 'Timestamp for the Paper Wallet PDF content.',
},
});

type Props = InjectedDialogContainerProps;

@inject('stores', 'actions')
@observer
export default class InstructionsDialogContainer extends Component<Props> {
static contextTypes = {
intl: intlShape.isRequired,
};
static defaultProps = {
actions: null,
stores: null,
Expand All @@ -17,12 +31,32 @@ export default class InstructionsDialogContainer extends Component<Props> {
};

onPrint = () => {
const { intl } = this.context;
const date = moment();
const locale = this.props.stores.profile.currentLocale;

moment.updateLocale(locale, {
longDateFormat: {
pdfTimestamp: intl.formatMessage(messages.timestamp),
},
});
const localizedDateFormat = moment()
.localeData()
.longDateFormat('pdfTimestamp');
const timestamp = moment(date).format(localizedDateFormat);
const name = generateFileNameWithTimestamp({
prefix: 'paper-wallet-certificate',
date,
extention: '',
isUTC: false,
});

// TODO: refactor this direct access to the dialog api
const filePath = global.dialog.showSaveDialog({
defaultPath: 'paper-wallet-certificate.pdf',
defaultPath: `${name}.pdf`,
filters: [
{
name: 'paper-wallet-certificate',
name,
extensions: ['pdf'],
},
],
Expand All @@ -31,7 +65,10 @@ export default class InstructionsDialogContainer extends Component<Props> {
// if cancel button is clicked or path is empty
if (!filePath) return;

this.props.actions.wallets.generateCertificate.trigger({ filePath });
this.props.actions.wallets.generateCertificate.trigger({
filePath,
timestamp,
});
};

render() {
Expand All @@ -43,6 +80,7 @@ export default class InstructionsDialogContainer extends Component<Props> {
return (
<InstructionsDialog
inProgress={wallets.generatingCertificateInProgress}
error={wallets.generatingCertificateError}
network={network}
onPrint={this.onPrint}
onClose={this.props.onClose}
Expand Down
10 changes: 10 additions & 0 deletions source/renderer/app/i18n/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,13 @@ export class WalletSupportRequestLogsCompressError extends LocalizableError {
});
}
}

export class WalletPaperWalletOpenPdfError extends LocalizableError {
constructor() {
super({
id: 'global.errors.paperWalletOpenPdfError',
defaultMessage:
'!!!The file you are trying to replace is open. Please close it and try again.',
});
}
}