Merge pull request #1314 from Ride-The-Lightning/Release-0.14.1

Release 0.14.1
pull/1315/head v0.14.1
ShahanaFarooqui 7 months ago committed by GitHub
commit d083be1196
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,7 +1,7 @@
{
"root": true,
"ignorePatterns": [
"src/**/*.js"
"backend/**/*.js"
],
"overrides": [
{
@ -20,17 +20,18 @@
"extends": [
"plugin:@angular-eslint/all",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/recommended--extra"
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"deprecation/deprecation": "error",
"@angular-eslint/prefer-on-push-component-change-detection": "off",
"@angular-eslint/prefer-standalone-component": "off",
"@angular-eslint/sort-ngmodule-metadata-arrays": "off",
"@angular-eslint/use-component-view-encapsulation": "off",
"@angular-eslint/use-injectable-provided-in": "off",
"@typescript-eslint/member-delimiter-style": ["error", { "multiline": { "delimiter": "semi", "requireLast": true}, "singleline": { "delimiter": "comma", "requireLast": false }}],
"@typescript-eslint/member-delimiter-style": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/type-annotation-spacing": 0,
"quotes": ["error", "single"],
"comma-dangle": ["error", "never"],
"comma-spacing": ["error", { "before": false, "after": true }],
@ -124,7 +125,7 @@
"indent": ["error", 2, { "SwitchCase": 1, "MemberExpression": 1, "ArrayExpression": "off" }],
"keyword-spacing": ["error", { "before": true, "after": true, "overrides": { "this": { "before": false }}}],
"lines-around-comment": "error",
"max-depth": ["error", { "max": 6 }],
"max-depth": ["error", { "max": 7 }],
"max-nested-callbacks": "error",
"max-statements-per-line": ["error", { "max": 3 }],
"no-array-constructor": "error",
@ -196,12 +197,14 @@
"@angular-eslint/template/click-events-have-key-events": "off",
"@angular-eslint/template/conditional-complexity": "off",
"@angular-eslint/template/cyclomatic-complexity": "off",
"@angular-eslint/template/elements-content": "off",
"@angular-eslint/template/i18n": "off",
"@angular-eslint/template/no-autofocus": "off",
"@angular-eslint/template/no-call-expression": "off",
"@angular-eslint/template/no-inline-styles": "off",
"@angular-eslint/template/no-interpolation-in-attributes": "off",
"@angular-eslint/template/no-positive-tabindex": "off",
"@angular-eslint/template/prefer-ngsrc": "off",
"@angular-eslint/template/use-track-by-function": "off"
}
}

@ -40,7 +40,7 @@ export const listChannels = (req, res, next) => {
request(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') {
channel.alias = channel.id.substring(0, 20);
channel.alias = channel.channel_id.substring(0, 20);
}
const local = channel.to_us_msat || 0;
const remote = (channel.total_msat - local) || 0;

@ -62,7 +62,6 @@ export const getInfo = (req, res, next) => {
req.session.selectedNode.ln_version = body.version || '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' });
clWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}

@ -16,14 +16,12 @@ export const simplifyAllChannels = (selNode, channels) => {
nodeId: channel.nodeId ? channel.nodeId : '',
channelId: channel.channelId ? channel.channelId : '',
state: channel.state ? channel.state : '',
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.channelFlags && channel.data.commitments.channelFlags.announceChannel ? channel.data.commitments.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0,
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.params && channel.data.commitments.params.channelFlags && channel.data.commitments.params.channelFlags.announceChannel ? channel.data.commitments.params.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.active[0].localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.active[0].localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.active[0].localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.active[0].localCommit.spec.toRemote / 1000) : 0,
shortChannelId: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.shortChannelId ? channel.data.channelUpdate.shortChannelId : '',
isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false,
buried: channel.data && channel.data.buried ? channel.data.buried : false,
isInitiator: channel.data && channel.data.commitments && channel.data.commitments.params && channel.data.commitments.params.localParams && channel.data.commitments.params.localParams.isInitiator ? channel.data.commitments.params.localParams.isInitiator : false,
feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,
feeRatePerKw: (channel.data.commitments.localCommit.spec.feeratePerKw) ? channel.data.commitments.localCommit.spec.feeratePerKw : 0,
feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0,
alias: ''
});
@ -159,7 +157,6 @@ export const closeChannel = (req, res, next) => {
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
// options.form = { sourceNodeId: req.params.source, targetNodeId: req.params.target, amountMsat: req.params.amount, ignoreNodeIds: req.params.ignore };
export const circularRebalance = (req, res, next) => {
const crInvDescription = 'Circular rebalancing invoice for ' + (req.body.amountMsat / 1000) + ' Sats';
options = common.getOptions(req);

@ -38,7 +38,6 @@ export const getInfo = (req, res, next) => {
body.lnImplementation = 'Eclair';
req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
eclWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {

@ -141,7 +141,11 @@ export const postChannel = (req, res, next) => {
else if (req.body.trans_type === '2') {
options.form.sat_per_byte = req.body.trans_type_value;
}
if (req.body.commitment_type) {
options.form.commitment_type = req.body.commitment_type;
}
options.form = JSON.stringify(options.form);
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channel Open Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body });
res.status(201).json(body);

@ -44,7 +44,6 @@ export const getInfo = (req, res, next) => {
else {
req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
lndWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}

@ -18,9 +18,12 @@ export const updateSelectedNode = (req, res, next) => {
req.session.selectedNode = common.findNode(selNodeIndex);
if (req.headers && req.headers.authorization && req.headers.authorization !== '') {
wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex);
if (req.params.prevNodeIndex !== -1) {
if (req.params.prevNodeIndex !== '-1') {
databaseService.unloadDatabase(req.params.prevNodeIndex, req.session.id);
}
if (req.params.currNodeIndex !== '-1') {
databaseService.loadDatabase(req.session);
}
}
const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node;
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Selected Node Updated To ' + responseVal });

@ -6,13 +6,7 @@ const logger = Logger;
const common = Common;
export const loopOut = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping Out..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out';
options.uri = '/v1/loop/out';
options.body = {
amt: req.body.amount,
sweep_conf_target: req.body.targetConf,
@ -41,13 +35,7 @@ export const loopOut = (req, res, next) => {
};
export const loopOutTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
options.uri = '/v1/loop/out/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Terms Received', data: body });
res.status(200).json(body);
@ -58,13 +46,7 @@ export const loopOutTerms = (req, res, next) => {
};
export const loopOutQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options.uri = '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Quote URL', data: options.url });
request(options).then((quoteRes) => {
quoteRes.amount = +req.params.amount;
@ -78,19 +60,13 @@ export const loopOutQuote = (req, res, next) => {
};
export const loopOutTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms & Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
options.uri = '/v1/loop/out/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Terms Received', data: terms });
const options1 = common.getSwapServerOptions(req);
const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
const options1 = options;
const options2 = options;
options1.uri = '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.uri = '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
@ -112,13 +88,7 @@ export const loopOutTermsAndQuotes = (req, res, next) => {
};
export const loopIn = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping In..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in';
options.uri = '/v1/loop/in';
options.body = {
amt: req.body.amount,
max_swap_fee: req.body.swapFee,
@ -136,13 +106,7 @@ export const loopIn = (req, res, next) => {
};
export const loopInTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
options.uri = '/v1/loop/in/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Terms Received', data: body });
res.status(200).json(body);
@ -153,13 +117,7 @@ export const loopInTerms = (req, res, next) => {
};
export const loopInQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options.uri = '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Quote Options', data: options.url });
request(options).then((body) => {
body.amount = +req.params.amount;
@ -173,19 +131,13 @@ export const loopInQuote = (req, res, next) => {
};
export const loopInTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms & Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
options.uri = '/v1/loop/in/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Terms Received', data: terms });
const options1 = common.getSwapServerOptions(req);
const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
const options1 = options;
const options2 = options;
options1.uri = '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.uri = '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
@ -207,13 +159,12 @@ export const loopInTermsAndQuotes = (req, res, next) => {
};
export const swaps = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting List Swaps..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'List Swaps Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/swaps';
options.uri = '/v1/loop/swaps';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body });
res.status(200).json(body.swaps);
@ -224,18 +175,29 @@ export const swaps = (req, res, next) => {
};
export const swap = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Swap Information..' });
options = common.getSwapServerOptions(req);
options.uri = '/v1/loop/swap/' + req.params.id;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Information..' });
options = common.setSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Get Swap Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
const err = common.handleError({ statusCode: 500, message: 'Get Loop Info Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/swap/' + req.params.id;
options.uri = '/v1/loop/info';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
const err = common.handleError(errRes, 'Loop', 'Get Loop Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -137,3 +137,25 @@ export const CollectionFieldsEnum = { ...OfferFieldsEnum, ...PageSettingsFieldsE
export const LNDCollection = [CollectionsEnum.PAGE_SETTINGS];
export const ECLCollection = [CollectionsEnum.PAGE_SETTINGS];
export const CLNCollection = [CollectionsEnum.PAGE_SETTINGS, CollectionsEnum.OFFERS];
export const ECL_UPDATED_DB = [
{
pageId: 'peers_channels',
tables: [
{
tableId: 'open_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
},
{
tableId: 'pending_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
},
{
tableId: 'inactive_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
}
]
}
];

@ -1,8 +1,9 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
import { loopInfo, loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
const router = Router();
router.get('/info', isAuthenticated, loopInfo);
router.get('/in/terms', isAuthenticated, loopInTerms);
router.get('/in/quote/:amount', isAuthenticated, loopInQuote);
router.get('/in/termsAndQuotes', isAuthenticated, loopInTermsAndQuotes);

@ -10,6 +10,7 @@ import sharedRoutes from '../routes/shared/index.js';
import lndRoutes from '../routes/lnd/index.js';
import clnRoutes from '../routes/cln/index.js';
import eclRoutes from '../routes/eclair/index.js';
import { Database } from './database.js';
import { Common } from './common.js';
import { Logger } from './logger.js';
import { CLWSClient } from '../controllers/cln/webSocketClient.js';
@ -24,6 +25,7 @@ export class ExpressApplication {
this.eclWsClient = ECLWSClient;
this.clWsClient = CLWSClient;
this.lndWsClient = LNDWSClient;
this.databaseService = Database;
this.directoryName = dirname(fileURLToPath(import.meta.url));
this.getApp = () => this.app;
this.setCORS = () => { CORS.mount(this.app); };
@ -79,6 +81,7 @@ export class ExpressApplication {
this.setCORS();
this.setCSRF();
this.setApplicationRoutes();
this.databaseService.migrateDatabase();
}
}
export default ExpressApplication;

@ -29,9 +29,10 @@ export class CommonService {
{ name: 'JAN', days: 31 }, { name: 'FEB', days: 28 }, { name: 'MAR', days: 31 }, { name: 'APR', days: 30 }, { name: 'MAY', days: 31 }, { name: 'JUN', days: 30 },
{ name: 'JUL', days: 31 }, { name: 'AUG', days: 31 }, { name: 'SEP', days: 30 }, { name: 'OCT', days: 31 }, { name: 'NOV', days: 30 }, { name: 'DEC', days: 31 }
];
this.getSwapServerOptions = (req) => {
this.setSwapServerOptions = (req) => {
const swapOptions = {
url: req.session.selectedNode.swap_server_url,
baseUrl: req.session.selectedNode.swap_server_url,
uri: '',
rejectUnauthorized: false,
json: true,
headers: { 'Grpc-Metadata-macaroon': '' }
@ -225,6 +226,10 @@ export class CommonService {
errRes.error = errRes.error.stack || errRes.error.message;
err = JSON.parse(JSON.stringify(errRes));
}
else if (errRes.message || errRes.stack) {
errRes.error = errRes.message || errRes.stack;
err = JSON.parse(JSON.stringify(errRes));
}
if (!selectedNode) {
selectedNode = this.initSelectedNode;
}

@ -2,7 +2,7 @@ import * as fs from 'fs';
import { join, sep } from 'path';
import { Common } from '../utils/common.js';
import { Logger } from '../utils/logger.js';
import { validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
import { CollectionsEnum, validateDocument, LNDCollection, ECLCollection, CLNCollection, ECL_UPDATED_DB } from '../models/database.model.js';
export class DatabaseService {
constructor() {
this.common = Common;
@ -10,6 +10,58 @@ export class DatabaseService {
this.dbDirectory = join(this.common.db_directory_path, 'database');
this.nodeDatabase = {};
}
migrateDatabase() {
this.common.nodes?.map((node) => {
if (node.ln_implementation === 'ECL') {
this.nodeDatabase[node.index] = { adapter: null, data: {} };
this.nodeDatabase[node.index].adapter = new DatabaseAdapter(this.dbDirectory, node);
this.fetchNodeData(node);
if (this.nodeDatabase[node.index].data.PageSettings) {
try {
const currPageSettings = JSON.parse(JSON.stringify(this.nodeDatabase[node.index].data.PageSettings));
ECL_UPDATED_DB.forEach((updatePage) => {
const foundPageDB = this.nodeDatabase[node.index].data.PageSettings.find((currPage) => currPage.pageId === updatePage.pageId);
if (foundPageDB) {
updatePage.tables.forEach((updateTable) => {
const foundTableDB = foundPageDB.tables.find((currTable) => currTable.tableId === updateTable.tableId);
if (foundTableDB) {
updateTable.removed.forEach((colToBeRemoved) => {
const foundIndex = foundTableDB.columnSelection.findIndex((col) => col === colToBeRemoved);
const foundIndexSM = foundTableDB.columnSelectionSM.findIndex((col) => col === colToBeRemoved);
if (foundIndex >= 0) {
foundTableDB.columnSelection?.splice(foundIndex, 1);
}
if (foundIndexSM >= 0) {
foundTableDB.columnSelectionSM?.splice(foundIndexSM, 1);
}
});
updateTable.renamed.forEach((colToBeRenamed) => {
const [oldName, newName] = colToBeRenamed.split(':');
const foundIndex = foundTableDB.columnSelection.findIndex((col) => col === oldName);
const foundIndexSM = foundTableDB.columnSelectionSM.findIndex((col) => col === oldName);
if (foundIndex >= 0) {
foundTableDB.columnSelection?.splice(foundIndex, 1, newName);
}
if (foundIndexSM >= 0) {
foundTableDB.columnSelectionSM?.splice(foundIndexSM, 1, newName);
}
});
}
});
}
});
if (currPageSettings !== this.nodeDatabase[node.index].data.PageSettings) {
this.saveDatabase(node, CollectionsEnum.PAGE_SETTINGS);
}
}
catch (err) {
this.logger.log({ selectedNode: node, level: 'ERROR', fileName: 'Database', msg: 'Database Migration Error', error: err });
}
}
}
return true;
});
}
loadDatabase(session) {
const { id, selectedNode } = session;
try {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -603,12 +603,90 @@ to represent the company, product, or service to which they refer.**
@ngrx/effects
MIT
The MIT License (MIT)
Copyright (c) 2017-2023 Brandon Roberts, Mike Ryan, Victor Savkin, Rob Wormald
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This repository includes a file "debounceSync.ts" originially copied from
https://github.com/cartant/rxjs-etc by Nicholas Jamieson, MIT licensed. See the
file header for details.
@ngrx/store
MIT
The MIT License (MIT)
Copyright (c) 2017-2023 Brandon Roberts, Mike Ryan, Victor Savkin, Rob Wormald
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This repository includes a file "debounceSync.ts" originially copied from
https://github.com/cartant/rxjs-etc by Nicholas Jamieson, MIT licensed. See the
file header for details.
@ngrx/store-devtools
MIT
The MIT License (MIT)
Copyright (c) 2017-2023 Brandon Roberts, Mike Ryan, Victor Savkin, Rob Wormald
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This repository includes a file "debounceSync.ts" originially copied from
https://github.com/cartant/rxjs-etc by Nicholas Jamieson, MIT licensed. See the
file header for details.
@otplib/core
MIT
@ -1113,163 +1191,93 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
d3-array
BSD-3-Clause
Copyright 2010-2020 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2023 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-brush
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-color
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2022 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-dispatch
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-drag
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-ease
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
Copyright 2010-2021 Mike Bostock
Copyright 2001 Robert Penner
All rights reserved.
@ -1300,157 +1308,88 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-format
BSD-3-Clause
Copyright 2010-2015 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-interpolate
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-scale
BSD-3-Clause
Copyright 2010-2015 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-selection
BSD-3-Clause
Copyright (c) 2010-2018, Michael Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-time
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2022 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-time-format
@ -1485,96 +1424,37 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-timer
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
d3-transition
BSD-3-Clause
Copyright (c) 2010-2015, Michael Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
TERMS OF USE - EASING EQUATIONS
Open source under the BSD License.
Copyright 2001 Robert Penner
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
ISC
Copyright 2010-2021 Mike Bostock
- Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
des.js
@ -1749,6 +1629,23 @@ PERFORMANCE OF THIS SOFTWARE.
internmap
ISC
Copyright 2021 Mike Bostock
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
material-icons
Apache-2.0
@ -3014,7 +2911,7 @@ zone.js
MIT
The MIT License
Copyright (c) 2010-2022 Google LLC. https://angular.io/license
Copyright (c) 2010-2023 Google LLC. https://angular.io/license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],s=!0,d=0;d<t.length;d++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[d]))?t.splice(d--,1):(s=!1,o<a&&(a=o));if(s){e.splice(n--,1);var u=i();void 0!==u&&(f=u)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>e+"."+{167:"a3774800f5f9ed5a",267:"5508f97536cb5708",315:"d20113f8d2f54786",636:"eaef3bec0eb4cb7a"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="RTLApp:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,s;if(void 0!==o)for(var d=document.getElementsByTagName("script"),u=0;u<d.length;u++){var l=d[u];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==f+o){a=l;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+o),a.src=r.tu(t)),e[t]=[i];var c=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:f=>f},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((l,c)=>n=e[i]=[l,c]);o.push(n[2]=a);var s=r.p+r.u(i),d=new Error;r.l(s,l=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var c=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;d.message="Loading chunk "+i+" failed.\n("+c+": "+p+")",d.name="ChunkLoadError",d.type=c,d.request=p,n[1](d)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var d,u,[n,a,s]=o,l=0;if(n.some(p=>0!==e[p])){for(d in a)r.o(a,d)&&(r.m[d]=a[d]);if(s)var c=s(r)}for(i&&i(o);l<n.length;l++)r.o(e,u=n[l])&&e[u]&&e[u][0](),e[u]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],s=!0,d=0;d<t.length;d++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[d]))?t.splice(d--,1):(s=!1,o<a&&(a=o));if(s){e.splice(n--,1);var u=i();void 0!==u&&(f=u)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>e+"."+{125:"020bb8ec6698fd7e",456:"a54c45d211d6d10c",570:"28ed94d292ccd982",758:"2801e2da6f8bba94"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="RTLApp:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,s;if(void 0!==o)for(var d=document.getElementsByTagName("script"),u=0;u<d.length;u++){var l=d[u];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==f+o){a=l;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+o),a.src=r.tu(t)),e[t]=[i];var c=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:f=>f},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((l,c)=>n=e[i]=[l,c]);o.push(n[2]=a);var s=r.p+r.u(i),d=new Error;r.l(s,l=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var c=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;d.message="Loading chunk "+i+" failed.\n("+c+": "+p+")",d.name="ChunkLoadError",d.type=c,d.request=p,n[1](d)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var d,u,[n,a,s]=o,l=0;if(n.some(p=>0!==e[p])){for(d in a)r.o(a,d)&&(r.m[d]=a[d]);if(s)var c=s(r)}for(i&&i(o);l<n.length;l++)r.o(e,u=n[l])&&e[u]&&e[u][0](),e[u]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

9336
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "rtl",
"version": "0.14.0-beta",
"version": "0.14.1-beta",
"license": "MIT",
"type": "module",
"scripts": {
@ -10,8 +10,8 @@
"prebuildfrontend": "node src/prebuild.cjs",
"buildfrontendtest": "ng test --watch=false && ng build",
"buildfrontend": "ng build --configuration production",
"buildbackend": "tsc --project tsconfig.json",
"watchbackend": "tsc --project tsconfig.json --watch",
"buildbackend": "tsc --project ./server/tsconfig.server.json",
"watchbackend": "tsc --project ./server/tsconfig.server.json --watch",
"server": "set NODE_ENV=development&&nodemon --watch backend --watch server ./rtl.js",
"serverUbuntu": "NODE_ENV=development nodemon --watch backend --watch server ./rtl.js",
"testdev": "ng test --watch=true --code-coverage",
@ -21,10 +21,10 @@
},
"private": true,
"dependencies": {
"@ngrx/effects": "^15.0.0",
"@ngrx/store": "^15.0.0",
"@swimlane/ngx-charts": "^20.1.2",
"angular-user-idle": "^3.0.0",
"@ngrx/effects": "^16.3.0",
"@ngrx/store": "^16.3.0",
"@swimlane/ngx-charts": "^20.4.1",
"angular-user-idle": "^4.0.0",
"atob": "^2.1.2",
"cookie-parser": "^1.4.6",
"crypto-browserify": "^3.12.0",
@ -32,65 +32,65 @@
"express": "^4.18.2",
"express-session": "^1.17.3",
"hocon-parser": "^1.0.1",
"ini": "^3.0.1",
"jsonwebtoken": "^9.0.0",
"ng-qrcode": "^8.0.1",
"ini": "^4.1.1",
"jsonwebtoken": "^9.0.2",
"ng-qrcode": "^16.0.0",
"ngx-perfect-scrollbar-next": "^10.1.1",
"otplib": "^12.0.1",
"pdfmake": "^0.2.6",
"pdfmake": "^0.2.7",
"request": "^2.88.2",
"request-promise": "^4.2.6",
"rxjs": "~7.5.0",
"rxjs": "~7.8.0",
"sha256": "^0.2.0",
"stream-browserify": "^3.0.0",
"tslib": "^2.3.0",
"ws": "^8.11.0",
"zone.js": "~0.12.0"
"ws": "^8.14.2",
"zone.js": "~0.13.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.1",
"@angular-eslint/builder": "^15.1.0",
"@angular-eslint/eslint-plugin": "^15.1.0",
"@angular-eslint/eslint-plugin-template": "^15.1.0",
"@angular-eslint/schematics": "^15.1.0",
"@angular-eslint/template-parser": "^15.1.0",
"@angular/animations": "^15.0.0",
"@angular/cdk": "^15.0.1",
"@angular/cli": "~15.0.1",
"@angular/common": "^15.0.0",
"@angular/compiler": "^15.0.0",
"@angular/compiler-cli": "^15.0.0",
"@angular/core": "^15.0.0",
"@angular/flex-layout": "^14.0.0-beta.41",
"@angular/forms": "^15.0.0",
"@angular/material": "^15.0.1",
"@angular/platform-browser": "^15.0.0",
"@angular/platform-browser-dynamic": "^15.0.0",
"@angular/router": "^15.0.0",
"@fortawesome/angular-fontawesome": "^0.12.0",
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-regular-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
"@ngrx/store-devtools": "^15.0.0",
"@angular-devkit/build-angular": "^16.2.5",
"@angular-eslint/builder": "^16.2.0",
"@angular-eslint/eslint-plugin": "^16.2.0",
"@angular-eslint/eslint-plugin-template": "^16.2.0",
"@angular-eslint/schematics": "^16.2.0",
"@angular-eslint/template-parser": "^16.2.0",
"@angular/animations": "^16.2.0",
"@angular/cdk": "^16.2.7",
"@angular/cli": "^16.2.5",
"@angular/compiler-cli": "^16.2.0",
"@angular/common": "^16.2.0",
"@angular/compiler": "^16.2.0",
"@angular/core": "^16.2.0",
"@angular/flex-layout": "^15.0.0-beta.42",
"@angular/forms": "^16.2.0",
"@angular/material": "^16.2.7",
"@angular/platform-browser": "^16.2.0",
"@angular/platform-browser-dynamic": "^16.2.0",
"@angular/router": "^16.2.0",
"@fortawesome/angular-fontawesome": "^0.13.0",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-regular-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@ngrx/store-devtools": "^16.3.0",
"@types/jasmine": "~4.3.0",
"@types/node": "^18.11.13",
"@typescript-eslint/eslint-plugin": "^5.44.0",
"@typescript-eslint/parser": "^5.44.0",
"dotenv": "^16.0.3",
"eslint": "^8.28.0",
"eslint-plugin-deprecation": "^1.3.3",
"jasmine-core": "~4.5.0",
"@types/node": "^20.8.2",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"dotenv": "^16.3.1",
"eslint": "^8.50.0",
"eslint-plugin-deprecation": "^2.0.0",
"jasmine-core": "~4.6.0",
"jasmine-spec-reporter": "^7.0.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"material-icons": "^1.13.1",
"nodemon": "^2.0.20",
"karma-jasmine-html-reporter": "~2.1.0",
"material-icons": "^1.13.12",
"nodemon": "^3.0.1",
"protractor": "^7.0.0",
"roboto-fontface": "^0.10.0",
"ts-node": "^10.9.1",
"typescript": "~4.8.4"
"typescript": "~5.1.3"
}
}

@ -35,7 +35,7 @@ export const listChannels = (req, res, next) => {
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels';
request(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') { channel.alias = channel.id.substring(0, 20); }
if (!channel.alias || channel.alias === '') { channel.alias = channel.channel_id.substring(0, 20); }
const local = channel.to_us_msat || 0;
const remote = (channel.total_msat - local) || 0;
const total = channel.total_msat || 0;

@ -56,7 +56,6 @@ export const getInfo = (req, res, next) => {
req.session.selectedNode.ln_version = body.version || '';
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' });
clWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}

@ -19,14 +19,12 @@ export const simplifyAllChannels = (selNode: CommonSelectedNode, channels) => {
nodeId: channel.nodeId ? channel.nodeId : '',
channelId: channel.channelId ? channel.channelId : '',
state: channel.state ? channel.state : '',
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.channelFlags && channel.data.commitments.channelFlags.announceChannel ? channel.data.commitments.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0,
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.params && channel.data.commitments.params.channelFlags && channel.data.commitments.params.channelFlags.announceChannel ? channel.data.commitments.params.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.active[0].localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.active[0].localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.active[0].localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.active[0].localCommit.spec.toRemote / 1000) : 0,
shortChannelId: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.shortChannelId ? channel.data.channelUpdate.shortChannelId : '',
isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false,
buried: channel.data && channel.data.buried ? channel.data.buried : false,
isInitiator: channel.data && channel.data.commitments && channel.data.commitments.params && channel.data.commitments.params.localParams && channel.data.commitments.params.localParams.isInitiator ? channel.data.commitments.params.localParams.isInitiator : false,
feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,
feeRatePerKw: (channel.data.commitments.localCommit.spec.feeratePerKw) ? channel.data.commitments.localCommit.spec.feeratePerKw : 0,
feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0,
alias: ''
});
@ -155,8 +153,6 @@ export const closeChannel = (req, res, next) => {
});
};
// options.form = { sourceNodeId: req.params.source, targetNodeId: req.params.target, amountMsat: req.params.amount, ignoreNodeIds: req.params.ignore };
export const circularRebalance = (req, res, next) => {
const crInvDescription = 'Circular rebalancing invoice for ' + (req.body.amountMsat / 1000) + ' Sats';
options = common.getOptions(req);

@ -36,7 +36,6 @@ export const getInfo = (req, res, next) => {
body.lnImplementation = 'Eclair';
req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
eclWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {

@ -140,7 +140,11 @@ export const postChannel = (req, res, next) => {
} else if (req.body.trans_type === '2') {
options.form.sat_per_byte = req.body.trans_type_value;
}
if (req.body.commitment_type) {
options.form.commitment_type = req.body.commitment_type;
}
options.form = JSON.stringify(options.form);
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channel Open Options', data: options.form });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Channel Opened', data: body });
res.status(201).json(body);

@ -40,7 +40,6 @@ export const getInfo = (req, res, next) => {
} else {
req.session.selectedNode.ln_version = body.version.split('-')[0] || '';
lndWsClient.updateSelectedNode(req.session.selectedNode);
databaseService.loadDatabase(req.session);
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body });
return res.status(200).json(body);
}

@ -21,9 +21,12 @@ export const updateSelectedNode = (req, res, next) => {
req.session.selectedNode = common.findNode(selNodeIndex);
if (req.headers && req.headers.authorization && req.headers.authorization !== '') {
wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex);
if (req.params.prevNodeIndex !== -1) {
if (req.params.prevNodeIndex !== '-1') {
databaseService.unloadDatabase(req.params.prevNodeIndex, req.session.id);
}
if (req.params.currNodeIndex !== '-1') {
databaseService.loadDatabase(req.session);
}
}
const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node;
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Selected Node Updated To ' + responseVal });

@ -7,13 +7,7 @@ const common: CommonService = Common;
export const loopOut = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping Out..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out';
options.uri = '/v1/loop/out';
options.body = {
amt: req.body.amount,
sweep_conf_target: req.body.targetConf,
@ -39,13 +33,7 @@ export const loopOut = (req, res, next) => {
export const loopOutTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
options.uri = '/v1/loop/out/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Terms Received', data: body });
res.status(200).json(body);
@ -57,13 +45,7 @@ export const loopOutTerms = (req, res, next) => {
export const loopOutQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options.uri = '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Quote URL', data: options.url });
request(options).then((quoteRes) => {
quoteRes.amount = +req.params.amount;
@ -78,18 +60,12 @@ export const loopOutQuote = (req, res, next) => {
export const loopOutTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms & Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
options.uri = '/v1/loop/out/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Terms Received', data: terms });
const options1 = common.getSwapServerOptions(req); const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
const options1 = options; const options2 = options;
options1.uri = '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.uri = '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
@ -112,13 +88,7 @@ export const loopOutTermsAndQuotes = (req, res, next) => {
export const loopIn = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping In..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in';
options.uri = '/v1/loop/in';
options.body = {
amt: req.body.amount,
max_swap_fee: req.body.swapFee,
@ -137,13 +107,7 @@ export const loopIn = (req, res, next) => {
export const loopInTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
options.uri = '/v1/loop/in/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Terms Received', data: body });
res.status(200).json(body);
@ -155,13 +119,7 @@ export const loopInTerms = (req, res, next) => {
export const loopInQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options.uri = '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Quote Options', data: options.url });
request(options).then((body) => {
body.amount = +req.params.amount;
@ -176,18 +134,12 @@ export const loopInQuote = (req, res, next) => {
export const loopInTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms & Quotes..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
options.uri = '/v1/loop/in/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Terms Received', data: terms });
const options1 = common.getSwapServerOptions(req); const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
const options1 = options; const options2 = options;
options1.uri = '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.uri = '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
@ -210,13 +162,12 @@ export const loopInTermsAndQuotes = (req, res, next) => {
export const swaps = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting List Swaps..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'List Swaps Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/swaps';
options.uri = '/v1/loop/swaps';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body });
res.status(200).json(body.swaps);
@ -228,18 +179,30 @@ export const swaps = (req, res, next) => {
export const swap = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Swap Information..' });
options = common.getSwapServerOptions(req);
options.uri = '/v1/loop/swap/' + req.params.id;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Information..' });
options = common.setSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Get Swap Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
const err = common.handleError({ statusCode: 500, message: 'Get Loop Info Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/swap/' + req.params.id;
options.uri = '/v1/loop/info';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
const err = common.handleError(errRes, 'Loop', 'Get Loop Info Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -154,3 +154,26 @@ export const CollectionFieldsEnum = { ...OfferFieldsEnum, ...PageSettingsFieldsE
export const LNDCollection = [CollectionsEnum.PAGE_SETTINGS];
export const ECLCollection = [CollectionsEnum.PAGE_SETTINGS];
export const CLNCollection = [CollectionsEnum.PAGE_SETTINGS, CollectionsEnum.OFFERS];
export const ECL_UPDATED_DB = [
{
pageId: 'peers_channels',
tables: [
{
tableId: 'open_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
},
{
tableId: 'pending_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
},
{
tableId: 'inactive_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
}
]
}
];

@ -1,10 +1,11 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
import { loopInfo, loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
const router = Router();
router.get('/info', isAuthenticated, loopInfo);
router.get('/in/terms', isAuthenticated, loopInTerms);
router.get('/in/quote/:amount', isAuthenticated, loopInQuote);
router.get('/in/termsAndQuotes', isAuthenticated, loopInTermsAndQuotes);

@ -0,0 +1,11 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": "../",
"outDir": "../backend",
},
"include": [
"./**/*"
]
}

@ -11,6 +11,7 @@ import sharedRoutes from '../routes/shared/index.js';
import lndRoutes from '../routes/lnd/index.js';
import clnRoutes from '../routes/cln/index.js';
import eclRoutes from '../routes/eclair/index.js';
import { Database, DatabaseService } from './database.js';
import { Common, CommonService } from './common.js';
import { Logger, LoggerService } from './logger.js';
import { CLWSClient, CLWebSocketClient } from '../controllers/cln/webSocketClient.js';
@ -27,6 +28,7 @@ export class ExpressApplication {
public eclWsClient: ECLWebSocketClient = ECLWSClient;
public clWsClient: CLWebSocketClient = CLWSClient;
public lndWsClient: LNDWebSocketClient = LNDWSClient;
public databaseService: DatabaseService = Database;
public directoryName = dirname(fileURLToPath(import.meta.url));
constructor() {
@ -40,6 +42,7 @@ export class ExpressApplication {
this.setCORS();
this.setCSRF();
this.setApplicationRoutes();
this.databaseService.migrateDatabase();
}
public getApp = () => this.app;

@ -34,9 +34,10 @@ export class CommonService {
constructor() { }
public getSwapServerOptions = (req) => {
public setSwapServerOptions = (req) => {
const swapOptions = {
url: req.session.selectedNode.swap_server_url,
baseUrl: req.session.selectedNode.swap_server_url,
uri: '',
rejectUnauthorized: false,
json: true,
headers: { 'Grpc-Metadata-macaroon': '' }
@ -241,6 +242,9 @@ export class CommonService {
if (err && err.error && Object.keys(err.error).length === 0 && errRes.error && (errRes.error.stack || errRes.error.message)) {
errRes.error = errRes.error.stack || errRes.error.message;
err = JSON.parse(JSON.stringify(errRes));
} else if (errRes.message || errRes.stack) {
errRes.error = errRes.message || errRes.stack;
err = JSON.parse(JSON.stringify(errRes));
}
if (!selectedNode) { selectedNode = this.initSelectedNode; }
switch (selectedNode.ln_implementation) {

@ -2,7 +2,7 @@ import * as fs from 'fs';
import { join, sep } from 'path';
import { Common, CommonService } from '../utils/common.js';
import { Logger, LoggerService } from '../utils/logger.js';
import { Collections, CollectionsEnum, validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
import { Collections, CollectionsEnum, validateDocument, LNDCollection, ECLCollection, CLNCollection, ECL_UPDATED_DB } from '../models/database.model.js';
import { CommonSelectedNode } from '../models/config.model.js';
export class DatabaseService {
@ -14,6 +14,58 @@ export class DatabaseService {
constructor() { }
migrateDatabase() {
this.common.nodes?.map((node: any) => {
if (node.ln_implementation === 'ECL') {
this.nodeDatabase[node.index] = { adapter: null, data: {} };
this.nodeDatabase[node.index].adapter = new DatabaseAdapter(this.dbDirectory, node);
this.fetchNodeData(node);
if (this.nodeDatabase[node.index].data.PageSettings) {
try {
const currPageSettings = JSON.parse(JSON.stringify(this.nodeDatabase[node.index].data.PageSettings));
ECL_UPDATED_DB.forEach((updatePage) => {
const foundPageDB = this.nodeDatabase[node.index].data.PageSettings.find((currPage) => currPage.pageId === updatePage.pageId);
if (foundPageDB) {
updatePage.tables.forEach((updateTable) => {
const foundTableDB = foundPageDB.tables.find((currTable) => currTable.tableId === updateTable.tableId);
if (foundTableDB) {
updateTable.removed.forEach((colToBeRemoved) => {
const foundIndex = foundTableDB.columnSelection.findIndex((col) => col === colToBeRemoved);
const foundIndexSM = foundTableDB.columnSelectionSM.findIndex((col) => col === colToBeRemoved);
if (foundIndex >= 0) {
foundTableDB.columnSelection?.splice(foundIndex, 1);
}
if (foundIndexSM >= 0) {
foundTableDB.columnSelectionSM?.splice(foundIndexSM, 1);
}
});
updateTable.renamed.forEach((colToBeRenamed) => {
const [oldName, newName] = colToBeRenamed.split(':');
const foundIndex = foundTableDB.columnSelection.findIndex((col) => col === oldName);
const foundIndexSM = foundTableDB.columnSelectionSM.findIndex((col) => col === oldName);
if (foundIndex >= 0) {
foundTableDB.columnSelection?.splice(foundIndex, 1, newName);
}
if (foundIndexSM >= 0) {
foundTableDB.columnSelectionSM?.splice(foundIndexSM, 1, newName);
}
});
}
});
}
});
if (currPageSettings !== this.nodeDatabase[node.index].data.PageSettings) {
this.saveDatabase(node, CollectionsEnum.PAGE_SETTINGS);
}
} catch (err) {
this.logger.log({ selectedNode: node, level: 'ERROR', fileName: 'Database', msg: 'Database Migration Error', error: err });
}
}
}
return true;
});
}
loadDatabase(session: any) {
const { id, selectedNode } = session;
try {
@ -198,7 +250,7 @@ export class DatabaseAdapter {
private dbFilePath = '';
private userSessions = [];
constructor(public dbDirectoryPath: string, private selNode: CommonSelectedNode = null, private id: string = '') {
constructor(public dbDirectoryPath: string, public selNode: CommonSelectedNode = null, public id: string = '') {
this.dbFilePath = dbDirectoryPath + sep + 'node-' + selNode.index;
// For backward compatibility Start
const oldFilePath = dbDirectoryPath + sep + 'rtldb-node-' + selNode.index + '.json';

@ -16,21 +16,21 @@
<span *ngIf="!smallScreen" class="font-size-120 font-weight-500">{{information.alias ? 'Ride The Lightning - ' + information.alias : 'Ride The Lightning'}}</span>
</div>
<div>
<rtl-top-menu></rtl-top-menu>
<rtl-top-menu />
</div>
</mat-toolbar>
<mat-sidenav-container>
<mat-sidenav #sideNavigation class="sidenav mat-elevation-z6" [perfectScrollbar] [opened]="flgSideNavOpened && flgLoggedIn" [mode]="(flgSidenavPinned && !smallScreen) ? 'side' : 'over'">
<rtl-side-navigation fxFlex="100" (ChildNavClicked)="onNavigationClicked($event)"></rtl-side-navigation>
<rtl-side-navigation fxFlex="100" (ChildNavClicked)="onNavigationClicked($event)" />
</mat-sidenav>
<mat-sidenav-content #sideNavContent [perfectScrollbar]>
<div class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<router-outlet #outlet="outlet"></router-outlet>
<router-outlet #outlet="outlet" />
</div>
</mat-sidenav-content>>
</mat-sidenav-container>
<div *ngIf="!settings.themeColor" class="rtl-spinner">
<mat-spinner color="accent"></mat-spinner>
<mat-spinner color="accent" />
<h4>Loading RTL...</h4>
</div>
</div>

@ -8,12 +8,11 @@ import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { UserIdleModule } from 'angular-user-idle';
import { provideUserIdleConfig } from 'angular-user-idle';
import { routing } from './app.routing';
import { SharedModule } from './shared/shared.module';
import { AppComponent } from './app.component';
import { AuthGuard } from './shared/services/auth.guard';
import { AuthInterceptor } from './shared/services/auth.interceptor';
import { SessionService } from './shared/services/session.service';
import { LoopService } from './shared/services/loop.service';
@ -42,7 +41,6 @@ if (isDevMode()) { isDevEnvironemt = true; }
routing,
LayoutModule,
HammerModule,
UserIdleModule.forRoot({ idle: (HOUR_SECONDS - 10), timeout: 10, ping: 12000 }),
StoreModule.forRoot(
{ root: RootReducer, lnd: LNDReducer, cln: CLNReducer, ecl: ECLReducer },
{
@ -56,6 +54,7 @@ if (isDevMode()) { isDevEnvironemt = true; }
],
declarations: [AppComponent],
providers: [
provideUserIdleConfig({ idle: (HOUR_SECONDS - 10), timeout: 10, ping: 12000 }),
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
SessionService, DataService, WebSocketClientService, LoopService, CommonService, BoltzService
],

@ -1,4 +1,4 @@
<div class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<router-outlet #outlet="outlet"></router-outlet>
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate" />
<router-outlet #outlet="outlet" />
</div>

@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
import { Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router, RouterEvent } from '@angular/router';
import { routeAnimation } from '../shared/animation/route-animation';
@Component({
@ -13,7 +13,7 @@ export class CLNRootComponent {
loading = false;
constructor(private router: Router) {
this.router.events.subscribe((event: Event) => {
this.router.events.subscribe((event: Event | RouterEvent) => {
switch (true) {
case event instanceof NavigationStart: {
this.loading = true;

@ -1,16 +1,16 @@
<div fxLayout="row wrap" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faSearch"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faSearch" />
<span class="page-title">Graph Lookups</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel">
<div *ngFor="let link of links" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === link.link" routerLink="{{link.link}}" (click)="activeLink = link.link">{{link.name}}</div>
<div *ngFor="let link of links" tabindex="1" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === link.link" routerLink="{{link.link}}" (click)="activeLink = link.link">{{link.name}}</div>
</nav>
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel>
<mat-tab-nav-panel #tabPanel />
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
<router-outlet></router-outlet>
<router-outlet />
</div>
</mat-card-content>
</mat-card>

@ -1,77 +1,77 @@
<div *ngIf="lookupResult" fxLayout="column" class="mt-1">
<mat-divider></mat-divider>
<mat-divider />
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row">
<div fxLayout="column" fxFlex="49" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
<div fxLayout="column">
<h3 *ngIf="!node1_match" class="page-title font-bold-500">Node 1</h3>
<h3 *ngIf="node1_match" class="page-title font-bold-500">Node 1 (Your Node)</h3>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Short Channel ID</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.short_channel_id}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Active</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.active ? 'True' : 'False'}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Last Update</h4>
<span class="foreground-secondary-text">{{(lookupResult[0]?.last_update * 1000) | date:'dd/MMM/y HH:mm'}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Amount (Sats)</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.amount_msat / 1000 | number:'1.0-0'}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Base Fee (mSats)</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.base_fee_millisatoshi | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Fee/Millionth</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.fee_per_millionth | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Channel Flags</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.channel_flags | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Delay</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.delay | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Max Htlc (mSat)</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.htlc_maximum_msat | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Min Htlc (mSat)</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.htlc_minimum_msat | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Message Flags</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.message_flags | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Public</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.public ? 'Yes' : 'No'}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Source</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.source}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Destination</h4>
<span class="foreground-secondary-text">{{lookupResult[0]?.destination}}</span>
@ -82,72 +82,72 @@
<h3 *ngIf="!node2_match" class="page-title font-bold-500">Node 2</h3>
<h3 *ngIf="node2_match" class="page-title font-bold-500">Node 2 (Your Node)</h3>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Short Channel ID</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.short_channel_id}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Active</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.active ? 'True' : 'False'}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Last Update</h4>
<span class="foreground-secondary-text">{{(lookupResult[1]?.last_update * 1000) | date:'dd/MMM/y HH:mm'}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Amount (Sats)</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.amount_msat / 1000 | number:'1.0-0'}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Base Fee (mSats)</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.base_fee_millisatoshi | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Fee/Millionth</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.fee_per_millionth | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Channel Flags</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.channel_flags | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Delay</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.delay | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Max Htlc (mSat)</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.htlc_maximum_msat | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Min Htlc (mSat)</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.htlc_minimum_msat | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Message Flags</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.message_flags | number}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Public</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.public ? 'Yes' : 'No'}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Source</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.source}}</span>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 class="font-bold-500">Destination</h4>
<span class="foreground-secondary-text">{{lookupResult[1]?.destination}}</span>

@ -24,8 +24,8 @@
<span class="page-title font-bold-500">{{lookupFields[selectedFieldId].name}} Details</span>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center" [ngSwitch]="selectedFieldId">
<span *ngSwitchCase="0" fxFlex="100"><div *ngIf="nodeLookupValue.nodeid !== ''; else errorBlock"><rtl-cln-node-lookup [lookupResult]="nodeLookupValue"></rtl-cln-node-lookup></div></span>
<span *ngSwitchCase="1" fxFlex="100"><div *ngIf="channelLookupValue.length>0; else errorBlock"><rtl-cln-channel-lookup [lookupResult]="channelLookupValue"></rtl-cln-channel-lookup></div></span>
<span *ngSwitchCase="0" fxFlex="100"><div *ngIf="nodeLookupValue.nodeid !== ''; else errorBlock"><rtl-cln-node-lookup [lookupResult]="nodeLookupValue" /></div></span>
<span *ngSwitchCase="1" fxFlex="100"><div *ngIf="channelLookupValue.length>0; else errorBlock"><rtl-cln-channel-lookup [lookupResult]="channelLookupValue" /></div></span>
<span *ngSwitchDefault> fxFlex="100"<h3>Error! Unable to find details!</h3></span>
</div>
</div>

@ -41,7 +41,7 @@ export class CLNLookupsComponent implements OnInit, OnDestroy {
}
ngOnInit() {
if (window.history.state && window.history.state.lookupType) {
if (window.history.state && (window.history.state.lookupType || window.history.state.lookupValue)) {
this.selectedFieldId = +window.history.state.lookupType || 0;
this.lookupKey = window.history.state.lookupValue || '';
}

@ -1,5 +1,5 @@
<div *ngIf="lookupResult" fxLayout="column" class="mt-1">
<mat-divider class="mb-1"></mat-divider>
<mat-divider class="mb-1" />
<div fxLayout="row">
<div fxFlex="30">
<h4 fxLayoutAlign="start" class="font-bold-500">Alias</h4>
@ -10,7 +10,7 @@
<span class="foreground-secondary-text w-100">{{lookupResult?.nodeid}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="row">
<div fxFlex="30">
<h4 fxLayoutAlign="start" class="font-bold-500">Last Update</h4>
@ -21,7 +21,7 @@
<span *ngFor="let featureDescription of featureDescriptions" class="foreground-secondary-text">{{featureDescription}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="column">
<h4 fxFlex="100" fxLayoutAlign="start" class="font-bold-500 mb-1">Addresses</h4>
<div fxLayout="row" fxFlex="100" class="table-container" [perfectScrollbar]>
@ -45,7 +45,7 @@
<td *matCellDef="let address" mat-cell fxLayoutAlign="end center">
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onConnectNode(address)">Connect</mat-option>
<mat-option rtlClipboard [payload]="lookupResult?.nodeid + '@' + address.address + ':' + address.port" (copied)="onCopyNodeURI($event)">Copy URI</mat-option>
</mat-select>

@ -1,7 +1,7 @@
<div fxLayout="column" fxFlex="100" class="padding-gap">
<form #queryRoutesForm="ngForm" fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" (ngSubmit)="queryRoutesForm.form.valid && onQueryRoutes()">
<div fxFlex="100" class="alert alert-warn">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>The actual routing fee on a payment can be different from the fee shown on query routes.</span>
</div>
<mat-form-field fxLayout="column" fxFlex="69" fxLayoutAlign="start end">
@ -21,12 +21,12 @@
</form>
<div fxLayout="row" fxLayoutAlign="start center" class="page-sub-title-container mt-2 mb-1">
<div fxFlex="70">
<fa-icon class="page-title-img mr-1" [icon]="faRoute"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faRoute" />
<span class="page-title">Transaction Route</span>
</div>
</div>
<div class="table-container mb-6" [perfectScrollbar]>
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate" />
<table #table mat-table matSort [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="qrHops" [ngClass]="{'overflow-auto error-border': flgLoading[0]==='error','overflow-auto': true}">
<ng-container matColumnDef="id">
<th *matHeaderCellDef mat-header-cell mat-sort-header>ID</th>

@ -2,12 +2,12 @@
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Lightning</h4>
<div class="overflow-wrap dashboard-info-value">{{balances.lightning | number:'1.0-0'}} Sats</div>
<mat-progress-bar class="dashboard-progress-bar" mode="determinate" value="{{balances.lightning/balances.total*100}}"></mat-progress-bar>
<mat-progress-bar class="dashboard-progress-bar" mode="determinate" value="{{balances.lightning/balances.total*100}}" />
</div>
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">On-chain</h4>
<div class="overflow-wrap dashboard-info-value">{{balances.onchain | number:'1.0-0'}} Sats</div>
<mat-progress-bar class="dashboard-progress-bar" mode="determinate" value="{{balances.onchain/balances.total*100}}"></mat-progress-bar>
<mat-progress-bar class="dashboard-progress-bar" mode="determinate" value="{{balances.onchain/balances.total*100}}" />
</div>
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Total</h4>

@ -4,14 +4,14 @@
<div fxLayout="row" fxLayoutAlign="space-between start" class="w-100">
<mat-hint fxFlex="40" fxLayoutAlign="start center" class="font-size-90"><strong class="font-weight-900 mr-5px">Local:</strong>{{channelBalances?.localBalance || 0 | number:'1.0-0'}} Sats</mat-hint>
<mat-hint fxFlex="20" fxLayoutAlign="center center" class="font-size-90">
<fa-icon class="mr-3px" matTooltip="Balance Score" [icon]="faBalanceScale"></fa-icon>
<fa-icon class="mr-3px" matTooltip="Balance Score" [icon]="faBalanceScale" />
({{channelBalances?.balancedness || 0 | number}})
</mat-hint>
<mat-hint fxFlex="40" fxLayoutAlign="end center" class="font-size-90"><strong class="font-weight-900 mr-5px">Remote:</strong>{{channelBalances?.remoteBalance || 0 | number:'1.0-0'}} Sats</mat-hint>
</div>
<mat-progress-bar class="dashboard-progress-bar this-channel-bar" mode="determinate" color="accent" value="{{channelBalances?.localBalance && channelBalances?.localBalance > 0 ? ((+channelBalances?.localBalance/((+channelBalances?.localBalance)+(+channelBalances?.remoteBalance)))*100) : 0}}"></mat-progress-bar>
<mat-progress-bar class="dashboard-progress-bar this-channel-bar" mode="determinate" color="accent" value="{{channelBalances?.localBalance && channelBalances?.localBalance > 0 ? ((+channelBalances?.localBalance/((+channelBalances?.localBalance)+(+channelBalances?.remoteBalance)))*100) : 0}}" />
</div>
<div fxLayout="column" fxFlex="3" fxLayoutAlign="end stretch"><mat-divider class="dashboard-divider"></mat-divider></div>
<div fxLayout="column" fxFlex="3" fxLayoutAlign="end stretch"><mat-divider class="dashboard-divider" /></div>
<div class="channels-capacity-scroll" [perfectScrollbar]>
<div *ngIf="activeChannels && activeChannels.length > 0; else noChannelBlock" fxLayout="column"fxFlex="100">
<div *ngFor="let channel of activeChannels" class="mt-2">
@ -21,12 +21,12 @@
<div fxLayout="row" fxLayoutAlign="space-between start" class="w-100">
<mat-hint fxFlex="40" fxLayoutAlign="start center" class="font-size-90 color-primary"><strong class="font-weight-900 mr-5px">Local:</strong>{{channel.to_us_msat/1000 || 0 | number:'1.0-0'}} Sats</mat-hint>
<mat-hint fxFlex="20" fxLayoutAlign="center center" class="font-size-90 color-primary">
<fa-icon class="color-primary mr-3px" matTooltip="Balance Score" [icon]="faBalanceScale"></fa-icon>
<fa-icon class="color-primary mr-3px" matTooltip="Balance Score" [icon]="faBalanceScale" />
({{channel.balancedness || 0 | number}})
</mat-hint>
<mat-hint fxFlex="40" fxLayoutAlign="end center" class="font-size-90 color-primary"><strong class="font-weight-900 mr-5px">Remote:</strong>{{channel.to_them_msat/1000 || 0 | number:'1.0-0'}} Sats</mat-hint>
</div>
<mat-progress-bar class="dashboard-progress-bar" mode="determinate" value="{{channel.to_us_msat && channel.to_us_msat > 0 ? ((channel.to_us_msat/((channel.to_us_msat)+(channel.to_them_msat)))*100) : 0}}"></mat-progress-bar>
<mat-progress-bar class="dashboard-progress-bar" mode="determinate" value="{{channel.to_us_msat && channel.to_us_msat > 0 ? ((channel.to_us_msat/((channel.to_us_msat)+(channel.to_them_msat)))*100) : 0}}" />
</div>
</div>
</div>

@ -2,9 +2,9 @@
<div fxLayout="column" fxFlex="8" fxLayoutAlign="end start">
<span class="dashboard-capacity-header this-channel-capacity">Total Capacity</span>
<mat-hint class="font-size-90">{{totalLiquidity | number:'1.0-0'}} Sats</mat-hint>
<mat-progress-bar class="dashboard-progress-bar this-channel-bar" mode="determinate" color="accent" value="100"></mat-progress-bar>
<mat-progress-bar class="dashboard-progress-bar this-channel-bar" mode="determinate" color="accent" value="100" />
</div>
<div fxLayout="column" fxFlex="3" fxLayoutAlign="end stretch"><mat-divider class="dashboard-divider"></mat-divider></div>
<div fxLayout="column" fxFlex="3" fxLayoutAlign="end stretch"><mat-divider class="dashboard-divider" /></div>
<div fxLayout="column" fxFlex.gt-sm="88" fxFlex="84" fxLayoutAlign="start start" [perfectScrollbar]>
<div *ngIf="activeChannels && activeChannels.length > 0; else noChannelBlock" fxLayout="column" fxFlex="100"class="w-100">
<div *ngFor="let channel of activeChannels" class="mt-2">
@ -15,8 +15,8 @@
<mat-hint *ngIf="direction === 'In'" fxFlex="100" fxLayoutAlign="start center" class="font-size-90 color-primary"><strong class="font-weight-900 mr-5px">Capacity: </strong>{{channel.to_them_msat/1000 || 0 | number:'1.0-0'}} Sats</mat-hint>
<mat-hint *ngIf="direction === 'Out'" fxFlex="100" fxLayoutAlign="start center" class="font-size-90 color-primary"><strong class="font-weight-900 mr-5px">Capacity: </strong>{{channel.to_us_msat/1000 || 0 | number:'1.0-0'}} Sats</mat-hint>
</div>
<mat-progress-bar *ngIf="direction === 'In'" class="dashboard-progress-bar" mode="determinate" value="{{(totalLiquidity > 0) ? ((channel.to_them_msat/1000 || 0)/(totalLiquidity) * 100) : 0}}"></mat-progress-bar>
<mat-progress-bar *ngIf="direction === 'Out'" class="dashboard-progress-bar" mode="determinate" value="{{(totalLiquidity > 0) ? ((channel.to_us_msat/1000 || 0)/(totalLiquidity) * 100) : 0}}"></mat-progress-bar>
<mat-progress-bar *ngIf="direction === 'In'" class="dashboard-progress-bar" mode="determinate" value="{{(totalLiquidity > 0) ? ((channel.to_them_msat/1000 || 0)/(totalLiquidity) * 100) : 0}}" />
<mat-progress-bar *ngIf="direction === 'Out'" class="dashboard-progress-bar" mode="determinate" value="{{(totalLiquidity > 0) ? ((channel.to_us_msat/1000 || 0)/(totalLiquidity) * 100) : 0}}" />
</div>
</div>
</div>

@ -10,6 +10,12 @@
<h4 fxLayoutAlign="start" class="dashboard-info-title">Transactions</h4>
<div class="overflow-wrap dashboard-info-value">{{fees?.totalTxCount | number}}</div>
</div>
<div *ngIf="!fees?.totalTxCount">
<h4 fxLayoutAlign="start" class="dashboard-info-title">Transactions</h4>
<a class="overflow-wrap dashboard-info-value" [routerLink]="['../routing']">
Go to Routing
</a>
</div>
</div>
</div>
<ng-template #errorBlock>

@ -1,6 +1,6 @@
<div *ngIf="selNode?.userPersona === userPersonaEnum.OPERATOR; else merchantDashboard"fxLayout="column">
<div fxLayout="row" fxLayoutAlign="start start" class="page-title-container mb-2">
<fa-icon class="page-title-img mr-1" [icon]="apiCallStatusNodeInfo.status === apiCallStatusEnum.ERROR ? faFrown : faSmile"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="apiCallStatusNodeInfo.status === apiCallStatusEnum.ERROR ? faFrown : faSmile" />
<span class="page-title">{{apiCallStatusNodeInfo.status === apiCallStatusEnum.COMPLETED ? 'Welcome ' + information.alias + '! Your node is up and running.' : apiCallStatusNodeInfo.status === apiCallStatusEnum.INITIATED ? 'Wait! Getting your node information...' : 'Error! Please check the server connection.'}}</span>
</div>
<mat-grid-list cols="10" gutterSize="20px" [rowHeight]="operatorCardHeight">
@ -9,7 +9,7 @@
<mat-card-header>
<mat-card-title fxLayoutAlign="space-between center">
<div>
<fa-icon class="mr-1" [icon]="card.icon"></fa-icon>
<fa-icon class="mr-1" [icon]="card.icon" />
<span>{{card.title}}</span>
</div>
<div>
@ -36,13 +36,13 @@
(card.id === 'fee' && (apiCallStatusFees.status === apiCallStatusEnum.INITIATED || apiCallStatusChannels.status === apiCallStatusEnum.INITIATED || apiCallStatusFHistory.status === apiCallStatusEnum.INITIATED)) ||
(card.id === 'status' && (apiCallStatusNodeInfo.status === apiCallStatusEnum.INITIATED || apiCallStatusChannels.status === apiCallStatusEnum.INITIATED || apiCallStatusLRBal.status === apiCallStatusEnum.INITIATED))"
mode="indeterminate"
></mat-progress-bar>
/>
<div fxLayout="column" fxFlex="100" [ngSwitch]="card.id">
<rtl-cln-node-info *ngSwitchCase="'node'" fxFlex="100" [information]="information" [showColorFieldSeparately]="false"></rtl-cln-node-info>
<rtl-cln-balances-info *ngSwitchCase="'balance'" fxFlex="100" [balances]="balances" [errorMessage]="errorMessages[2] + ' ' + errorMessages[3]"></rtl-cln-balances-info>
<rtl-cln-channel-capacity-info *ngSwitchCase="'capacity'" fxFlex="100" [sortBy]="sortField" [channelBalances]="channelBalances" [activeChannels]="activeChannelsCapacity" [errorMessage]="errorMessages[4] + ' ' + errorMessages[3]"></rtl-cln-channel-capacity-info>
<rtl-cln-fee-info *ngSwitchCase="'fee'" fxFlex="100" [fees]="fees" [errorMessage]="errorMessages[1] + ' ' + errorMessages[4] + ' ' + errorMessages[5]"></rtl-cln-fee-info>
<rtl-cln-channel-status-info *ngSwitchCase="'status'" fxFlex="100" [channelsStatus]="channelsStatus" [errorMessage]="errorMessages[0] + ' ' + errorMessages[3] + ' ' + errorMessages[4]"></rtl-cln-channel-status-info>
<rtl-cln-node-info *ngSwitchCase="'node'" fxFlex="100" [information]="information" [showColorFieldSeparately]="false" />
<rtl-cln-balances-info *ngSwitchCase="'balance'" fxFlex="100" [balances]="balances" [errorMessage]="errorMessages[2] + ' ' + errorMessages[3]" />
<rtl-cln-channel-capacity-info *ngSwitchCase="'capacity'" fxFlex="100" [sortBy]="sortField" [channelBalances]="channelBalances" [activeChannels]="activeChannelsCapacity" [errorMessage]="errorMessages[4] + ' ' + errorMessages[3]" />
<rtl-cln-fee-info *ngSwitchCase="'fee'" fxFlex="100" [fees]="fees" [errorMessage]="errorMessages[1] + ' ' + errorMessages[4] + ' ' + errorMessages[5]" />
<rtl-cln-channel-status-info *ngSwitchCase="'status'" fxFlex="100" [channelsStatus]="channelsStatus" [errorMessage]="errorMessages[0] + ' ' + errorMessages[3] + ' ' + errorMessages[4]" />
<h3 *ngSwitchDefault>Error! Unable to find information!</h3>
</div>
</mat-card-content>
@ -52,7 +52,7 @@
</div>
<ng-template #merchantDashboard>
<div fxLayout="row" fxLayoutAlign="start end" class="page-title-container mb-2">
<fa-icon class="page-title-img mr-1" [icon]="faSmile"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faSmile" />
<span class="page-title">Welcome {{information.alias}}! Your node is up and running.</span>
</div>
<mat-grid-list cols="6" gutterSize="20px" [rowHeight]="merchantCardHeight">
@ -61,7 +61,7 @@
<mat-card-header *ngIf="card.id !== 'transactions'">
<mat-card-title fxLayoutAlign="space-between center">
<div>
<fa-icon class="mr-1" [icon]="card.icon"></fa-icon>
<fa-icon class="mr-1" [icon]="card.icon" />
<span>{{card.title}}</span>
</div>
<div>
@ -83,16 +83,16 @@
(card.id === 'balance' && (apiCallStatusBalance.status === apiCallStatusEnum.INITIATED || apiCallStatusLRBal.status === apiCallStatusEnum.INITIATED)) ||
((card.id === 'inboundLiq' || card.id === 'outboundLiq') && apiCallStatusChannels.status === apiCallStatusEnum.INITIATED)"
mode="indeterminate"
></mat-progress-bar>
/>
<div fxLayout="column" fxFlex="100" [ngSwitch]="card.id">
<rtl-cln-node-info *ngSwitchCase="'node'" fxFlex="100" [information]="information"></rtl-cln-node-info>
<rtl-cln-balances-info *ngSwitchCase="'balance'" fxFlex="100" [balances]="balances" [errorMessage]="errorMessages[2] + ' ' + errorMessages[3]"></rtl-cln-balances-info>
<rtl-cln-channel-liquidity-info *ngSwitchCase="'inboundLiq'" fxFlex="100" [direction]="'In'" [totalLiquidity]="totalInboundLiquidity" [activeChannels]="allInboundChannels" [errorMessage]="errorMessages[4]"></rtl-cln-channel-liquidity-info>
<rtl-cln-channel-liquidity-info *ngSwitchCase="'outboundLiq'" fxFlex="100" [direction]="'Out'" [totalLiquidity]="totalOutboundLiquidity" [activeChannels]="allOutboundChannels" [errorMessage]="errorMessages[4]"></rtl-cln-channel-liquidity-info>
<rtl-cln-node-info *ngSwitchCase="'node'" fxFlex="100" [information]="information" />
<rtl-cln-balances-info *ngSwitchCase="'balance'" fxFlex="100" [balances]="balances" [errorMessage]="errorMessages[2] + ' ' + errorMessages[3]" />
<rtl-cln-channel-liquidity-info *ngSwitchCase="'inboundLiq'" fxFlex="100" [direction]="'In'" [totalLiquidity]="totalInboundLiquidity" [activeChannels]="allInboundChannels" [errorMessage]="errorMessages[4]" />
<rtl-cln-channel-liquidity-info *ngSwitchCase="'outboundLiq'" fxFlex="100" [direction]="'Out'" [totalLiquidity]="totalOutboundLiquidity" [activeChannels]="allOutboundChannels" [errorMessage]="errorMessages[4]" />
<span *ngSwitchCase="'transactions'" fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start">
<mat-tab-group mat-stretch-tabs="false" mat-align-tabs="start" fxLayout="column" class="dashboard-tabs-group">
<mat-tab label="Receive"><rtl-cln-lightning-invoices-table class="h-100" [calledFrom]="'home'"></rtl-cln-lightning-invoices-table></mat-tab>
<mat-tab label="Pay"><rtl-cln-lightning-payments [calledFrom]="'home'"></rtl-cln-lightning-payments></mat-tab>
<mat-tab label="Receive"><rtl-cln-lightning-invoices-table class="h-100" [calledFrom]="'home'" /></mat-tab>
<mat-tab label="Pay"><rtl-cln-lightning-payments [calledFrom]="'home'" /></mat-tab>
</mat-tab-group>
<div class="underline">
<button mat-icon-button class="more-button" aria-label="Toggle menu" [matMenuTriggerFor]="menuTransactions">

@ -1,5 +1,5 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faBullhorn"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faBullhorn" />
<span class="page-title">Liquidity Ads</span>
</div>
<div fxLayout="column" class="padding-gap-x">
@ -8,7 +8,7 @@
<div fxLayout="column" fxLayoutAlign="space-between stretch">
<form #formAsk="ngForm" fxFlex="100" fxLayout="column" fxLayoutAlign="start stretch" fxLayoutAlign.gt-sm="space-between stretch" fxLayout.gt-sm="row wrap">
<div fxFlex.gt-xs="100" fxLayout="row" class="alert alert-warn">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>Ads should be supplemented with additional research of the node, before buying liquidity.</span>
</div>
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch" class="page-sub-title-container mt-1">
@ -50,7 +50,7 @@
</form> -->
<div fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="page-sub-title-container mt-2">
<div fxFlex="70">
<fa-icon class="page-title-img mr-1" [icon]="faUsers"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faUsers" />
<span class="page-title">Liquidity Providing Peers</span>
</div>
<div fxFlex.gt-xs="30" fxLayoutAlign.gt-xs="space-between center" fxLayout="row" fxLayoutAlign="space-between stretch">
@ -67,7 +67,7 @@
</div>
</div>
<div fxLayout="column" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="listNodesCallStatus === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="listNodesCallStatus === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #table mat-table matSort [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="liquidityNodes" [ngClass]="{'error-border': errorMessage !== ''}">
<ng-container matColumnDef="alias">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Alias</th>
@ -138,7 +138,7 @@
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -146,7 +146,7 @@
<td *matCellDef="let lqNode" mat-cell fxLayoutAlign="end center">
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onViewLeaseInfo(lqNode)">View Info</mat-option>
<mat-option (click)="onOpenChannel(lqNode)">Open Channel</mat-option>
<mat-option (click)="viewLeaseOn(lqNode, 'LN')">View on Lnrouter</mat-option>
@ -167,7 +167,7 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>
</mat-card-content>
</mat-card>

@ -8,7 +8,7 @@
</mat-card-header>
<mat-card-content class="padding-gap-x-large">
<form #form="ngForm" fxLayout="column">
<ng-container *ngTemplateOutlet="nodeDetailsExpansionBlock"></ng-container>
<ng-container *ngTemplateOutlet="nodeDetailsExpansionBlock" />
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-sm="space-between center" fxLayout.gt-sm="row wrap">
<mat-form-field fxLayout="column" fxFlex="30" fxLayoutAlign="start end">
<mat-label>Requested Amount</mat-label>
@ -36,7 +36,7 @@
<span>Total cost to lease {{node.channel_opening_fee | number}} (Sats)</span>
</div>
<div *ngIf="channelConnectionError !== ''" fxFlex="100" class="alert alert-danger mt-2">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span *ngIf="channelConnectionError !== ''">{{channelConnectionError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="end center">
@ -62,14 +62,14 @@
<span class="foreground-secondary-text">{{node.nodeid}}</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Last Timestamp</h4>
<span class="overflow-wrap foreground-secondary-text">{{(node.last_timestamp * 1000) | date:'dd/MMM/y HH:mm'}}</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
<div fxLayout="column" fxLayoutAlign="start stretch">
<h4 fxFlex="100" class="font-bold-500 mb-1">Addresses</h4>
<div class="table-container">

@ -4,7 +4,7 @@
<div fxLayout="column" fxLayoutAlign="stretch start" fxFlex="100" class="h-100">
<div fxLayout="row" fxLayoutAlign="start start" class="w-100">
<div fxLayout="row" fxLayoutAlign="start start" class="page-title-container pl-2">
<fa-icon class="mr-1" [icon]="card.icon"></fa-icon>
<fa-icon class="mr-1" [icon]="card.icon" />
<span>{{card.title}}</span>
</div>
</div>
@ -25,14 +25,14 @@
(card.id === 'feeRatesKW' && apiCallStatusPerKW.status === apiCallStatusEnum.INITIATED) ||
(card.id === 'onChainFeeEstimates' && apiCallStatusPerKW.status === apiCallStatusEnum.INITIATED)"
mode="indeterminate"
></mat-progress-bar>
/>
<div fxLayout="column" fxFlex="100" [ngSwitch]="card.id">
<rtl-cln-node-info *ngSwitchCase="'node'" fxFlex="100" [information]="information" [showColorFieldSeparately]="false"></rtl-cln-node-info>
<rtl-cln-channel-status-info *ngSwitchCase="'status'" fxFlex="100" [channelsStatus]="channelsStatus" [errorMessage]="errorMessages[0] + ' ' + errorMessages[2]"></rtl-cln-channel-status-info>
<rtl-cln-fee-info *ngSwitchCase="'fee'" fxFlex="100" [fees]="fees" [errorMessage]="errorMessages[1] + ' ' + errorMessages[3] + ' ' + errorMessages[4]"></rtl-cln-fee-info>
<rtl-cln-fee-rates *ngSwitchCase="'feeRatesKB'" class="h-100" [feeRates]="feeRatesPerKB" [feeRateStyle]="'KB'" [errorMessage]="errorMessages[5]"></rtl-cln-fee-rates>
<rtl-cln-fee-rates *ngSwitchCase="'feeRatesKW'" class="h-100" [feeRates]="feeRatesPerKW" [feeRateStyle]="'KW'" [errorMessage]="errorMessages[6]"></rtl-cln-fee-rates>
<rtl-cln-onchain-fee-estimates *ngSwitchCase="'onChainFeeEstimates'" class="h-100" [feeRates]="feeRatesPerKW" [errorMessage]="errorMessages[5]"></rtl-cln-onchain-fee-estimates>
<rtl-cln-node-info *ngSwitchCase="'node'" fxFlex="100" [information]="information" [showColorFieldSeparately]="false" />
<rtl-cln-channel-status-info *ngSwitchCase="'status'" fxFlex="100" [channelsStatus]="channelsStatus" [errorMessage]="errorMessages[0] + ' ' + errorMessages[2]" />
<rtl-cln-fee-info *ngSwitchCase="'fee'" fxFlex="100" [fees]="fees" [errorMessage]="errorMessages[1] + ' ' + errorMessages[3] + ' ' + errorMessages[4]" />
<rtl-cln-fee-rates *ngSwitchCase="'feeRatesKB'" class="h-100" [feeRates]="feeRatesPerKB" [feeRateStyle]="'KB'" [errorMessage]="errorMessages[5]" />
<rtl-cln-fee-rates *ngSwitchCase="'feeRatesKW'" class="h-100" [feeRates]="feeRatesPerKW" [feeRateStyle]="'KW'" [errorMessage]="errorMessages[6]" />
<rtl-cln-onchain-fee-estimates *ngSwitchCase="'onChainFeeEstimates'" class="h-100" [feeRates]="feeRatesPerKW" [errorMessage]="errorMessages[5]" />
</div>
</mat-card-content>
</mat-card>
@ -45,7 +45,7 @@
<div fxLayout="column" fxLayoutAlign="stretch start" fxFlex="100" class="h-100">
<div fxLayout="row" fxLayoutAlign="start start" class="w-100">
<div fxLayout="row" fxLayoutAlign="start start" class="page-title-container pl-15px">
<fa-icon class="mr-1" [icon]="card.icon"></fa-icon>
<fa-icon class="mr-1" [icon]="card.icon" />
<span>{{card.title}}</span>
</div>
</div>
@ -66,14 +66,14 @@
(card.id === 'feeRatesKW' && apiCallStatusPerKW.status === apiCallStatusEnum.INITIATED) ||
(card.id === 'onChainFeeEstimates' && apiCallStatusPerKW.status === apiCallStatusEnum.INITIATED)"
mode="indeterminate"
></mat-progress-bar>
/>
<div fxLayout="column" fxFlex="100" [ngSwitch]="card.id">
<rtl-cln-node-info *ngSwitchCase="'node'" fxFlex="100" [information]="information" [showColorFieldSeparately]="false"></rtl-cln-node-info>
<rtl-cln-channel-status-info *ngSwitchCase="'status'" fxFlex="100" [channelsStatus]="channelsStatus" [errorMessage]="errorMessages[0] + ' ' + errorMessages[2]"></rtl-cln-channel-status-info>
<rtl-cln-fee-info *ngSwitchCase="'fee'" fxFlex="100" [fees]="fees" [errorMessage]="errorMessages[1] + ' ' + errorMessages[3] + ' ' + errorMessages[4]"></rtl-cln-fee-info>
<rtl-cln-fee-rates *ngSwitchCase="'feeRatesKB'" class="h-100" [feeRates]="feeRatesPerKB" [feeRateStyle]="'KB'" [errorMessage]="errorMessages[5]"></rtl-cln-fee-rates>
<rtl-cln-fee-rates *ngSwitchCase="'feeRatesKW'" class="h-100" [feeRates]="feeRatesPerKW" [feeRateStyle]="'KW'" [errorMessage]="errorMessages[5]"></rtl-cln-fee-rates>
<rtl-cln-onchain-fee-estimates *ngSwitchCase="'onChainFeeEstimates'" class="h-100" [feeRates]="feeRatesPerKW" [errorMessage]="errorMessages[5]"></rtl-cln-onchain-fee-estimates>
<rtl-cln-node-info *ngSwitchCase="'node'" fxFlex="100" [information]="information" [showColorFieldSeparately]="false" />
<rtl-cln-channel-status-info *ngSwitchCase="'status'" fxFlex="100" [channelsStatus]="channelsStatus" [errorMessage]="errorMessages[0] + ' ' + errorMessages[2]" />
<rtl-cln-fee-info *ngSwitchCase="'fee'" fxFlex="100" [fees]="fees" [errorMessage]="errorMessages[1] + ' ' + errorMessages[3] + ' ' + errorMessages[4]" />
<rtl-cln-fee-rates *ngSwitchCase="'feeRatesKB'" class="h-100" [feeRates]="feeRatesPerKB" [feeRateStyle]="'KB'" [errorMessage]="errorMessages[5]" />
<rtl-cln-fee-rates *ngSwitchCase="'feeRatesKW'" class="h-100" [feeRates]="feeRatesPerKW" [feeRateStyle]="'KW'" [errorMessage]="errorMessages[5]" />
<rtl-cln-onchain-fee-estimates *ngSwitchCase="'onChainFeeEstimates'" class="h-100" [feeRates]="feeRatesPerKW" [errorMessage]="errorMessages[5]" />
</div>
</mat-card-content>
</mat-card>

@ -18,7 +18,7 @@ import { getNewAddress } from '../../store/cln.actions';
export class CLNOnChainReceiveComponent {
public addressTypes = ADDRESS_TYPES;
public selectedAddressType = ADDRESS_TYPES[0];
public selectedAddressType = ADDRESS_TYPES[2];
public newAddress = '';
constructor(private store: Store<RTLState>, private clnEffects: CLNEffects) { }

@ -43,7 +43,7 @@
</mat-form-field>
</div>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="7" tabindex="5" color="primary" name="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}" [(ngModel)]="flgMinConf" (change)="flgMinConf ? selFeeRate=null : minConfValue=null"></mat-checkbox>
<mat-checkbox fxFlex="7" tabindex="5" color="primary" name="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}" [(ngModel)]="flgMinConf" (change)="flgMinConf ? selFeeRate=null : minConfValue=null" />
<mat-form-field fxLayout="column" fxFlex="93">
<mat-label>Min Confirmation Blocks</mat-label>
<input #blocks="ngModel" matInput type="number" name="blocks" tabindex="8" [step]="1" [min]="0" [required]="flgMinConf" [disabled]="!flgMinConf" [(ngModel)]="minConfValue">
@ -77,7 +77,7 @@
</mat-expansion-panel>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch"></div>
<div *ngIf="sendFundError !== ''" fxFlex="100" class="alert alert-danger mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span *ngIf="sendFundError !== ''">{{sendFundError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxFlex="100" fxLayoutAlign="end center">
@ -134,7 +134,7 @@
</mat-form-field>
</div>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="7" tabindex="5" color="primary" formControlName="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}"></mat-checkbox>
<mat-checkbox fxFlex="7" tabindex="5" color="primary" formControlName="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}" />
<mat-form-field fxLayout="column" fxFlex="93">
<mat-label>Min Confirmation Blocks</mat-label>
<input matInput formControlName="minConfValue" type="number" name="blocks" tabindex="8" [step]="1" [min]="0" [required]="sendFundFormGroup.controls.flgMinConf.value">
@ -153,11 +153,11 @@
<ng-template matStepLabel>{{confirmFormLabel}}</ng-template>
<div fxLayout="column">
<div fxFlex="100" class="w-100 alert alert-warn">
<fa-icon class="mt-1 mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mt-1 mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>You are about to sweep all funds from RTL. Are you sure?</span>
</div>
<div *ngIf="sendFundError !== ''" fxFlex="100" class="alert alert-danger mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span *ngIf="sendFundError !== ''">{{sendFundError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">

@ -1,30 +1,30 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faChartPie"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faChartPie" />
<span class="page-title">On-chain Balance</span>
</div>
<div fxLayout="column" class="padding-gap-x mb-4">
<mat-card>
<mat-card-content fxLayout="column">
<rtl-currency-unit-converter [values]="balances"></rtl-currency-unit-converter>
<rtl-currency-unit-converter [values]="balances" />
</mat-card-content>
</mat-card>
</div>
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faExchangeAlt"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faExchangeAlt" />
<span class="page-title">On-chain Transactions</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel">
<div *ngFor="let link of links" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === link?.link" [routerLink]="[link?.link, selectedTable?.name]" (click)="activeLink = link?.link">{{link?.name}}</div>
<div *ngFor="let link of links" tabindex="1" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === link?.link" [routerLink]="[link?.link, selectedTable?.name]" (click)="activeLink = link?.link">{{link?.name}}</div>
</nav>
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel>
<mat-tab-nav-panel #tabPanel />
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
<router-outlet></router-outlet>
<router-outlet />
</div>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap-x-large">
<rtl-cln-utxo-tables fxLayout="row" fxFlex="100" [selectedTableIndex]="selectedTable?.id" (selectedTableIndexChange)="onSelectedTableIndexChanged($event)"></rtl-cln-utxo-tables>
<rtl-cln-utxo-tables fxLayout="row" fxFlex="100" [selectedTableIndex]="selectedTable?.id" (selectedTableIndexChange)="onSelectedTableIndexChanged($event)" />
</div>
</mat-card-content>
</mat-card>

@ -4,13 +4,13 @@
<ng-template mat-tab-label>
<span matBadgeOverlap="false" class="tab-badge" matBadgeColor="primary" matBadge="{{numUtxos}}">UTXOs</span>
</ng-template>
<rtl-cln-on-chain-utxos fxLayout="row" fxFlex="100" [numDustUTXOs]="numDustUtxos" [isDustUTXO]="false" [dustAmount]="DUST_AMOUNT"></rtl-cln-on-chain-utxos>
<rtl-cln-on-chain-utxos fxLayout="row" fxFlex="100" [numDustUTXOs]="numDustUtxos" [isDustUTXO]="false" [dustAmount]="DUST_AMOUNT" />
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<span matBadgeOverlap="false" class="tab-badge" matBadge="{{numDustUtxos}}">Dust UTXOs</span>
</ng-template>
<rtl-cln-on-chain-utxos fxLayout="row" fxFlex="100" [numDustUTXOs]="numDustUtxos" [isDustUTXO]="true" [dustAmount]="DUST_AMOUNT"></rtl-cln-on-chain-utxos>
<rtl-cln-on-chain-utxos fxLayout="row" fxFlex="100" [numDustUTXOs]="numDustUtxos" [isDustUTXO]="true" [dustAmount]="DUST_AMOUNT" />
</mat-tab>
</mat-tab-group>
</div>

@ -16,7 +16,7 @@
</div>
<div fxLayout="row" fxLayoutAlign="start start">
<div class="table-container" fxFlex="100" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #table mat-table matSort [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="listUTXOs" [ngClass]="{'error-border': errorMessage !== ''}">
<ng-container matColumnDef="is_dust">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before" matTooltip="Dust/Nondust"></th>
@ -84,7 +84,7 @@
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -104,10 +104,10 @@
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>
</div>
</div>
<ng-template #emptySpace>
<mat-icon fxLayoutAlign="start center" color="warn" class="mr-1"></mat-icon>
<mat-icon fxLayoutAlign="start center" color="warn" class="mr-1" />
</ng-template>

@ -10,11 +10,11 @@
<form fxLayout="column">
<div fxLayout="column" class="bordered-box mb-1 p-2">
<p fxLayoutAlign="start center" class="pb-1 word-break">Bump fee for transaction id: {{bumpFeeChannel?.funding_txid}}
<fa-icon class="ml-1" matSuffix rtlClipboard matTooltip="Copy transaction ID" [icon]="faCopy" [payload]="bumpFeeChannel?.funding_txid" (copied)="onCopyID($event)"></fa-icon>
<fa-icon class="ml-1" matSuffix rtlClipboard matTooltip="Copy transaction ID" [icon]="faCopy" [payload]="bumpFeeChannel?.funding_txid" (copied)="onCopyID($event)" />
</p>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch">
<div fxFlex="100" class="alert alert-info">
<fa-icon class="mr-1 alert-icon" [icon]="faInfoCircle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faInfoCircle" />
<span fxLayout="column" fxFlex="100">Bumping fee on pending open channels is an advanced feature, attempt it only if you are familiar with the functionality of Bitcoin transactions.
<div>Before attempting fee bump ensure the following:</div>
<div class="pl-1">1: Use a Bitcoin block explorer to ensure that channel opening transaction is not confirmed.</div>
@ -39,7 +39,7 @@
</mat-form-field>
</div>
<div *ngIf="bumpFeeError !== ''" fxFlex="100" class="alert alert-danger mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>{{bumpFeeError}}</span>
</div>
</div>

@ -2,7 +2,7 @@
<div fxFlex="100">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<fa-icon class="page-title-img mr-1" [icon]="faReceipt"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faReceipt" />
<span class="page-title">Channel Information</span>
</div>
<button tabindex="3" fxFlex="5" fxLayoutAlign="center center" class="btn-close-x p-0" mat-button (click)="onClose()">X</button>
@ -12,7 +12,7 @@
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Short Channel ID</h4>
<span class="foreground-secondary-text go-to-link" matTooltip="Go To Graph Lookup" (click)="onGoToLink()" >
<span tabindex="4" class="foreground-secondary-text go-to-link" matTooltip="Go To Graph Lookup" (click)="onGoToLink()" >
{{channel.short_channel_id}}
</span>
</div>
@ -21,21 +21,21 @@
<span class="foreground-secondary-text">{{channel.alias}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Channel ID</h4>
<span class="foreground-secondary-text">{{channel.channel_id}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Peer Public Key</h4>
<span class="foreground-secondary-text">{{channel.peer_id}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="row">
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">State</h4>
@ -50,7 +50,7 @@
<span class="overflow-wrap foreground-secondary-text">{{channel.private ? 'Yes' : 'No'}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="row">
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Remote Balance (Sats)</h4>
@ -65,7 +65,7 @@
<span class="overflow-wrap foreground-secondary-text">{{channel.total_msat / 1000 | number:'1.0-0'}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div *ngIf="showAdvanced">
<div fxLayout="row">
<div fxFlex="50">
@ -77,7 +77,7 @@
<span class="overflow-wrap foreground-secondary-text">{{channel.spendable_msat / 1000 | number:'1.0-0'}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Their Reserve (Sats)</h4>
@ -88,14 +88,14 @@
<span class="overflow-wrap foreground-secondary-text">{{channel.our_reserve_msat / 1000 | number:'1.0-2'}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Funding Transaction ID</h4>
<span class="foreground-secondary-text">{{channel.funding_txid}}</span>
</div>
</div>
<mat-divider class="my-1"></mat-divider>
<mat-divider class="my-1" />
</div>
<div fxLayout="row" fxLayoutAlign="end center" fxFlex="100" [ngClass]="{'mt-2': !showAdvanced, 'mt-1': showAdvanced}">
<button mat-button color="primary" type="reset" tabindex="1" class="mr-1" (click)="onShowAdvanced()">

@ -15,7 +15,7 @@
</div>
</div>
<div fxLayout="column" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #table mat-table fxFlex="100" matSort [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="channels" [ngClass]="{'error-border': errorMessage !== ''}">
<!-- Channel Group Row Start -->
<ng-container matColumnDef="amount_msat">
@ -113,7 +113,7 @@
<th *matHeaderCellDef mat-header-cell class="px-2">
<div class="bordered-box table-actions-select" fxLayoutAlign="end center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -142,5 +142,5 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>

@ -14,14 +14,14 @@
</mat-form-field>
</div>
</div>
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<div fxLayout="row" fxLayoutAlign="start center" fxFlex="100" class="table-container w-100" [perfectScrollbar]>
<table #table mat-table matSort [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="channels" [ngClass]="{'error-border': errorMessage !== ''}">
<ng-container matColumnDef="private">
<th *matHeaderCellDef mat-header-cell mat-sort-header matTooltip="Private"></th>
<td *matCellDef="let channel" mat-cell>
<span *ngIf="channel.private" class="mr-1" matTooltip="Private" matTooltipPosition="right"><fa-icon [icon]="faEyeSlash"></fa-icon></span>
<span *ngIf="!channel.private" class="mr-1" matTooltip="Public" matTooltipPosition="right"><fa-icon [icon]="faEye"></fa-icon></span>
<span *ngIf="channel.private" class="mr-1" matTooltip="Private" matTooltipPosition="right"><fa-icon [icon]="faEyeSlash" /></span>
<span *ngIf="!channel.private" class="mr-1" matTooltip="Public" matTooltipPosition="right"><fa-icon [icon]="faEye" /></span>
</td>
</ng-container>
<ng-container matColumnDef="short_channel_id">
@ -104,14 +104,14 @@
<div fxLayout="row">
<mat-hint fxFlex="100" fxLayoutAlign="center center" class="font-size-80">{{channel.balancedness || 0 | number}}</mat-hint>
</div>
<mat-progress-bar mode="determinate" value="{{channel.to_us_msat && channel.to_us_msat > 0 ? ((channel.to_us_msat/((channel.to_us_msat)+(channel.to_them_msat)))*100) : 0}}"></mat-progress-bar>
<mat-progress-bar mode="determinate" value="{{channel.to_us_msat && channel.to_us_msat > 0 ? ((channel.to_us_msat/((channel.to_us_msat)+(channel.to_them_msat)))*100) : 0}}" />
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onChannelUpdate('all')">Update Fee Policy</mat-option>
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
@ -120,7 +120,7 @@
<td *matCellDef="let channel" mat-cell fxLayoutAlign="end center">
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="2" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onChannelClick(channel, $event)">View Info</mat-option>
<mat-option (click)="onViewRemotePolicy(channel)">View Remote Fee</mat-option>
<mat-option (click)="onChannelUpdate(channel)">Update Fee Policy</mat-option>
@ -142,5 +142,5 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>

@ -68,7 +68,7 @@ export class CLNChannelOpenTableComponent implements OnInit, AfterViewInit, OnDe
}
ngOnInit() {
if (window.history.state && window.history.state.filterColumn) {
if (window.history.state && (window.history.state.filterColumn || window.history.state.filterValue)) {
this.selFilterBy = window.history.state.filterColumn || 'all';
this.selFilter = window.history.state.filterValue || '';
}

@ -14,14 +14,14 @@
</mat-form-field>
</div>
</div>
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<div fxLayout="row" fxLayoutAlign="start center" fxFlex="100" class="table-container w-100" [perfectScrollbar]>
<table #table mat-table matSort [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="channels" [ngClass]="{'error-border': errorMessage !== ''}">
<ng-container matColumnDef="private">
<th *matHeaderCellDef mat-header-cell mat-sort-header matTooltip="Private"></th>
<td *matCellDef="let channel" mat-cell>
<span *ngIf="channel.private" class="mr-1" matTooltip="Private" matTooltipPosition="right"><fa-icon [icon]="faEyeSlash"></fa-icon></span>
<span *ngIf="!channel.private" class="mr-1" matTooltip="Public" matTooltipPosition="right"><fa-icon [icon]="faEye"></fa-icon></span>
<span *ngIf="channel.private" class="mr-1" matTooltip="Private" matTooltipPosition="right"><fa-icon [icon]="faEyeSlash" /></span>
<span *ngIf="!channel.private" class="mr-1" matTooltip="Public" matTooltipPosition="right"><fa-icon [icon]="faEye" /></span>
</td>
</ng-container>
<ng-container matColumnDef="alias">
@ -98,7 +98,7 @@
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -106,7 +106,7 @@
<td *matCellDef="let channel" mat-cell fxLayoutAlign="end center">
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="4" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onChannelClick(channel, $event)">View Info</mat-option>
<mat-option *ngIf="(channel.state === 'CHANNELD_SHUTTING_DOWN' || channel.state === 'CLOSINGD_SIGEXCHANGE' || (!channel.connected && channel.state === 'CHANNELD_NORMAL'))" (click)="onChannelClose(channel)">Close Channel</mat-option>
<mat-option *ngIf="channel.state === 'CHANNELD_AWAITING_LOCKIN'" (click)="onBumpFee(channel)">Bump Fee</mat-option>
@ -127,5 +127,5 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>

@ -21,7 +21,7 @@
</mat-tab>
</mat-tab-group>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap-x-large">
<router-outlet></router-outlet>
<router-outlet />
</div>
</div>
</div>

@ -19,7 +19,7 @@
<mat-error *ngIf="selectedPeer.errors?.notfound">Peer not found in the list.</mat-error>
</mat-form-field>
</div>
<ng-container *ngTemplateOutlet="peerDetailsExpansionBlock"></ng-container>
<ng-container *ngTemplateOutlet="peerDetailsExpansionBlock" />
<div fxLayout="column">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxLayout="column" fxFlex="70" fxLayoutAlign="start end">
@ -58,7 +58,7 @@
</mat-form-field>
</div>
<div fxFlex="42" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="7" tabindex="5" color="primary" name="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}" [(ngModel)]="flgMinConf" (change)="flgMinConf ? selFeeRate=null : minConfValue=null"></mat-checkbox>
<mat-checkbox fxFlex="7" tabindex="5" color="primary" name="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}" [(ngModel)]="flgMinConf" (change)="flgMinConf ? selFeeRate=null : minConfValue=null" />
<mat-form-field fxLayout="column" fxFlex="93">
<mat-label>Min Confirmation Blocks</mat-label>
<input #blocks="ngModel" matInput type="number" name="blocks" tabindex="8" [step]="1" [min]="0" [required]="flgMinConf" [disabled]="!flgMinConf" [(ngModel)]="minConfValue">
@ -83,7 +83,7 @@
</mat-expansion-panel>
</div>
<div *ngIf="channelConnectionError !== ''" fxFlex="100" class="alert alert-danger mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span *ngIf="channelConnectionError !== ''">{{channelConnectionError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="end center">
@ -109,7 +109,7 @@
<span class="foreground-secondary-text">{{peer.id}}</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Address</h4>

@ -18,7 +18,7 @@
<mat-error *ngIf="peerFormGroup.controls.peerAddress.errors?.required">Address is required.</mat-error>
</mat-form-field>
<div *ngIf="peerConnectionError !== ''" fxFlex="100" class="alert alert-danger mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>{{peerConnectionError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">
@ -61,7 +61,7 @@
</mat-form-field>
</div>
<div fxFlex="45" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="7" tabindex="5" color="primary" formControlName="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}"></mat-checkbox>
<mat-checkbox fxFlex="7" tabindex="5" color="primary" formControlName="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}" />
<mat-form-field fxLayout="column" fxFlex="93">
<mat-label>Min Confirmation Blocks</mat-label>
<input matInput formControlName="minConfValue" type="number" name="blocks" tabindex="8" [step]="1" [min]="0" [required]="channelFormGroup.controls.flgMinConf.value">
@ -71,7 +71,7 @@
</div>
</div>
<div *ngIf="channelConnectionError !== ''" fxFlex="100" class="alert alert-danger mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>{{channelConnectionError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">

@ -1,16 +1,16 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faChartPie"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faChartPie" />
<span class="page-title">On-chain Balance</span>
</div>
<div fxLayout="column" class="padding-gap-x mb-4">
<mat-card>
<mat-card-content fxLayout="column">
<rtl-currency-unit-converter [values]="balances"></rtl-currency-unit-converter>
<rtl-currency-unit-converter [values]="balances" />
</mat-card-content>
</mat-card>
</div>
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faUsers"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faUsers" />
<span class="page-title">Connections</span>
</div>
<div fxLayout="column" class="padding-gap-x">
@ -29,7 +29,7 @@
</mat-tab>
</mat-tab-group>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap-x-large">
<router-outlet></router-outlet>
<router-outlet />
</div>
</mat-card-content>
</mat-card>

@ -5,7 +5,7 @@
<div fxLayout="column">
<div fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="page-sub-title-container">
<div fxFlex="70">
<fa-icon class="page-title-img mr-1" [icon]="faUsers"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faUsers" />
<span class="page-title">Connected Peers</span>
</div>
<div fxFlex.gt-xs="30" fxLayoutAlign.gt-xs="space-between center" fxLayout="row" fxLayoutAlign="space-between stretch">
@ -22,7 +22,7 @@
</div>
</div>
<div fxLayout="column" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #table mat-table matSort class="overflow-x-hidden overflow-y-hidden" [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="peers" [ngClass]="{'error-border': errorMessage !== ''}">
<ng-container matColumnDef="connected">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before" matTooltip="Connected"></th>
@ -59,7 +59,7 @@
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -67,7 +67,7 @@
<td *matCellDef="let peer" mat-cell fxLayoutAlign="end center">
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onPeerClick(peer, $event)">View Info</mat-option>
<mat-option (click)="onOpenChannel(peer)">Open Channel</mat-option>
<mat-option *ngIf="peer.connected" (click)="onPeerDetach(peer)">Disconnect</mat-option>
@ -88,6 +88,6 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>
</div>

@ -1,15 +1,15 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faChartBar"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faChartBar" />
<span class="page-title">Reports</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel">
<div *ngFor="let link of links" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === link.link" routerLink="{{link.link}}" (click)="activeLink = link.link">{{link.name}}</div>
<div *ngFor="let link of links" tabindex="1" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === link.link" routerLink="{{link.link}}" (click)="activeLink = link.link">{{link.name}}</div>
</nav>
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel>
<router-outlet></router-outlet>
<mat-tab-nav-panel #tabPanel />
<router-outlet />
</mat-card-content>
</mat-card>
</div>

@ -1,5 +1,5 @@
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100" class="padding-gap-x-large">
<rtl-horizontal-scroller (stepChanged)="onSelectionChange($event)"></rtl-horizontal-scroller>
<rtl-horizontal-scroller (stepChanged)="onSelectionChange($event)" />
<div fxLayout="column" fxLayoutAlign="center center" class="padding-gap-x">
<mat-radio-group class="my-1" color="primary" name="selReportBy" fxFlex="100" fxLayoutAlign="start center" [(ngModel)]="selReportBy" (change)="onSelReportByChange()">
<span class="mr-2">Report By: </span>
@ -9,7 +9,7 @@
</div>
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100" class="padding-gap-x">
<div *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" fxLayout="column" fxLayoutAlign="center center" fxFlex="100" class="font-size-120 mt-1">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
<mat-progress-bar mode="indeterminate" />
<p>Getting Forwarding History...</p>
</div>
<div *ngIf="apiCallStatus?.status === apiCallStatusEnum.ERROR" fxLayout="column" fxLayoutAlign="center center" fxFlex="100" class="font-size-120 mt-1 error-border">{{errorMessage}}</div>
@ -42,7 +42,7 @@
</ngx-charts-bar-vertical>
</div>
<div class="mt-1">
<rtl-cln-forwarding-history *ngIf="filteredEventsBySelectedPeriod && filteredEventsBySelectedPeriod.length > 0" [pageId]="'reports'" [tableId]="'routing'" [eventsData]="filteredEventsBySelectedPeriod" [selFilter]="eventFilterValue"></rtl-cln-forwarding-history>
<rtl-cln-forwarding-history *ngIf="filteredEventsBySelectedPeriod && filteredEventsBySelectedPeriod.length > 0" [pageId]="'reports'" [tableId]="'routing'" [eventsData]="filteredEventsBySelectedPeriod" [selFilter]="eventFilterValue" />
</div>
</div>
</div>

@ -1,5 +1,5 @@
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100" class="padding-gap-x-large">
<rtl-horizontal-scroller (stepChanged)="onSelectionChange($event)"></rtl-horizontal-scroller>
<rtl-horizontal-scroller (stepChanged)="onSelectionChange($event)" />
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100" class="padding-gap-x">
<div *ngIf="transactionsNonZeroReportData.length > 0" fxLayout="column" fxLayoutAlign="center center" fxFlex="100" class="font-size-120 font-bold-700 mt-1" [@fadeIn]="transactionsReportSummary">
<div *ngIf="transactionsReportSummary.paymentsSelectedPeriod" fxLayout="row" fxLayoutAlign="start stretch" fxFlex="100">
@ -35,7 +35,7 @@
</ngx-charts-bar-vertical-2d>
</div>
<div class="mt-1">
<rtl-transactions-report-table *ngIf="transactionsNonZeroReportData.length > 0" [displayedColumns]="displayedColumns" [tableSetting]="tableSetting" [dataList]="transactionsNonZeroReportData" [dataRange]="reportPeriod" [selFilter]="transactionFilterValue"></rtl-transactions-report-table>
<rtl-transactions-report-table *ngIf="transactionsNonZeroReportData.length > 0" [displayedColumns]="displayedColumns" [tableSetting]="tableSetting" [dataList]="transactionsNonZeroReportData" [dataRange]="reportPeriod" [selFilter]="transactionFilterValue" />
</div>
</div>
</div>

@ -2,7 +2,7 @@
<div *ngIf="errorMessage !== ''" class="p-2 error-border my-2">{{errorMessage}}</div>
<div *ngIf="errorMessage === ''" fxLayout="column" fxLayoutAlign="start stretch" class="page-sub-title-container">
<div fxFlex="100" class="alert alert-warn mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>Maximum 1,000 failed transactions only.</span>
</div>
<div fxFlex="100">
@ -22,7 +22,7 @@
</div>
</div>
<div *ngIf="errorMessage === ''" fxLayout="column" fxLayoutAlign="start center" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #table mat-table fxFlex="100" matSort class="overflow-auto" [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="failedForwardingEvents">
<ng-container matColumnDef="received_time">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Received Time</th>
@ -87,7 +87,7 @@
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -108,5 +108,5 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator *ngIf="errorMessage === ''" class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator *ngIf="errorMessage === ''" class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>

@ -16,7 +16,7 @@
</div>
</div>
<div *ngIf="errorMessage === ''" fxLayout="column" fxLayoutAlign="start center" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #table mat-table fxFlex="100" matSort class="overflow-auto" [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="forwardingHistoryEvents">
<ng-container matColumnDef="received_time">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Received Time</th>
@ -89,7 +89,7 @@
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -110,5 +110,5 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator *ngIf="errorMessage === ''" class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator *ngIf="errorMessage === ''" class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>

@ -59,6 +59,23 @@ export class CLNForwardingHistoryComponent implements OnInit, OnChanges, AfterVi
this.screenSize = this.commonService.getScreenSize();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.eventsData) {
this.apiCallStatus = { status: APICallStatusEnum.COMPLETED, action: 'FetchForwardingHistory' };
this.eventsData = changes.eventsData.currentValue;
this.successfulEvents = this.eventsData;
this.totalForwardedTransactions = this.eventsData.length;
if (this.paginator) { this.paginator.firstPage(); }
if (!changes.eventsData.firstChange) {
this.loadForwardingEventsTable(this.successfulEvents);
}
}
if (changes.selFilter && !changes.selFilter.firstChange) {
this.selFilterBy = 'all';
this.applyFilter();
}
}
ngOnInit() {
this.store.select(clnPageSettings).pipe(takeUntil(this.unSubs[0])).
subscribe((settings: { pageSettings: PageSettings[], apiCallStatus: ApiCallStatusPayload }) => {
@ -110,23 +127,6 @@ export class CLNForwardingHistoryComponent implements OnInit, OnChanges, AfterVi
}, 0);
}
ngOnChanges(changes: SimpleChanges) {
if (changes.eventsData) {
this.apiCallStatus = { status: APICallStatusEnum.COMPLETED, action: 'FetchForwardingHistory' };
this.eventsData = changes.eventsData.currentValue;
this.successfulEvents = this.eventsData;
this.totalForwardedTransactions = this.eventsData.length;
if (this.paginator) { this.paginator.firstPage(); }
if (!changes.eventsData.firstChange) {
this.loadForwardingEventsTable(this.successfulEvents);
}
}
if (changes.selFilter && !changes.selFilter.firstChange) {
this.selFilterBy = 'all';
this.applyFilter();
}
}
onForwardingEventClick(selFEvent: ForwardingEvent, event: any) {
const reorderedFHEvent = [
[{ key: 'status', value: 'Settled', title: 'Status', width: 50, type: DataTypeEnum.STRING },

@ -2,7 +2,7 @@
<div *ngIf="errorMessage !== ''" class="p-2 error-border my-2">{{errorMessage}}</div>
<div *ngIf="errorMessage === ''" fxLayout="column" fxLayoutAlign="start stretch" class="page-sub-title-container">
<div fxFlex="100" class="alert alert-warn mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>Maximum 1,000 local failed transactions only.</span>
</div>
<div fxFlex="100">
@ -22,7 +22,7 @@
</div>
</div>
<div *ngIf="errorMessage === ''" fxLayout="column" fxLayoutAlign="start center" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #table mat-table fxFlex="100" matSort class="overflow-auto" [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="failedLocalForwardingEvents">
<ng-container matColumnDef="received_time">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Received Time</th>
@ -72,7 +72,7 @@
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -93,5 +93,5 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator *ngIf="errorMessage === ''" class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator *ngIf="errorMessage === ''" class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>

@ -8,7 +8,7 @@
</div>
</div>
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #tableIn mat-table matSort class="overflow-auto incoming-table" [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="routingPeersIncoming">
<ng-container matColumnDef="channel_id">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Channel ID</th>
@ -51,7 +51,7 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator #paginatorIn class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator #paginatorIn class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>
<div fxLayout="column" fxFlex="49" fxLayoutAlign="end stretch">
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch" class="page-sub-title-container w-100" [ngClass]="{'mt-2': screenSize !== screenSizeEnum.LG}">
@ -70,7 +70,7 @@
</div>
</div>
<div fxLayout="column" fxLayoutAlign="start end" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #tableOut mat-table matSort class="overflow-auto outgoing-table" [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="routingPeersOutgoing">
<ng-container matColumnDef="channel_id">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Channel ID</th>
@ -112,7 +112,7 @@
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
<mat-paginator #paginatorOut class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator #paginatorOut class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>
</div>
</div>

@ -59,6 +59,17 @@ export class CLNRoutingPeersComponent implements OnInit, OnChanges, AfterViewIni
this.screenSize = this.commonService.getScreenSize();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.eventsData) {
this.apiCallStatus = { status: APICallStatusEnum.COMPLETED, action: 'FetchForwardingHistory' };
this.eventsData = changes.eventsData.currentValue;
this.successfulEvents = this.eventsData;
if (!changes.eventsData.firstChange) {
this.loadRoutingPeersTable(this.successfulEvents);
}
}
}
ngOnInit() {
this.store.pipe(take(1)).subscribe((state) => {
if (state.cln.apisCallStatus.FetchForwardingHistoryS.status === APICallStatusEnum.UN_INITIATED && !state.cln.forwardingHistory.listForwards?.length) {
@ -105,17 +116,6 @@ export class CLNRoutingPeersComponent implements OnInit, OnChanges, AfterViewIni
}
}
ngOnChanges(changes: SimpleChanges) {
if (changes.eventsData) {
this.apiCallStatus = { status: APICallStatusEnum.COMPLETED, action: 'FetchForwardingHistory' };
this.eventsData = changes.eventsData.currentValue;
this.successfulEvents = this.eventsData;
if (!changes.eventsData.firstChange) {
this.loadRoutingPeersTable(this.successfulEvents);
}
}
}
applyIncomingFilter() {
this.routingPeersIncoming.filter = this.filterIn.toLowerCase();
}

@ -1,6 +1,6 @@
<div fxLayout="column" class="mb-2">
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faMapSigns"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faMapSigns" />
<span class="page-title">Routing</span>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start start" class="padding-gap-x">
@ -8,12 +8,12 @@
<mat-card-content fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<div fxLayout="row" fxFlex="100">
<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" fxFlex="100" [tabPanel]="tabPanel">
<div *ngFor="let link of links" mat-tab-link role="tab" class="mat-tab-label" [active]="activeLink === link.link" routerLink="{{link.link}}" (click)="activeLink = link.link">{{link.name}}</div>
<div *ngFor="let link of links" tabindex="1" mat-tab-link role="tab" class="mat-tab-label" [active]="activeLink === link.link" routerLink="{{link.link}}" (click)="activeLink = link.link">{{link.name}}</div>
</nav>
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel>
<mat-tab-nav-panel #tabPanel />
</div>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="padding-gap-x-large">
<router-outlet></router-outlet>
<router-outlet />
</div>
</mat-card-content>
</mat-card>

@ -1,16 +1,16 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faUserCheck"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faUserCheck" />
<span class="page-title">Sign/Verify Message</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel">
<div *ngFor="let link of links" mat-tab-link role="tab" class="mat-tab-label" [active]="activeLink === link.link" routerLink="{{link.link}}" (click)="activeLink = link.link">{{link.name}}</div>
<div *ngFor="let link of links" tabindex="1" mat-tab-link role="tab" class="mat-tab-label" [active]="activeLink === link.link" routerLink="{{link.link}}" (click)="activeLink = link.link">{{link.name}}</div>
</nav>
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel>
<mat-tab-nav-panel #tabPanel />
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper mb-2">
<router-outlet></router-outlet>
<router-outlet />
</div>
</mat-card-content>
</mat-card>

@ -9,7 +9,7 @@
<button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button>
<button mat-flat-button color="primary" tabindex="3" type="submit" (click)="onSign()">Sign</button>
</div>
<mat-divider class="my-2"></mat-divider>
<mat-divider class="my-2" />
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center"><p>Generated Signature</p></div>
<div fxLayout="row" fxLayoutAlign="start center" class="signature-box bordered-box read-only">{{signature}}</div>
<div fxLayout="row" class="mt-2">

@ -16,7 +16,7 @@
<button mat-flat-button color="primary" tabindex="4" type="submit" (click)="onVerify()">Verify</button>
</div>
<div *ngIf="showVerifyStatus && verifyRes.verified" fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap">
<mat-divider class="my-2"></mat-divider>
<mat-divider class="my-2" />
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center">
<p *ngIf="verifyRes.verified">Pubkey Used</p>
</div>

@ -7,11 +7,11 @@ import { GetInfo, Fees, Peer, Payment, QueryRoutes, Channel, FeeRates, Invoice,
GetNewAddress, DetachPeer, UpdateChannel, CloseChannel, SendPayment, GetQueryRoutes, ChannelLookup, OfferInvoice, Offer, OfferBookmark, ListForwards, FetchListForwards } from '../../shared/models/clnModels';
import { PageSettings } from '../../shared/models/pageSettings';
export const updateCLAPICallStatus = createAction(CLNActions.UPDATE_API_CALL_STATUS_CLN, props<{ payload: ApiCallStatusPayload }>());
export const updateCLNAPICallStatus = createAction(CLNActions.UPDATE_API_CALL_STATUS_CLN, props<{ payload: ApiCallStatusPayload }>());
export const resetCLStore = createAction(CLNActions.RESET_CLN_STORE, props<{ payload: SelNodeChild | null }>());
export const resetCLNStore = createAction(CLNActions.RESET_CLN_STORE, props<{ payload: SelNodeChild | null }>());
export const setChildNodeSettingsCL = createAction(CLNActions.SET_CHILD_NODE_SETTINGS_CLN, props<{ payload: SelNodeChild }>());
export const setChildNodeSettingsCLN = createAction(CLNActions.SET_CHILD_NODE_SETTINGS_CLN, props<{ payload: SelNodeChild }>());
export const fetchPageSettings = createAction(CLNActions.FETCH_PAGE_SETTINGS_CLN);
@ -19,7 +19,7 @@ export const setPageSettings = createAction(CLNActions.SET_PAGE_SETTINGS_CLN, pr
export const savePageSettings = createAction(CLNActions.SAVE_PAGE_SETTINGS_CLN, props<{ payload: PageSettings[] }>());
export const fetchInfoCL = createAction(CLNActions.FETCH_INFO_CLN, props<{ payload: { loadPage: string } }>());
export const fetchInfoCLN = createAction(CLNActions.FETCH_INFO_CLN, props<{ payload: { loadPage: string } }>());
export const setInfo = createAction(CLNActions.SET_INFO_CLN, props<{ payload: GetInfo }>());

@ -19,9 +19,9 @@ import { closeAllDialogs, closeSpinner, logout, openAlert, openSnackBar, openSpi
import { RTLState } from '../../store/rtl.state';
import { addUpdateOfferBookmark, fetchBalance, fetchChannels, fetchFeeRates, fetchFees, fetchInvoices, fetchLocalRemoteBalance,
fetchPayments, fetchPeers, fetchUTXOs, setLookup, setPeers, setQueryRoutes, updateCLAPICallStatus, updateInvoice, setOfferInvoice,
sendPaymentStatus, setForwardingHistory, fetchPageSettings } from './cln.actions';
import { allAPIsCallStatus, clnNodeInformation, nodeInfoAndBalance } from './cln.selector';
fetchPayments, fetchPeers, fetchUTXOs, setLookup, setPeers, setQueryRoutes, updateCLNAPICallStatus, updateInvoice, setOfferInvoice,
sendPaymentStatus, setForwardingHistory } from './cln.actions';
import { allAPIsCallStatus } from './cln.selector';
import { ApiCallsListCL } from '../../shared/models/apiCallsPayload';
import { CLNOfferInformationComponent } from '../transactions/offers/offer-information-modal/offer-information.component';
@ -30,7 +30,7 @@ export class CLNEffects implements OnDestroy {
CHILD_API_URL = API_URL + '/cln';
API_VERION = '';
NODE_VERISON = '';
CLN_VERISON = '';
private flgInitialized = false;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
@ -89,7 +89,7 @@ export class CLNEffects implements OnDestroy {
mergeMap((action: { type: string, payload: { loadPage: string } }) => {
this.flgInitialized = false;
this.store.dispatch(setApiUrl({ payload: this.CHILD_API_URL }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_NODE_INFO }));
return this.httpClient.get<GetInfo>(this.CHILD_API_URL + API_END_POINTS.GETINFO_API).
pipe(
@ -97,13 +97,13 @@ export class CLNEffects implements OnDestroy {
map((info) => {
this.logger.info(info);
this.API_VERION = info.api_version || '';
this.NODE_VERISON = info.version || '';
this.CLN_VERISON = info.version || '';
if (info.chains && info.chains.length && info.chains[0] &&
(typeof info.chains[0] === 'object' && info.chains[0].hasOwnProperty('chain') && info?.chains[0].chain &&
(info?.chains[0].chain.toLowerCase().indexOf('bitcoin') < 0 && info?.chains[0].chain.toLowerCase().indexOf('liquid') < 0)
)
) {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_NODE_INFO }));
this.store.dispatch(closeAllDialogs());
setTimeout(() => {
@ -122,7 +122,7 @@ export class CLNEffects implements OnDestroy {
};
} else {
this.initializeRemainingData(info, action.payload.loadPage);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchInfo', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_NODE_INFO }));
return {
type: CLNActions.SET_INFO_CLN,
@ -144,12 +144,12 @@ export class CLNEffects implements OnDestroy {
fetchFeesCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_FEES_CLN),
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchFees', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchFees', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get<Fees>(this.CHILD_API_URL + API_END_POINTS.FEES_API);
}),
map((fees) => {
this.logger.info(fees);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchFees', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchFees', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_FEES_CLN,
payload: fees ? fees : {}
@ -164,12 +164,12 @@ export class CLNEffects implements OnDestroy {
fetchFeeRatesCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_FEE_RATES_CLN),
mergeMap((action: { type: string, payload: string }) => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchFeeRates' + action.payload, status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchFeeRates' + action.payload, status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get<FeeRates>(this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/feeRates/' + action.payload).
pipe(
map((feeRates) => {
this.logger.info(feeRates);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchFeeRates' + action.payload, status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchFeeRates' + action.payload, status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_FEE_RATES_CLN,
payload: feeRates ? feeRates : {}
@ -186,12 +186,12 @@ export class CLNEffects implements OnDestroy {
fetchBalanceCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_BALANCE_CLN),
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchBalance', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchBalance', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get<Balance>(this.CHILD_API_URL + API_END_POINTS.BALANCE_API);
}),
map((balance) => {
this.logger.info(balance);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchBalance', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchBalance', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_BALANCE_CLN,
payload: balance ? balance : {}
@ -206,12 +206,12 @@ export class CLNEffects implements OnDestroy {
fetchLocalRemoteBalanceCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_LOCAL_REMOTE_BALANCE_CLN),
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchLocalRemoteBalance', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchLocalRemoteBalance', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get<LocalRemoteBalance>(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/localRemoteBalance');
}),
map((lrBalance) => {
this.logger.info(lrBalance);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchLocalRemoteBalance', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchLocalRemoteBalance', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_LOCAL_REMOTE_BALANCE_CLN,
payload: lrBalance ? lrBalance : {}
@ -259,12 +259,12 @@ export class CLNEffects implements OnDestroy {
peersFetchCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_PEERS_CLN),
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchPeers', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchPeers', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.PEERS_API).
pipe(
map((peers: any) => {
this.logger.info(peers);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchPeers', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchPeers', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_PEERS_CLN,
payload: peers || []
@ -282,12 +282,12 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.SAVE_NEW_PEER_CLN),
mergeMap((action: { type: string, payload: { id: string } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.CONNECT_PEER }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SaveNewPeer', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewPeer', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post<Peer[]>(this.CHILD_API_URL + API_END_POINTS.PEERS_API, { id: action.payload.id }).
pipe(
map((postRes: Peer[]) => {
this.logger.info(postRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SaveNewPeer', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewPeer', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.CONNECT_PEER }));
this.store.dispatch(setPeers({ payload: (postRes || []) }));
return {
@ -329,16 +329,16 @@ export class CLNEffects implements OnDestroy {
channelsFetchCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_CHANNELS_CLN),
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchChannels', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchChannels', status: APICallStatusEnum.INITIATED } }));
const listChannelsEndpoint =
this.commonService.isVersionCompatible(this.NODE_VERISON, '23.02') &&
this.commonService.isVersionCompatible(this.CLN_VERISON, '23.02') &&
this.commonService.isVersionCompatible(this.API_VERION, '0.10.3') ?
'/listPeerChannels' : '/listChannels';
return this.httpClient.get<Channel[]>(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + listChannelsEndpoint);
}),
map((channels: Channel[]) => {
this.logger.info(channels);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchChannels', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchChannels', status: APICallStatusEnum.COMPLETED } }));
const sortedChannels = { activeChannels: <Channel[]>[], pendingChannels: <Channel[]>[], inactiveChannels: <Channel[]>[] };
channels.forEach((channel) => {
if (channel.id) { channel.peer_id = channel.id; }
@ -368,7 +368,7 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.SAVE_NEW_CHANNEL_CLN),
mergeMap((action: { type: string, payload: SaveChannel }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.OPEN_CHANNEL }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SaveNewChannel', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewChannel', status: APICallStatusEnum.INITIATED } }));
const newPayload = { id: action.payload.peerId, satoshis: action.payload.satoshis, feeRate: action.payload.feeRate, announce: action.payload.announce };
if (action.payload.minconf) { newPayload['minconf'] = action.payload.minconf; }
if (action.payload.utxos) { newPayload['utxos'] = action.payload.utxos; }
@ -378,7 +378,7 @@ export class CLNEffects implements OnDestroy {
pipe(
map((postRes: any) => {
this.logger.info(postRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SaveNewChannel', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewChannel', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.OPEN_CHANNEL }));
this.store.dispatch(openSnackBar({ payload: 'Channel Added Successfully!' }));
this.store.dispatch(fetchBalance());
@ -450,12 +450,12 @@ export class CLNEffects implements OnDestroy {
paymentsFetchCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_PAYMENTS_CLN),
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchPayments', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchPayments', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get<Payment[]>(this.CHILD_API_URL + API_END_POINTS.PAYMENTS_API);
}),
map((payments) => {
this.logger.info(payments);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchPayments', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchPayments', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_PAYMENTS_CLN,
payload: payments || []
@ -472,13 +472,13 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.FETCH_OFFER_INVOICE_CLN),
mergeMap((action: { type: string, payload: { offer: string, amount_msat?: string } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.FETCH_INVOICE }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchOfferInvoice', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchOfferInvoice', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.OFFERS_API + '/fetchOfferInvoice', action.payload).
pipe(
map((fetchedInvoice: any) => {
this.logger.info(fetchedInvoice);
setTimeout(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchOfferInvoice', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchOfferInvoice', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.FETCH_INVOICE }));
this.store.dispatch(setOfferInvoice({ payload: (fetchedInvoice ? fetchedInvoice : {}) }));
}, 500);
@ -507,11 +507,11 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.SEND_PAYMENT_CLN),
mergeMap((action: { type: string, payload: SendPayment }) => {
this.store.dispatch(openSpinner({ payload: action.payload.uiMessage }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.PAYMENTS_API, action.payload).pipe(
map((sendRes: any) => {
this.logger.info(sendRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.COMPLETED } }));
let snackBarMessageStr = 'Payment Sent Successfully!';
if (sendRes.saveToDBError) {
snackBarMessageStr = 'Payment Sent Successfully but Offer Saving to Database Failed.';
@ -546,12 +546,12 @@ export class CLNEffects implements OnDestroy {
queryRoutesFetchCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.GET_QUERY_ROUTES_CLN),
mergeMap((action: { type: string, payload: GetQueryRoutes }) => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'GetQueryRoutes', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'GetQueryRoutes', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/getRoute/' + action.payload.destPubkey + '/' + action.payload.amount).
pipe(
map((qrRes: any) => {
this.logger.info(qrRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'GetQueryRoutes', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'GetQueryRoutes', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_QUERY_ROUTES_CLN,
payload: qrRes
@ -578,12 +578,12 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.PEER_LOOKUP_CLN),
mergeMap((action: { type: string, payload: string }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SEARCHING_NODE }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/listNode/' + action.payload).
pipe(
map((resPeer) => {
this.logger.info(resPeer);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SEARCHING_NODE }));
return {
type: CLNActions.SET_LOOKUP_CLN,
@ -602,12 +602,12 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.CHANNEL_LOOKUP_CLN),
mergeMap((action: { type: string, payload: ChannelLookup }) => {
this.store.dispatch(openSpinner({ payload: action.payload.uiMessage }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.NETWORK_API + '/listChannel/' + action.payload.shortChannelID).
pipe(
map((resChannel) => {
this.logger.info(resChannel);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: action.payload.uiMessage }));
return {
type: CLNActions.SET_LOOKUP_CLN,
@ -631,12 +631,12 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.INVOICE_LOOKUP_CLN),
mergeMap((action: { type: string, payload: string }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SEARCHING_INVOICE }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.INVOICES_API + '?label=' + action.payload).
pipe(
map((resInvoice: any) => {
this.logger.info(resInvoice);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'Lookup', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SEARCHING_INVOICE }));
if (resInvoice.invoices && resInvoice.invoices.length && resInvoice.invoices.length > 0) {
this.store.dispatch(updateInvoice({ payload: resInvoice.invoices[0] }));
@ -670,12 +670,12 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.GET_FORWARDING_HISTORY_CLN),
mergeMap((action: { type: string, payload: { status: string } }) => {
const statusInitial = action.payload.status.charAt(0).toUpperCase();
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchForwardingHistory' + statusInitial, status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchForwardingHistory' + statusInitial, status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/listForwards?status=' + action.payload.status).
pipe(
map((fhRes: any) => {
this.logger.info(fhRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchForwardingHistory' + statusInitial, status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchForwardingHistory' + statusInitial, status: APICallStatusEnum.COMPLETED } }));
if (action.payload.status === CLNForwardingEventsStatusEnum.FAILED) {
this.store.dispatch(setForwardingHistory({ payload: { status: CLNForwardingEventsStatusEnum.FAILED, totalForwards: fhRes.length, listForwards: fhRes } }));
} else if (action.payload.status === CLNForwardingEventsStatusEnum.LOCAL_FAILED) {
@ -721,14 +721,14 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.SAVE_NEW_INVOICE_CLN),
mergeMap((action: { type: string, payload: { amount: number, label: string, description: string, expiry: number, private: boolean } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.ADD_INVOICE }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SaveNewInvoice', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewInvoice', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.INVOICES_API, {
label: action.payload.label, amount: action.payload.amount, description: action.payload.description, expiry: action.payload.expiry, private: action.payload.private
}).
pipe(
map((postRes: Invoice) => {
this.logger.info(postRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SaveNewInvoice', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewInvoice', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.ADD_INVOICE }));
postRes.amount_msat = action.payload.amount;
postRes.label = action.payload.label;
@ -763,12 +763,12 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.SAVE_NEW_OFFER_CLN),
mergeMap((action: { type: string, payload: { amount: string, description: string, issuer: string } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.CREATE_OFFER }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SaveNewOffer', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewOffer', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.OFFERS_API, {
amount: action.payload.amount, description: action.payload.description, issuer: action.payload.issuer
}).pipe(map((postRes: Offer) => {
this.logger.info(postRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SaveNewOffer', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewOffer', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.CREATE_OFFER }));
setTimeout(() => {
this.store.dispatch(openAlert({
@ -796,7 +796,7 @@ export class CLNEffects implements OnDestroy {
invoicesFetchCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_INVOICES_CLN),
mergeMap((action: { type: string, payload: FetchInvoices }) => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchInvoices', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchInvoices', status: APICallStatusEnum.INITIATED } }));
const num_max_invoices = (action.payload.num_max_invoices) ? action.payload.num_max_invoices : 1000000;
const index_offset = (action.payload.index_offset) ? action.payload.index_offset : 0;
const reversed = (action.payload.reversed) ? action.payload.reversed : true;
@ -804,7 +804,7 @@ export class CLNEffects implements OnDestroy {
pipe(
map((res: ListInvoices) => {
this.logger.info(res);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchInvoices', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchInvoices', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_INVOICES_CLN,
payload: res
@ -821,11 +821,11 @@ export class CLNEffects implements OnDestroy {
offersFetchCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_OFFERS_CLN),
mergeMap((action: { type: string, payload: any }) => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchOffers', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchOffers', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.OFFERS_API).
pipe(map((res: any) => {
this.logger.info(res);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchOffers', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchOffers', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_OFFERS_CLN,
payload: res.offers ? res.offers : []
@ -841,11 +841,11 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.DISABLE_OFFER_CLN),
mergeMap((action: { type: string, payload: { offer_id: string } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.DISABLE_OFFER }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'DisableOffer', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'DisableOffer', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.delete(this.CHILD_API_URL + API_END_POINTS.OFFERS_API + '/' + action.payload.offer_id).
pipe(map((postRes: any) => {
this.logger.info(postRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'DisableOffer', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'DisableOffer', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.DISABLE_OFFER }));
this.store.dispatch(openSnackBar({ payload: 'Offer Disabled Successfully!' }));
return {
@ -862,11 +862,11 @@ export class CLNEffects implements OnDestroy {
offerBookmarksFetchCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_OFFER_BOOKMARKS_CLN),
mergeMap((action: { type: string, payload: any }) => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchOfferBookmarks', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchOfferBookmarks', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.OFFERS_API + '/offerbookmarks').
pipe(map((res: any) => {
this.logger.info(res);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchOfferBookmarks', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchOfferBookmarks', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_OFFER_BOOKMARKS_CLN,
payload: res || []
@ -882,11 +882,11 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.DELETE_OFFER_BOOKMARK_CLN),
mergeMap((action: { type: string, payload: { bolt12: string } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.DELETE_OFFER_BOOKMARK }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'DeleteOfferBookmark', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'DeleteOfferBookmark', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.delete(this.CHILD_API_URL + API_END_POINTS.OFFERS_API + '/offerbookmark/' + action.payload.bolt12).
pipe(map((postRes: any) => {
this.logger.info(postRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'DeleteOfferBookmark', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'DeleteOfferBookmark', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.DELETE_OFFER_BOOKMARK }));
this.store.dispatch(openSnackBar({ payload: 'Offer Bookmark Deleted Successfully!' }));
return {
@ -904,12 +904,12 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.SET_CHANNEL_TRANSACTION_CLN),
mergeMap((action: { type: string, payload: OnChain }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.SEND_FUNDS }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SetChannelTransaction', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SetChannelTransaction', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.ON_CHAIN_API, action.payload).
pipe(
map((postRes: any) => {
this.logger.info(postRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SetChannelTransaction', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SetChannelTransaction', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.SEND_FUNDS }));
this.store.dispatch(fetchBalance());
this.store.dispatch(fetchUTXOs());
@ -929,12 +929,12 @@ export class CLNEffects implements OnDestroy {
utxosFetch = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_UTXOS_CLN),
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchUTXOs', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchUTXOs', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(this.CHILD_API_URL + API_END_POINTS.ON_CHAIN_API + '/utxos');
}),
map((utxos: any) => {
this.logger.info(utxos);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchUTXOs', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchUTXOs', status: APICallStatusEnum.COMPLETED } }));
utxos.outputs.forEach((output) => { // For backward compatibility
if (output.value) {
output.amount_msat = output.value;
@ -954,11 +954,11 @@ export class CLNEffects implements OnDestroy {
pageSettingsFetchCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.FETCH_PAGE_SETTINGS_CLN),
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchPageSettings', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchPageSettings', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.get(API_END_POINTS.PAGE_SETTINGS_API).pipe(
map((pageSettings: any) => {
this.logger.info(pageSettings);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchPageSettings', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'FetchPageSettings', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLNActions.SET_PAGE_SETTINGS_CLN,
payload: pageSettings || []
@ -976,12 +976,12 @@ export class CLNEffects implements OnDestroy {
ofType(CLNActions.SAVE_PAGE_SETTINGS_CLN),
mergeMap((action: { type: string, payload: any }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.UPDATE_PAGE_SETTINGS }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SavePageSettings', status: APICallStatusEnum.INITIATED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SavePageSettings', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(API_END_POINTS.PAGE_SETTINGS_API, action.payload).
pipe(
map((postRes: any) => {
this.logger.info(postRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'SavePageSettings', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SavePageSettings', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.UPDATE_PAGE_SETTINGS }));
this.store.dispatch(openSnackBar({ payload: 'Page Layout Updated Successfully!' }));
return {
@ -1021,7 +1021,6 @@ export class CLNEffects implements OnDestroy {
newRoute = '/cln/home';
}
this.router.navigate([newRoute]);
this.store.dispatch(fetchPageSettings());
this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: 1000000, index_offset: 0, reversed: true } }));
this.store.dispatch(fetchFees());
this.store.dispatch(fetchChannels());
@ -1044,7 +1043,7 @@ export class CLNEffects implements OnDestroy {
} else {
this.store.dispatch(closeSpinner({ payload: uiMessage }));
const errMsg = this.commonService.extractErrorMessage(err, genericErrorMessage);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: actionName, status: APICallStatusEnum.ERROR, statusCode: err.status.toString(), message: errMsg } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: actionName, status: APICallStatusEnum.ERROR, statusCode: err.status.toString(), message: errMsg } }));
}
}
@ -1068,7 +1067,7 @@ export class CLNEffects implements OnDestroy {
}
}
}));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: actionName, status: APICallStatusEnum.ERROR, statusCode: err.status.toString(), message: errMsg, URL: errURL } }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: actionName, status: APICallStatusEnum.ERROR, statusCode: err.status.toString(), message: errMsg, URL: errURL } }));
}
}

@ -1,17 +1,17 @@
import { createReducer, on } from '@ngrx/store';
import { initCLNState } from './cln.state';
import {
addInvoice, addPeer, removeChannel, removePeer, resetCLStore, setBalance, setChannels,
setChildNodeSettingsCL, setFeeRates, setFees, setForwardingHistory,
addInvoice, addPeer, removeChannel, removePeer, resetCLNStore, setBalance, setChannels,
setChildNodeSettingsCLN, setFeeRates, setFees, setForwardingHistory,
setInfo, setInvoices, setLocalRemoteBalance, setOffers, addOffer, setPayments, setPeers, setUTXOs,
updateCLAPICallStatus, updateInvoice, updateOffer, setOfferBookmarks, addUpdateOfferBookmark, removeOfferBookmark, setPageSettings
updateCLNAPICallStatus, updateInvoice, updateOffer, setOfferBookmarks, addUpdateOfferBookmark, removeOfferBookmark, setPageSettings
} from './cln.actions';
import { Channel, OfferBookmark } from '../../shared/models/clnModels';
import { CLNForwardingEventsStatusEnum, CLN_DEFAULT_PAGE_SETTINGS } from '../../shared/services/consts-enums-functions';
import { PageSettings } from '../../shared/models/pageSettings';
export const CLNReducer = createReducer(initCLNState,
on(updateCLAPICallStatus, (state, { payload }) => {
on(updateCLNAPICallStatus, (state, { payload }) => {
const updatedApisCallStatus = JSON.parse(JSON.stringify(state.apisCallStatus));
if (payload.action) {
updatedApisCallStatus[payload.action] = {
@ -27,11 +27,11 @@ export const CLNReducer = createReducer(initCLNState,
apisCallStatus: updatedApisCallStatus
};
}),
on(setChildNodeSettingsCL, (state, { payload }) => ({
on(setChildNodeSettingsCLN, (state, { payload }) => ({
...state,
nodeSettings: payload
})),
on(resetCLStore, (state, { payload }) => ({
on(resetCLNStore, (state, { payload }) => ({
...initCLNState,
nodeSettings: payload
})),

@ -35,7 +35,7 @@
<mat-icon matTooltip="Include routing hints for private channels" matTooltipPosition="above" class="info-icon">info_outline</mat-icon>
</div>
<div *ngIf="invoiceError !== ''" fxFlex="100" class="alert alert-danger mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span *ngIf="invoiceError !== ''">{{invoiceError}}</span>
</div>
<div fxLayout="row" fxFlex="100" class="mt-1" fxLayoutAlign="end center">

@ -1,12 +1,12 @@
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign="space-between stretch">
<div fxFlex="35" class="modal-qr-code-container padding-gap-large" [fxLayoutAlign]="((invoice?.bolt11 && invoice?.bolt11 !== '') || (invoice?.bolt12 && invoice?.bolt12 !== '')) ? 'center start' : 'center center'" [ngClass]="{'display-none': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">
<qr-code *ngIf="((invoice?.bolt11 && invoice?.bolt11 !== '') || (invoice?.bolt12 && invoice?.bolt12 !== ''))" [value]="invoice?.bolt11 || invoice?.bolt12" [size]="qrWidth" [errorCorrectionLevel]="'L'"></qr-code>
<qr-code *ngIf="((invoice?.bolt11 && invoice?.bolt11 !== '') || (invoice?.bolt12 && invoice?.bolt12 !== ''))" [value]="invoice?.bolt11 || invoice?.bolt12" [size]="qrWidth" [errorCorrectionLevel]="'L'" />
<span *ngIf="!invoice?.bolt11 && !invoice?.bolt12" class="font-size-300">N/A</span>
</div>
<div fxFlex="65">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<fa-icon class="page-title-img mr-1" [icon]="faReceipt"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faReceipt" />
<span class="page-title">
{{screenSize === screenSizeEnum.XS ? (newlyAdded ? 'Created' : 'Invoice') : (newlyAdded ? 'Invoice Created' : 'Invoice Information')}}
<span *ngIf="invoice?.status === 'paid'" class="dot green ml-1" matTooltip="Paid" matTooltipPosition="right" [ngClass]="{'mr-0': screenSize === screenSizeEnum.XS}"></span>
@ -19,13 +19,13 @@
<mat-card-content class="padding-gap-x-large" [ngClass]="{'xs-scroll-y': screenSize === screenSizeEnum.XS}">
<div fxLayout="column">
<div fxFlex="30" class="modal-qr-code-container padding-gap" [fxLayoutAlign]="((invoice?.bolt11 && invoice?.bolt11 !== '') || (invoice?.bolt12 && invoice?.bolt12 !== '')) ? 'center start' : 'center center'" [ngClass]="{'display-none': screenSize !== screenSizeEnum.XS && screenSize !== screenSizeEnum.SM}">
<qr-code *ngIf="((invoice?.bolt11 && invoice?.bolt11 !== '') || (invoice?.bolt12 && invoice?.bolt12 !== ''))" [value]="invoice?.bolt11 || invoice?.bolt12" [size]="qrWidth" [errorCorrectionLevel]="'L'"></qr-code>
<qr-code *ngIf="((invoice?.bolt11 && invoice?.bolt11 !== '') || (invoice?.bolt12 && invoice?.bolt12 !== ''))" [value]="invoice?.bolt11 || invoice?.bolt12" [size]="qrWidth" [errorCorrectionLevel]="'L'" />
<span *ngIf="!invoice?.bolt11 && !invoice?.bolt12" class="font-size-120">QR Code Not Applicable</span>
</div>
<mat-divider *ngIf="screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM" class="my-1"></mat-divider>
<mat-divider *ngIf="screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM" class="my-1" />
<div *ngIf="invoice?.warning_capacity" fxLayout="row">
<div fxFlex="100" class="alert alert-warn">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>{{invoice?.warning_capacity}}</span>
</div>
</div>
@ -50,12 +50,12 @@
</ng-container>
<ng-container *ngIf="invoice?.status !== 'paid'">
<span *ngIf="invoice?.status !== 'unpaid' || !flgVersionCompatible">-</span>
<mat-spinner *ngIf="invoice?.status === 'unpaid' && flgVersionCompatible" [diameter]="20"></mat-spinner>
<mat-spinner *ngIf="invoice?.status === 'unpaid' && flgVersionCompatible" [diameter]="20" />
</ng-container>
</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Date Expiry</h4>
@ -66,14 +66,14 @@
<span class="foreground-secondary-text">{{((invoice?.paid_at * 1000) | date:'dd/MMM/y HH:mm') || '-'}}</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Description</h4>
<span class="foreground-secondary-text">{{invoice?.description || '-'}}</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">{{ invoice?.bolt12 ? 'Bolt12' : (invoice?.bolt11 && !invoice.label.includes('keysend-')) ? 'Bolt11' : 'Keysend' }} Invoice</h4>
@ -81,21 +81,21 @@
</div>
</div>
<div *ngIf="showAdvanced">
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Payment Hash</h4>
<span class="overflow-wrap foreground-secondary-text">{{invoice?.payment_hash}}</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Label</h4>
<span class="overflow-wrap foreground-secondary-text">{{invoice?.label}}</span>
</div>
</div>
<mat-divider class="w-100 my-1"></mat-divider>
<mat-divider class="w-100 my-1" />
</div>
<div fxLayout="row" fxLayoutAlign="end center" [ngClass]="{'mt-2': !showAdvanced, 'mt-1': showAdvanced}">
<button class="mr-1" mat-button color="primary" type="reset" tabindex="1" (click)="onShowAdvanced()">

@ -22,7 +22,7 @@
<div *ngIf="calledFrom === 'transactions'" fxLayout="column" fxLayoutAlign="start stretch">
<div fxLayout="column" fxLayoutAlign="start stretch" fxLayout.gt-sm="row wrap" class="page-sub-title-container mt-1">
<div fxFlex="70" fxLayoutAlign="start start" fxLayoutAlign.gt-sm="start center">
<fa-icon class="page-title-img mr-1" [icon]="faHistory"></fa-icon>
<fa-icon class="page-title-img mr-1" [icon]="faHistory" />
<span class="page-title">Invoices History</span>
</div>
<div fxFlex.gt-xs="30" fxLayoutAlign.gt-xs="space-between center" fxLayout="row" fxLayoutAlign="space-between stretch">
@ -39,7 +39,7 @@
</div>
</div>
<div fxLayout="column" fxFlex="100" class="table-container" [perfectScrollbar]>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate" />
<table #table mat-table fxFlex="100" matSort [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="invoices" [ngClass]="{'error-border': errorMessage !== ''}">
<ng-container matColumnDef="status">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before" matTooltip="Status"></th>
@ -107,7 +107,7 @@
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
</mat-select>
</div>
@ -115,7 +115,7 @@
<td *matCellDef="let invoice" mat-cell fxLayoutAlign="end center">
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="4" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-select-trigger />
<mat-option (click)="onInvoiceClick(invoice)">View Info</mat-option>
<mat-option (click)="onRefreshInvoice(invoice)">Refresh</mat-option>
</mat-select>
@ -134,6 +134,6 @@
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
</table>
</div>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" />
</div>
</div>

@ -25,7 +25,7 @@
</mat-form-field>
</div>
<div *ngIf="offerError !== ''" fxFlex="100" class="alert alert-danger mt-1">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span *ngIf="offerError !== ''">{{offerError}}</span>
</div>
<div fxLayout="row" fxFlex="100" class="mt-1" fxLayoutAlign="end center">

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save