Compare commits

...

48 Commits
0.3.7 ... 0.4.6

Author SHA1 Message Date
Mark Qvist
8b69947098 Updated readme 2024-01-18 16:05:45 +01:00
Mark Qvist
ca730ded3b Updated version 2024-01-18 16:00:28 +01:00
Mark Qvist
6a460d9600 Fixed ILB scrolling 2024-01-18 15:57:49 +01:00
markqvist
92a49c12c0 Merge pull request #48 from penguinolog/urwid_constants
Use urwid constants for widgets instead of string equivalents
2024-01-18 15:38:38 +01:00
markqvist
a876c09b4c Merge branch 'master' into urwid_constants 2024-01-18 15:38:11 +01:00
markqvist
bf0b08c154 Merge pull request #47 from penguinolog/urwid_regression_fixed
Ban `urwid==2.4.3` as containing UI regression.
2024-01-18 15:34:34 +01:00
Aleksei Stepanov
77eca5d23e Use urwid constants for widgets instead of string equivalents
* Reduce `DeprecationWarning` amount:
  `get_focus` -> `focus_position` for `Frame` and `focus` for `Pile`
  `set_focus` -> use property `focus_position`
2024-01-18 11:58:22 +01:00
Aleksei Stepanov
ed6ba63317 Ban urwid==2.4.3 as containing UI regression.
Fix urwid entities init: use correct `super()`,
hack `__super` is officially deprecated.
Method <Class>.method(self, ...) is only for fallback in corner cases
(normally not recommended to use).
2024-01-18 10:22:38 +01:00
Mark Qvist
d856f3fd28 Pin Urwid to 2.4.2 due to rendering bug in 2.4.3 2024-01-17 23:31:16 +01:00
Mark Qvist
24d850107d Updated LXMF dependency 2024-01-17 23:15:00 +01:00
Mark Qvist
9943cad15f Updated readme 2024-01-17 23:08:52 +01:00
Mark Qvist
dcd5f7df95 Updated readme 2024-01-15 20:26:10 +01:00
Mark Qvist
d840ca32ae Resolve merge 2024-01-15 19:42:48 +01:00
markqvist
37063932bb Merge pull request #46 from penguinolog/patch-1
Allow newer versions of urwid
2024-01-15 19:40:58 +01:00
Mark Qvist
0ed4e09b82 Updated nomadnet to use Urwid 2.4.2 2024-01-15 19:40:01 +01:00
Mark Qvist
910e527cc7 Terminate log tail when view is inactive 2024-01-15 19:30:26 +01:00
Alexey Stepanov
4eef326d6b Allow newer versions of urwid
Last urwid versions are backward-compatible with 2.1.2 (but may produce "DeprecationWarning" on legacy logic)
2024-01-15 10:39:35 +01:00
Mark Qvist
691f4df098 Updated versions 2024-01-14 01:08:10 +01:00
Mark Qvist
407cc8fb5f Updated version 2024-01-08 00:12:38 +01:00
Mark Qvist
1f7302903a Fixed missing timestamp update on page/file periodic scan 2024-01-08 00:10:55 +01:00
Mark Qvist
5736012f2c Updated version 2024-01-03 13:07:20 +01:00
Mark Qvist
bb98a512f3 Added page and file refresh intervals to guide 2024-01-03 13:05:38 +01:00
markqvist
ae0d4c6e0c Merge pull request #41 from faragher/master
Made save paths relative, added page/file refresh
2024-01-03 12:55:19 +01:00
faragher
2449b39f77 Added file and path scans to jobs 2023-11-29 05:43:49 -06:00
faragher
e022d469f8 Made save paths relative 2023-11-29 03:23:35 -06:00
markqvist
a4f665e650 Merge pull request #39 from faragher/master
Possible fix for Directory error
2023-11-28 14:16:04 +01:00
faragher
19d1d8504f Possible fix for Directory error 2023-11-26 15:51:47 -06:00
Mark Qvist
276a5f56e1 Changed text 2023-11-02 18:30:23 +01:00
Mark Qvist
89dd17ece5 Updated version and dependencies 2023-11-02 12:48:40 +01:00
Mark Qvist
82bb479957 Adjusted timeouts 2023-11-02 12:25:36 +01:00
Mark Qvist
082026ca1b Updated dependencies 2023-10-27 20:43:31 +02:00
Mark Qvist
df9fccf199 Updated version 2023-10-27 20:42:03 +02:00
Mark Qvist
efd1d08399 Improved LXMF sync feedback 2023-10-27 20:41:18 +02:00
Mark Qvist
5a7c84d2a4 Updated LXMF version 2023-10-16 01:50:56 +02:00
Mark Qvist
a3bf538afe Added propagation node list sorting and manual delivery sync initiation 2023-10-16 01:46:50 +02:00
Mark Qvist
8d72a83411 Added option to disable propagation node for pageserving-only nodes 2023-10-16 00:51:23 +02:00
Mark Qvist
0fb9e180ba Added node list sorting 2023-10-16 00:16:04 +02:00
Mark Qvist
2322a254a8 LXM URI file output 2023-10-15 22:53:56 +02:00
Mark Qvist
37ad5cc5d3 Updated dependencies 2023-10-15 21:01:39 +02:00
Mark Qvist
a921adbdbe Cleanup 2023-10-15 21:00:47 +02:00
Mark Qvist
5c1a2c3485 Updated version 2023-10-15 21:00:42 +02:00
Mark Qvist
0cd1c9cf37 Added paper message output options 2023-10-15 20:35:21 +02:00
Mark Qvist
cfc0f4f9c1 Updated readme 2023-10-15 20:35:08 +02:00
Mark Qvist
2aa5c6f665 Updated version and dependencies 2023-10-07 16:44:09 +02:00
Mark Qvist
72a5ed9c8e Updated version 2023-10-03 11:54:09 +02:00
Mark Qvist
c411ce703f Fixed incorrect checkbox display in UI 2023-10-03 11:53:52 +02:00
Mark Qvist
c670aa82ad Updated RNS dependency 2023-09-22 12:03:00 +02:00
Mark Qvist
c52efbfb75 Temporarily fixed urwid at 2.1.2 due to breaking changes in 2.2.0 2023-09-21 22:58:00 +02:00
20 changed files with 971 additions and 329 deletions

View File

@@ -4,7 +4,7 @@ Off-grid, resilient mesh communication with strong encryption, forward secrecy a
![Screenshot](https://github.com/markqvist/NomadNet/raw/master/docs/screenshots/1.png) ![Screenshot](https://github.com/markqvist/NomadNet/raw/master/docs/screenshots/1.png)
Nomad Network Allows you to build private and resilient communications platforms that are in complete control and ownership of the people that use them. No signups, no agreements, no handover of any data, no permissions and gatekeepers. Nomad Network allows you to build private and resilient communications platforms that are in complete control and ownership of the people that use them. No signups, no agreements, no handover of any data, no permissions and gatekeepers.
Nomad Network is build on [LXMF](https://github.com/markqvist/LXMF) and [Reticulum](https://github.com/markqvist/Reticulum), which together provides the cryptographic mesh functionality and peer-to-peer message routing that Nomad Network relies on. This foundation also makes it possible to use the program over a very wide variety of communication mediums, from packet radio to fiber optics. Nomad Network is build on [LXMF](https://github.com/markqvist/LXMF) and [Reticulum](https://github.com/markqvist/Reticulum), which together provides the cryptographic mesh functionality and peer-to-peer message routing that Nomad Network relies on. This foundation also makes it possible to use the program over a very wide variety of communication mediums, from packet radio to fiber optics.
@@ -22,9 +22,6 @@ If you'd rather want to use an LXMF client with a graphical user interface, you
- An easy to use and bandwidth efficient markup language for writing pages - An easy to use and bandwidth efficient markup language for writing pages
- Page caching in browser - Page caching in browser
## Current Status
The current version of the program should be considered a beta release. The program works well, but there will most probably be bugs and possibly sub-optimal performance in some scenarios. On the other hand, this is the ideal time to have an influence on the direction of the development of Nomad Network. To do so, join the discussion, report bugs and request features here on the GitHub project.
## How do I get started? ## How do I get started?
The easiest way to install Nomad Network is via pip: The easiest way to install Nomad Network is via pip:
@@ -121,6 +118,23 @@ $ docker run -d \
$ docker run -i ghcr.io/markqvist/nomadnet:master --daemon --console $ docker run -i ghcr.io/markqvist/nomadnet:master --daemon --console
``` ```
## Tools & Extensions
Nomad Network is a very flexible and extensible platform, and a variety of community-provided tools, utilities and node-side extensions exist:
- [NomadForum](https://codeberg.org/AutumnSpark1226/nomadForum) ([GitHub mirror](https://github.com/AutumnSpark1226/nomadForum))
- [NomadForecast](https://github.com/faragher/NomadForecast)
- [micron-blog](https://github.com/randogoth/micron-blog)
- [md2mu](https://github.com/randogoth/md2mu)
- [Any2MicronConverter](https://github.com/SebastianObi/Any2MicronConverter)
- [Some nomadnet page examples](https://github.com/SebastianObi/NomadNet-Pages)
- [More nomadnet page examples](https://github.com/epenguins/NomadNet_pages)
- [LXMF-Bot](https://github.com/randogoth/lxmf-bot)
- [LXMF Messageboard](https://github.com/chengtripp/lxmf_messageboard)
- [LXMEvent](https://github.com/faragher/LXMEvent)
- [POPR](https://github.com/faragher/POPR)
- [LXMF Tools](https://github.com/SebastianObi/LXMF-Tools)
## Help & Discussion ## Help & Discussion
For help requests, discussion, sharing ideas or anything else related to Nomad Network, please have a look at the [Nomad Network discussions pages](https://github.com/markqvist/Reticulum/discussions/categories/nomad-network). For help requests, discussion, sharing ideas or anything else related to Nomad Network, please have a look at the [Nomad Network discussions pages](https://github.com/markqvist/Reticulum/discussions/categories/nomad-network).

View File

@@ -142,7 +142,7 @@ class Conversation:
self.__changed_callback = None self.__changed_callback = None
if not RNS.Transport.has_path(bytes.fromhex(source_hash)): if not RNS.Identity.recall(bytes.fromhex(self.source_hash)):
RNS.Transport.request_path(bytes.fromhex(source_hash)) RNS.Transport.request_path(bytes.fromhex(source_hash))
self.source_identity = RNS.Identity.recall(bytes.fromhex(self.source_hash)) self.source_identity = RNS.Identity.recall(bytes.fromhex(self.source_hash))
@@ -227,7 +227,7 @@ class Conversation:
RNS.log("Destination is not known, cannot create LXMF Message.", RNS.LOG_VERBOSE) RNS.log("Destination is not known, cannot create LXMF Message.", RNS.LOG_VERBOSE)
return False return False
def paper_output(self, content="", title=""): def paper_output(self, content="", title="", mode="print_qr"):
if self.send_destination: if self.send_destination:
try: try:
dest = self.send_destination dest = self.send_destination
@@ -235,6 +235,8 @@ class Conversation:
desired_method = LXMF.LXMessage.PAPER desired_method = LXMF.LXMessage.PAPER
lxm = LXMF.LXMessage(dest, source, content, title=title, desired_method=desired_method) lxm = LXMF.LXMessage(dest, source, content, title=title, desired_method=desired_method)
if mode == "print_qr":
qr_code = lxm.as_qr() qr_code = lxm.as_qr()
qr_tmp_path = self.app.tmpfilespath+"/"+str(RNS.hexrep(lxm.hash, delimit=False)) qr_tmp_path = self.app.tmpfilespath+"/"+str(RNS.hexrep(lxm.hash, delimit=False))
qr_code.save(qr_tmp_path) qr_code.save(qr_tmp_path)
@@ -248,6 +250,27 @@ class Conversation:
return print_result return print_result
elif mode == "save_qr":
qr_code = lxm.as_qr()
qr_save_path = self.app.downloads_path+"/LXM_"+str(RNS.hexrep(lxm.hash, delimit=False)+".png")
qr_code.save(qr_save_path)
message_path = Conversation.ingest(lxm, self.app, originator=True)
self.messages.append(ConversationMessage(message_path))
return qr_save_path
elif mode == "save_uri":
lxm_uri = lxm.as_uri()+"\n"
uri_save_path = self.app.downloads_path+"/LXM_"+str(RNS.hexrep(lxm.hash, delimit=False)+".txt")
with open(uri_save_path, "wb") as f:
f.write(lxm_uri.encode("utf-8"))
message_path = Conversation.ingest(lxm, self.app, originator=True)
self.messages.append(ConversationMessage(message_path))
return uri_save_path
elif mode == "return_uri":
return lxm.as_uri()
except Exception as e: except Exception as e:
RNS.log("An error occurred while generating paper message, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("An error occurred while generating paper message, the contained exception was: "+str(e), RNS.LOG_ERROR)
return False return False

View File

@@ -66,7 +66,7 @@ class Directory:
packed_list = [] packed_list = []
for source_hash in self.directory_entries: for source_hash in self.directory_entries:
e = self.directory_entries[source_hash] e = self.directory_entries[source_hash]
packed_list.append((e.source_hash, e.display_name, e.trust_level, e.hosts_node, e.preferred_delivery, e.identify)) packed_list.append((e.source_hash, e.display_name, e.trust_level, e.hosts_node, e.preferred_delivery, e.identify, e.sort_rank))
directory = { directory = {
"entry_list": packed_list, "entry_list": packed_list,
@@ -91,6 +91,9 @@ class Directory:
entries = {} entries = {}
for e in unpacked_list: for e in unpacked_list:
if e[1] == None:
e[1] = "Undefined"
if len(e) > 3: if len(e) > 3:
hosts_node = e[3] hosts_node = e[3]
else: else:
@@ -106,13 +109,17 @@ class Directory:
else: else:
identify = False identify = False
entries[e[0]] = DirectoryEntry(e[0], e[1], e[2], hosts_node, preferred_delivery=preferred_delivery, identify_on_connect=identify) if len(e) > 6:
sort_rank = e[6]
else:
sort_rank = None
entries[e[0]] = DirectoryEntry(e[0], e[1], e[2], hosts_node, preferred_delivery=preferred_delivery, identify_on_connect=identify, sort_rank=sort_rank)
self.directory_entries = entries self.directory_entries = entries
self.announce_stream = unpacked_directory["announce_stream"] self.announce_stream = unpacked_directory["announce_stream"]
except Exception as e: except Exception as e:
RNS.log("Could not load directory from disk. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Could not load directory from disk. The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -220,6 +227,13 @@ class Directory:
def simplest_display_str(self, source_hash): def simplest_display_str(self, source_hash):
trust_level = self.trust_level(source_hash) trust_level = self.trust_level(source_hash)
if trust_level == DirectoryEntry.WARNING or trust_level == DirectoryEntry.UNTRUSTED: if trust_level == DirectoryEntry.WARNING or trust_level == DirectoryEntry.UNTRUSTED:
if source_hash in self.directory_entries:
dn = self.directory_entries[source_hash].display_name
if dn == None:
return RNS.prettyhexrep(source_hash)
else:
return dn+" <"+RNS.hexrep(source_hash, delimit=False)+">"
else:
return "<"+RNS.hexrep(source_hash, delimit=False)+">" return "<"+RNS.hexrep(source_hash, delimit=False)+">"
else: else:
if source_hash in self.directory_entries: if source_hash in self.directory_entries:
@@ -253,6 +267,18 @@ class Directory:
else: else:
return DirectoryEntry.UNKNOWN return DirectoryEntry.UNKNOWN
def pn_trust_level(self, source_hash):
recalled_identity = RNS.Identity.recall(source_hash)
if recalled_identity != None:
associated_node = RNS.Destination.hash_from_name_and_identity("nomadnetwork.node", recalled_identity)
return self.trust_level(associated_node)
def sort_rank(self, source_hash):
if source_hash in self.directory_entries:
return self.directory_entries[source_hash].sort_rank
else:
return None
def preferred_delivery(self, source_hash): def preferred_delivery(self, source_hash):
if source_hash in self.directory_entries: if source_hash in self.directory_entries:
return self.directory_entries[source_hash].preferred_delivery return self.directory_entries[source_hash].preferred_delivery
@@ -312,6 +338,7 @@ class Directory:
if e.hosts_node: if e.hosts_node:
node_list.append(e) node_list.append(e)
node_list.sort(key = lambda e: (e.sort_rank if e.sort_rank != None else 2^32, DirectoryEntry.TRUSTED-e.trust_level, e.display_name))
return node_list return node_list
def number_of_known_nodes(self): def number_of_known_nodes(self):
@@ -336,10 +363,11 @@ class DirectoryEntry:
DIRECT = 0x01 DIRECT = 0x01
PROPAGATED = 0x02 PROPAGATED = 0x02
def __init__(self, source_hash, display_name=None, trust_level=UNKNOWN, hosts_node=False, preferred_delivery=None, identify_on_connect=False): def __init__(self, source_hash, display_name=None, trust_level=UNKNOWN, hosts_node=False, preferred_delivery=None, identify_on_connect=False, sort_rank=None):
if len(source_hash) == RNS.Identity.TRUNCATED_HASHLENGTH//8: if len(source_hash) == RNS.Identity.TRUNCATED_HASHLENGTH//8:
self.source_hash = source_hash self.source_hash = source_hash
self.display_name = display_name self.display_name = display_name
self.sort_rank = sort_rank
if preferred_delivery == None: if preferred_delivery == None:
self.preferred_delivery = DirectoryEntry.DIRECT self.preferred_delivery = DirectoryEntry.DIRECT

View File

@@ -15,7 +15,11 @@ class Node:
self.identity = self.app.identity self.identity = self.app.identity
self.destination = RNS.Destination(self.identity, RNS.Destination.IN, RNS.Destination.SINGLE, "nomadnetwork", "node") self.destination = RNS.Destination(self.identity, RNS.Destination.IN, RNS.Destination.SINGLE, "nomadnetwork", "node")
self.last_announce = time.time() self.last_announce = time.time()
self.last_file_refresh = time.time()
self.last_page_refresh = time.time()
self.announce_interval = self.app.node_announce_interval self.announce_interval = self.app.node_announce_interval
self.page_refresh_interval = self.app.page_refresh_interval
self.file_refresh_interval = self.app.file_refresh_interval
self.job_interval = Node.JOB_INTERVAL self.job_interval = Node.JOB_INTERVAL
self.should_run_jobs = True self.should_run_jobs = True
self.app_data = None self.app_data = None
@@ -46,6 +50,8 @@ class Node:
def register_pages(self): def register_pages(self):
# TODO: Deregister previously registered pages
# that no longer exist.
self.servedpages = [] self.servedpages = []
self.scan_pages(self.app.pagespath) self.scan_pages(self.app.pagespath)
@@ -65,6 +71,8 @@ class Node:
) )
def register_files(self): def register_files(self):
# TODO: Deregister previously registered files
# that no longer exist.
self.servedfiles = [] self.servedfiles = []
self.scan_files(self.app.filespath) self.scan_files(self.app.filespath)
@@ -223,6 +231,16 @@ class Node:
if now > self.last_announce + self.announce_interval*60: if now > self.last_announce + self.announce_interval*60:
self.announce() self.announce()
if self.page_refresh_interval > 0:
if now > self.last_page_refresh + self.page_refresh_interval*60:
self.register_pages()
self.last_page_refresh = time.time()
if self.file_refresh_interval > 0:
if now > self.last_file_refresh + self.file_refresh_interval*60:
self.register_files()
self.last_file_refresh = time.time()
time.sleep(self.job_interval) time.sleep(self.job_interval)
def peer_connected(self, link): def peer_connected(self, link):

View File

@@ -119,9 +119,12 @@ class NomadNetworkApp:
self.should_run_jobs = True self.should_run_jobs = True
self.job_interval = 5 self.job_interval = 5
self.defer_jobs = 90 self.defer_jobs = 90
self.page_refresh_interval = 0
self.file_refresh_interval = 0
self.peer_announce_at_start = True self.peer_announce_at_start = True
self.try_propagation_on_fail = True self.try_propagation_on_fail = True
self.disable_propagation = False
self.periodic_lxmf_sync = True self.periodic_lxmf_sync = True
self.lxmf_sync_interval = 360*60 self.lxmf_sync_interval = 360*60
@@ -308,15 +311,27 @@ class NomadNetworkApp:
except Exception as e: except Exception as e:
RNS.log("Cannot prioritise "+str(dest_str)+", it is not a valid destination hash", RNS.LOG_ERROR) RNS.log("Cannot prioritise "+str(dest_str)+", it is not a valid destination hash", RNS.LOG_ERROR)
if self.disable_propagation:
if os.path.isfile(self.pnannouncedpath):
try:
RNS.log("Sending indication to peered LXMF Propagation Node that this node is no longer participating", RNS.LOG_DEBUG)
self.message_router.disable_propagation()
os.unlink(self.pnannouncedpath)
except Exception as e:
RNS.log("An error ocurred while indicating that this LXMF Propagation Node is no longer participating. The contained exception was: "+str(e), RNS.LOG_ERROR)
else:
self.message_router.enable_propagation() self.message_router.enable_propagation()
try: try:
with open(self.pnannouncedpath, "wb") as pnf: with open(self.pnannouncedpath, "wb") as pnf:
pnf.write(msgpack.packb(time.time())) pnf.write(msgpack.packb(time.time()))
pnf.close() pnf.close()
except Exception as e: except Exception as e:
RNS.log("An error ocurred while writing Propagation Node announce timestamp. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("An error ocurred while writing Propagation Node announce timestamp. The contained exception was: "+str(e), RNS.LOG_ERROR)
if not self.disable_propagation:
RNS.log("LXMF Propagation Node started on: "+RNS.prettyhexrep(self.message_router.propagation_destination.hash)) RNS.log("LXMF Propagation Node started on: "+RNS.prettyhexrep(self.message_router.propagation_destination.hash))
self.node = nomadnet.Node(self) self.node = nomadnet.Node(self)
else: else:
self.node = None self.node = None
@@ -414,16 +429,24 @@ class NomadNetworkApp:
return "Receiving messages" return "Receiving messages"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_RESPONSE_RECEIVED: elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_RESPONSE_RECEIVED:
return "Messages received" return "Messages received"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_NO_PATH:
return "No path to node"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_LINK_FAILED:
return "Link establisment failed"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_TRANSFER_FAILED:
return "Sync request failed"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_NO_IDENTITY_RCVD:
return "Remote got no identity"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_NO_ACCESS:
return "Node rejected request"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_FAILED:
return "Sync failed"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_COMPLETE: elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_COMPLETE:
new_msgs = self.message_router.propagation_transfer_last_result new_msgs = self.message_router.propagation_transfer_last_result
if new_msgs == 0: if new_msgs == 0:
return "Done, no new messages" return "Done, no new messages"
else: else:
return "Downloaded "+str(new_msgs)+" new messages" return "Downloaded "+str(new_msgs)+" new messages"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_NO_IDENTITY_RCVD:
return "Node did not receive identification"
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_NO_ACCESS:
return "Node did not allow request"
else: else:
return "Unknown" return "Unknown"
@@ -788,6 +811,11 @@ class NomadNetworkApp:
else: else:
self.node_name = self.config["node"]["node_name"] self.node_name = self.config["node"]["node_name"]
if not "disable_propagation" in self.config["node"]:
self.disable_propagation = False
else:
self.disable_propagation = self.config["node"].as_bool("disable_propagation")
if not "announce_at_start" in self.config["node"]: if not "announce_at_start" in self.config["node"]:
self.node_announce_at_start = False self.node_announce_at_start = False
else: else:
@@ -805,9 +833,27 @@ class NomadNetworkApp:
if "pages_path" in self.config["node"]: if "pages_path" in self.config["node"]:
self.pagespath = self.config["node"]["pages_path"] self.pagespath = self.config["node"]["pages_path"]
if not "page_refresh_interval" in self.config["node"]:
self.page_refresh_interval = 0
else:
value = self.config["node"].as_int("page_refresh_interval")
if value < 0:
value = 0
self.page_refresh_interval = value
if "files_path" in self.config["node"]: if "files_path" in self.config["node"]:
self.filespath = self.config["node"]["files_path"] self.filespath = self.config["node"]["files_path"]
if not "file_refresh_interval" in self.config["node"]:
self.file_refresh_interval = 0
else:
value = self.config["node"].as_int("file_refresh_interval")
if value < 0:
value = 0
self.file_refresh_interval = value
if "prioritise_destinations" in self.config["node"]: if "prioritise_destinations" in self.config["node"]:
self.prioritised_lxmf_destinations = self.config["node"].as_list("prioritise_destinations") self.prioritised_lxmf_destinations = self.config["node"].as_list("prioritise_destinations")
else: else:
@@ -1003,6 +1049,14 @@ announce_interval = 360
# Whether to announce when the node starts. # Whether to announce when the node starts.
announce_at_start = Yes announce_at_start = Yes
# By default, when Nomad Network is hosting a
# node, it will also act as an LXMF propagation
# node. If there is already a large amount of
# propagation nodes on the network, or you
# simply want to run a pageserving-only node,
# you can disable running a propagation node.
# disable_propagation = False
# The maximum amount of storage to use for # The maximum amount of storage to use for
# the LXMF Propagation Node message store, # the LXMF Propagation Node message store,
# specified in megabytes. When this limit # specified in megabytes. When this limit
@@ -1023,6 +1077,22 @@ announce_at_start = Yes
# and generally you do not need to use it. # and generally you do not need to use it.
# prioritise_destinations = 41d20c727598a3fbbdf9106133a3a0ed, d924b81822ca24e68e2effea99bcb8cf # prioritise_destinations = 41d20c727598a3fbbdf9106133a3a0ed, d924b81822ca24e68e2effea99bcb8cf
# Automatic rescan interval of the pages directory in minutes.
# Default: int = 0 (no rescan)
page_refresh_interval = 0
# You can specify the interval in minutes for
# rescanning the hosted pages path. By default,
# this option is disabled, and the pages path
# will only be scanned on startup.
# page_refresh_interval = 0
# You can specify the interval in minutes for
# rescanning the hosted files path. By default,
# this option is disabled, and the files path
# will only be scanned on startup.
# file_refresh_interval = 0
[printing] [printing]
# You can configure Nomad Network to print # You can configure Nomad Network to print

View File

@@ -1 +1 @@
__version__ = "0.3.7" __version__ = "0.4.6"

View File

@@ -27,13 +27,13 @@ class BrowserFrame(urwid.Frame):
self.delegate.save_node_dialog() self.delegate.save_node_dialog()
elif key == "ctrl g": elif key == "ctrl g":
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.sub_displays.network_display.toggle_fullscreen() nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.sub_displays.network_display.toggle_fullscreen()
elif self.get_focus() == "body": elif self.focus_position == "body":
if key == "down" or key == "up": if key == "down" or key == "up":
try: try:
if hasattr(self.delegate, "page_pile") and self.delegate.page_pile: if hasattr(self.delegate, "page_pile") and self.delegate.page_pile:
def df(loop, user_data): def df(loop, user_data):
st = None st = None
nf = self.delegate.page_pile.get_focus() nf = self.delegate.page_pile.focus
if hasattr(nf, "key_timeout"): if hasattr(nf, "key_timeout"):
st = nf st = nf
elif hasattr(nf, "original_widget"): elif hasattr(nf, "original_widget"):
@@ -279,7 +279,10 @@ class Browser:
self.browser_footer = urwid.Text("") self.browser_footer = urwid.Text("")
self.page_pile = None self.page_pile = None
self.browser_body = urwid.Filler(urwid.Text("Disconnected\n"+self.g["arrow_l"]+" "+self.g["arrow_r"], align="center"), "middle") self.browser_body = urwid.Filler(
urwid.Text("Disconnected\n"+self.g["arrow_l"]+" "+self.g["arrow_r"], align=urwid.CENTER),
urwid.MIDDLE,
)
self.frame = BrowserFrame(self.browser_body, header=self.browser_header, footer=self.browser_footer) self.frame = BrowserFrame(self.browser_body, header=self.browser_header, footer=self.browser_footer)
self.frame.delegate = self self.frame.delegate = self
@@ -306,28 +309,29 @@ class Browser:
self.update_display() self.update_display()
columns = urwid.Columns([ columns = urwid.Columns([
("weight", 0.5, urwid.Text(" ")), (urwid.WEIGHT, 0.5, urwid.Text(" ")),
(8, urwid.Button("Back", on_press=back_action)), (8, urwid.Button("Back", on_press=back_action)),
("weight", 0.5, urwid.Text(" ")) (urwid.WEIGHT, 0.5, urwid.Text(" ")),
]) ])
if len(self.attr_maps) > 0: if len(self.attr_maps) > 0:
pile = urwid.Pile([ pile = urwid.Pile([
urwid.Text("!\n\n"+self.status_text()+"\n", align="center"), urwid.Text("!\n\n"+self.status_text()+"\n", align=urwid.CENTER),
columns columns
]) ])
else: else:
pile = urwid.Pile([ pile = urwid.Pile([urwid.Text("!\n\n"+self.status_text(), align=urwid.CENTER)])
urwid.Text("!\n\n"+self.status_text(), align="center")
])
return urwid.Filler(pile, "middle") return urwid.Filler(pile, urwid.MIDDLE)
def update_display(self): def update_display(self):
if self.status == Browser.DISCONECTED: if self.status == Browser.DISCONECTED:
self.display_widget.set_attr_map({None: "inactive_text"}) self.display_widget.set_attr_map({None: "inactive_text"})
self.page_pile = None self.page_pile = None
self.browser_body = urwid.Filler(urwid.Text("Disconnected\n"+self.g["arrow_l"]+" "+self.g["arrow_r"], align="center"), "middle") self.browser_body = urwid.Filler(
urwid.Text("Disconnected\n"+self.g["arrow_l"]+" "+self.g["arrow_r"], align=urwid.CENTER),
urwid.MIDDLE,
)
self.browser_footer = urwid.Text("") self.browser_footer = urwid.Text("")
self.browser_header = urwid.Text("") self.browser_header = urwid.Text("")
self.linebox.set_title("Remote Node") self.linebox.set_title("Remote Node")
@@ -354,7 +358,10 @@ class Browser:
elif self.status <= Browser.REQUEST_SENT: elif self.status <= Browser.REQUEST_SENT:
if len(self.attr_maps) == 0: if len(self.attr_maps) == 0:
self.browser_body = urwid.Filler(urwid.Text("Retrieving\n["+self.current_url()+"]", align="center"), "middle") self.browser_body = urwid.Filler(
urwid.Text("Retrieving\n["+self.current_url()+"]", align=urwid.CENTER),
urwid.MIDDLE,
)
self.browser_footer = self.make_status_widget() self.browser_footer = self.make_status_widget()
@@ -517,7 +524,7 @@ class Browser:
self.status = Browser.PATH_REQUESTED self.status = Browser.PATH_REQUESTED
self.update_display() self.update_display()
pr_time = time.time() pr_time = time.time()+RNS.Transport.first_hop_timeout(self.destination_hash)
while not RNS.Transport.has_path(self.destination_hash): while not RNS.Transport.has_path(self.destination_hash):
now = time.time() now = time.time()
if now > pr_time+self.timeout: if now > pr_time+self.timeout:
@@ -608,7 +615,7 @@ class Browser:
self.load_page() self.load_page()
def close_dialogs(self): def close_dialogs(self):
options = self.delegate.columns.options("weight", self.delegate.right_area_width) options = self.delegate.columns.options(urwid.WEIGHT, self.delegate.right_area_width)
self.delegate.columns.contents[1] = (self.display_widget, options) self.delegate.columns.contents[1] = (self.display_widget, options)
def url_dialog(self): def url_dialog(self):
@@ -629,7 +636,11 @@ class Browser:
dialog = UrlDialogLineBox( dialog = UrlDialogLineBox(
urwid.Pile([ urwid.Pile([
e_url, e_url,
urwid.Columns([("weight", 0.45, urwid.Button("Cancel", on_press=dismiss_dialog)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Go", on_press=confirmed))]) urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Cancel", on_press=dismiss_dialog)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("Go", on_press=confirmed)),
])
]), title="Enter URL" ]), title="Enter URL"
) )
e_url.confirmed = confirmed e_url.confirmed = confirmed
@@ -637,9 +648,18 @@ class Browser:
dialog.delegate = self dialog.delegate = self
bottom = self.display_widget bottom = self.display_widget
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 65), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=(urwid.RELATIVE, 65),
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
options = self.delegate.columns.options("weight", self.delegate.right_area_width) options = self.delegate.columns.options(urwid.WEIGHT, self.delegate.right_area_width)
self.delegate.columns.contents[1] = (overlay, options) self.delegate.columns.contents[1] = (overlay, options)
self.delegate.columns.focus_position = 1 self.delegate.columns.focus_position = 1
@@ -663,7 +683,11 @@ class Browser:
dialog = UrlDialogLineBox( dialog = UrlDialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("Save connected node"+disp_str+" "+RNS.prettyhexrep(self.destination_hash)+" to Known Nodes?\n"), urwid.Text("Save connected node"+disp_str+" "+RNS.prettyhexrep(self.destination_hash)+" to Known Nodes?\n"),
urwid.Columns([("weight", 0.45, urwid.Button("Cancel", on_press=dismiss_dialog)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Save", on_press=confirmed))]) urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Cancel", on_press=dismiss_dialog)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("Save", on_press=confirmed)),
])
]), title="Save Node" ]), title="Save Node"
) )
@@ -671,9 +695,18 @@ class Browser:
dialog.delegate = self dialog.delegate = self
bottom = self.display_widget bottom = self.display_widget
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 50), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=(urwid.RELATIVE, 50),
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
options = self.delegate.columns.options("weight", self.delegate.right_area_width) options = self.delegate.columns.options(urwid.WEIGHT, self.delegate.right_area_width)
self.delegate.columns.contents[1] = (overlay, options) self.delegate.columns.contents[1] = (overlay, options)
self.delegate.columns.focus_position = 1 self.delegate.columns.focus_position = 1
@@ -770,7 +803,7 @@ class Browser:
self.status = Browser.PATH_REQUESTED self.status = Browser.PATH_REQUESTED
self.update_display() self.update_display()
pr_time = time.time() pr_time = time.time()+RNS.Transport.first_hop_timeout(self.destination_hash)
while not RNS.Transport.has_path(self.destination_hash): while not RNS.Transport.has_path(self.destination_hash):
now = time.time() now = time.time()
if now > pr_time+self.timeout: if now > pr_time+self.timeout:
@@ -976,7 +1009,10 @@ class Browser:
try: try:
file_name = request_receipt.response[0] file_name = request_receipt.response[0]
file_data = request_receipt.response[1] file_data = request_receipt.response[1]
file_destination = self.app.downloads_path+"/"+file_name file_destination_name = file_name.split("/")
file_destination_name = file_destination_name[len(file_destination_name)-1]
file_destination = self.app.downloads_path+"/"+file_destination_name
counter = 0 counter = 0
while os.path.isfile(file_destination): while os.path.isfile(file_destination):

View File

@@ -12,13 +12,13 @@ class ConfigDisplayShortcuts():
class ConfigFiller(urwid.WidgetWrap): class ConfigFiller(urwid.WidgetWrap):
def __init__(self, widget, app): def __init__(self, widget, app):
self.app = app self.app = app
self.filler = urwid.Filler(widget, "top") self.filler = urwid.Filler(widget, urwid.TOP)
urwid.WidgetWrap.__init__(self, self.filler) super().__init__(self.filler)
def keypress(self, size, key): def keypress(self, size, key):
if key == "up": if key == "up":
self.app.ui.main_display.frame.set_focus("header") self.app.ui.main_display.frame.focus_position = "header"
return super(ConfigFiller, self).keypress(size, key) return super(ConfigFiller, self).keypress(size, key)
@@ -31,12 +31,20 @@ class ConfigDisplay():
self.editor_term = EditorTerminal(self.app, self) self.editor_term = EditorTerminal(self.app, self)
self.widget = urwid.LineBox(self.editor_term) self.widget = urwid.LineBox(self.editor_term)
self.app.ui.main_display.update_active_sub_display() self.app.ui.main_display.update_active_sub_display()
self.app.ui.main_display.frame.set_focus("body") self.app.ui.main_display.frame.focus_position = "body"
self.editor_term.term.change_focus(True) self.editor_term.term.change_focus(True)
pile = urwid.Pile([ pile = urwid.Pile([
urwid.Text(("body_text", "\nTo change the configuration, edit the config file located at:\n\n"+self.app.configpath+"\n\nRestart Nomad Network for changes to take effect\n"), align="center"), urwid.Text(
urwid.Padding(urwid.Button("Open Editor", on_press=open_editor), width=15, align="center"), (
"body_text",
"\nTo change the configuration, edit the config file located at:\n\n"
+self.app.configpath
+"\n\nRestart Nomad Network for changes to take effect\n",
),
align=urwid.CENTER,
),
urwid.Padding(urwid.Button("Open Editor", on_press=open_editor), width=15, align=urwid.CENTER),
]) ])
self.config_explainer = ConfigFiller(pile, self.app) self.config_explainer = ConfigFiller(pile, self.app)
@@ -71,11 +79,11 @@ class EditorTerminal(urwid.WidgetWrap):
urwid.connect_signal(self.term, 'closed', quit_term) urwid.connect_signal(self.term, 'closed', quit_term)
urwid.WidgetWrap.__init__(self, self.term) super().__init__(self.term)
def keypress(self, size, key): def keypress(self, size, key):
# TODO: Decide whether there should be a way to get out while editing # TODO: Decide whether there should be a way to get out while editing
#if key == "up": #if key == "up":
# nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") # nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
return super(EditorTerminal, self).keypress(size, key) return super(EditorTerminal, self).keypress(size, key)

View File

@@ -37,9 +37,9 @@ class ConversationsArea(urwid.LineBox):
elif key == "ctrl g": elif key == "ctrl g":
self.delegate.toggle_fullscreen() self.delegate.toggle_fullscreen()
elif key == "tab": elif key == "tab":
self.delegate.app.ui.main_display.frame.set_focus("header") self.delegate.app.ui.main_display.frame.focus_position = "header"
elif key == "up" and (self.delegate.ilb.first_item_is_selected() or self.delegate.ilb.body_is_empty()): elif key == "up" and (self.delegate.ilb.first_item_is_selected() or self.delegate.ilb.body_is_empty()):
self.delegate.app.ui.main_display.frame.set_focus("header") self.delegate.app.ui.main_display.frame.focus_position = "header"
else: else:
return super(ConversationsArea, self).keypress(size, key) return super(ConversationsArea, self).keypress(size, key)
@@ -69,10 +69,10 @@ class ConversationsDisplay():
self.columns_widget = urwid.Columns( self.columns_widget = urwid.Columns(
[ [
# ("weight", ConversationsDisplay.list_width, self.listbox), # (urwid.WEIGHT, ConversationsDisplay.list_width, self.listbox),
# ("weight", 1-ConversationsDisplay.list_width, self.make_conversation_widget(None)) # (urwid.WEIGHT, 1-ConversationsDisplay.list_width, self.make_conversation_widget(None))
(ConversationsDisplay.given_list_width, self.listbox), (ConversationsDisplay.given_list_width, self.listbox),
("weight", 1, self.make_conversation_widget(None)) (urwid.WEIGHT, 1, self.make_conversation_widget(None))
], ],
dividechars=0, focus_column=0, box_columns=[0] dividechars=0, focus_column=0, box_columns=[0]
) )
@@ -105,7 +105,7 @@ class ConversationsDisplay():
highlight_offFocus="list_off_focus" highlight_offFocus="list_off_focus"
) )
self.listbox = ConversationsArea(urwid.Filler(self.ilb, height=("relative", 100)), title="Conversations") self.listbox = ConversationsArea(urwid.Filler(self.ilb, height=urwid.RELATIVE_100), title="Conversations")
self.listbox.delegate = self self.listbox.delegate = self
def delete_selected_conversation(self): def delete_selected_conversation(self):
@@ -127,17 +127,33 @@ class ConversationsDisplay():
dialog = DialogLineBox( dialog = DialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("Delete conversation with\n"+self.app.directory.simplest_display_str(bytes.fromhex(source_hash))+"\n", align="center"), urwid.Text(
urwid.Columns([("weight", 0.45, urwid.Button("Yes", on_press=confirmed)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("No", on_press=dismiss_dialog))]) "Delete conversation with\n"+self.app.directory.simplest_display_str(bytes.fromhex(source_hash))+"\n",
align=urwid.CENTER,
),
urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Yes", on_press=confirmed)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("No", on_press=dismiss_dialog)),
])
]), title="?" ]), title="?"
) )
dialog.delegate = self dialog.delegate = self
bottom = self.listbox bottom = self.listbox
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
# options = self.columns_widget.options("weight", ConversationsDisplay.list_width) # options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
self.columns_widget.contents[0] = (overlay, options) self.columns_widget.contents[0] = (overlay, options)
def edit_selected_in_directory(self): def edit_selected_in_directory(self):
@@ -223,9 +239,12 @@ class ConversationsDisplay():
RNS.log("Could not save directory entry. The contained exception was: "+str(e), RNS.LOG_VERBOSE) RNS.log("Could not save directory entry. The contained exception was: "+str(e), RNS.LOG_VERBOSE)
if not dialog_pile.error_display: if not dialog_pile.error_display:
dialog_pile.error_display = True dialog_pile.error_display = True
options = dialog_pile.options(height_type="pack") options = dialog_pile.options(height_type=urwid.PACK)
dialog_pile.contents.append((urwid.Text(""), options)) dialog_pile.contents.append((urwid.Text(""), options))
dialog_pile.contents.append((urwid.Text(("error_text", "Could not save entry. Check your input."), align="center"), options)) dialog_pile.contents.append((
urwid.Text(("error_text", "Could not save entry. Check your input."), align=urwid.CENTER),
options,)
)
source_is_known = self.app.directory.is_known(bytes.fromhex(source_hash_text)) source_is_known = self.app.directory.is_known(bytes.fromhex(source_hash_text))
if source_is_known: if source_is_known:
@@ -234,13 +253,23 @@ class ConversationsDisplay():
def query_action(sender, user_data): def query_action(sender, user_data):
self.close_conversation_by_hash(user_data) self.close_conversation_by_hash(user_data)
nomadnet.Conversation.query_for_peer(user_data) nomadnet.Conversation.query_for_peer(user_data)
options = dialog_pile.options(height_type="pack") options = dialog_pile.options(height_type=urwid.PACK)
dialog_pile.contents = [ dialog_pile.contents = [
(urwid.Text("Query sent"), options), (urwid.Text("Query sent"), options),
(urwid.Button("OK", on_press=dismiss_dialog), options) (urwid.Button("OK", on_press=dismiss_dialog), options)
] ]
query_button = urwid.Button("Query network for keys", on_press=query_action, user_data=source_hash_text) query_button = urwid.Button("Query network for keys", on_press=query_action, user_data=source_hash_text)
known_section = urwid.Pile([urwid.Divider(g["divider1"]), urwid.Text(g["info"]+"\n", align="center"), urwid.Text("The identity of this peer is not known, and you cannot currently send messages to it. You can query the network to obtain the identity.\n", align="center"), query_button, urwid.Divider(g["divider1"])]) known_section = urwid.Pile([
urwid.Divider(g["divider1"]),
urwid.Text(g["info"]+"\n", align=urwid.CENTER),
urwid.Text(
"The identity of this peer is not known, and you cannot currently send messages to it. "
"You can query the network to obtain the identity.\n",
align=urwid.CENTER,
),
query_button,
urwid.Divider(g["divider1"]),
])
dialog_pile = urwid.Pile([ dialog_pile = urwid.Pile([
selected_id_widget, selected_id_widget,
@@ -253,7 +282,11 @@ class ConversationsDisplay():
r_direct, r_direct,
r_propagated, r_propagated,
known_section, known_section,
urwid.Columns([("weight", 0.45, urwid.Button("Save", on_press=confirmed)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Back", on_press=dismiss_dialog))]) urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Save", on_press=confirmed)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("Back", on_press=dismiss_dialog)),
])
]) ])
dialog_pile.error_display = False dialog_pile.error_display = False
@@ -261,10 +294,19 @@ class ConversationsDisplay():
dialog.delegate = self dialog.delegate = self
bottom = self.listbox bottom = self.listbox
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
# options = self.columns_widget.options("weight", ConversationsDisplay.list_width) # options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
self.columns_widget.contents[0] = (overlay, options) self.columns_widget.contents[0] = (overlay, options)
def new_conversation(self): def new_conversation(self):
@@ -312,9 +354,15 @@ class ConversationsDisplay():
RNS.log("Could not start conversation. The contained exception was: "+str(e), RNS.LOG_VERBOSE) RNS.log("Could not start conversation. The contained exception was: "+str(e), RNS.LOG_VERBOSE)
if not dialog_pile.error_display: if not dialog_pile.error_display:
dialog_pile.error_display = True dialog_pile.error_display = True
options = dialog_pile.options(height_type="pack") options = dialog_pile.options(height_type=urwid.PACK)
dialog_pile.contents.append((urwid.Text(""), options)) dialog_pile.contents.append((urwid.Text(""), options))
dialog_pile.contents.append((urwid.Text(("error_text", "Could not start conversation. Check your input."), align="center"), options)) dialog_pile.contents.append((
urwid.Text(
("error_text", "Could not start conversation. Check your input."),
align=urwid.CENTER,
),
options,
))
dialog_pile = urwid.Pile([ dialog_pile = urwid.Pile([
e_id, e_id,
@@ -324,7 +372,11 @@ class ConversationsDisplay():
r_unknown, r_unknown,
r_trusted, r_trusted,
urwid.Text(""), urwid.Text(""),
urwid.Columns([("weight", 0.45, urwid.Button("Create", on_press=confirmed)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Back", on_press=dismiss_dialog))]) urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Create", on_press=confirmed)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("Back", on_press=dismiss_dialog)),
])
]) ])
dialog_pile.error_display = False dialog_pile.error_display = False
@@ -332,10 +384,19 @@ class ConversationsDisplay():
dialog.delegate = self dialog.delegate = self
bottom = self.listbox bottom = self.listbox
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
# options = self.columns_widget.options("weight", ConversationsDisplay.list_width) # options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
self.columns_widget.contents[0] = (overlay, options) self.columns_widget.contents[0] = (overlay, options)
def ingest_lxm_uri(self): def ingest_lxm_uri(self):
@@ -366,7 +427,10 @@ class ConversationsDisplay():
rdialog_pile = urwid.Pile([ rdialog_pile = urwid.Pile([
urwid.Text("Message was decoded, decrypted successfully, and added to your conversation list."), urwid.Text("Message was decoded, decrypted successfully, and added to your conversation list."),
urwid.Text(""), urwid.Text(""),
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))]) urwid.Columns([
(urwid.WEIGHT, 0.6, urwid.Text("")),
(urwid.WEIGHT, 0.4, urwid.Button("OK", on_press=dismiss_dialog)),
])
]) ])
rdialog_pile.error_display = False rdialog_pile.error_display = False
@@ -374,16 +438,28 @@ class ConversationsDisplay():
rdialog.delegate = self rdialog.delegate = self
bottom = self.listbox bottom = self.listbox
roverlay = urwid.Overlay(rdialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) roverlay = urwid.Overlay(
rdialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
self.columns_widget.contents[0] = (roverlay, options) self.columns_widget.contents[0] = (roverlay, options)
elif ingest_result == duplicate_signal: elif ingest_result == duplicate_signal:
rdialog_pile = urwid.Pile([ rdialog_pile = urwid.Pile([
urwid.Text("The decoded message has already been processed by the LXMF Router, and will not be ingested again."), urwid.Text("The decoded message has already been processed by the LXMF Router, and will not be ingested again."),
urwid.Text(""), urwid.Text(""),
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))]) urwid.Columns([
(urwid.WEIGHT, 0.6, urwid.Text("")),
(urwid.WEIGHT, 0.4, urwid.Button("OK", on_press=dismiss_dialog)),
])
]) ])
rdialog_pile.error_display = False rdialog_pile.error_display = False
@@ -391,9 +467,18 @@ class ConversationsDisplay():
rdialog.delegate = self rdialog.delegate = self
bottom = self.listbox bottom = self.listbox
roverlay = urwid.Overlay(rdialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) roverlay = urwid.Overlay(
rdialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
self.columns_widget.contents[0] = (roverlay, options) self.columns_widget.contents[0] = (roverlay, options)
else: else:
@@ -405,7 +490,10 @@ class ConversationsDisplay():
rdialog_pile = urwid.Pile([ rdialog_pile = urwid.Pile([
urwid.Text(propagation_text), urwid.Text(propagation_text),
urwid.Text(""), urwid.Text(""),
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))]) urwid.Columns([
(urwid.WEIGHT, 0.6, urwid.Text("")),
(urwid.WEIGHT, 0.4, urwid.Button("OK", on_press=dismiss_dialog)),
])
]) ])
rdialog_pile.error_display = False rdialog_pile.error_display = False
@@ -413,23 +501,36 @@ class ConversationsDisplay():
rdialog.delegate = self rdialog.delegate = self
bottom = self.listbox bottom = self.listbox
roverlay = urwid.Overlay(rdialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) roverlay = urwid.Overlay(
rdialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
self.columns_widget.contents[0] = (roverlay, options) self.columns_widget.contents[0] = (roverlay, options)
except Exception as e: except Exception as e:
RNS.log("Could not ingest LXM URI. The contained exception was: "+str(e), RNS.LOG_VERBOSE) RNS.log("Could not ingest LXM URI. The contained exception was: "+str(e), RNS.LOG_VERBOSE)
if not dialog_pile.error_display: if not dialog_pile.error_display:
dialog_pile.error_display = True dialog_pile.error_display = True
options = dialog_pile.options(height_type="pack") options = dialog_pile.options(height_type=urwid.PACK)
dialog_pile.contents.append((urwid.Text(""), options)) dialog_pile.contents.append((urwid.Text(""), options))
dialog_pile.contents.append((urwid.Text(("error_text", "Could ingest LXM from URI data. Check your input."), align="center"), options)) dialog_pile.contents.append((urwid.Text(("error_text", "Could ingest LXM from URI data. Check your input."), align=urwid.CENTER), options))
dialog_pile = urwid.Pile([ dialog_pile = urwid.Pile([
e_uri, e_uri,
urwid.Text(""), urwid.Text(""),
urwid.Columns([("weight", 0.45, urwid.Button("Ingest", on_press=confirmed)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Back", on_press=dismiss_dialog))]) urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Ingest", on_press=confirmed)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("Back", on_press=dismiss_dialog)),
])
]) ])
dialog_pile.error_display = False dialog_pile.error_display = False
@@ -437,9 +538,18 @@ class ConversationsDisplay():
dialog.delegate = self dialog.delegate = self
bottom = self.listbox bottom = self.listbox
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
self.columns_widget.contents[0] = (overlay, options) self.columns_widget.contents[0] = (overlay, options)
def delete_conversation(self, source_hash): def delete_conversation(self, source_hash):
@@ -471,7 +581,7 @@ class ConversationsDisplay():
r_mall = urwid.RadioButton(max_messages_group, "Download all", state=True) r_mall = urwid.RadioButton(max_messages_group, "Download all", state=True)
r_mlim = urwid.RadioButton(max_messages_group, "Limit to", state=False) r_mlim = urwid.RadioButton(max_messages_group, "Limit to", state=False)
ie_lim = urwid.IntEdit("", 5) ie_lim = urwid.IntEdit("", 5)
rbs = urwid.GridFlow([r_mlim, ie_lim], 12, 1, 0, align="left") rbs = urwid.GridFlow([r_mlim, ie_lim], 12, 1, 0, align=urwid.LEFT)
def sync_now(sender): def sync_now(sender):
limit = None limit = None
@@ -495,7 +605,11 @@ class ConversationsDisplay():
else: else:
sync_button = hidden_sync_button sync_button = hidden_sync_button
button_columns = urwid.Columns([("weight", 0.45, sync_button), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, cancel_button)]) button_columns = urwid.Columns([
(urwid.WEIGHT, 0.45, sync_button),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, cancel_button),
])
real_sync_button.bc = button_columns real_sync_button.bc = button_columns
pn_ident = None pn_ident = None
@@ -518,7 +632,7 @@ class ConversationsDisplay():
dialog = DialogLineBox( dialog = DialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text(""+g["node"]+pn_display_str, align="center"), urwid.Text(""+g["node"]+pn_display_str, align=urwid.CENTER),
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
sync_progress, sync_progress,
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
@@ -529,12 +643,23 @@ class ConversationsDisplay():
]), title="Message Sync" ]), title="Message Sync"
) )
else: else:
button_columns = urwid.Columns([("weight", 0.45, urwid.Text("" )), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, cancel_button)]) button_columns = urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Text("" )),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, cancel_button),
])
dialog = DialogLineBox( dialog = DialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text(""), urwid.Text(""),
urwid.Text("No trusted nodes found, cannot sync!\n", align="center"), urwid.Text("No trusted nodes found, cannot sync!\n", align=urwid.CENTER),
urwid.Text("To syncronise messages from the network, one or more nodes must be marked as trusted in the Known Nodes list, or a node must manually be selected as the default propagation node. Nomad Network will then automatically sync from the nearest trusted node, or the manually selected one.", align="left"), urwid.Text(
"To synchronise messages from the network, "
"one or more nodes must be marked as trusted in the Known Nodes list, "
"or a node must manually be selected as the default propagation node. "
"Nomad Network will then automatically sync from the nearest trusted node, "
"or the manually selected one.",
align=urwid.LEFT,
),
urwid.Text(""), urwid.Text(""),
button_columns button_columns
]), title="Message Sync" ]), title="Message Sync"
@@ -550,10 +675,19 @@ class ConversationsDisplay():
self.sync_dialog = dialog self.sync_dialog = dialog
bottom = self.listbox bottom = self.listbox
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
# options = self.columns_widget.options("weight", ConversationsDisplay.list_width) # options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
self.columns_widget.contents[0] = (overlay, options) self.columns_widget.contents[0] = (overlay, options)
def update_sync_dialog(self, loop = None, sender = None): def update_sync_dialog(self, loop = None, sender = None):
@@ -561,9 +695,9 @@ class ConversationsDisplay():
self.sync_dialog.sync_progress.set_completion(self.app.get_sync_progress()) self.sync_dialog.sync_progress.set_completion(self.app.get_sync_progress())
if self.app.get_sync_status() == "Idle" or self.app.message_router.propagation_transfer_state >= LXMF.LXMRouter.PR_COMPLETE: if self.app.get_sync_status() == "Idle" or self.app.message_router.propagation_transfer_state >= LXMF.LXMRouter.PR_COMPLETE:
self.sync_dialog.bc.contents[0] = (self.sync_dialog.real_sync_button, self.sync_dialog.bc.options("weight", 0.45)) self.sync_dialog.bc.contents[0] = (self.sync_dialog.real_sync_button, self.sync_dialog.bc.options(urwid.WEIGHT, 0.45))
else: else:
self.sync_dialog.bc.contents[0] = (self.sync_dialog.hidden_sync_button, self.sync_dialog.bc.options("weight", 0.45)) self.sync_dialog.bc.contents[0] = (self.sync_dialog.hidden_sync_button, self.sync_dialog.bc.options(urwid.WEIGHT, 0.45))
self.app.ui.loop.set_alarm_in(0.2, self.update_sync_dialog) self.app.ui.loop.set_alarm_in(0.2, self.update_sync_dialog)
@@ -574,13 +708,22 @@ class ConversationsDisplay():
def update_conversation_list(self): def update_conversation_list(self):
ilb_position = self.ilb.get_selected_position() ilb_position = self.ilb.get_selected_position()
self.update_listbox() self.update_listbox()
# options = self.columns_widget.options("weight", ConversationsDisplay.list_width) # options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width) options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
if not (self.dialog_open and self.sync_dialog != None): if not (self.dialog_open and self.sync_dialog != None):
self.columns_widget.contents[0] = (self.listbox, options) self.columns_widget.contents[0] = (self.listbox, options)
else: else:
bottom = self.listbox bottom = self.listbox
overlay = urwid.Overlay(self.sync_dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
self.sync_dialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
self.columns_widget.contents[0] = (overlay, options) self.columns_widget.contents[0] = (overlay, options)
if ilb_position != None: if ilb_position != None:
@@ -606,17 +749,17 @@ class ConversationsDisplay():
self.app.mark_conversation_read(self.currently_displayed_conversation) self.app.mark_conversation_read(self.currently_displayed_conversation)
self.currently_displayed_conversation = source_hash self.currently_displayed_conversation = source_hash
# options = self.widget.options("weight", 1-ConversationsDisplay.list_width) # options = self.widget.options(urwid.WEIGHT, 1-ConversationsDisplay.list_width)
options = self.widget.options("weight", 1) options = self.widget.options(urwid.WEIGHT, 1)
self.widget.contents[1] = (self.make_conversation_widget(source_hash), options) self.widget.contents[1] = (self.make_conversation_widget(source_hash), options)
if source_hash == None: if source_hash == None:
self.widget.set_focus_column(0) self.widget.focus_position = 0
else: else:
if self.app.conversation_is_unread(source_hash): if self.app.conversation_is_unread(source_hash):
self.app.mark_conversation_read(source_hash) self.app.mark_conversation_read(source_hash)
self.update_conversation_list() self.update_conversation_list()
self.widget.set_focus_column(1) self.widget.focus_position = 1
conversation_position = None conversation_position = None
index = 0 index = 0
for widget in self.list_widgets: for widget in self.list_widgets:
@@ -756,9 +899,9 @@ class MessageEdit(urwid.Edit):
y = self.get_cursor_coords(size)[1] y = self.get_cursor_coords(size)[1]
if y == 0: if y == 0:
if self.delegate.full_editor_active and self.name == "title_editor": if self.delegate.full_editor_active and self.name == "title_editor":
self.delegate.frame.set_focus("body") self.delegate.frame.focus_position = "body"
elif not self.delegate.full_editor_active and self.name == "content_editor": elif not self.delegate.full_editor_active and self.name == "content_editor":
self.delegate.frame.set_focus("body") self.delegate.frame.focus_position = "body"
else: else:
return super(MessageEdit, self).keypress(size, key) return super(MessageEdit, self).keypress(size, key)
else: else:
@@ -769,11 +912,11 @@ class MessageEdit(urwid.Edit):
class ConversationFrame(urwid.Frame): class ConversationFrame(urwid.Frame):
def keypress(self, size, key): def keypress(self, size, key):
if self.get_focus() == "body": if self.focus_position == "body":
if key == "up" and self.delegate.messagelist.top_is_visible: if key == "up" and self.delegate.messagelist.top_is_visible:
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
elif key == "down" and self.delegate.messagelist.bottom_is_visible: elif key == "down" and self.delegate.messagelist.bottom_is_visible:
self.set_focus("footer") self.focus_position = "footer"
else: else:
return super(ConversationFrame, self).keypress(size, key) return super(ConversationFrame, self).keypress(size, key)
elif key == "ctrl k": elif key == "ctrl k":
@@ -788,7 +931,7 @@ class ConversationWidget(urwid.WidgetWrap):
if source_hash == None: if source_hash == None:
self.frame = None self.frame = None
display_widget = urwid.LineBox(urwid.Filler(urwid.Text("\n No conversation selected"), "top")) display_widget = urwid.LineBox(urwid.Filler(urwid.Text("\n No conversation selected"), "top"))
urwid.WidgetWrap.__init__(self, display_widget) super().__init__(display_widget)
else: else:
if source_hash in ConversationsDisplay.cached_conversation_widgets: if source_hash in ConversationsDisplay.cached_conversation_widgets:
return ConversationsDisplay.cached_conversation_widgets[source_hash] return ConversationsDisplay.cached_conversation_widgets[source_hash]
@@ -815,7 +958,11 @@ class ConversationWidget(urwid.WidgetWrap):
header = None header = None
if self.conversation.trust_level == DirectoryEntry.UNTRUSTED: if self.conversation.trust_level == DirectoryEntry.UNTRUSTED:
header = urwid.AttrMap(urwid.Padding(urwid.Text(g["warning"]+" Warning: Conversation with untrusted peer "+g["warning"], align="center")), "msg_warning_untrusted") header = urwid.AttrMap(
urwid.Padding(
urwid.Text(g["warning"]+" Warning: Conversation with untrusted peer "+g["warning"], align=urwid.CENTER)),
"msg_warning_untrusted",
)
self.minimal_editor = urwid.AttrMap(msg_editor, "msg_editor") self.minimal_editor = urwid.AttrMap(msg_editor, "msg_editor")
self.minimal_editor.name = "minimal_editor" self.minimal_editor.name = "minimal_editor"
@@ -852,7 +999,7 @@ class ConversationWidget(urwid.WidgetWrap):
self.frame self.frame
) )
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def clear_history_dialog(self): def clear_history_dialog(self):
def dismiss_dialog(sender): def dismiss_dialog(sender):
@@ -867,17 +1014,30 @@ class ConversationWidget(urwid.WidgetWrap):
dialog = DialogLineBox( dialog = DialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("Clear conversation history\n", align="center"), urwid.Text("Clear conversation history\n", align=urwid.CENTER),
urwid.Columns([("weight", 0.45, urwid.Button("Yes", on_press=confirmed)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("No", on_press=dismiss_dialog))]) urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Yes", on_press=confirmed)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("No", on_press=dismiss_dialog)),
])
]), title="?" ]), title="?"
) )
dialog.delegate = self dialog.delegate = self
bottom = self.messagelist bottom = self.messagelist
overlay = urwid.Overlay(dialog, bottom, align="center", width=34, valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=34,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
self.frame.contents["body"] = (overlay, self.frame.options()) self.frame.contents["body"] = (overlay, self.frame.options())
self.frame.set_focus("body") self.frame.focus_position = "body"
def toggle_editor(self): def toggle_editor(self):
if self.full_editor_active: if self.full_editor_active:
@@ -894,7 +1054,17 @@ class ConversationWidget(urwid.WidgetWrap):
if allowed: if allowed:
self.frame.contents["footer"] = (self.minimal_editor, None) self.frame.contents["footer"] = (self.minimal_editor, None)
else: else:
warning = urwid.AttrMap(urwid.Padding(urwid.Text("\n"+g["info"]+"\n\nYou cannot currently message this peer, since it's identity keys are not known.\n\nWait for an announce to arrive from the peer, or query the network for it.\n\nTo query the network, select this conversation in the conversation list, press Ctrl-E, and use the query button.\n", align="center")), "msg_header_caution") warning = urwid.AttrMap(
urwid.Padding(urwid.Text(
"\n"+g["info"]+"\n\nYou cannot currently message this peer, since it's identity keys are not known. "
"The keys have been requested from the network and should arrive shortly, if available. "
"Close this conversation and reopen it to try again.\n\n"
"To query the network manually, select this conversation in the conversation list, "
"press Ctrl-E, and use the query button.\n",
align=urwid.CENTER,
)),
"msg_header_caution",
)
self.frame.contents["footer"] = (warning, None) self.frame.contents["footer"] = (warning, None)
def toggle_focus_area(self): def toggle_focus_area(self):
@@ -905,9 +1075,9 @@ class ConversationWidget(urwid.WidgetWrap):
pass pass
if name == "messagelist": if name == "messagelist":
self.frame.set_focus("footer") self.frame.focus_position = "footer"
elif name == "minimal_editor" or name == "full_editor": elif name == "minimal_editor" or name == "full_editor":
self.frame.set_focus("body") self.frame.focus_position = "body"
def keypress(self, size, key): def keypress(self, size, key):
if key == "tab": if key == "tab":
@@ -974,7 +1144,30 @@ class ConversationWidget(urwid.WidgetWrap):
else: else:
pass pass
def paper_message(self): def paper_message_saved(self, path):
g = self.app.ui.glyphs
def dismiss_dialog(sender):
self.dialog_open = False
self.conversation_changed(None)
dialog = DialogLineBox(
urwid.Pile([
urwid.Text("The paper message was saved to:\n\n"+str(path)+"\n", align=urwid.CENTER),
urwid.Columns([
(urwid.WEIGHT, 0.6, urwid.Text("")),
(urwid.WEIGHT, 0.4, urwid.Button("OK", on_press=dismiss_dialog)),
])
]), title=g["papermsg"].replace(" ", "")
)
dialog.delegate = self
bottom = self.messagelist
overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=60, valign=urwid.MIDDLE, height=urwid.PACK, left=2, right=2)
self.frame.contents["body"] = (overlay, self.frame.options())
self.frame.focus_position = "body"
def print_paper_message_qr(self):
content = self.content_editor.get_edit_text() content = self.content_editor.get_edit_text()
title = self.title_editor.get_edit_text() title = self.title_editor.get_edit_text()
if not content == "": if not content == "":
@@ -983,6 +1176,70 @@ class ConversationWidget(urwid.WidgetWrap):
else: else:
self.paper_message_failed() self.paper_message_failed()
def save_paper_message_qr(self):
content = self.content_editor.get_edit_text()
title = self.title_editor.get_edit_text()
if not content == "":
output_result = self.conversation.paper_output(content, title, mode="save_qr")
if output_result != False:
self.clear_editor()
self.paper_message_saved(output_result)
else:
self.paper_message_failed()
def save_paper_message_uri(self):
content = self.content_editor.get_edit_text()
title = self.title_editor.get_edit_text()
if not content == "":
output_result = self.conversation.paper_output(content, title, mode="save_uri")
if output_result != False:
self.clear_editor()
self.paper_message_saved(output_result)
else:
self.paper_message_failed()
def paper_message(self):
def dismiss_dialog(sender):
self.dialog_open = False
self.conversation_changed(None)
def print_qr(sender):
dismiss_dialog(self)
self.print_paper_message_qr()
def save_qr(sender):
dismiss_dialog(self)
self.save_paper_message_qr()
def save_uri(sender):
dismiss_dialog(self)
self.save_paper_message_uri()
dialog = DialogLineBox(
urwid.Pile([
urwid.Text(
"Select the desired paper message output method.\nSaved files will be written to:\n\n"+str(self.app.downloads_path)+"\n",
align=urwid.CENTER,
),
urwid.Columns([
(urwid.WEIGHT, 0.5, urwid.Button("Print QR", on_press=print_qr)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.5, urwid.Button("Save QR", on_press=save_qr)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.5, urwid.Button("Save URI", on_press=save_uri)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.5, urwid.Button("Cancel", on_press=dismiss_dialog))
])
]), title="Create Paper Message"
)
dialog.delegate = self
bottom = self.messagelist
overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=60, valign=urwid.MIDDLE, height=urwid.PACK, left=2, right=2)
self.frame.contents["body"] = (overlay, self.frame.options())
self.frame.focus_position = "body"
def paper_message_failed(self): def paper_message_failed(self):
def dismiss_dialog(sender): def dismiss_dialog(sender):
self.dialog_open = False self.dialog_open = False
@@ -990,17 +1247,23 @@ class ConversationWidget(urwid.WidgetWrap):
dialog = DialogLineBox( dialog = DialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("Could not output paper message,\ncheck your settings. See the log\nfile for any error messages.\n", align="center"), urwid.Text(
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))]) "Could not output paper message,\ncheck your settings. See the log\nfile for any error messages.\n",
align=urwid.CENTER,
),
urwid.Columns([
(urwid.WEIGHT, 0.6, urwid.Text("")),
(urwid.WEIGHT, 0.4, urwid.Button("OK", on_press=dismiss_dialog)),
])
]), title="!" ]), title="!"
) )
dialog.delegate = self dialog.delegate = self
bottom = self.messagelist bottom = self.messagelist
overlay = urwid.Overlay(dialog, bottom, align="center", width=34, valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=34, valign=urwid.MIDDLE, height=urwid.PACK, left=2, right=2)
self.frame.contents["body"] = (overlay, self.frame.options()) self.frame.contents["body"] = (overlay, self.frame.options())
self.frame.set_focus("body") self.frame.focus_position = "body"
def close(self): def close(self):
self.delegate.close_conversation(self) self.delegate.close_conversation(self)
@@ -1060,7 +1323,7 @@ class LXMessageWidget(urwid.WidgetWrap):
urwid.Text("") urwid.Text("")
]) ])
urwid.WidgetWrap.__init__(self, display_widget) super().__init__(display_widget)
class SyncProgressBar(urwid.ProgressBar): class SyncProgressBar(urwid.ProgressBar):
def get_text(self): def get_text(self):

View File

@@ -15,7 +15,7 @@ class DirectoryDisplay():
]) ])
self.shortcuts_display = DirectoryDisplayShortcuts(self.app) self.shortcuts_display = DirectoryDisplayShortcuts(self.app)
self.widget = urwid.Filler(pile, 'top') self.widget = urwid.Filler(pile, urwid.TOP)
def shortcuts(self): def shortcuts(self):
return self.shortcuts_display return self.shortcuts_display

View File

@@ -6,13 +6,13 @@ class IntroDisplay():
font = urwid.font.HalfBlock5x4Font() font = urwid.font.HalfBlock5x4Font()
big_text = urwid.BigText(("intro_title", self.app.config["textui"]["intro_text"]), font) big_text = urwid.BigText(("intro_title", self.app.config["textui"]["intro_text"]), font)
big_text = urwid.Padding(big_text, align="center", width="clip") big_text = urwid.Padding(big_text, align=urwid.CENTER, width=urwid.CLIP)
intro = urwid.Pile([ intro = urwid.Pile([
big_text, big_text,
urwid.Text(("Version %s" % (str(self.app.version))), align="center"), urwid.Text(("Version %s" % (str(self.app.version))), align=urwid.CENTER),
urwid.Divider(), urwid.Divider(),
urwid.Text(("-= Starting =- "), align="center"), urwid.Text(("-= Starting =- "), align=urwid.CENTER),
]) ])
self.widget = urwid.Filler(intro) self.widget = urwid.Filler(intro)

View File

@@ -10,7 +10,7 @@ class GuideDisplayShortcuts():
self.app = app self.app = app
g = app.ui.glyphs g = app.ui.glyphs
self.widget = urwid.AttrMap(urwid.Padding(urwid.Text(""), align="left"), "shortcutbar") self.widget = urwid.AttrMap(urwid.Padding(urwid.Text(""), align=urwid.LEFT), "shortcutbar")
class ListEntry(urwid.Text): class ListEntry(urwid.Text):
_selectable = True _selectable = True
@@ -75,7 +75,7 @@ class GuideEntry(urwid.WidgetWrap):
style = "topic_list_normal" style = "topic_list_normal"
focus_style = "list_focus" focus_style = "list_focus"
self.display_widget = urwid.AttrMap(widget, style, focus_style) self.display_widget = urwid.AttrMap(widget, style, focus_style)
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def display_topic(self, event, topic): def display_topic(self, event, topic):
markup = TOPICS[topic] markup = TOPICS[topic]
@@ -125,12 +125,12 @@ class TopicList(urwid.WidgetWrap):
highlight_offFocus="list_off_focus" highlight_offFocus="list_off_focus"
) )
urwid.WidgetWrap.__init__(self, urwid.LineBox(self.ilb, title="Topics")) super().__init__(urwid.LineBox(self.ilb, title="Topics"))
def keypress(self, size, key): def keypress(self, size, key):
if key == "up" and (self.ilb.first_item_is_selected()): if key == "up" and (self.ilb.first_item_is_selected()):
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
return super(TopicList, self).keypress(size, key) return super(TopicList, self).keypress(size, key)
@@ -141,16 +141,16 @@ class GuideDisplay():
self.app = app self.app = app
g = self.app.ui.glyphs g = self.app.ui.glyphs
topic_text = urwid.Text("\n No topic selected", align="left") topic_text = urwid.Text("\n No topic selected", align=urwid.LEFT)
self.left_area = TopicList(self.app, self) self.left_area = TopicList(self.app, self)
self.right_area = urwid.LineBox(urwid.Filler(topic_text, "top")) self.right_area = urwid.LineBox(urwid.Filler(topic_text, urwid.TOP))
self.columns = urwid.Columns( self.columns = urwid.Columns(
[ [
("weight", GuideDisplay.list_width, self.left_area), (urwid.WEIGHT, GuideDisplay.list_width, self.left_area),
("weight", 1-GuideDisplay.list_width, self.right_area) (urwid.WEIGHT, 1-GuideDisplay.list_width, self.right_area)
], ],
dividechars=0, focus_column=0 dividechars=0, focus_column=0
) )
@@ -163,9 +163,8 @@ class GuideDisplay():
entry.display_topic(entry.display_topic, entry.topic_name) entry.display_topic(entry.display_topic, entry.topic_name)
def set_content_widgets(self, new_content): def set_content_widgets(self, new_content):
options = self.columns.options(width_type="weight", width_amount=1-GuideDisplay.list_width) options = self.columns.options(width_type=urwid.WEIGHT, width_amount=1-GuideDisplay.list_width, box_widget=True)
pile = urwid.Pile(new_content) pile = urwid.Pile(new_content)
#content = urwid.LineBox(urwid.Filler(pile, "top"))
content = urwid.LineBox(urwid.AttrMap(ScrollBar(Scrollable(pile), thumb_char="\u2503", trough_char=" "), "scrollbar")) content = urwid.LineBox(urwid.AttrMap(ScrollBar(Scrollable(pile), thumb_char="\u2503", trough_char=" "), "scrollbar"))
self.columns.contents[1] = (content, options) self.columns.contents[1] = (content, options)
@@ -217,6 +216,15 @@ The different sections of the program has a number of keyboard shortcuts mapped,
- Ctrl-W Close conversation - Ctrl-W Close conversation
>>`!Network Window`! >>`!Network Window`!
>>>Browser
- Ctrl-D Back
- Ctrl-F Forward
- Ctrl-R Reload page
- Ctrl-U Open URL entry dialog
- Ctrl-S Save connected node
- Ctrl-G Toggle fullscreen browser window
- Ctrl-W Disconnect from node
>>>Announce Stream >>>Announce Stream
- Ctrl-L Switch to Known Nodes list - Ctrl-L Switch to Known Nodes list
- Ctrl-X Delete selected announce - Ctrl-X Delete selected announce
@@ -227,14 +235,10 @@ The different sections of the program has a number of keyboard shortcuts mapped,
- Ctrl-X Delete selected node entry - Ctrl-X Delete selected node entry
- Ctrl-P Display peered LXMF Propagation Nodes - Ctrl-P Display peered LXMF Propagation Nodes
>>>Browser >>>Peered LXMF Propagation Nodes
- Ctrl-D Back - Ctrl-L Switch to Announce Stream or Known Nodes
- Ctrl-F Forward - Ctrl-X Break peering with selected node entry
- Ctrl-R Reload page - Ctrl-R Request immediate delivery sync of unhandled LXMs
- Ctrl-U Open URL entry dialog
- Ctrl-S Save connected node
- Ctrl-G Toggle fullscreen browser window
- Ctrl-W Disconnect from node
''' '''
TOPIC_CONCEPTS = '''>Concepts and Terminology TOPIC_CONCEPTS = '''>Concepts and Terminology
@@ -302,11 +306,13 @@ By default, no content is defined, apart from a short placeholder home page. To
>>Distributed Message Store >>Distributed Message Store
All nodes on the network automatically form a distributed message store that allows users to exchange messages, even when they are not available at the same time. All nodes on the network will automatically participate in a distributed message store that allows users to exchange messages, even when they are not connected to the network at the same time.
When Nomad Network is configured to host a node, it also configures itself as an LXMF Propagation Node, and automatically discovers and peers with other propagation nodes on the network. This process is completely automatic and requires no configuration from the node operator. When Nomad Network is configured to host a node, by default it also configures itself as an LXMF Propagation Node, and automatically discovers and peers with other propagation nodes on the network. This process is completely automatic and requires no configuration from the node operator.
To view LXMF Propagation nodes that are currently peered with your node, go to the `![ Network ]`! part of the program and press `!Ctrl-P`!. If there is already an abundance of Propagation Nodes on the network, or the operator simply wishes to host a pageserving-only node, Propagation Node hosting can be disabled in the configuration file.
To view LXMF Propagation nodes that are currently peered with your node, go to the `![ Network ]`! part of the program and press `!Ctrl-P`!. In the list of peered Propagation Nodes, it is possible to break peering with a node by pressing `!Ctrl-X`!. It is also possible to request an immediate delivery sync of all unhandled messages for a node, by pressing `!Ctrl-R`!.
The distributed message store is resilient to intermittency, and will remain functional as long as at least one node remains on the network. Nodes that were offline for a time will automatically be synced up to date when they regain connectivity. The distributed message store is resilient to intermittency, and will remain functional as long as at least one node remains on the network. Nodes that were offline for a time will automatically be synced up to date when they regain connectivity.
@@ -391,7 +397,7 @@ You're currently located in the guide section of the program. I'm sorry I had to
To get the most out of Nomad Network, you will need a terminal that supports UTF-8 and at least 256 colors, ideally true-color. If your terminal supports true-color, you can go to the `![ Config ]`! menu item, launch the editor and change the configuration. To get the most out of Nomad Network, you will need a terminal that supports UTF-8 and at least 256 colors, ideally true-color. If your terminal supports true-color, you can go to the `![ Config ]`! menu item, launch the editor and change the configuration.
It is recommended to use a terminal size of at least 122x32. Nomad Network will work with smaller terminal sizes, but the interface might feel a bit cramped. It is recommended to use a terminal size of at least 135x32. Nomad Network will work with smaller terminal sizes, but the interface might feel a bit cramped.
If you don't already have a Nerd Font installed (see https://www.nerdfonts.com/), I also highly recommend to do so, since it will greatly expand the amount of glyphs, icons and graphics that Nomad Network can use. Once you have your terminal set up with a Nerd Font, go to the `![ Config ]`! menu item and enable Nerd Fonts in the configuration instead of normal unicode glyphs. If you don't already have a Nerd Font installed (see https://www.nerdfonts.com/), I also highly recommend to do so, since it will greatly expand the amount of glyphs, icons and graphics that Nomad Network can use. Once you have your terminal set up with a Nerd Font, go to the `![ Config ]`! menu item and enable Nerd Fonts in the configuration instead of normal unicode glyphs.
@@ -614,12 +620,30 @@ Determines how often, in minutes, your node is announced on the network. Default
Determines where the node server will look for hosted pages. Must be a readable filesystem path. Determines where the node server will look for hosted pages. Must be a readable filesystem path.
< <
>>>
`!page_refresh_interval = 0`!
>>>>
Determines the interval in minutes for rescanning the hosted pages path. By default, this option is disabled, and the pages path will only be scanned on startup.
<
>>> >>>
`!files_path = ~/.nomadnetwork/storage/files`! `!files_path = ~/.nomadnetwork/storage/files`!
>>>> >>>>
Determines where the node server will look for downloadable files. Must be a readable filesystem path. Determines where the node server will look for downloadable files. Must be a readable filesystem path.
< <
>>>
`!file_refresh_interval = 0`!
>>>>
Determines the interval in minutes for rescanning the hosted files path. By default, this option is disabled, and the files path will only be scanned on startup.
<
>>>
`!disable_propagation = no`!
>>>>
By default, when Nomad Network is hosting a node, it will also run an LXMF propagation node. If there is already a large amount of propagation nodes on the network, or you simply want to run a pageserving-only node, you can disable running a propagation node.
<
>>> >>>
`!message_storage_limit = 2000`! `!message_storage_limit = 2000`!
>>>> >>>>
@@ -1131,7 +1155,7 @@ To display literal content, for example source-code, or blocks of text that shou
`= `=
''' '''
TOPIC_MARKUP += TOPIC_MARKUP.replace("`=", "\\`=") + "[ micron source for document goes here, we don't want infinite recursion now, do we? ]\n\\`=" TOPIC_MARKUP += TOPIC_MARKUP.replace("`=", "\\`=") + "[ micron source for document goes here, we don't want infinite recursion now, do we? ]\n\\`="
TOPIC_MARKUP += "\n`=\n\n>Closing Remarks\n\nIf you made it all the way here, you should be well equipped to write documents, pages and applications using micron and Nomad Network. Thank you for staying with me.\n\n`c\U0001F332\n" TOPIC_MARKUP += "\n`=\n\n>Closing Remarks\n\nIf you made it all the way here, you should be well equipped to write documents, pages and applications using micron and Nomad Network. Thank you for staying with me.\n"
TOPICS = { TOPICS = {

View File

@@ -10,13 +10,23 @@ class LogDisplayShortcuts():
class LogDisplay(): class LogDisplay():
def __init__(self, app): def __init__(self, app):
import urwid
self.app = app self.app = app
self.log_term = LogTerminal(self.app) self.log_term = None
self.shortcuts_display = LogDisplayShortcuts(self.app) self.shortcuts_display = LogDisplayShortcuts(self.app)
self.widget = None
def show(self):
if self.log_term == None:
self.log_term = LogTerminal(self.app)
self.widget = urwid.LineBox(self.log_term) self.widget = urwid.LineBox(self.log_term)
def kill(self):
if self.log_term != None:
self.log_term.terminate()
self.log_term = None
self.widget = None
def shortcuts(self): def shortcuts(self):
return self.shortcuts_display return self.shortcuts_display
@@ -26,13 +36,17 @@ class LogTerminal(urwid.WidgetWrap):
self.log_term = urwid.Terminal( self.log_term = urwid.Terminal(
("tail", "-fn50", self.app.logfilepath), ("tail", "-fn50", self.app.logfilepath),
encoding='utf-8', encoding='utf-8',
escape_sequence="up" escape_sequence="up",
main_loop=self.app.ui.loop,
) )
urwid.WidgetWrap.__init__(self, self.log_term) super().__init__(self.log_term)
def terminate(self):
self.log_term.terminate()
def keypress(self, size, key): def keypress(self, size, key):
if key == "up": if key == "up":
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
return super(LogTerminal, self).keypress(size, key) return super(LogTerminal, self).keypress(size, key)

View File

@@ -1,5 +1,4 @@
import RNS import RNS
import time
from .Network import * from .Network import *
from .Conversations import * from .Conversations import *
@@ -116,6 +115,7 @@ class MainDisplay():
def show_log(self, user_data): def show_log(self, user_data):
self.sub_displays.active_display = self.sub_displays.log_display self.sub_displays.active_display = self.sub_displays.log_display
self.sub_displays.log_display.show()
self.update_active_sub_display() self.update_active_sub_display()
def show_guide(self, user_data): def show_guide(self, user_data):
@@ -125,6 +125,8 @@ class MainDisplay():
def update_active_sub_display(self): def update_active_sub_display(self):
self.frame.contents["body"] = (self.sub_displays.active().widget, None) self.frame.contents["body"] = (self.sub_displays.active().widget, None)
self.update_active_shortcuts() self.update_active_shortcuts()
if self.sub_displays.active_display != self.sub_displays.log_display:
self.sub_displays.log_display.kill()
def update_active_shortcuts(self): def update_active_shortcuts(self):
self.frame.contents["footer"] = (self.sub_displays.active().shortcuts().widget, None) self.frame.contents["footer"] = (self.sub_displays.active().shortcuts().widget, None)
@@ -155,7 +157,7 @@ class MainDisplay():
class MenuColumns(urwid.Columns): class MenuColumns(urwid.Columns):
def keypress(self, size, key): def keypress(self, size, key):
if key == "tab" or key == "down": if key == "tab" or key == "down":
self.handler.frame.set_focus("body") self.handler.frame.focus_position = "body"
return super(MenuColumns, self).keypress(size, key) return super(MenuColumns, self).keypress(size, key)
@@ -169,7 +171,7 @@ class MenuDisplay():
self.menu_indicator = urwid.Text("") self.menu_indicator = urwid.Text("")
menu_text = ("pack", self.menu_indicator) menu_text = (urwid.PACK, self.menu_indicator)
button_network = (11, MenuButton("Network", on_press=handler.show_network)) button_network = (11, MenuButton("Network", on_press=handler.show_network))
button_conversations = (17, MenuButton("Conversations", on_press=handler.show_conversations)) button_conversations = (17, MenuButton("Conversations", on_press=handler.show_conversations))
button_directory = (13, MenuButton("Directory", on_press=handler.show_directory)) button_directory = (13, MenuButton("Directory", on_press=handler.show_directory))

View File

@@ -15,7 +15,7 @@ class MapDisplay():
]) ])
self.shortcuts_display = MapDisplayShortcuts(self.app) self.shortcuts_display = MapDisplayShortcuts(self.app)
self.widget = urwid.Filler(pile, 'top') self.widget = urwid.Filler(pile, urwid.TOP)
def shortcuts(self): def shortcuts(self):
return self.shortcuts_display return self.shortcuts_display

View File

@@ -4,7 +4,6 @@ import random
import time import time
from urwid.util import is_mouse_press from urwid.util import is_mouse_press
from urwid.text_layout import calc_coords from urwid.text_layout import calc_coords
import re
DEFAULT_FG_DARK = "ddd" DEFAULT_FG_DARK = "ddd"
DEFAULT_FG_LIGHT = "222" DEFAULT_FG_LIGHT = "222"
@@ -162,7 +161,7 @@ def parse_line(line, state, url_delegate):
else: else:
tw = urwid.Text(o, align=state["align"]) tw = urwid.Text(o, align=state["align"])
widgets.append(("pack", tw)) widgets.append((urwid.PACK, tw))
else: else:
if o["type"] == "field": if o["type"] == "field":
fw = o["width"] fw = o["width"]
@@ -608,7 +607,7 @@ class LinkSpec(urwid.AttrSpec):
self.link_target = link_target self.link_target = link_target
self.link_fields = None self.link_fields = None
urwid.AttrSpec.__init__(self, orig_spec.foreground, orig_spec.background) super().__init__(orig_spec.foreground, orig_spec.background)
class LinkableText(urwid.Text): class LinkableText(urwid.Text):
@@ -618,7 +617,7 @@ class LinkableText(urwid.Text):
signals = ["click", "change"] signals = ["click", "change"]
def __init__(self, text, align=None, cursor_position=0, delegate=None): def __init__(self, text, align=None, cursor_position=0, delegate=None):
self.__super.__init__(text, align=align) super().__init__(text, align=align)
self.delegate = delegate self.delegate = delegate
self._cursor_position = 0 self._cursor_position = 0
self.key_timeout = 3 self.key_timeout = 3
@@ -729,7 +728,7 @@ class LinkableText(urwid.Text):
def render(self, size, focus=False): def render(self, size, focus=False):
now = time.time() now = time.time()
c = self.__super.render(size, focus) c = super().render(size, focus)
if focus and (self.delegate == None or now < self.delegate.last_keypress+self.key_timeout): if focus and (self.delegate == None or now < self.delegate.last_keypress+self.key_timeout):
c = urwid.CompositeCanvas(c) c = urwid.CompositeCanvas(c)

View File

@@ -2,6 +2,7 @@ import RNS
import urwid import urwid
import nomadnet import nomadnet
import time import time
import threading
from datetime import datetime from datetime import datetime
from nomadnet.Directory import DirectoryEntry from nomadnet.Directory import DirectoryEntry
from nomadnet.vendor.additional_urwid_widgets import IndicativeListBox, MODIFIER_KEY from nomadnet.vendor.additional_urwid_widgets import IndicativeListBox, MODIFIER_KEY
@@ -51,7 +52,7 @@ class ListEntry(urwid.Text):
class AnnounceInfo(urwid.WidgetWrap): class AnnounceInfo(urwid.WidgetWrap):
def keypress(self, size, key): def keypress(self, size, key):
if key == "esc": if key == "esc":
options = self.parent.left_pile.options(height_type="weight", height_amount=1) options = self.parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.parent.left_pile.contents[0] = (self.parent.announce_stream_display, options) self.parent.left_pile.contents[0] = (self.parent.announce_stream_display, options)
else: else:
return super(AnnounceInfo, self).keypress(size, key) return super(AnnounceInfo, self).keypress(size, key)
@@ -114,7 +115,7 @@ class AnnounceInfo(urwid.WidgetWrap):
style = "list_untrusted" style = "list_untrusted"
def show_announce_stream(sender): def show_announce_stream(sender):
options = self.parent.left_pile.options(height_type="weight", height_amount=1) options = self.parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.parent.left_pile.contents[0] = (self.parent.announce_stream_display, options) self.parent.left_pile.contents[0] = (self.parent.announce_stream_display, options)
def connect(sender): def connect(sender):
@@ -186,54 +187,66 @@ class AnnounceInfo(urwid.WidgetWrap):
RNS.log("Error while setting active propagation node from announce. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Error while setting active propagation node from announce. The contained exception was: "+str(e), RNS.LOG_ERROR)
if is_node: if is_node:
type_button = ("weight", 0.45, urwid.Button("Connect", on_press=connect)) type_button = (urwid.WEIGHT, 0.45, urwid.Button("Connect", on_press=connect))
msg_button = ("weight", 0.45, urwid.Button("Msg Op", on_press=msg_op)) msg_button = (urwid.WEIGHT, 0.45, urwid.Button("Msg Op", on_press=msg_op))
save_button = ("weight", 0.45, urwid.Button("Save", on_press=save_node)) save_button = (urwid.WEIGHT, 0.45, urwid.Button("Save", on_press=save_node))
elif is_pn: elif is_pn:
type_button = ("weight", 0.45, urwid.Button("Use as default", on_press=use_pn)) type_button = (urwid.WEIGHT, 0.45, urwid.Button("Use as default", on_press=use_pn))
save_button = None save_button = None
else: else:
type_button = ("weight", 0.45, urwid.Button("Converse", on_press=converse)) type_button = (urwid.WEIGHT, 0.45, urwid.Button("Converse", on_press=converse))
save_button = None save_button = None
if is_node: if is_node:
button_columns = urwid.Columns([("weight", 0.45, urwid.Button("Back", on_press=show_announce_stream)), ("weight", 0.1, urwid.Text("")), type_button, ("weight", 0.1, urwid.Text("")), msg_button, ("weight", 0.1, urwid.Text("")), save_button]) button_columns = urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Back", on_press=show_announce_stream)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
type_button,
(urwid.WEIGHT, 0.1, urwid.Text("")),
msg_button,
(urwid.WEIGHT, 0.1, urwid.Text("")),
save_button,
])
else: else:
button_columns = urwid.Columns([("weight", 0.45, urwid.Button("Back", on_press=show_announce_stream)), ("weight", 0.1, urwid.Text("")), type_button]) button_columns = urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Back", on_press=show_announce_stream)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
type_button,
])
pile_widgets = [] pile_widgets = []
if is_pn: if is_pn:
pile_widgets = [ pile_widgets = [
urwid.Text("Time : "+ts_string, align="left"), urwid.Text("Time : "+ts_string, align=urwid.LEFT),
urwid.Text("Addr : "+addr_str, align="left"), urwid.Text("Addr : "+addr_str, align=urwid.LEFT),
urwid.Text("Type : "+type_string, align="left"), urwid.Text("Type : "+type_string, align=urwid.LEFT),
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
button_columns button_columns
] ]
else: else:
pile_widgets = [ pile_widgets = [
urwid.Text("Time : "+ts_string, align="left"), urwid.Text("Time : "+ts_string, align=urwid.LEFT),
urwid.Text("Addr : "+addr_str, align="left"), urwid.Text("Addr : "+addr_str, align=urwid.LEFT),
urwid.Text("Type : "+type_string, align="left"), urwid.Text("Type : "+type_string, align=urwid.LEFT),
urwid.Text("Name : "+display_str, align="left"), urwid.Text("Name : "+display_str, align=urwid.LEFT),
urwid.Text(["Trust : ", (style, trust_str)], align="left"), urwid.Text(["Trust : ", (style, trust_str)], align=urwid.LEFT),
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
urwid.Text(["Announce Data: \n", (data_style, data_str)], align="left"), urwid.Text(["Announce Data: \n", (data_style, data_str)], align=urwid.LEFT),
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
button_columns button_columns
] ]
if is_node: if is_node:
operator_entry = urwid.Text("Oprtr : "+op_str, align="left") operator_entry = urwid.Text("Oprtr : "+op_str, align=urwid.LEFT)
pile_widgets.insert(4, operator_entry) pile_widgets.insert(4, operator_entry)
pile = urwid.Pile(pile_widgets) pile = urwid.Pile(pile_widgets)
self.display_widget = urwid.Filler(pile, valign="top", height="pack") self.display_widget = urwid.Filler(pile, valign=urwid.TOP, height=urwid.PACK)
urwid.WidgetWrap.__init__(self, urwid.LineBox(self.display_widget, title="Announce Info")) super().__init__(urwid.LineBox(self.display_widget, title="Announce Info"))
class AnnounceStreamEntry(urwid.WidgetWrap): class AnnounceStreamEntry(urwid.WidgetWrap):
@@ -295,13 +308,13 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
urwid.connect_signal(widget, "click", self.display_announce, announce) urwid.connect_signal(widget, "click", self.display_announce, announce)
self.display_widget = urwid.AttrMap(widget, style, focus_style) self.display_widget = urwid.AttrMap(widget, style, focus_style)
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def display_announce(self, event, announce): def display_announce(self, event, announce):
try: try:
parent = self.app.ui.main_display.sub_displays.network_display parent = self.app.ui.main_display.sub_displays.network_display
info_widget = AnnounceInfo(announce, parent, self.app) info_widget = AnnounceInfo(announce, parent, self.app)
options = parent.left_pile.options(height_type="weight", height_amount=1) options = parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
parent.left_pile.contents[0] = (info_widget, options) parent.left_pile.contents[0] = (info_widget, options)
except KeyError as e: except KeyError as e:
@@ -312,17 +325,21 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
def close_req(sender): def close_req(sender):
self.delegate.parent.close_list_dialogs() self.delegate.parent.close_list_dialogs()
dialog_pile.contents[0] = (urwid.Text("\nKeys requested from network\n", align="center"), options) dialog_pile.contents[0] = (urwid.Text("\nKeys requested from network\n", align=urwid.CENTER), options)
RNS.Transport.request_path(announce[1]) RNS.Transport.request_path(announce[1])
confirmed_button = urwid.Button("Request keys", on_press=confirmed) confirmed_button = urwid.Button("Request keys", on_press=confirmed)
dialog_pile = urwid.Pile([ dialog_pile = urwid.Pile([
urwid.Text("The keys for the announced destination could not be recalled. You can wait for an announce to arrive, or request the keys from the network.\n", align="center"), urwid.Text(
"The keys for the announced destination could not be recalled. "
"You can wait for an announce to arrive, or request the keys from the network.\n",
align=urwid.CENTER,
),
urwid.Columns([ urwid.Columns([
("weight", 0.45, confirmed_button), (urwid.WEIGHT, 0.45, confirmed_button),
("weight", 0.1, urwid.Text("")), (urwid.WEIGHT, 0.1, urwid.Text("")),
("weight", 0.45, urwid.Button("Close", on_press=dismiss_dialog)), (urwid.WEIGHT, 0.45, urwid.Button("Close", on_press=dismiss_dialog)),
]) ])
]) ])
@@ -334,9 +351,18 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
dialog.delegate = self.delegate.parent dialog.delegate = self.delegate.parent
bottom = self.delegate bottom = self.delegate
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
options = self.delegate.parent.left_pile.options("weight", 1) options = self.delegate.parent.left_pile.options(urwid.WEIGHT, 1)
self.delegate.parent.left_pile.contents[0] = (overlay, options) self.delegate.parent.left_pile.contents[0] = (overlay, options)
def timestamp(self): def timestamp(self):
@@ -364,11 +390,11 @@ class AnnounceStream(urwid.WidgetWrap):
) )
self.display_widget = self.ilb self.display_widget = self.ilb
urwid.WidgetWrap.__init__(self, urwid.LineBox(self.display_widget, title="Announce Stream")) super().__init__(urwid.LineBox(self.display_widget, title="Announce Stream"))
def keypress(self, size, key): def keypress(self, size, key):
if key == "up" and (self.no_content or self.ilb.first_item_is_selected()): if key == "up" and (self.no_content or self.ilb.first_item_is_selected()):
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
elif key == "ctrl x": elif key == "ctrl x":
self.delete_selected_entry() self.delete_selected_entry()
@@ -463,7 +489,7 @@ class ListDialogLineBox(urwid.LineBox):
class KnownNodeInfo(urwid.WidgetWrap): class KnownNodeInfo(urwid.WidgetWrap):
def keypress(self, size, key): def keypress(self, size, key):
if key == "esc": if key == "esc":
options = self.parent.left_pile.options(height_type="weight", height_amount=1) options = self.parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.parent.left_pile.contents[0] = (self.parent.known_nodes_display, options) self.parent.left_pile.contents[0] = (self.parent.known_nodes_display, options)
else: else:
return super(KnownNodeInfo, self).keypress(size, key) return super(KnownNodeInfo, self).keypress(size, key)
@@ -480,6 +506,12 @@ class KnownNodeInfo(urwid.WidgetWrap):
trust_level = self.app.directory.trust_level(source_hash) trust_level = self.app.directory.trust_level(source_hash)
trust_str = "" trust_str = ""
node_entry = self.app.directory.find(source_hash) node_entry = self.app.directory.find(source_hash)
sort_str = self.app.directory.sort_rank(source_hash)
if sort_str == None:
sort_str = "None"
else:
sort_str = str(sort_str)
if node_entry == None: if node_entry == None:
display_str = self.app.directory.simplest_display_str(source_hash) display_str = self.app.directory.simplest_display_str(source_hash)
else: else:
@@ -540,6 +572,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
r_trusted = urwid.RadioButton(trust_button_group, "Trusted", state=trusted_selected) r_trusted = urwid.RadioButton(trust_button_group, "Trusted", state=trusted_selected)
e_name = urwid.Edit(caption="Name : ",edit_text=display_str) e_name = urwid.Edit(caption="Name : ",edit_text=display_str)
e_sort = urwid.Edit(caption="Sort Rank : ",edit_text=sort_str)
node_ident = RNS.Identity.recall(source_hash) node_ident = RNS.Identity.recall(source_hash)
op_hash = None op_hash = None
@@ -551,7 +584,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
op_str = "Unknown" op_str = "Unknown"
def show_known_nodes(sender): def show_known_nodes(sender):
options = self.parent.left_pile.options(height_type="weight", height_amount=1) options = self.parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.parent.left_pile.contents[0] = (self.parent.known_nodes_display, options) self.parent.left_pile.contents[0] = (self.parent.known_nodes_display, options)
def connect(sender): def connect(sender):
@@ -586,7 +619,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
def ident_change(sender, userdata): def ident_change(sender, userdata):
pass pass
propagation_node_checkbox = urwid.CheckBox("Use as default propagation node", state=(self.app.get_user_selected_propagation_node() == source_hash), on_state_change=pn_change) propagation_node_checkbox = urwid.CheckBox("Use as default propagation node", state=(self.app.get_user_selected_propagation_node() == pn_hash), on_state_change=pn_change)
connect_identify_checkbox = urwid.CheckBox("Identify when connecting", state=self.app.directory.should_identify_on_connect(source_hash), on_state_change=ident_change) connect_identify_checkbox = urwid.CheckBox("Identify when connecting", state=self.app.directory.should_identify_on_connect(source_hash), on_state_change=ident_change)
def save_node(sender): def save_node(sender):
@@ -604,8 +637,16 @@ class KnownNodeInfo(urwid.WidgetWrap):
trust_level = DirectoryEntry.TRUSTED trust_level = DirectoryEntry.TRUSTED
display_str = e_name.get_edit_text() display_str = e_name.get_edit_text()
sort_rank = e_sort.get_edit_text()
try:
if int(sort_rank) >= 0:
sort_rank = int(sort_rank)
else:
sort_rank = None
except:
sort_rank = None
node_entry = DirectoryEntry(source_hash, display_name=display_str, trust_level=trust_level, hosts_node=True, identify_on_connect=connect_identify_checkbox.get_state()) node_entry = DirectoryEntry(source_hash, display_name=display_str, trust_level=trust_level, hosts_node=True, identify_on_connect=connect_identify_checkbox.get_state(), sort_rank=sort_rank)
self.app.directory.remember(node_entry) self.app.directory.remember(node_entry)
self.app.ui.main_display.sub_displays.network_display.directory_change_callback() self.app.ui.main_display.sub_displays.network_display.directory_change_callback()
@@ -614,20 +655,21 @@ class KnownNodeInfo(urwid.WidgetWrap):
show_known_nodes(None) show_known_nodes(None)
back_button = ("weight", 0.2, urwid.Button("Back", on_press=show_known_nodes)) back_button = (urwid.WEIGHT, 0.2, urwid.Button("Back", on_press=show_known_nodes))
connect_button = ("weight", 0.2, urwid.Button("Connect", on_press=connect)) connect_button = (urwid.WEIGHT, 0.2, urwid.Button("Connect", on_press=connect))
save_button = ("weight", 0.2, urwid.Button("Save", on_press=save_node)) save_button = (urwid.WEIGHT, 0.2, urwid.Button("Save", on_press=save_node))
msg_button = ("weight", 0.2, urwid.Button("Msg Op", on_press=msg_op)) msg_button = (urwid.WEIGHT, 0.2, urwid.Button("Msg Op", on_press=msg_op))
bdiv = ("weight", 0.02, urwid.Text("")) bdiv = (urwid.WEIGHT, 0.02, urwid.Text(""))
button_columns = urwid.Columns([back_button, bdiv, connect_button, bdiv, msg_button, bdiv, save_button]) button_columns = urwid.Columns([back_button, bdiv, connect_button, bdiv, msg_button, bdiv, save_button])
pile_widgets = [ pile_widgets = [
urwid.Text("Type : "+type_string, align="left"), urwid.Text("Type : "+type_string, align=urwid.LEFT),
e_name, e_name,
urwid.Text("Node Addr : "+addr_str, align="left"), urwid.Text("Node Addr : "+addr_str, align=urwid.LEFT),
e_sort,
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
urwid.Text(lxmf_addr_str, align="center"), urwid.Text(lxmf_addr_str, align=urwid.CENTER),
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
propagation_node_checkbox, propagation_node_checkbox,
connect_identify_checkbox, connect_identify_checkbox,
@@ -639,7 +681,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
button_columns button_columns
] ]
operator_entry = urwid.Text("Operator : "+op_str, align="left") operator_entry = urwid.Text("Operator : "+op_str, align=urwid.LEFT)
pile_widgets.insert(3, operator_entry) pile_widgets.insert(3, operator_entry)
hops = RNS.Transport.hops_to(source_hash) hops = RNS.Transport.hops_to(source_hash)
@@ -653,7 +695,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
else: else:
hops_str = "Unknown" hops_str = "Unknown"
operator_entry = urwid.Text("Distance : "+hops_str, align="left") operator_entry = urwid.Text("Distance : "+hops_str, align=urwid.LEFT)
pile_widgets.insert(4, operator_entry) pile_widgets.insert(4, operator_entry)
pile = urwid.Pile(pile_widgets) pile = urwid.Pile(pile_widgets)
@@ -662,9 +704,9 @@ class KnownNodeInfo(urwid.WidgetWrap):
button_columns.focus_position = 0 button_columns.focus_position = 0
self.display_widget = urwid.Filler(pile, valign="top", height="pack") self.display_widget = urwid.Filler(pile, valign=urwid.TOP, height=urwid.PACK)
urwid.WidgetWrap.__init__(self, urwid.LineBox(self.display_widget, title="Node Info")) super().__init__(urwid.LineBox(self.display_widget, title="Node Info"))
# Yes, this is weird. There is a bug in Urwid/ILB that causes # Yes, this is weird. There is a bug in Urwid/ILB that causes
@@ -677,9 +719,9 @@ class ExceptionHandlingListBox(IndicativeListBox):
except Exception as e: except Exception as e:
if key == "up": if key == "up":
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
elif key == "down": elif key == "down":
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.sub_displays.network_display.left_pile.set_focus(1) nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.sub_displays.network_display.left_pile.focus_position = 1
else: else:
RNS.log("An error occurred while processing an interface event. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("An error occurred while processing an interface event. The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -706,14 +748,23 @@ class KnownNodes(urwid.WidgetWrap):
else: else:
self.no_content = True self.no_content = True
widget_style = "inactive_text" widget_style = "inactive_text"
self.pile = urwid.Pile([urwid.Text(("warning_text", g["info"]+"\n"), align="center"), SelectText(("warning_text", "Currently, no nodes are saved\n\nCtrl+L to view the announce stream\n\n"), align="center")]) self.pile = urwid.Pile([
self.display_widget = urwid.Filler(self.pile, valign="top", height="pack") urwid.Text(("warning_text", g["info"]+"\n"), align=urwid.CENTER),
SelectText(
(
"warning_text",
"Currently, no nodes are saved\n\nCtrl+L to view the announce stream\n\n",
),
align=urwid.CENTER,
),
])
self.display_widget = urwid.Filler(self.pile, valign=urwid.TOP, height=urwid.PACK)
urwid.WidgetWrap.__init__(self, urwid.AttrMap(urwid.LineBox(self.display_widget, title="Saved Nodes"), widget_style)) super().__init__(urwid.AttrMap(urwid.LineBox(self.display_widget, title="Saved Nodes"), widget_style))
def keypress(self, size, key): def keypress(self, size, key):
if key == "up" and (self.no_content or self.ilb.first_item_is_selected()): if key == "up" and (self.no_content or self.ilb.first_item_is_selected()):
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
elif key == "ctrl x": elif key == "ctrl x":
self.delete_selected_entry() self.delete_selected_entry()
@@ -740,27 +791,27 @@ class KnownNodes(urwid.WidgetWrap):
def show_info(sender): def show_info(sender):
info_widget = KnownNodeInfo(source_hash) info_widget = KnownNodeInfo(source_hash)
options = parent.left_pile.options(height_type="weight", height_amount=1) options = parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
parent.left_pile.contents[0] = (info_widget, options) parent.left_pile.contents[0] = (info_widget, options)
dialog = ListDialogLineBox( dialog = ListDialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("Connect to node\n"+self.app.directory.simplest_display_str(source_hash)+"\n", align="center"), urwid.Text("Connect to node\n"+self.app.directory.simplest_display_str(source_hash)+"\n", align=urwid.CENTER),
urwid.Columns([ urwid.Columns([
("weight", 0.45, urwid.Button("Yes", on_press=confirmed)), (urwid.WEIGHT, 0.45, urwid.Button("Yes", on_press=confirmed)),
("weight", 0.1, urwid.Text("")), (urwid.WEIGHT, 0.1, urwid.Text("")),
("weight", 0.45, urwid.Button("No", on_press=dismiss_dialog)), (urwid.WEIGHT, 0.45, urwid.Button("No", on_press=dismiss_dialog)),
("weight", 0.1, urwid.Text("")), (urwid.WEIGHT, 0.1, urwid.Text("")),
("weight", 0.45, urwid.Button("Info", on_press=show_info))]) (urwid.WEIGHT, 0.45, urwid.Button("Info", on_press=show_info))])
]), title="?" ]), title="?"
) )
dialog.delegate = self.delegate dialog.delegate = self.delegate
bottom = self bottom = self
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=urwid.RELATIVE_100, valign=urwid.MIDDLE, height=urwid.PACK, left=2, right=2)
options = self.delegate.left_pile.options("weight", 1) options = self.delegate.left_pile.options(urwid.WEIGHT, 1)
self.delegate.left_pile.contents[0] = (overlay, options) self.delegate.left_pile.contents[0] = (overlay, options)
def delete_selected_entry(self): def delete_selected_entry(self):
@@ -779,16 +830,29 @@ class KnownNodes(urwid.WidgetWrap):
dialog = ListDialogLineBox( dialog = ListDialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("Delete Node\n"+self.app.directory.simplest_display_str(source_hash)+"\n", align="center"), urwid.Text("Delete Node\n"+self.app.directory.simplest_display_str(source_hash)+"\n", align=urwid.CENTER),
urwid.Columns([("weight", 0.45, urwid.Button("Yes", on_press=confirmed)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("No", on_press=dismiss_dialog))]) urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Yes", on_press=confirmed)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("No", on_press=dismiss_dialog)),
])
]), title="?" ]), title="?"
) )
dialog.delegate = self.delegate dialog.delegate = self.delegate
bottom = self bottom = self
overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=2, right=2) overlay = urwid.Overlay(
dialog,
bottom,
align=urwid.CENTER,
width=urwid.RELATIVE_100,
valign=urwid.MIDDLE,
height=urwid.PACK,
left=2,
right=2,
)
options = self.delegate.left_pile.options("weight", 1) options = self.delegate.left_pile.options(urwid.WEIGHT, 1)
self.delegate.left_pile.contents[0] = (overlay, options) self.delegate.left_pile.contents[0] = (overlay, options)
@@ -852,7 +916,7 @@ class NodeEntry(urwid.WidgetWrap):
self.display_widget = urwid.AttrMap(widget, style, focus_style) self.display_widget = urwid.AttrMap(widget, style, focus_style)
self.display_widget.source_hash = source_hash self.display_widget.source_hash = source_hash
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
class AnnounceTime(urwid.WidgetWrap): class AnnounceTime(urwid.WidgetWrap):
@@ -863,7 +927,7 @@ class AnnounceTime(urwid.WidgetWrap):
self.display_widget = urwid.Text("") self.display_widget = urwid.Text("")
self.update_time() self.update_time()
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def update_time(self): def update_time(self):
self.last_announce_string = "Never" self.last_announce_string = "Never"
@@ -895,7 +959,7 @@ class NodeAnnounceTime(urwid.WidgetWrap):
self.display_widget = urwid.Text("") self.display_widget = urwid.Text("")
self.update_time() self.update_time()
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def update_time(self): def update_time(self):
self.last_announce_string = "Never" self.last_announce_string = "Never"
@@ -926,7 +990,7 @@ class NodeActiveConnections(urwid.WidgetWrap):
self.display_widget = urwid.Text("") self.display_widget = urwid.Text("")
self.update_stat() self.update_stat()
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def update_stat(self): def update_stat(self):
self.stat_string = "None" self.stat_string = "None"
@@ -957,16 +1021,16 @@ class NodeStorageStats(urwid.WidgetWrap):
self.display_widget = urwid.Text("") self.display_widget = urwid.Text("")
self.update_stat() self.update_stat()
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def update_stat(self): def update_stat(self):
self.stat_string = "None" self.stat_string = "None"
if self.app.node != None: if self.app.node != None and not self.app.disable_propagation:
limit = self.app.message_router.message_storage_limit limit = self.app.message_router.message_storage_limit
used = self.app.message_router.message_storage_size() used = self.app.message_router.message_storage_size()
if limit != None: if limit != None and used != None:
pct = round((used/limit)*100, 1) pct = round((used/limit)*100, 1)
pct_str = str(pct)+"%, " pct_str = str(pct)+"%, "
limit_str = " of "+RNS.prettysize(limit) limit_str = " of "+RNS.prettysize(limit)
@@ -1000,7 +1064,7 @@ class NodeTotalConnections(urwid.WidgetWrap):
self.display_widget = urwid.Text("") self.display_widget = urwid.Text("")
self.update_stat() self.update_stat()
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def update_stat(self): def update_stat(self):
self.stat_string = "None" self.stat_string = "None"
@@ -1032,7 +1096,7 @@ class NodeTotalPages(urwid.WidgetWrap):
self.display_widget = urwid.Text("") self.display_widget = urwid.Text("")
self.update_stat() self.update_stat()
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def update_stat(self): def update_stat(self):
self.stat_string = "None" self.stat_string = "None"
@@ -1064,7 +1128,7 @@ class NodeTotalFiles(urwid.WidgetWrap):
self.display_widget = urwid.Text("") self.display_widget = urwid.Text("")
self.update_stat() self.update_stat()
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def update_stat(self): def update_stat(self):
self.stat_string = "None" self.stat_string = "None"
@@ -1113,45 +1177,45 @@ class LocalPeer(urwid.WidgetWrap):
dialog = DialogLineBox( dialog = DialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("\n\n\nSaved\n\n", align="center"), urwid.Text("\n\n\nSaved\n\n", align=urwid.CENTER),
urwid.Button("OK", on_press=dismiss_dialog) urwid.Button("OK", on_press=dismiss_dialog)
]), title=g["info"] ]), title=g["info"]
) )
dialog.delegate = self dialog.delegate = self
bottom = self bottom = self
#overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=4, right=4) #overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=urwid.RELATIVE_100, valign=urwid.MIDDLE, height=urwid.PACK, left=4, right=4)
overlay = dialog overlay = dialog
options = self.parent.left_pile.options(height_type="pack", height_amount=None) options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
self.dialog_open = True self.dialog_open = True
self.parent.left_pile.contents[1] = (overlay, options) self.parent.left_pile.contents[1] = (overlay, options)
def announce_query(sender): def announce_query(sender):
def dismiss_dialog(sender): def dismiss_dialog(sender):
self.dialog_open = False self.dialog_open = False
options = self.parent.left_pile.options(height_type="pack", height_amount=None) options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
self.parent.left_pile.contents[1] = (LocalPeer(self.app, self.parent), options) self.parent.left_pile.contents[1] = (LocalPeer(self.app, self.parent), options)
self.app.announce_now() self.app.announce_now()
dialog = DialogLineBox( dialog = DialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("\n\n\nAnnounce Sent\n\n\n", align="center"), urwid.Text("\n\n\nAnnounce Sent\n\n\n", align=urwid.CENTER),
urwid.Button("OK", on_press=dismiss_dialog) urwid.Button("OK", on_press=dismiss_dialog)
]), title=g["info"] ]), title=g["info"]
) )
dialog.delegate = self dialog.delegate = self
bottom = self bottom = self
#overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=4, right=4) #overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=urwid.RELATIVE_100, valign=urwid.MIDDLE, height=urwid.PACK, left=4, right=4)
overlay = dialog overlay = dialog
self.dialog_open = True self.dialog_open = True
options = self.parent.left_pile.options(height_type="pack", height_amount=None) options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
self.parent.left_pile.contents[1] = (overlay, options) self.parent.left_pile.contents[1] = (overlay, options)
def node_info_query(sender): def node_info_query(sender):
options = self.parent.left_pile.options(height_type="pack", height_amount=None) options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
self.parent.left_pile.contents[1] = (self.parent.node_info_display, options) self.parent.left_pile.contents[1] = (self.parent.node_info_display, options)
if LocalPeer.announce_timer == None: if LocalPeer.announce_timer == None:
@@ -1172,11 +1236,15 @@ class LocalPeer(urwid.WidgetWrap):
self.t_last_announce, self.t_last_announce,
announce_button, announce_button,
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
urwid.Columns([("weight", 0.45, urwid.Button("Save", on_press=save_query)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Node Info", on_press=node_info_query))]) urwid.Columns([
(urwid.WEIGHT, 0.45, urwid.Button("Save", on_press=save_query)),
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("Node Info", on_press=node_info_query)),
])
] ]
) )
urwid.WidgetWrap.__init__(self, urwid.LineBox(self.display_widget, title="Local Peer Info")) super().__init__(urwid.LineBox(self.display_widget, title="Local Peer Info"))
def start(self): def start(self):
self.t_last_announce.start() self.t_last_announce.start()
@@ -1200,7 +1268,7 @@ class NodeInfo(urwid.WidgetWrap):
widget_style = "" widget_style = ""
def show_peer_info(sender): def show_peer_info(sender):
options = self.parent.left_pile.options(height_type="pack", height_amount=None) options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
self.parent.left_pile.contents[1] = (LocalPeer(self.app, self.parent), options) self.parent.left_pile.contents[1] = (LocalPeer(self.app, self.parent), options)
if self.app.enable_node: if self.app.enable_node:
@@ -1224,25 +1292,25 @@ class NodeInfo(urwid.WidgetWrap):
def announce_query(sender): def announce_query(sender):
def dismiss_dialog(sender): def dismiss_dialog(sender):
self.dialog_open = False self.dialog_open = False
options = self.parent.left_pile.options(height_type="pack", height_amount=None) options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
self.parent.left_pile.contents[1] = (NodeInfo(self.app, self.parent), options) self.parent.left_pile.contents[1] = (NodeInfo(self.app, self.parent), options)
self.app.node.announce() self.app.node.announce()
dialog = DialogLineBox( dialog = DialogLineBox(
urwid.Pile([ urwid.Pile([
urwid.Text("\n\n\nAnnounce Sent\n\n", align="center"), urwid.Text("\n\n\nAnnounce Sent\n\n", align=urwid.CENTER),
urwid.Button("OK", on_press=dismiss_dialog) urwid.Button("OK", on_press=dismiss_dialog)
]), title=g["info"] ]), title=g["info"]
) )
dialog.delegate = self dialog.delegate = self
bottom = self bottom = self
#overlay = urwid.Overlay(dialog, bottom, align="center", width=("relative", 100), valign="middle", height="pack", left=4, right=4) #overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=urwid.RELATIVE_100, valign=urwid.MIDDLE, height=urwid.PACK, left=4, right=4)
overlay = dialog overlay = dialog
self.dialog_open = True self.dialog_open = True
options = self.parent.left_pile.options(height_type="pack", height_amount=None) options = self.parent.left_pile.options(height_type=urwid.PACK, height_amount=None)
self.parent.left_pile.contents[1] = (overlay, options) self.parent.left_pile.contents[1] = (overlay, options)
def connect_query(sender): def connect_query(sender):
@@ -1291,12 +1359,13 @@ class NodeInfo(urwid.WidgetWrap):
self.t_total_files.update_stat() self.t_total_files.update_stat()
lxmf_addr_str = g["sent"]+" LXMF Propagation Node Address is "+RNS.prettyhexrep(RNS.Destination.hash_from_name_and_identity("lxmf.propagation", self.app.node.destination.identity)) lxmf_addr_str = g["sent"]+" LXMF Propagation Node Address is "+RNS.prettyhexrep(RNS.Destination.hash_from_name_and_identity("lxmf.propagation", self.app.node.destination.identity))
e_lxmf = urwid.Text(lxmf_addr_str, align="center") e_lxmf = urwid.Text(lxmf_addr_str, align=urwid.CENTER)
announce_button = urwid.Button("Announce", on_press=announce_query) announce_button = urwid.Button("Announce", on_press=announce_query)
connect_button = urwid.Button("Browse", on_press=connect_query) connect_button = urwid.Button("Browse", on_press=connect_query)
reset_button = urwid.Button("Rst Stats", on_press=stats_query) reset_button = urwid.Button("Rst Stats", on_press=stats_query)
if not self.app.disable_propagation:
pile = urwid.Pile([ pile = urwid.Pile([
t_id, t_id,
e_name, e_name,
@@ -1311,25 +1380,47 @@ class NodeInfo(urwid.WidgetWrap):
self.t_total_files, self.t_total_files,
urwid.Divider(g["divider1"]), urwid.Divider(g["divider1"]),
urwid.Columns([ urwid.Columns([
("weight", 5, urwid.Button("Back", on_press=show_peer_info)), (urwid.WEIGHT, 5, urwid.Button("Back", on_press=show_peer_info)),
("weight", 0.5, urwid.Text("")), (urwid.WEIGHT, 0.5, urwid.Text("")),
("weight", 6, connect_button), (urwid.WEIGHT, 6, connect_button),
("weight", 0.5, urwid.Text("")), (urwid.WEIGHT, 0.5, urwid.Text("")),
("weight", 8, reset_button), (urwid.WEIGHT, 8, reset_button),
("weight", 0.5, urwid.Text("")), (urwid.WEIGHT, 0.5, urwid.Text("")),
("weight", 7, announce_button), (urwid.WEIGHT, 7, announce_button),
]) ])
]) ])
else: else:
pile = urwid.Pile([ pile = urwid.Pile([
urwid.Text("\n"+g["info"], align="center"), t_id,
urwid.Text("\nThis instance is not hosting a node\n\n", align="center"), e_name,
urwid.Padding(urwid.Button("Back", on_press=show_peer_info), "center", "pack") urwid.Divider(g["divider1"]),
self.t_last_announce,
self.t_storage_stats,
self.t_active_links,
self.t_total_connections,
self.t_total_pages,
self.t_total_files,
urwid.Divider(g["divider1"]),
urwid.Columns([
(urwid.WEIGHT, 5, urwid.Button("Back", on_press=show_peer_info)),
(urwid.WEIGHT, 0.5, urwid.Text("")),
(urwid.WEIGHT, 6, connect_button),
(urwid.WEIGHT, 0.5, urwid.Text("")),
(urwid.WEIGHT, 8, reset_button),
(urwid.WEIGHT, 0.5, urwid.Text("")),
(urwid.WEIGHT, 7, announce_button),
])
])
else:
pile = urwid.Pile([
urwid.Text("\n"+g["info"], align=urwid.CENTER),
urwid.Text("\nThis instance is not hosting a node\n\n", align=urwid.CENTER),
urwid.Padding(urwid.Button("Back", on_press=show_peer_info), urwid.CENTER, urwid.PACK)
]) ])
self.display_widget = pile self.display_widget = pile
urwid.WidgetWrap.__init__(self, urwid.AttrMap(urwid.LineBox(self.display_widget, title="Local Node Info"), widget_style)) super().__init__(urwid.AttrMap(urwid.LineBox(self.display_widget, title="Local Node Info"), widget_style))
def start(self): def start(self):
if self.app.node != None: if self.app.node != None:
@@ -1352,7 +1443,7 @@ class UpdatingText(urwid.WidgetWrap):
self.append_text = append_text self.append_text = append_text
self.update() self.update()
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def update(self): def update(self):
self.value = self.value_method() self.value = self.value_method()
@@ -1394,7 +1485,7 @@ class NetworkStats(urwid.WidgetWrap):
self.display_widget = urwid.LineBox(pile, title="Network Stats") self.display_widget = urwid.LineBox(pile, title="Network Stats")
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def start(self): def start(self):
self.w_heard_peers.start() self.w_heard_peers.start()
@@ -1446,9 +1537,9 @@ class NetworkDisplay():
self.list_display = 1 self.list_display = 1
self.left_pile = NetworkLeftPile([ self.left_pile = NetworkLeftPile([
("weight", 1, self.known_nodes_display), (urwid.WEIGHT, 1, self.known_nodes_display),
# ("pack", self.network_stats_display), # (urwid.PACK, self.network_stats_display),
("pack", self.local_peer_display), (urwid.PACK, self.local_peer_display),
]) ])
self.left_pile.parent = self self.left_pile.parent = self
@@ -1459,10 +1550,10 @@ class NetworkDisplay():
self.columns = urwid.Columns( self.columns = urwid.Columns(
[ [
# ("weight", NetworkDisplay.list_width, self.left_area), # (urwid.WEIGHT, NetworkDisplay.list_width, self.left_area),
# ("weight", self.right_area_width, self.right_area) # (urwid.WEIGHT, self.right_area_width, self.right_area)
(NetworkDisplay.given_list_width, self.left_area), (NetworkDisplay.given_list_width, self.left_area),
("weight", 1, self.right_area) (urwid.WEIGHT, 1, self.right_area)
], ],
dividechars=0, focus_column=0 dividechars=0, focus_column=0
) )
@@ -1472,11 +1563,11 @@ class NetworkDisplay():
def toggle_list(self): def toggle_list(self):
if self.list_display != 0: if self.list_display != 0:
options = self.left_pile.options(height_type="weight", height_amount=1) options = self.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.left_pile.contents[0] = (self.announce_stream_display, options) self.left_pile.contents[0] = (self.announce_stream_display, options)
self.list_display = 0 self.list_display = 0
else: else:
options = self.left_pile.options(height_type="weight", height_amount=1) options = self.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.left_pile.contents[0] = (self.known_nodes_display, options) self.left_pile.contents[0] = (self.known_nodes_display, options)
self.list_display = 1 self.list_display = 1
@@ -1491,7 +1582,7 @@ class NetworkDisplay():
self.widget.contents[0] = (self.left_area, options) self.widget.contents[0] = (self.left_area, options)
def show_peers(self): def show_peers(self):
options = self.left_pile.options(height_type="weight", height_amount=1) options = self.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.left_pile.contents[0] = (self.lxmf_peers_display, options) self.left_pile.contents[0] = (self.lxmf_peers_display, options)
if self.list_display != 0: if self.list_display != 0:
@@ -1508,7 +1599,7 @@ class NetworkDisplay():
if selected_node_hash != None: if selected_node_hash != None:
info_widget = KnownNodeInfo(selected_node_hash) info_widget = KnownNodeInfo(selected_node_hash)
options = parent.left_pile.options(height_type="weight", height_amount=1) options = parent.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
parent.left_pile.contents[0] = (info_widget, options) parent.left_pile.contents[0] = (info_widget, options)
def focus_lists(self): def focus_lists(self):
@@ -1527,10 +1618,10 @@ class NetworkDisplay():
def close_list_dialogs(self): def close_list_dialogs(self):
if self.list_display == 0: if self.list_display == 0:
options = self.left_pile.options(height_type="weight", height_amount=1) options = self.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.left_pile.contents[0] = (self.announce_stream_display, options) self.left_pile.contents[0] = (self.announce_stream_display, options)
else: else:
options = self.left_pile.options(height_type="weight", height_amount=1) options = self.left_pile.options(height_type=urwid.WEIGHT, height_amount=1)
self.left_pile.contents[0] = (self.known_nodes_display, options) self.left_pile.contents[0] = (self.known_nodes_display, options)
def start(self): def start(self):
@@ -1573,20 +1664,25 @@ class LXMFPeers(urwid.WidgetWrap):
else: else:
self.no_content = True self.no_content = True
widget_style = "inactive_text" widget_style = "inactive_text"
self.pile = urwid.Pile([urwid.Text(("warning_text", g["info"]+"\n"), align="center"), SelectText(("warning_text", "Currently, no LXMF nodes are peered\n\n"), align="center")]) self.pile = urwid.Pile([
self.display_widget = urwid.Filler(self.pile, valign="top", height="pack") urwid.Text(("warning_text", g["info"]+"\n"), align=urwid.CENTER),
SelectText(("warning_text", "Currently, no LXMF nodes are peered\n\n"), align=urwid.CENTER),
])
self.display_widget = urwid.Filler(self.pile, valign=urwid.TOP, height=urwid.PACK)
if hasattr(self, "peer_list") and self.peer_list: if hasattr(self, "peer_list") and self.peer_list:
pl = len(self.peer_list) pl = len(self.peer_list)
else: else:
pl = 0 pl = 0
urwid.WidgetWrap.__init__(self, urwid.AttrMap(urwid.LineBox(self.display_widget, title=f"LXMF Propagation Peers ({pl})"), widget_style)) super().__init__(urwid.AttrMap(urwid.LineBox(self.display_widget, title=f"LXMF Propagation Peers ({pl})"), widget_style))
def keypress(self, size, key): def keypress(self, size, key):
if key == "up" and (self.no_content or self.ilb.first_item_is_selected()): if key == "up" and (self.no_content or self.ilb.first_item_is_selected()):
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.set_focus("header") nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.frame.focus_position = "header"
elif key == "ctrl x": elif key == "ctrl x":
self.delete_selected_entry() self.delete_selected_entry()
elif key == "ctrl r":
self.sync_selected_entry()
return super(LXMFPeers, self).keypress(size, key) return super(LXMFPeers, self).keypress(size, key)
@@ -1602,6 +1698,48 @@ class LXMFPeers(urwid.WidgetWrap):
self.delegate.reinit_lxmf_peers() self.delegate.reinit_lxmf_peers()
self.delegate.show_peers() self.delegate.show_peers()
def sync_selected_entry(self):
sync_grace = 10
si = self.ilb.get_selected_item()
if si != None:
destination_hash = si.original_widget.destination_hash
if destination_hash in self.app.message_router.peers:
peer = self.app.message_router.peers[destination_hash]
if time.time() > peer.last_sync_attempt+sync_grace:
peer.next_sync_attempt = time.time()-1
def job():
peer.sync()
threading.Thread(target=job, daemon=True).start()
time.sleep(0.25)
def dismiss_dialog(sender):
self.close_list_dialogs()
dialog = ListDialogLineBox(
urwid.Pile([
urwid.Text("A delivery sync of all unhandled LXMs was manually requested for the selected node\n", align=urwid.CENTER),
urwid.Columns([
(urwid.WEIGHT, 0.1, urwid.Text("")),
(urwid.WEIGHT, 0.45, urwid.Button("OK", on_press=dismiss_dialog)),
])
]),
title="!",
)
dialog.delegate = self.delegate
bottom = self
overlay = urwid.Overlay(dialog, bottom, align=urwid.CENTER, width=urwid.RELATIVE_100, valign=urwid.MIDDLE, height=urwid.PACK, left=2, right=2)
options = self.delegate.left_pile.options(urwid.WEIGHT, 1)
self.delegate.left_pile.contents[0] = (overlay, options)
def close_list_dialogs(self):
self.delegate.reinit_lxmf_peers()
self.delegate.show_peers()
def rebuild_widget_list(self): def rebuild_widget_list(self):
self.peer_list = self.app.message_router.peers self.peer_list = self.app.message_router.peers
@@ -1615,17 +1753,18 @@ class LXMFPeers(urwid.WidgetWrap):
def make_peer_widgets(self): def make_peer_widgets(self):
widget_list = [] widget_list = []
sorted_peers = sorted(self.peer_list, key=lambda pid: self.peer_list[pid].link_establishment_rate, reverse=True) sorted_peers = sorted(self.peer_list, key=lambda pid: (self.app.directory.pn_trust_level(pid), self.peer_list[pid].link_establishment_rate), reverse=True)
for peer_id in sorted_peers: for peer_id in sorted_peers:
peer = self.peer_list[peer_id] peer = self.peer_list[peer_id]
pe = LXMFPeerEntry(self.app, peer, self) trust_level = self.app.directory.pn_trust_level(peer_id)
pe = LXMFPeerEntry(self.app, peer, self, trust_level)
pe.destination_hash = peer.destination_hash pe.destination_hash = peer.destination_hash
widget_list.append(pe) widget_list.append(pe)
return widget_list return widget_list
class LXMFPeerEntry(urwid.WidgetWrap): class LXMFPeerEntry(urwid.WidgetWrap):
def __init__(self, app, peer, delegate): def __init__(self, app, peer, delegate, trust_level):
destination_hash = peer.destination_hash destination_hash = peer.destination_hash
self.app = app self.app = app
@@ -1647,6 +1786,10 @@ class LXMFPeerEntry(urwid.WidgetWrap):
if hasattr(peer, "alive"): if hasattr(peer, "alive"):
if peer.alive: if peer.alive:
alive_string = "Available" alive_string = "Available"
if trust_level == DirectoryEntry.TRUSTED:
style = "list_trusted"
focus_style = "list_focus_trusted"
else:
style = "list_normal" style = "list_normal"
focus_style = "list_focus" focus_style = "list_focus"
else: else:
@@ -1655,11 +1798,9 @@ class LXMFPeerEntry(urwid.WidgetWrap):
focus_style = "list_focus_unresponsive" focus_style = "list_focus_unresponsive"
widget = ListEntry(sym+" "+display_str+"\n "+alive_string+", last heard "+pretty_date(int(peer.last_heard))+"\n "+str(len(peer.unhandled_messages))+" unhandled LXMs, "+RNS.prettysize(peer.link_establishment_rate/8, "b")+"/s LER") widget = ListEntry(sym+" "+display_str+"\n "+alive_string+", last heard "+pretty_date(int(peer.last_heard))+"\n "+str(len(peer.unhandled_messages))+" unhandled LXMs, "+RNS.prettysize(peer.link_establishment_rate/8, "b")+"/s LER")
# urwid.connect_signal(widget, "click", delegate.connect_node, node)
self.display_widget = urwid.AttrMap(widget, style, focus_style) self.display_widget = urwid.AttrMap(widget, style, focus_style)
self.display_widget.destination_hash = destination_hash self.display_widget.destination_hash = destination_hash
urwid.WidgetWrap.__init__(self, self.display_widget) super().__init__(self.display_widget)
def pretty_date(time=False): def pretty_date(time=False):

View File

@@ -268,10 +268,10 @@ class Scrollable(urwid.WidgetDecoration):
def _get_original_widget_size(self, size): def _get_original_widget_size(self, size):
ow = self._original_widget ow = self._original_widget
sizing = ow.sizing() sizing = ow.sizing()
if FIXED in sizing: if FLOW in sizing:
return ()
elif FLOW in sizing:
return (size[0],) return (size[0],)
elif FIXED in sizing:
return ()
def get_scrollpos(self, size=None, focus=False): def get_scrollpos(self, size=None, focus=False):
"""Current scrolling position """Current scrolling position

View File

@@ -269,11 +269,13 @@ class IndicativeListBox(urwid.WidgetWrap):
# mousewheel up # mousewheel up
elif button == 4.0: elif button == 4.0:
was_handeled = self._pass_key_to_contained_listbox(modified_size, "page up") # was_handeled = self._pass_key_to_contained_listbox(modified_size, "page up")
was_handeled = self._pass_key_to_contained_listbox(modified_size, "up")
# mousewheel down # mousewheel down
elif button == 5.0: elif button == 5.0:
was_handeled = self._pass_key_to_contained_listbox(modified_size, "page down") # was_handeled = self._pass_key_to_contained_listbox(modified_size, "page down")
was_handeled = self._pass_key_to_contained_listbox(modified_size, "down")
focus_position_after_input = self.get_selected_position() focus_position_after_input = self.get_selected_position()

View File

@@ -30,6 +30,6 @@ setuptools.setup(
entry_points= { entry_points= {
'console_scripts': ['nomadnet=nomadnet.nomadnet:main'] 'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
}, },
install_requires=["rns>=0.5.9", "lxmf>=0.3.3", "urwid>=2.1.2", "qrcode"], install_requires=["rns>=0.7.0", "lxmf>=0.3.9", "urwid>=2.4.2,!=2.4.3", "qrcode"],
python_requires=">=3.6", python_requires=">=3.6",
) )