Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.

Commit 04d45bc

Browse files
committed
Merge branch 'export-logs' of git://github.com/wbobeirne/lightning-app into wbobeirne-export-logs
2 parents 2b93c8a + 60f092f commit 04d45bc

File tree

7 files changed

+140
-5
lines changed

7 files changed

+140
-5
lines changed

src/action/index-mobile.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
NativeModules,
1515
ActionSheetIOS,
1616
NativeEventEmitter,
17+
Share,
1718
} from 'react-native';
1819
import * as Random from 'expo-random';
1920
import * as LocalAuthentication from 'expo-local-authentication';
@@ -47,7 +48,7 @@ store.init(); // initialize computed values
4748
export const db = new AppStorage(store, AsyncStorage);
4849
export const grpc = new GrpcAction(store, NativeModules, NativeEventEmitter);
4950
export const ipc = new IpcAction(grpc);
50-
export const log = new LogAction(store, ipc, false);
51+
export const log = new LogAction(store, ipc, false, RNFS, Share);
5152
export const nav = new NavAction(store, NavigationActions, StackActions);
5253
export const notify = new NotificationAction(store, nav);
5354
export const wallet = new WalletAction(store, grpc, db, nav, notify, RNFS);

src/action/log.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { MAX_LOG_LENGTH } from '../config';
1010
let _store;
1111
let _ipc;
1212
let _printErrObj;
13+
let _FS;
14+
let _Share;
1315

1416
/**
1517
* Log an info event e.g. when something relevant but non-critical happens.
@@ -49,6 +51,49 @@ export function error(...args) {
4951
_ipc && _ipc.send('log-error', null, args);
5052
}
5153

54+
/**
55+
* Gets the location of the LND log file as a string.
56+
* @param {string} network The network the user's on
57+
* @return {string}
58+
*/
59+
export function getLogPath(network) {
60+
if (!_FS) {
61+
throw new Error('Cannot get log path with no FS in action/log.js');
62+
}
63+
const lndDir = _FS.DocumentDirectoryPath;
64+
return `${lndDir}/logs/bitcoin/${network}/lnd.log`;
65+
}
66+
67+
/**
68+
* Retrieves the entire LND log file as a string.
69+
* @return {Promise<string>}
70+
*/
71+
export async function getLogs() {
72+
if (!_FS) {
73+
throw new Error('Cannot get logs with no FS in action/log.js');
74+
}
75+
return _FS.readFile(getLogPath(_store.network), 'utf8');
76+
}
77+
78+
/**
79+
* Shares the log file using whatever native share function we have.
80+
* @return {Promise}
81+
*/
82+
export async function shareLogs() {
83+
try {
84+
if (!_Share) {
85+
throw new Error('Cannot share logs with no Share in action/log.js');
86+
}
87+
const logs = await getLogs();
88+
return _Share.share({
89+
title: 'Lightning App logs',
90+
message: logs,
91+
});
92+
} catch (err) {
93+
error(err.message);
94+
}
95+
}
96+
5297
function pushLogs(message) {
5398
if (!_store) return;
5499
_store.logs += '\n' + message.replace(/\s+$/, '');
@@ -59,10 +104,12 @@ function pushLogs(message) {
59104
}
60105

61106
class LogAction {
62-
constructor(store, ipc, printErrObj = true) {
107+
constructor(store, ipc, printErrObj = true, FS, Share) {
63108
_store = store;
64109
_ipc = ipc;
65110
_printErrObj = printErrObj;
111+
_FS = FS;
112+
_Share = Share;
66113
_ipc.listen('logs', (event, message) => pushLogs(message));
67114
_ipc.send('logs-ready', null, true);
68115
}

src/asset/icon/share.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import Svg, { G, Path } from '../../component/svg';
3+
const Share = props => (
4+
<Svg viewBox="0 0 44 47" width="1em" height="1em" {...props}>
5+
<G fill="#FFFFFF" fillRule="nonzero">
6+
<Path
7+
d="M20.2823393,45.8089582 L8.6208326,33.9983559 C6.7386017,32.112201 9.561948,29.2839686 11.4431809,31.1701236 L19.3034582,39.0535264 C19.6891903,39.4403934 20.0040283,39.3079355 20.0040283,38.7550254 L20.0040283,17.0049295 C20.0040283,15.8957094 20.8976693,14.9999992 22.0000313,14.9999992 C23.1100789,14.9999992 23.9960344,15.8976371 23.9960344,17.0049295 L23.9960344,38.7550254 C23.9960344,39.3053352 24.3096903,39.4415789 24.6966045,39.0535264 L32.5568818,31.1701236 C34.4391127,29.2839686 37.261461,32.112201 35.3792301,33.9983559 L23.7942534,45.7949866 C22.8210143,46.7860065 21.2509472,46.7899499 20.2823393,45.8089582 Z M4,4.99702929 L4,11.9999992 C4,13.1045687 3.1045695,13.9999992 2,13.9999992 C0.8954305,13.9999992 -7.10542736e-15,13.1045687 -7.10542736e-15,11.9999992 L-7.10542736e-15,2.99539679 C-7.10542736e-15,1.33499009 1.3506856,-8.07948688e-07 3.0087752,-8.07948688e-07 L40.9912248,-8.07948688e-07 C42.6465785,-8.07948688e-07 44,1.34474899 44,2.99539679 L44,11.9999992 C44,13.1045687 43.1045695,13.9999992 42,13.9999992 C40.8954305,13.9999992 40,13.1045687 40,11.9999992 L40,4.99702929 C40,4.45303549 39.552097,3.99999919 38.9995806,3.99999919 L5.0004194,3.99999919 C4.455173,3.99999919 4,4.44638479 4,4.99702929 Z"
8+
transform="translate(22.000000, 23.270746) rotate(180.000000) translate(-22.000000, -23.270746)"
9+
/>
10+
</G>
11+
</Svg>
12+
);
13+
14+
export default Share;

src/asset/icon/share.svg

Lines changed: 6 additions & 0 deletions
Loading

src/component/button.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import BackIcon from '../asset/icon/back';
1313
import CancelIcon from '../asset/icon/cancel';
1414
import PlusIcon from '../asset/icon/plus';
1515
import QrIcon from '../asset/icon/qr';
16+
import ShareIcon from '../asset/icon/share';
1617
import { color, font } from './style';
1718

1819
//
@@ -558,4 +559,20 @@ MaxButton.propTypes = {
558559
style: ViewPropTypes.style,
559560
};
560561

562+
//
563+
// Share Button
564+
//
565+
566+
export const ShareButton = ({ onPress, disabled, style }) => (
567+
<Button onPress={onPress} disabled={disabled} style={style}>
568+
<ShareIcon height={22} width={16} />
569+
</Button>
570+
);
571+
572+
ShareButton.propTypes = {
573+
onPress: PropTypes.func,
574+
disabled: PropTypes.bool,
575+
style: ViewPropTypes.style,
576+
};
577+
561578
export default Button;

src/view/cli.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import React, { Component } from 'react';
2-
import { ScrollView, StyleSheet } from 'react-native';
2+
import { ScrollView, StyleSheet, Platform } from 'react-native';
33
import { observer } from 'mobx-react';
44
import PropTypes from 'prop-types';
55
import { SplitBackground } from '../component/background';
66
import { Header, Title } from '../component/header';
77
import Text from '../component/text';
8-
import { Button, BackButton } from '../component/button';
8+
import { BackButton, ShareButton, Button } from '../component/button';
99
import { createStyles, maxWidth } from '../component/media-query';
1010
import { color, font, breakWidth } from '../component/style';
11+
import { shareLogs } from '../action/log';
1112

1213
//
1314
// CLI View
@@ -24,7 +25,11 @@ const CLIView = ({ store, nav }) => (
2425
<Header separator style={styles.header}>
2526
<BackButton onPress={() => nav.goSettings()} />
2627
<Title title="Logs" />
27-
<Button disabled onPress={() => {}} />
28+
{Platform.OS === 'web' ? (
29+
<Button onPress={() => {}} />
30+
) : (
31+
<ShareButton onPress={() => shareLogs()} />
32+
)}
2833
</Header>
2934
<LogOutput logs={store.logs} />
3035
</SplitBackground>

test/unit/action/log.spec.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,22 @@ describe('Action Logs Unit Tests', () => {
99
let sandbox;
1010
let ipc;
1111
let ipcRenderer;
12+
let RNFS;
13+
let Share;
1214

1315
beforeEach(() => {
1416
sandbox = sinon.createSandbox({});
1517
ipcRenderer = {
1618
send: sinon.stub(),
1719
on: sinon.stub().yields('some-event', 'some-arg'),
1820
};
21+
RNFS = {
22+
DocumentDirectoryPath: '/foo/bar',
23+
readFile: sinon.stub().resolves('logs'),
24+
};
25+
Share = {
26+
share: sinon.stub().resolves(true),
27+
};
1928
});
2029

2130
afterEach(() => {
@@ -115,5 +124,41 @@ describe('Action Logs Unit Tests', () => {
115124
expect(store.logs.length, 'to equal', 56);
116125
});
117126
});
127+
128+
describe('getLogPath()', () => {
129+
it('should throw without a FS constructor argument', () => {
130+
expect(() => log.getLogPath(), 'to throw');
131+
});
132+
133+
it('should return a log path with FS constructor argument', () => {
134+
new LogAction(store, ipc, false, RNFS);
135+
expect(log.getLogPath(), 'to be a string');
136+
});
137+
});
138+
139+
describe('getLogs()', () => {
140+
it('should throw without a FS constructor argument', async () => {
141+
await expect(log.getLogs(), 'to be rejected');
142+
});
143+
144+
it('should return logs with an FS constructor argument', async () => {
145+
new LogAction(store, ipc, false, RNFS);
146+
expect(await log.getLogs(), 'to be a string');
147+
expect(RNFS.readFile.called, 'to be true');
148+
});
149+
});
150+
151+
describe('shareLogs()', () => {
152+
it('should resolve false-y without Share or FS constructor arguments', async () => {
153+
expect(await log.shareLogs(), 'not to be ok');
154+
});
155+
156+
it('should share the logs with a Share and FS constructor argument', async () => {
157+
new LogAction(store, ipc, false, RNFS, Share);
158+
expect(await log.shareLogs(), 'to be ok');
159+
expect(RNFS.readFile.called, 'to be true');
160+
expect(Share.share.called, 'to be true');
161+
});
162+
});
118163
});
119164
});

0 commit comments

Comments
 (0)