Skip to content

Commit

Permalink
Merge pull request #1385 from input-output-hk/feature/ddw-637-add-tim…
Browse files Browse the repository at this point in the history
…estamp-for-paper-wallet-pdf-name

[DDW-637] Add timestamp for Paper wallet
  • Loading branch information
nikolaglumac committed May 20, 2019
2 parents 0995e32 + 5453adb commit 17953da
Show file tree
Hide file tree
Showing 26 changed files with 349 additions and 95 deletions.
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.',
});
}
}

0 comments on commit 17953da

Please sign in to comment.