From c713fde3f43f1a4119054fc18d816f42af641375 Mon Sep 17 00:00:00 2001 From: Karl Ranna Date: Mon, 17 Sep 2018 13:47:05 +0300 Subject: [PATCH 1/5] Add channel force delete story --- src/view/channel-force-delete.js | 55 ++++++++++++++++++++++++++++++++ stories/screen-story.js | 4 +++ 2 files changed, 59 insertions(+) create mode 100644 src/view/channel-force-delete.js diff --git a/src/view/channel-force-delete.js b/src/view/channel-force-delete.js new file mode 100644 index 000000000..bf01e9c07 --- /dev/null +++ b/src/view/channel-force-delete.js @@ -0,0 +1,55 @@ +import React from 'react'; +import { StyleSheet } from 'react-native'; +import { observer } from 'mobx-react'; +import PropTypes from 'prop-types'; +import Background from '../component/background'; +import MainContent from '../component/main-content'; +import { H1Text, CopyText } from '../component/text'; +import { FormStretcher } from '../component/form'; +import { Button, ButtonText, PillButton } from '../component/button'; +import { color } from '../component/style'; + +const styles = StyleSheet.create({ + copyTxt: { + marginTop: 10, + }, + deleteBtn: { + alignSelf: 'center', + backgroundColor: color.glas, + width: 400, + }, + cancelBtn: { + marginTop: 5, + marginBottom: 25, + }, +}); + +const ChannelForceDeleteView = ({ nav, channel }) => ( + + + + Force close channel? + + Mutual close of the channel failed. Force closing the channel will + result in a delay to access your funds. + + + channel.closeSelectedChannel()} + > + Force close this channel + + + + +); + +ChannelForceDeleteView.propTypes = { + nav: PropTypes.object.isRequired, + channel: PropTypes.object.isRequired, +}; + +export default observer(ChannelForceDeleteView); diff --git a/stories/screen-story.js b/stories/screen-story.js index 689b31a1b..72e1e27fa 100644 --- a/stories/screen-story.js +++ b/stories/screen-story.js @@ -27,6 +27,7 @@ import TransactionDetail from '../src/view/transaction-detail'; import Channel from '../src/view/channel'; import ChannelDetail from '../src/view/channel-detail'; import ChannelDelete from '../src/view/channel-delete'; +import ChannelForceDelete from '../src/view/channel-force-delete'; import ChannelCreate from '../src/view/channel-create'; import Home from '../src/view/home'; import Deposit from '../src/view/deposit'; @@ -138,6 +139,9 @@ storiesOf('Screens', module) .add('Channel Delete', () => ( )) + .add('Channel Force Delete', () => ( + + )) .add('Channel Create', () => ( )) From 3b99c4917ce354d23a0d4a04635d6126d9c64965 Mon Sep 17 00:00:00 2001 From: Karl Ranna Date: Mon, 17 Sep 2018 13:49:34 +0300 Subject: [PATCH 2/5] Add ChannelForceDelete route --- src/action/nav.js | 4 ++++ src/view/main.js | 4 ++++ test/unit/action/nav.spec.js | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/src/action/nav.js b/src/action/nav.js index cba7268ad..f52502404 100644 --- a/src/action/nav.js +++ b/src/action/nav.js @@ -110,6 +110,10 @@ class NavAction { this._store.route = 'ChannelDelete'; } + goChannelForceDelete() { + this._store.route = 'ChannelForceDelete'; + } + goChannelCreate() { this._store.route = 'ChannelCreate'; } diff --git a/src/view/main.js b/src/view/main.js index 00d41ca0a..f71f66ba8 100644 --- a/src/view/main.js +++ b/src/view/main.js @@ -29,6 +29,7 @@ import Deposit from './deposit'; import Channel from './channel'; import ChannelDetail from './channel-detail'; import ChannelDelete from './channel-delete'; +import ChannelForceDelete from './channel-force-delete'; import ChannelCreate from './channel-create'; import Transaction from './transaction'; import Setting from './setting'; @@ -142,6 +143,9 @@ class MainView extends Component { {route === 'ChannelDelete' && ( )} + {route === 'ChannelForceDelete' && ( + + )} {route === 'ChannelCreate' && ( )} diff --git a/test/unit/action/nav.spec.js b/test/unit/action/nav.spec.js index 54163d50f..4de7feb25 100644 --- a/test/unit/action/nav.spec.js +++ b/test/unit/action/nav.spec.js @@ -195,6 +195,13 @@ describe('Action Nav Unit Tests', () => { }); }); + describe('goChannelForceDelete()', () => { + it('should set correct route', () => { + nav.goChannelForceDelete(); + expect(store.route, 'to equal', 'ChannelForceDelete'); + }); + }); + describe('goTransactions()', () => { it('should set correct route', () => { nav.goTransactions(); From 90246fb59d5b84ec6941a7385c96c610b73ffcea Mon Sep 17 00:00:00 2001 From: Karl Ranna Date: Mon, 17 Sep 2018 15:22:55 +0300 Subject: [PATCH 3/5] Navigate to ChannelForceDelete in case of channel link not found --- src/action/channel.js | 10 +++++++++- test/unit/action/channel.spec.js | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/action/channel.js b/src/action/channel.js index ed6949483..fb0c18a13 100644 --- a/src/action/channel.js +++ b/src/action/channel.js @@ -265,7 +265,15 @@ class ChannelAction { this._nav.goChannels(); await this.closeChannel({ channelPoint: selectedChannel.channelPoint }); } catch (err) { - this._notification.display({ msg: 'Closing channel failed!', err }); + if ( + err && + err.details && + err.details.includes('try force closing it instead') + ) { + this._nav.goChannelForceDelete(); + } else { + this._notification.display({ msg: 'Closing channel failed!', err }); + } } } diff --git a/test/unit/action/channel.spec.js b/test/unit/action/channel.spec.js index 7f284d7dc..7690881d5 100644 --- a/test/unit/action/channel.spec.js +++ b/test/unit/action/channel.spec.js @@ -274,6 +274,18 @@ describe('Action Channels Unit Tests', () => { }); }); + it('should navigate to force delete channel view in case of channel link error', async () => { + channel.closeChannel.rejects({ + code: 2, + details: + 'unable to gracefully close channel while peer is offline (try force closing it instead): channel link not found', + }); + await channel.closeSelectedChannel(); + expect(nav.goChannels, 'was called once'); + expect(notification.display, 'was not called'); + expect(nav.goChannelForceDelete, 'was called once'); + }); + it('should display notification in case of error event', async () => { channel.closeChannel.rejects(new Error('Boom!')); await channel.closeSelectedChannel(); From eb2272e4591cb5cd5a122a1bb93fc286ae936e92 Mon Sep 17 00:00:00 2001 From: Karl Ranna Date: Mon, 17 Sep 2018 15:35:26 +0300 Subject: [PATCH 4/5] Enable force closing channel via closeSelectedChannel action --- src/action/channel.js | 7 +++++-- src/view/channel-force-delete.js | 2 +- test/unit/action/channel.spec.js | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/action/channel.js b/src/action/channel.js index fb0c18a13..ed7df7e82 100644 --- a/src/action/channel.js +++ b/src/action/channel.js @@ -259,11 +259,14 @@ class ChannelAction { * the necessary error handling and notification display. * @return {Promise} */ - async closeSelectedChannel() { + async closeSelectedChannel(force = false) { try { const { selectedChannel } = this._store; this._nav.goChannels(); - await this.closeChannel({ channelPoint: selectedChannel.channelPoint }); + await this.closeChannel({ + channelPoint: selectedChannel.channelPoint, + force, + }); } catch (err) { if ( err && diff --git a/src/view/channel-force-delete.js b/src/view/channel-force-delete.js index bf01e9c07..980126352 100644 --- a/src/view/channel-force-delete.js +++ b/src/view/channel-force-delete.js @@ -36,7 +36,7 @@ const ChannelForceDeleteView = ({ nav, channel }) => ( channel.closeSelectedChannel()} + onPress={() => channel.closeSelectedChannel(true)} > Force close this channel diff --git a/test/unit/action/channel.spec.js b/test/unit/action/channel.spec.js index 7690881d5..64c700028 100644 --- a/test/unit/action/channel.spec.js +++ b/test/unit/action/channel.spec.js @@ -274,6 +274,15 @@ describe('Action Channels Unit Tests', () => { }); }); + it('should force close open channel and navigate to channels view', async () => { + await channel.closeSelectedChannel(true); + expect(nav.goChannels, 'was called once'); + expect(channel.closeChannel, 'was called with', { + channelPoint: 'some-channel-point', + force: true, + }); + }); + it('should navigate to force delete channel view in case of channel link error', async () => { channel.closeChannel.rejects({ code: 2, From 1b57465964c116a67b5644046faf145608e477c5 Mon Sep 17 00:00:00 2001 From: Karl Ranna Date: Mon, 17 Sep 2018 16:40:46 +0300 Subject: [PATCH 5/5] Disable close channel button when already closing --- src/computed/channel.js | 1 + src/view/channel-detail.js | 6 +++++- stories/screen-story.js | 11 +++++++++++ test/unit/computed/channel.spec.js | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/computed/channel.js b/src/computed/channel.js index 6b99cabbe..8f5efbcbf 100644 --- a/src/computed/channel.js +++ b/src/computed/channel.js @@ -17,6 +17,7 @@ const ComputedChannel = store => { ); all.sort((a, b) => (a.active === b.active ? 0 : a.active ? -1 : 1)); all.forEach(c => { + c.isClosing = !c.status.includes('open'); c.statusLabel = toCaps(c.status); c.capacityLabel = toAmountLabel(c.capacity, settings); c.localBalanceLabel = toAmountLabel(c.localBalance, settings); diff --git a/src/view/channel-detail.js b/src/view/channel-detail.js index 0fa80bc9d..4bf658ae1 100644 --- a/src/view/channel-detail.js +++ b/src/view/channel-detail.js @@ -49,7 +49,11 @@ const ChannelDetailView = ({ store, nav }) => ( {store.selectedChannel.localBalanceLabel} {store.unitLabel} - diff --git a/stories/screen-story.js b/stories/screen-story.js index 72e1e27fa..ceccaea34 100644 --- a/stories/screen-story.js +++ b/stories/screen-story.js @@ -136,6 +136,17 @@ storiesOf('Screens', module) )) .add('Channel Details', () => ) + .add('Channel Details (Closing)', () => ( + c.status === 'pending-closing' + ), + }} + nav={nav} + /> + )) .add('Channel Delete', () => ( )) diff --git a/test/unit/computed/channel.spec.js b/test/unit/computed/channel.spec.js index e374f62e4..9dafb1c12 100644 --- a/test/unit/computed/channel.spec.js +++ b/test/unit/computed/channel.spec.js @@ -54,6 +54,7 @@ describe('Computed Channels Unit Tests', () => { expect(store.computedChannels.length, 'to equal', 3); const ch = store.computedChannels.find(t => t.id === '0'); expect(ch.statusLabel, 'to equal', 'Open'); + expect(ch.isClosing, 'to equal', false); expect(ch.capacityLabel, 'to match', /0[,.]02005/); expect(ch.localBalanceLabel, 'to match', /0[,.]0199/); expect(ch.remoteBalanceLabel, 'to match', /0[,.]0001/); @@ -61,6 +62,8 @@ describe('Computed Channels Unit Tests', () => { expect(store.channelBalancePendingLabel, 'to match', /0[,.]006/); expect(store.channelBalanceClosingLabel, 'to match', /0[,.]005/); expect(store.showChannelAlert, 'to equal', false); + const ch2 = store.computedChannels.find(t => t.id === '2'); + expect(ch2.isClosing, 'to equal', true); }); it('should channel values in usd', () => {