Skip to content

Commit

Permalink
Channels: search and sorting functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
kaloudis committed Mar 17, 2023
1 parent a17042a commit a512924
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 68 deletions.
97 changes: 97 additions & 0 deletions components/Channels/SortButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import * as React from 'react';
import { ActionSheetIOS, Platform, View } from 'react-native';
import { Picker } from '@react-native-picker/picker';

import Button from '../../components/Button';
import { themeColor } from '../../utils/ThemeUtils';
import { localeString } from '../../utils/LocaleUtils';

interface SortButtonProps {
onValueChange: (value: any) => void;
values: Array<any>;
}

export default class SortButton extends React.Component<SortButtonProps, {}> {
render() {
const { onValueChange, values } = this.props;

// TODO for some reason it will not responsd to item 0 so a cancel item
// is placed here
const pickerValuesAndroid: Array<any> = [
<Picker.Item
key={'cancel'}
label={localeString('general.cancel')}
/>
];
const pickerValuesIOS: Array<string> = [localeString('general.cancel')];
values.forEach((value: { key: string; value: any }) => {
pickerValuesAndroid.push(
<Picker.Item
key={value.key}
label={value.key}
value={value.value}
/>
);
pickerValuesIOS.push(value.key);
});

return (
<React.Fragment>
{Platform.OS === 'android' && (
<View
style={{
height: 50,
overflow: 'hidden',
borderRadius: 16
}}
>
<Picker
onValueChange={(itemValue: any) =>
onValueChange(itemValue)
}
style={{
marginTop: -2,
width: 48,
color: themeColor('text'),
backgroundColor: themeColor('secondary')
}}
dropdownIconColor={themeColor('text')}
>
{pickerValuesAndroid}
</Picker>
</View>
)}

{Platform.OS === 'ios' && (
<View>
<Button
title=""
icon={{
name: 'sort',
size: 25
}}
adaptiveWidth
buttonStyle={{ width: 50 }}
quinary
onPress={() =>
ActionSheetIOS.showActionSheetWithOptions(
{
options: pickerValuesIOS,
cancelButtonIndex: 0
},
(buttonIndex) => {
if (buttonIndex) {
onValueChange(
values[buttonIndex - 1].value
);
}
}
)
}
/>
</View>
)}
</React.Fragment>
);
}
}
6 changes: 6 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@
"views.Channel.closed": "Closed",
"views.Channel.pendingClose": "Pending close",
"views.Channel.pendingOpen": "Pending open",
"views.Channel.SortButton.largestFirst": "largest first",
"views.Channel.SortButton.smallestFirst": "smallest first",
"views.Channel.SortButton.ascending": "ascending",
"views.Channel.SortButton.descending": "descending",
"views.Channel.channelId": "Channel ID",
"views.Channel.displayName": "Display name",
"views.UTXOs.CoinControl.noUTXOs": "No UTXOs available",
"views.EditFee.mainText": "Edit network fee",
"views.EditFee.fastestFee": "Fastest fee",
Expand Down
25 changes: 18 additions & 7 deletions models/Channel.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BigNumber from 'bignumber.js';
import { observable, computed } from 'mobx';
import BaseModel from './BaseModel';
import { localeString } from './../utils/LocaleUtils';
Expand Down Expand Up @@ -42,19 +43,19 @@ export default class Channel extends BaseModel {
// pending
remote_node_pub: string;

// enrichments
displayName?: string;

@computed
public get isActive(): boolean {
return this.active || this.state === 'CHANNELD_NORMAL';
}

@computed
public get remoteBalance(): string {
return this.msatoshi_total
? (
(Number(this.msatoshi_total) - Number(this.msatoshi_to_us)) /
1000
).toString()
: this.remote_balance || '0';
public get channelCapacity(): string {
return new BigNumber(this.localBalance)
.plus(this.remoteBalance)
.toString();
}

@computed
Expand All @@ -64,6 +65,16 @@ export default class Channel extends BaseModel {
: this.local_balance || '0';
}

@computed
public get remoteBalance(): string {
return this.msatoshi_total
? (
(Number(this.msatoshi_total) - Number(this.msatoshi_to_us)) /
1000
).toString()
: this.remote_balance || '0';
}

@computed
public get channelId(): string {
return (
Expand Down
150 changes: 119 additions & 31 deletions stores/ChannelsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ export default class ChannelsStore {
@observable public totalOffline = 0;
@observable public chanInfo: ChannelInfoIndex = {};
@observable public channelsType = ChannelsType.Open;
// enriched
@observable public enrichedChannels: Array<Channel> = [];
@observable public enrichedPendingChannels: Array<Channel> = [];
@observable public enrichedClosedChannels: Array<Channel> = [];
// search and sort
@observable public search: string = '';
@observable public filteredChannels: Array<Channel> = [];
@observable public filteredPendingChannels: Array<Channel> = [];
@observable public filteredClosedChannels: Array<Channel> = [];
@observable public sort = {
param: 'channelCapacity',
dir: 'DESC',
type: 'numeric'
};

settingsStore: SettingsStore;

Expand All @@ -68,33 +82,36 @@ export default class ChannelsStore {

reaction(
() => this.channels,
() => {
async () => {
if (this.channels) {
this.channels.forEach((channel: any) =>
this.enrichChannel(channel)
this.enrichedChannels = await this.enrichChannels(
this.channels
);
this.filterChannels();
}
}
);

reaction(
() => this.pendingChannels,
() => {
async () => {
if (this.pendingChannels) {
this.pendingChannels.forEach((channel: any) =>
this.enrichChannel(channel)
this.enrichedPendingChannels = await this.enrichChannels(
this.pendingChannels
);
this.filterPendingChannels();
}
}
);

reaction(
() => this.closedChannels,
() => {
async () => {
if (this.closedChannels) {
this.closedChannels.forEach((channel: any) =>
this.enrichChannel(channel)
this.enrichedClosedChannels = await this.enrichChannels(
this.closedChannels
);
this.filterClosedChannels();
}
}
);
Expand Down Expand Up @@ -129,32 +146,104 @@ export default class ChannelsStore {
this.channelsType = ChannelsType.Open;
};

@action
setSearch = (query: string) => {
this.search = query;
this.filterChannels();
this.filterPendingChannels();
this.filterClosedChannels();
};

@action
setSort = (value: any) => {
this.sort = value;
this.filterChannels();
this.filterPendingChannels();
this.filterClosedChannels();
};

@action
filter = (channels: Array<Channel>) => {
const query = this.search;
const filtered = channels.filter(
(channel: Channel) =>
channel.alias?.includes(query) ||
channel.alias?.toLocaleLowerCase().includes(query) ||
channel.remotePubkey.includes(query) ||
channel.remotePubkey.toLowerCase().includes(query) ||
channel.channelId.includes(query) ||
channel.channelId.toLowerCase().includes(query)
);

const sorted = filtered.sort((a: any, b: any) => {
if (this.sort.type === 'numeric') {
return Number(a[this.sort.param]) < Number(b[this.sort.param])
? 1
: -1;
} else {
return a[this.sort.param].toLowerCase() <
b[this.sort.param].toLowerCase()
? 1
: -1;
}
});

return this.sort.dir === 'DESC' ? sorted : sorted.reverse();
};

@action
filterChannels = () => {
this.filteredChannels = this.filter(this.enrichedChannels);
};

@action
filterPendingChannels = () => {
this.filteredPendingChannels = this.filter(
this.enrichedPendingChannels
);
};

@action
filterClosedChannels = () => {
this.filteredClosedChannels = this.filter(this.enrichedClosedChannels);
};

@action
getNodeInfo = (pubkey: string) =>
BackendUtils.getNodeInfo([pubkey]).then((data: any) => {
return data.node;
});

@action
enrichChannel = (channel: any) => {
if (!channel.remotePubkey) return;
if (
this.settingsStore.implementation !== 'c-lightning-REST' &&
!this.nodes[channel.remotePubkey]
) {
this.getNodeInfo(channel.remotePubkey)
.then((nodeInfo: any) => {
if (!nodeInfo) return;
this.nodes[channel.remotePubkey] = nodeInfo;
this.aliasesById[channel.channelId] = nodeInfo.alias;
})
.catch((err: any) => {
console.log(
`Couldn't find node alias for ${channel.remotePubkey}`,
err
);
});
}
enrichChannels = (channels: Array<Channel>) => {
this.loading = true;
channels.forEach(async (channel: Channel) => {
if (!channel.remotePubkey) return;
if (
this.settingsStore.implementation !== 'c-lightning-REST' &&
!this.nodes[channel.remotePubkey]
) {
await this.getNodeInfo(channel.remotePubkey)
.then((nodeInfo: any) => {
if (!nodeInfo) return;
this.nodes[channel.remotePubkey] = nodeInfo;
this.aliasesById[channel.channelId] = nodeInfo.alias;
})
.catch((err: any) => {
console.log(
`Couldn't find node alias for ${channel.remotePubkey}`,
err
);
});
}
if (!channel.alias && this.aliasesById[channel.channelId]) {
channel.alias = this.aliasesById[channel.channelId];
}
channel.displayName =
channel.alias || channel.remotePubkey || channel.channelId;
});
this.loading = false;
return channels;
};

getChannelsError = () => {
Expand Down Expand Up @@ -396,9 +485,8 @@ export default class ChannelsStore {
};

BackendUtils.openChannel(openChanRequest)
.then((data: any) => {
console.log('chan2 data');
console.log(data);
.then(() => {
// TODO
})
.catch((error: any) => {
this.errorMsgChannel = error.toString();
Expand Down
Loading

0 comments on commit a512924

Please sign in to comment.