Commit 4a40015b authored by Luke Schoen's avatar Luke Schoen
Browse files

merge latest from master and fix conflicts

parent 0418e87f
Pipeline #31622 passed with stage
in 1 minute and 48 seconds
......@@ -18,17 +18,6 @@ export default async (fetherApp, event, action, ...args) => {
return;
}
switch (action) {
case 'app-resize': {
if (!fetherApp.win || !args[0]) {
return;
}
const [width] = fetherApp.win.getContentSize();
// Conversion to integer is required to pass as argument to setContentSize.
// Reference: https://electronjs.org/docs/all#winsetcontentsizewidth-height-animate
const newHeight = parseInt(args[0]);
fetherApp.win.setContentSize(width, Math.round(newHeight) + 2);
break;
}
case 'app-right-click': {
if (!fetherApp.win) {
return;
......
......@@ -8,13 +8,7 @@ import Pino from '../utils/pino';
const pino = Pino();
function setupWin32Listeners (fetherApp) {
const {
moveWindowUp,
onWindowClose,
processSaveWinPosition,
showTrayBalloon,
win
} = fetherApp;
const { onWindowClose, processSaveWinPosition, win } = fetherApp;
if (process.platform === 'win32') {
/**
......@@ -52,7 +46,6 @@ function setupWin32Listeners (fetherApp) {
} else if (wParam.readUInt32LE(0) === 0xf030) {
// SC_MAXIMIZE
eventName = 'maximize';
showTrayBalloon(fetherApp);
} else if (wParam.readUInt32LE(0) === 0xf020) {
// SC_MINIMIZE
eventName = 'minimize';
......@@ -60,7 +53,6 @@ function setupWin32Listeners (fetherApp) {
} else if (wParam.readUInt32LE(0) === 0xf120) {
// SC_RESTORE
eventName = 'restored';
showTrayBalloon(fetherApp);
}
if (eventName !== null) {
......@@ -74,17 +66,7 @@ function setupWin32Listeners (fetherApp) {
* Detect event on Windows when Fether window was moved or resized
*/
win.hookWindowMessage(Number.parseInt('0x0232'), (wParam, lParam) => {
pino.info('Detected completion of move or resize event');
// Move Fether window back up into view if it was a resize event
// that causes the bottom to be cropped
moveWindowUp(fetherApp);
// Try again after a delay incase Fether window resize occurs
// x seconds after navigating to a new page.
setTimeout(() => {
moveWindowUp(fetherApp);
}, 5000);
pino.info('Detected completion of moved or resize event');
// Save Fether window position to Electron settings
processSaveWinPosition(fetherApp);
......
......@@ -11,12 +11,7 @@ import Pino from '../utils/pino';
const pino = Pino();
function setupWinListeners (fetherApp) {
const {
moveWindowUp,
onWindowClose,
processSaveWinPosition,
win
} = fetherApp;
const { onWindowClose, processSaveWinPosition, win } = fetherApp;
// Open external links in browser
win.webContents.on('new-window', (event, url) => {
......@@ -24,7 +19,7 @@ function setupWinListeners (fetherApp) {
electron.shell.openExternal(url);
});
// Linux (unchecked on others)
// Windows and Linux (unchecked on others)
win.on('move', () => {
/**
* On Linux using this with debouncing is the closest equivalent
......@@ -49,17 +44,12 @@ function setupWinListeners (fetherApp) {
* On Linux the closest equivalent to achieving 'moved' is debouncing
* on the 'move' event. It also works in 'close' even when app crashes
*/
processSaveWinPosition(fetherApp);
pino.info('Detected moved event');
});
// macOS and Linux (not Windows)
// macOS and Linux and Windows
win.on('resize', () => {
pino.info('Detected resize event');
moveWindowUp(fetherApp);
setTimeout(() => {
moveWindowUp(fetherApp);
}, 5000);
});
win.on('blur', () => {
......
......@@ -16,6 +16,8 @@ function showWindow (fetherApp, trayPos) {
calculateWinPosition,
createWindow,
fixWinPosition,
moveWindowUp,
processSaveWinPosition,
setupWinListeners,
setupWin32Listeners,
win
......@@ -84,6 +86,13 @@ function showWindow (fetherApp, trayPos) {
fetherApp.hasSetupWinListeners = true;
}
moveWindowUp(fetherApp);
setTimeout(() => {
moveWindowUp(fetherApp);
}, 5000);
processSaveWinPosition(fetherApp);
fetherApp.emit('after-show-window');
}
......
......@@ -17,7 +17,7 @@ const INDEX_HTML_PATH =
});
// Icon path differs when started with `yarn electron` or `yarn start`
let iconPath = path.join(staticPath, 'assets', 'icons', 'icon.png');
let iconPath = path.join(staticPath, 'assets', 'icons', 'mac', 'iconDock.png');
let iconDockPath = '';
if (process.platform === 'win32') {
......@@ -71,7 +71,7 @@ const DEFAULT_OPTIONS = {
const TASKBAR_OPTIONS = {
frame: false,
height: 464,
height: 515,
// On Linux the user must click the tray icon and then click the tooltip
// to toggle the Fether window open/close
tooltip: tooltip,
......
......@@ -68,7 +68,8 @@
"react-router-dom": "^4.2.2",
"react-scripts": "^2.1.3",
"recompose": "^0.27.1",
"rxjs": "^6.2.0"
"rxjs": "^6.2.0",
"semver": "^5.6.0"
},
"devDependencies": {
"@babel/plugin-proposal-decorators": "^7.2.0",
......
......@@ -90,7 +90,6 @@ class AccountsList extends Component {
)}
</div>
</div>
<nav className='footer-nav'>
<div className='footer-nav_status'>
<Health />
......
......@@ -11,7 +11,6 @@ export const Feedback = ({ accountsListLength }) => (
href='https://github.com/paritytech/fether/issues/new'
rel='noopener noreferrer'
target='_blank'
style={{ marginBottom: accountsListLength > 1 ? '-2px' : '-10px' }}
>
Feedback
</a>
......
......@@ -168,7 +168,7 @@ class AccountImportOptions extends Component {
{importingFromSigner ? (
<Scanner
onScan={this.handleSignerImported}
label='Please show the QR code of the account on the webcam.'
label='Scan Parity Signer account QR code'
/>
) : (
<button
......@@ -203,15 +203,16 @@ class AccountImportOptions extends Component {
</Card>
);
const spacer = <div style={{ height: '0.5rem' }} />;
return (
<RequireHealthOverlay require='node'>
<div className='center-md'>
{!importingFromSigner && jsonCard}
<br />
{spacer}
{signerCard}
<br />
{spacer}
{!importingFromSigner && phraseCard}
<br />
<p>{error}</p>
<nav className='form-nav -space-around'>
{currentStep > 1 && (
......
......@@ -63,7 +63,7 @@ class CreateAccount extends Component {
const Steps = this.getSteps(isImport);
return (
<div>
<React.Fragment>
<Header
left={
// Show back button if we already have some accounts, so we can go back to AccountsList
......@@ -98,17 +98,7 @@ class CreateAccount extends Component {
</div>
) : (
<div className='footer-nav_option'>
{isImport ? (
<p>
Need to create an account?
<button
className='button -footer'
onClick={this.handleToggleCreateImport}
>
New account
</button>
</p>
) : (
{isImport ? null : (
<p>
Already have an account?
<button
......@@ -122,7 +112,7 @@ class CreateAccount extends Component {
</div>
)}
</nav>
</div>
</React.Fragment>
);
}
}
......
......@@ -13,7 +13,9 @@ import {
} from 'react-router-dom';
import { inject, observer } from 'mobx-react';
import isElectron from 'is-electron';
import ReactResizeDetector from 'react-resize-detector';
import { Modal } from 'fether-ui';
import semver from 'semver';
import { version } from '../../package.json';
import Accounts from '../Accounts';
import BackupAccount from '../BackupAccount';
......@@ -23,29 +25,71 @@ import Send from '../Send';
import Tokens from '../Tokens';
import Whitelist from '../Whitelist';
const currentVersion = version;
// Use MemoryRouter for production viewing in file:// protocol
// https://github.com/facebook/create-react-app/issues/3591
const Router =
process.env.NODE_ENV === 'production' ? MemoryRouter : BrowserRouter;
const electron = isElectron() ? window.require('electron') : null;
@inject('onboardingStore', 'parityStore')
@observer
class App extends Component {
state = {
newRelease: false // false | {name, url, ignore}
};
componentDidMount () {
window.addEventListener('contextmenu', this.handleRightClick);
window
.fetch('https://api.github.com/repos/paritytech/fether/releases/latest')
.then(j => j.json())
.then(({ name, html_url: url, tag_name: tag }) => {
const latestVersion = tag.match(/v(\d+\.\d+(\.\d+)?)/)[1];
if (semver.gt(latestVersion, currentVersion)) {
this.setState({
newRelease: {
name,
url,
ignore: false
}
});
}
})
.catch(e => {
console.error('Error while checking for a new version of Fether:', e);
});
}
componentDidUnmount () {
window.removeEventListener('contextmenu', this.handleRightClick);
}
handleResize = (_, height) => {
if (!electron) {
return;
}
// Send height to main process
electron.ipcRenderer.send('asynchronous-message', 'app-resize', height);
renderModalLinks = () => {
return (
<nav className='form-nav -binary'>
<button className='button -back' onClick={this.hideNewReleaseModal}>
Remind me later
</button>
<button className='button' onClick={this.openNewReleaseUrl}>
Download
</button>
</nav>
);
};
hideNewReleaseModal = () => {
this.setState({
newRelease: { ...this.state.newRelease, ignore: true }
});
};
openNewReleaseUrl = () => {
window.open(this.state.newRelease.url, '_blank', 'noopener noreferrer');
};
handleRightClick = () => {
......@@ -64,6 +108,8 @@ class App extends Component {
parityStore: { api }
} = this.props;
const { newRelease } = this.state;
if (isFirstRun) {
return (
<div className='window'>
......@@ -79,21 +125,24 @@ class App extends Component {
// the children, just a <RequireHealthOverlay />.
if (!api) {
return (
<ReactResizeDetector handleHeight onResize={this.handleResize}>
<RequireHealthOverlay fullscreen require='node'>
{/* Adding these components to have minimum height on window */}
<div className='content'>
<div className='window' />
</div>
</RequireHealthOverlay>
</ReactResizeDetector>
<RequireHealthOverlay fullscreen require='node'>
{/* Adding these components to have minimum height on window */}
<div className='content'>
<div className='window' />
</div>
</RequireHealthOverlay>
);
}
return (
<ReactResizeDetector handleHeight onResize={this.handleResize}>
<div className='content'>
<div className='window'>
<div className='content'>
<div className='window'>
<Modal
title='New version available'
description={newRelease ? `${newRelease.name} was released!` : ''}
visible={newRelease && !newRelease.ignore}
buttons={this.renderModalLinks()}
>
<Router>
<Switch>
{/* The next line is the homepage */}
......@@ -116,9 +165,9 @@ class App extends Component {
<Redirect from='*' to='/' />
</Switch>
</Router>
</div>
</Modal>
</div>
</ReactResizeDetector>
</div>
);
}
}
......
......@@ -49,11 +49,11 @@ class Onboarding extends Component {
render () {
return (
<div>
<React.Fragment>
<Header title={<h1>Terms of Use</h1>} />
<div className='window_content'>
<div className='box -padded-extra'>
<div className='box -padded -scroller'>
<div className='terms-and-conditions-wrapper'>
<FetherForm.Field
as={ReactMarkdown}
......@@ -76,7 +76,7 @@ class Onboarding extends Component {
</button>
</div>
</nav>
</div>
</React.Fragment>
);
}
}
......
......@@ -41,7 +41,7 @@ class HealthModal extends Component {
<Modal
description={this.renderDescription()}
fullscreen={fullscreen}
loading={loading}
icon={loading}
title={this.renderTitle()}
visible={visible}
>
......
......@@ -73,7 +73,7 @@ export default class Scanner extends React.PureComponent {
const size = 300;
return (
<div>
<React.Fragment>
{isLoading ? (
<img alt='loading' src={loading} />
) : webcamError ? (
......@@ -81,11 +81,10 @@ export default class Scanner extends React.PureComponent {
) : (
<div>
<p>{label}</p>
<br />
<QrSigner scan onScan={onScan} size={size} />
</div>
)}
</div>
</React.Fragment>
);
}
}
......@@ -76,7 +76,7 @@ class ScanSignedTx extends Component {
<Card className='-centered'>
<Scanner
onScan={this.onScanSignedTx}
label='Please show the QR code of the signed transaction on the webcam'
label='Show the signed transaction QR code'
/>
{error && <p className='text -standard'>{error}</p>}
......
......@@ -57,8 +57,8 @@ class Sent extends Component {
description={this.renderDescription()}
fullscreen
link={this.renderLink()}
loading={this.renderIcon()}
navigateTo={this.renderGoHomepage()}
icon={this.renderIcon()}
buttons={this.renderGoHomepage()}
title={this.renderTitle()}
visible
/>
......
......@@ -90,9 +90,7 @@ class Unlock extends Component {
render={({ handleSubmit, pristine, submitting }) => (
<form onSubmit={handleSubmit}>
<div className='text'>
<p>
Enter your password to confirm this transaction.
</p>
<p>Unlock account:</p>
</div>
<Field
......
......@@ -66,39 +66,40 @@ class Tokens extends PureComponent {
const { isMenuOpen } = this.state;
return (
<div className='tokens'>
<div className={isMenuOpen ? 'popup-underlay' : ''} />
<AccountHeader
address={address}
copyAddress
name={name}
type={type}
left={
<Link to='/accounts' className='icon -back'>
Back
</Link>
}
right={
<MenuPopup
className='popup-menu-account'
horizontalOffset={1}
menuItems={this.menuItems()}
onClose={this.handleMenuClose}
onOpen={this.handleMenuOpen}
size='small'
trigger={<Clickable className='icon -menu' />}
/>
}
/>
<TokensList />
<React.Fragment>
<div className='tokens'>
<div className={isMenuOpen ? 'popup-underlay' : ''} />
<AccountHeader
address={address}
copyAddress
name={name}
type={type}
left={
<Link to='/accounts' className='icon -back'>
Back
</Link>
}
right={
<MenuPopup
className='popup-menu-account'
horizontalOffset={1}
menuItems={this.menuItems()}
onClose={this.handleMenuClose}
onOpen={this.handleMenuOpen}
size='small'
trigger={<Clickable className='icon -menu' />}
/>
}
/>
<TokensList />
</div>
<nav className='footer-nav'>
<div className='footer-nav_status'>
<Health />
</div>
</nav>
</div>
</React.Fragment>
);
}
}
......
......@@ -31,7 +31,7 @@ class TokensList extends Component {
<Modal
description='Please wait...'
fullscreen={false}
loading={loading}
icon={loading}
title='Loading account tokens...'
visible={isLoadingAccountTokens}
/>
......
......@@ -100,40 +100,35 @@ class Whitelist extends Component {
const displayedTokens = search ? matches : this.props.tokensArrayWithoutEth;
return (
<RequireHealthOverlay require='sync'>
<div>
<Header
left={
<Link
to='/tokens'
className='icon -back'
onClick={history.goBack}
>
Close
</Link>
}
title={<h1>Search tokens</h1>}
/>
<React.Fragment>
<Header
left={
<Link to='/tokens' className='icon -back' onClick={history.goBack}>
Close
</Link>
}
title={<h1>Search tokens</h1>}
/>