mirror of
https://github.com/markqvist/NomadNet.git
synced 2025-12-17 14:54:26 +01:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b69947098 | ||
|
|
ca730ded3b | ||
|
|
6a460d9600 | ||
|
|
92a49c12c0 | ||
|
|
a876c09b4c | ||
|
|
bf0b08c154 | ||
|
|
77eca5d23e | ||
|
|
ed6ba63317 | ||
|
|
d856f3fd28 | ||
|
|
24d850107d | ||
|
|
9943cad15f | ||
|
|
dcd5f7df95 | ||
|
|
d840ca32ae | ||
|
|
37063932bb | ||
|
|
0ed4e09b82 | ||
|
|
910e527cc7 | ||
|
|
4eef326d6b | ||
|
|
691f4df098 | ||
|
|
407cc8fb5f | ||
|
|
1f7302903a | ||
|
|
5736012f2c | ||
|
|
bb98a512f3 | ||
|
|
ae0d4c6e0c | ||
|
|
2449b39f77 | ||
|
|
e022d469f8 | ||
|
|
a4f665e650 | ||
|
|
19d1d8504f | ||
|
|
276a5f56e1 | ||
|
|
89dd17ece5 | ||
|
|
82bb479957 | ||
|
|
082026ca1b | ||
|
|
df9fccf199 | ||
|
|
efd1d08399 | ||
|
|
5a7c84d2a4 | ||
|
|
a3bf538afe | ||
|
|
8d72a83411 | ||
|
|
0fb9e180ba | ||
|
|
2322a254a8 | ||
|
|
37ad5cc5d3 | ||
|
|
a921adbdbe | ||
|
|
5c1a2c3485 | ||
|
|
0cd1c9cf37 | ||
|
|
cfc0f4f9c1 | ||
|
|
2aa5c6f665 | ||
|
|
72a5ed9c8e | ||
|
|
c411ce703f | ||
|
|
c670aa82ad | ||
|
|
c52efbfb75 | ||
|
|
da348c3b42 | ||
|
|
92c3c55e03 | ||
|
|
8d9f4956db | ||
|
|
062e31964a | ||
|
|
f36018632f | ||
|
|
81f65e3453 | ||
|
|
34b3987ded | ||
|
|
22a7acf259 | ||
|
|
919a146da1 | ||
|
|
f0a4efa28b | ||
|
|
3d0043499c | ||
|
|
b6e6c4bd3d | ||
|
|
d06e1d3f1b | ||
|
|
02f9a5a760 | ||
|
|
bf7004fd0f | ||
|
|
8109bce5a3 |
53
README.md
53
README.md
@@ -4,7 +4,7 @@ Off-grid, resilient mesh communication with strong encryption, forward secrecy a
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
@@ -22,15 +22,41 @@ 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
|
||||
- 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?
|
||||
The easiest way to install Nomad Network is via pip:
|
||||
|
||||
```bash
|
||||
# Install Nomad Network and dependencies
|
||||
pip3 install nomadnet
|
||||
pip install nomadnet
|
||||
|
||||
# Run the client
|
||||
nomadnet
|
||||
|
||||
# Or alternatively run as a daemon, with no user interface
|
||||
nomadnet --daemon
|
||||
|
||||
# List options
|
||||
nomadnet --help
|
||||
```
|
||||
|
||||
If you are using an operating system that blocks normal user package installation via `pip`, you can return `pip` to normal behaviour by editing the `~/.config/pip/pip.conf` file, and adding the following directive in the `[global]` section:
|
||||
|
||||
```text
|
||||
[global]
|
||||
break-system-packages = true
|
||||
```
|
||||
|
||||
Alternatively, you can use the `pipx` tool to install Nomad Network in an isolated environment:
|
||||
|
||||
```bash
|
||||
# Install Nomad Network
|
||||
pipx install nomadnet
|
||||
|
||||
# Optionally install Reticulum utilities
|
||||
pipx install rns
|
||||
|
||||
# Optionally install standalone LXMF utilities
|
||||
pipx install lxmf
|
||||
|
||||
# Run the client
|
||||
nomadnet
|
||||
@@ -92,6 +118,23 @@ $ docker run -d \
|
||||
$ 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
|
||||
|
||||
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).
|
||||
|
||||
@@ -142,7 +142,7 @@ class Conversation:
|
||||
|
||||
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))
|
||||
|
||||
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)
|
||||
return False
|
||||
|
||||
def paper_output(self, content="", title=""):
|
||||
def paper_output(self, content="", title="", mode="print_qr"):
|
||||
if self.send_destination:
|
||||
try:
|
||||
dest = self.send_destination
|
||||
@@ -235,18 +235,41 @@ class Conversation:
|
||||
desired_method = LXMF.LXMessage.PAPER
|
||||
|
||||
lxm = LXMF.LXMessage(dest, source, content, title=title, desired_method=desired_method)
|
||||
qr_code = lxm.as_qr()
|
||||
qr_tmp_path = self.app.tmpfilespath+"/"+str(RNS.hexrep(lxm.hash, delimit=False))
|
||||
qr_code.save(qr_tmp_path)
|
||||
|
||||
print_result = self.app.print_file(qr_tmp_path)
|
||||
os.unlink(qr_tmp_path)
|
||||
if mode == "print_qr":
|
||||
qr_code = lxm.as_qr()
|
||||
qr_tmp_path = self.app.tmpfilespath+"/"+str(RNS.hexrep(lxm.hash, delimit=False))
|
||||
qr_code.save(qr_tmp_path)
|
||||
|
||||
if print_result:
|
||||
print_result = self.app.print_file(qr_tmp_path)
|
||||
os.unlink(qr_tmp_path)
|
||||
|
||||
if print_result:
|
||||
message_path = Conversation.ingest(lxm, self.app, originator=True)
|
||||
self.messages.append(ConversationMessage(message_path))
|
||||
|
||||
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
|
||||
|
||||
return print_result
|
||||
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:
|
||||
RNS.log("An error occurred while generating paper message, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
@@ -34,16 +34,21 @@ class Directory:
|
||||
aspect_filter = "nomadnetwork.node"
|
||||
@staticmethod
|
||||
def received_announce(destination_hash, announced_identity, app_data):
|
||||
app = nomadnet.NomadNetworkApp.get_shared_instance()
|
||||
try:
|
||||
app = nomadnet.NomadNetworkApp.get_shared_instance()
|
||||
|
||||
if not destination_hash in app.ignored_list:
|
||||
associated_peer = RNS.Destination.hash_from_name_and_identity("lxmf.delivery", announced_identity)
|
||||
if not destination_hash in app.ignored_list:
|
||||
associated_peer = RNS.Destination.hash_from_name_and_identity("lxmf.delivery", announced_identity)
|
||||
|
||||
app.directory.node_announce_received(destination_hash, app_data, associated_peer)
|
||||
app.autoselect_propagation_node()
|
||||
app.directory.node_announce_received(destination_hash, app_data, associated_peer)
|
||||
app.autoselect_propagation_node()
|
||||
|
||||
else:
|
||||
RNS.log("Ignored announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log("Ignored announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while evaluating LXMF destination announce, ignoring announce.", RNS.LOG_DEBUG)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
|
||||
|
||||
|
||||
def __init__(self, app):
|
||||
@@ -61,7 +66,7 @@ class Directory:
|
||||
packed_list = []
|
||||
for source_hash in self.directory_entries:
|
||||
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 = {
|
||||
"entry_list": packed_list,
|
||||
@@ -86,6 +91,9 @@ class Directory:
|
||||
entries = {}
|
||||
for e in unpacked_list:
|
||||
|
||||
if e[1] == None:
|
||||
e[1] = "Undefined"
|
||||
|
||||
if len(e) > 3:
|
||||
hosts_node = e[3]
|
||||
else:
|
||||
@@ -101,18 +109,35 @@ class Directory:
|
||||
else:
|
||||
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.announce_stream = unpacked_directory["announce_stream"]
|
||||
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could not load directory from disk. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
def lxmf_announce_received(self, source_hash, app_data):
|
||||
if app_data != None:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "peer"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
@@ -123,6 +148,19 @@ class Directory:
|
||||
|
||||
def node_announce_received(self, source_hash, app_data, associated_peer):
|
||||
if app_data != None:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "node"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
@@ -150,6 +188,19 @@ class Directory:
|
||||
break
|
||||
|
||||
if not found_node:
|
||||
if self.app.compact_stream:
|
||||
try:
|
||||
remove_announces = []
|
||||
for announce in self.announce_stream:
|
||||
if announce[1] == source_hash:
|
||||
remove_announces.append(announce)
|
||||
|
||||
for a in remove_announces:
|
||||
self.announce_stream.remove(a)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("An error occurred while compacting the announce stream. The contained exception was:"+str(e), RNS.LOG_ERROR)
|
||||
|
||||
timestamp = time.time()
|
||||
self.announce_stream.insert(0, (timestamp, source_hash, app_data, "pn"))
|
||||
while len(self.announce_stream) > Directory.ANNOUNCE_STREAM_MAXLENGTH:
|
||||
@@ -176,7 +227,14 @@ class Directory:
|
||||
def simplest_display_str(self, source_hash):
|
||||
trust_level = self.trust_level(source_hash)
|
||||
if trust_level == DirectoryEntry.WARNING or trust_level == DirectoryEntry.UNTRUSTED:
|
||||
return "<"+RNS.hexrep(source_hash, delimit=False)+">"
|
||||
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)+">"
|
||||
else:
|
||||
if source_hash in self.directory_entries:
|
||||
dn = self.directory_entries[source_hash].display_name
|
||||
@@ -209,6 +267,18 @@ class Directory:
|
||||
else:
|
||||
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):
|
||||
if source_hash in self.directory_entries:
|
||||
return self.directory_entries[source_hash].preferred_delivery
|
||||
@@ -268,6 +338,7 @@ class Directory:
|
||||
if e.hosts_node:
|
||||
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
|
||||
|
||||
def number_of_known_nodes(self):
|
||||
@@ -292,10 +363,11 @@ class DirectoryEntry:
|
||||
DIRECT = 0x01
|
||||
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:
|
||||
self.source_hash = source_hash
|
||||
self.display_name = display_name
|
||||
self.sort_rank = sort_rank
|
||||
|
||||
if preferred_delivery == None:
|
||||
self.preferred_delivery = DirectoryEntry.DIRECT
|
||||
|
||||
@@ -15,7 +15,11 @@ class Node:
|
||||
self.identity = self.app.identity
|
||||
self.destination = RNS.Destination(self.identity, RNS.Destination.IN, RNS.Destination.SINGLE, "nomadnetwork", "node")
|
||||
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.page_refresh_interval = self.app.page_refresh_interval
|
||||
self.file_refresh_interval = self.app.file_refresh_interval
|
||||
self.job_interval = Node.JOB_INTERVAL
|
||||
self.should_run_jobs = True
|
||||
self.app_data = None
|
||||
@@ -46,6 +50,8 @@ class Node:
|
||||
|
||||
|
||||
def register_pages(self):
|
||||
# TODO: Deregister previously registered pages
|
||||
# that no longer exist.
|
||||
self.servedpages = []
|
||||
self.scan_pages(self.app.pagespath)
|
||||
|
||||
@@ -65,6 +71,8 @@ class Node:
|
||||
)
|
||||
|
||||
def register_files(self):
|
||||
# TODO: Deregister previously registered files
|
||||
# that no longer exist.
|
||||
self.servedfiles = []
|
||||
self.scan_files(self.app.filespath)
|
||||
|
||||
@@ -153,6 +161,8 @@ class Node:
|
||||
RNS.log("Serving page: "+file_path, RNS.LOG_VERBOSE)
|
||||
if os.access(file_path, os.X_OK):
|
||||
env_map = {}
|
||||
if "PATH" in os.environ:
|
||||
env_map["PATH"] = os.environ["PATH"]
|
||||
if link_id != None:
|
||||
env_map["link_id"] = RNS.hexrep(link_id, delimit=False)
|
||||
if remote_identity != None:
|
||||
@@ -221,6 +231,16 @@ class Node:
|
||||
if now > self.last_announce + self.announce_interval*60:
|
||||
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)
|
||||
|
||||
def peer_connected(self, link):
|
||||
|
||||
@@ -115,17 +115,21 @@ class NomadNetworkApp:
|
||||
|
||||
self.downloads_path = os.path.expanduser("~/Downloads")
|
||||
|
||||
self.firstrun = False
|
||||
self.should_run_jobs = True
|
||||
self.job_interval = 5
|
||||
self.defer_jobs = 90
|
||||
self.firstrun = False
|
||||
self.should_run_jobs = True
|
||||
self.job_interval = 5
|
||||
self.defer_jobs = 90
|
||||
self.page_refresh_interval = 0
|
||||
self.file_refresh_interval = 0
|
||||
|
||||
self.peer_announce_at_start = True
|
||||
self.try_propagation_on_fail = True
|
||||
self.disable_propagation = False
|
||||
|
||||
self.periodic_lxmf_sync = True
|
||||
self.lxmf_sync_interval = 360*60
|
||||
self.lxmf_sync_limit = 8
|
||||
self.compact_stream = False
|
||||
|
||||
if not os.path.isdir(self.storagepath):
|
||||
os.makedirs(self.storagepath)
|
||||
@@ -307,15 +311,27 @@ class NomadNetworkApp:
|
||||
except Exception as e:
|
||||
RNS.log("Cannot prioritise "+str(dest_str)+", it is not a valid destination hash", RNS.LOG_ERROR)
|
||||
|
||||
self.message_router.enable_propagation()
|
||||
try:
|
||||
with open(self.pnannouncedpath, "wb") as pnf:
|
||||
pnf.write(msgpack.packb(time.time()))
|
||||
pnf.close()
|
||||
except Exception as e:
|
||||
RNS.log("An error ocurred while writing Propagation Node announce timestamp. The contained exception was: "+str(e), 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()
|
||||
try:
|
||||
with open(self.pnannouncedpath, "wb") as pnf:
|
||||
pnf.write(msgpack.packb(time.time()))
|
||||
pnf.close()
|
||||
|
||||
except Exception as e:
|
||||
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)
|
||||
else:
|
||||
self.node = None
|
||||
@@ -413,16 +429,24 @@ class NomadNetworkApp:
|
||||
return "Receiving messages"
|
||||
elif self.message_router.propagation_transfer_state == LXMF.LXMRouter.PR_RESPONSE_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:
|
||||
new_msgs = self.message_router.propagation_transfer_last_result
|
||||
if new_msgs == 0:
|
||||
return "Done, no new messages"
|
||||
else:
|
||||
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:
|
||||
return "Unknown"
|
||||
|
||||
@@ -698,6 +722,10 @@ class NomadNetworkApp:
|
||||
else:
|
||||
self.lxmf_sync_limit = None
|
||||
|
||||
if option == "compact_announce_stream":
|
||||
value = self.config["client"].as_bool(option)
|
||||
self.compact_stream = value
|
||||
|
||||
if option == "user_interface":
|
||||
value = value.lower()
|
||||
if value == "none":
|
||||
@@ -783,6 +811,11 @@ class NomadNetworkApp:
|
||||
else:
|
||||
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"]:
|
||||
self.node_announce_at_start = False
|
||||
else:
|
||||
@@ -800,9 +833,27 @@ class NomadNetworkApp:
|
||||
if "pages_path" in self.config["node"]:
|
||||
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"]:
|
||||
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"]:
|
||||
self.prioritised_lxmf_destinations = self.config["node"].as_list("prioritise_destinations")
|
||||
else:
|
||||
@@ -929,6 +980,12 @@ lxmf_sync_interval = 360
|
||||
# the limit, and download everything every time.
|
||||
lxmf_sync_limit = 8
|
||||
|
||||
# The announce stream will only show one entry
|
||||
# per destination or node by default. You can
|
||||
# change this to show as many announces as have
|
||||
# been received, for every destination.
|
||||
compact_announce_stream = yes
|
||||
|
||||
[textui]
|
||||
|
||||
# Amount of time to show intro screen
|
||||
@@ -992,6 +1049,14 @@ announce_interval = 360
|
||||
# Whether to announce when the node starts.
|
||||
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 LXMF Propagation Node message store,
|
||||
# specified in megabytes. When this limit
|
||||
@@ -1012,6 +1077,22 @@ announce_at_start = Yes
|
||||
# and generally you do not need to use it.
|
||||
# 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]
|
||||
|
||||
# You can configure Nomad Network to print
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.3.4"
|
||||
__version__ = "0.4.6"
|
||||
|
||||
@@ -45,6 +45,8 @@ THEMES = {
|
||||
("list_normal", "dark gray", "default", "default", "#bbb", "default"),
|
||||
("list_untrusted", "dark red", "default", "default", "#a22", "default"),
|
||||
("list_focus_untrusted", "black", "light gray", "standout", "#810", "#aaa"),
|
||||
("list_unresponsive", "yellow", "default", "default", "#b92", "default"),
|
||||
("list_focus_unresponsive", "black", "light gray", "standout", "#530", "#aaa"),
|
||||
("topic_list_normal", "light gray", "default", "default", "#ddd", "default"),
|
||||
("browser_controls", "light gray", "default", "default", "#bbb", "default"),
|
||||
("progress_full", "black", "light gray", "standout", "#111", "#bbb"),
|
||||
@@ -78,6 +80,8 @@ THEMES = {
|
||||
("list_normal", "dark gray", "default", "default", "#444", "default"),
|
||||
("list_untrusted", "dark red", "default", "default", "#a22", "default"),
|
||||
("list_focus_untrusted", "black", "dark gray", "standout", "#810", "#aaa"),
|
||||
("list_unresponsive", "yellow", "default", "default", "#b92", "default"),
|
||||
("list_focus_unresponsive", "black", "light gray", "standout", "#530", "#aaa"),
|
||||
("topic_list_normal", "dark gray", "default", "default", "#222", "default"),
|
||||
("browser_controls", "dark gray", "default", "default", "#444", "default"),
|
||||
("progress_full", "black", "dark gray", "standout", "#111", "#bbb"),
|
||||
|
||||
@@ -23,15 +23,17 @@ class BrowserFrame(urwid.Frame):
|
||||
self.delegate.url_dialog()
|
||||
elif key == "ctrl s":
|
||||
self.delegate.save_node_dialog()
|
||||
elif key == "ctrl b":
|
||||
self.delegate.save_node_dialog()
|
||||
elif key == "ctrl g":
|
||||
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":
|
||||
try:
|
||||
if hasattr(self.delegate, "page_pile") and self.delegate.page_pile:
|
||||
def df(loop, user_data):
|
||||
st = None
|
||||
nf = self.delegate.page_pile.get_focus()
|
||||
nf = self.delegate.page_pile.focus
|
||||
if hasattr(nf, "key_timeout"):
|
||||
st = nf
|
||||
elif hasattr(nf, "original_widget"):
|
||||
@@ -277,7 +279,10 @@ class Browser:
|
||||
self.browser_footer = urwid.Text("")
|
||||
|
||||
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.delegate = self
|
||||
@@ -304,28 +309,29 @@ class Browser:
|
||||
self.update_display()
|
||||
|
||||
columns = urwid.Columns([
|
||||
("weight", 0.5, urwid.Text(" ")),
|
||||
(urwid.WEIGHT, 0.5, urwid.Text(" ")),
|
||||
(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:
|
||||
pile = urwid.Pile([
|
||||
urwid.Text("!\n\n"+self.status_text()+"\n", align="center"),
|
||||
columns
|
||||
urwid.Text("!\n\n"+self.status_text()+"\n", align=urwid.CENTER),
|
||||
columns
|
||||
])
|
||||
else:
|
||||
pile = urwid.Pile([
|
||||
urwid.Text("!\n\n"+self.status_text(), align="center")
|
||||
])
|
||||
pile = urwid.Pile([urwid.Text("!\n\n"+self.status_text(), align=urwid.CENTER)])
|
||||
|
||||
return urwid.Filler(pile, "middle")
|
||||
return urwid.Filler(pile, urwid.MIDDLE)
|
||||
|
||||
def update_display(self):
|
||||
if self.status == Browser.DISCONECTED:
|
||||
self.display_widget.set_attr_map({None: "inactive_text"})
|
||||
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_header = urwid.Text("")
|
||||
self.linebox.set_title("Remote Node")
|
||||
@@ -352,7 +358,10 @@ class Browser:
|
||||
|
||||
elif self.status <= Browser.REQUEST_SENT:
|
||||
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()
|
||||
|
||||
@@ -515,7 +524,7 @@ class Browser:
|
||||
self.status = Browser.PATH_REQUESTED
|
||||
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):
|
||||
now = time.time()
|
||||
if now > pr_time+self.timeout:
|
||||
@@ -606,7 +615,7 @@ class Browser:
|
||||
self.load_page()
|
||||
|
||||
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)
|
||||
|
||||
def url_dialog(self):
|
||||
@@ -627,7 +636,11 @@ class Browser:
|
||||
dialog = UrlDialogLineBox(
|
||||
urwid.Pile([
|
||||
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"
|
||||
)
|
||||
e_url.confirmed = confirmed
|
||||
@@ -635,9 +648,18 @@ class Browser:
|
||||
dialog.delegate = self
|
||||
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.focus_position = 1
|
||||
|
||||
@@ -661,7 +683,11 @@ class Browser:
|
||||
dialog = UrlDialogLineBox(
|
||||
urwid.Pile([
|
||||
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"
|
||||
)
|
||||
|
||||
@@ -669,9 +695,18 @@ class Browser:
|
||||
dialog.delegate = self
|
||||
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.focus_position = 1
|
||||
|
||||
@@ -719,6 +754,10 @@ class Browser:
|
||||
env_map = self.request_data
|
||||
else:
|
||||
env_map = {}
|
||||
|
||||
if "PATH" in os.environ:
|
||||
env_map["PATH"] = os.environ["PATH"]
|
||||
|
||||
generated = subprocess.run([page_path], stdout=subprocess.PIPE, env=env_map)
|
||||
page_data = generated.stdout
|
||||
else:
|
||||
@@ -750,7 +789,7 @@ class Browser:
|
||||
def __load(self):
|
||||
# If an established link exists, but it doesn't match the target
|
||||
# destination, we close and clear it.
|
||||
if self.link != None and self.link.destination.hash != self.destination_hash:
|
||||
if self.link != None and (self.link.destination.hash != self.destination_hash or self.link.status != RNS.Link.ACTIVE):
|
||||
self.link.teardown()
|
||||
self.link = None
|
||||
|
||||
@@ -764,7 +803,7 @@ class Browser:
|
||||
self.status = Browser.PATH_REQUESTED
|
||||
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):
|
||||
now = time.time()
|
||||
if now > pr_time+self.timeout:
|
||||
@@ -970,7 +1009,10 @@ class Browser:
|
||||
try:
|
||||
file_name = request_receipt.response[0]
|
||||
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
|
||||
while os.path.isfile(file_destination):
|
||||
@@ -999,6 +1041,11 @@ class Browser:
|
||||
self.response_transfer_size = None
|
||||
|
||||
self.update_display()
|
||||
if self.link != None:
|
||||
try:
|
||||
self.link.teardown()
|
||||
except Exception as e:
|
||||
pass
|
||||
else:
|
||||
self.status = Browser.REQUEST_FAILED
|
||||
self.response_progress = 0
|
||||
@@ -1006,6 +1053,11 @@ class Browser:
|
||||
self.response_transfer_size = None
|
||||
|
||||
self.update_display()
|
||||
if self.link != None:
|
||||
try:
|
||||
self.link.teardown()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
def request_timeout(self, request_receipt=None):
|
||||
@@ -1015,6 +1067,11 @@ class Browser:
|
||||
self.response_transfer_size = None
|
||||
|
||||
self.update_display()
|
||||
if self.link != None:
|
||||
try:
|
||||
self.link.teardown()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
def response_progressed(self, request_receipt):
|
||||
|
||||
@@ -12,13 +12,13 @@ class ConfigDisplayShortcuts():
|
||||
class ConfigFiller(urwid.WidgetWrap):
|
||||
def __init__(self, widget, app):
|
||||
self.app = app
|
||||
self.filler = urwid.Filler(widget, "top")
|
||||
urwid.WidgetWrap.__init__(self, self.filler)
|
||||
self.filler = urwid.Filler(widget, urwid.TOP)
|
||||
super().__init__(self.filler)
|
||||
|
||||
|
||||
def keypress(self, size, key):
|
||||
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)
|
||||
|
||||
@@ -31,12 +31,20 @@ class ConfigDisplay():
|
||||
self.editor_term = EditorTerminal(self.app, self)
|
||||
self.widget = urwid.LineBox(self.editor_term)
|
||||
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)
|
||||
|
||||
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.Padding(urwid.Button("Open Editor", on_press=open_editor), width=15, align="center"),
|
||||
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=urwid.CENTER,
|
||||
),
|
||||
urwid.Padding(urwid.Button("Open Editor", on_press=open_editor), width=15, align=urwid.CENTER),
|
||||
])
|
||||
|
||||
self.config_explainer = ConfigFiller(pile, self.app)
|
||||
@@ -71,11 +79,11 @@ class EditorTerminal(urwid.WidgetWrap):
|
||||
|
||||
urwid.connect_signal(self.term, 'closed', quit_term)
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.term)
|
||||
super().__init__(self.term)
|
||||
|
||||
|
||||
def keypress(self, size, key):
|
||||
# TODO: Decide whether there should be a way to get out while editing
|
||||
#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)
|
||||
@@ -37,9 +37,9 @@ class ConversationsArea(urwid.LineBox):
|
||||
elif key == "ctrl g":
|
||||
self.delegate.toggle_fullscreen()
|
||||
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()):
|
||||
self.delegate.app.ui.main_display.frame.set_focus("header")
|
||||
self.delegate.app.ui.main_display.frame.focus_position = "header"
|
||||
else:
|
||||
return super(ConversationsArea, self).keypress(size, key)
|
||||
|
||||
@@ -69,10 +69,10 @@ class ConversationsDisplay():
|
||||
|
||||
self.columns_widget = urwid.Columns(
|
||||
[
|
||||
# ("weight", ConversationsDisplay.list_width, self.listbox),
|
||||
# ("weight", 1-ConversationsDisplay.list_width, self.make_conversation_widget(None))
|
||||
# (urwid.WEIGHT, ConversationsDisplay.list_width, self.listbox),
|
||||
# (urwid.WEIGHT, 1-ConversationsDisplay.list_width, self.make_conversation_widget(None))
|
||||
(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]
|
||||
)
|
||||
@@ -105,7 +105,7 @@ class ConversationsDisplay():
|
||||
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
|
||||
|
||||
def delete_selected_conversation(self):
|
||||
@@ -127,17 +127,33 @@ class ConversationsDisplay():
|
||||
|
||||
dialog = DialogLineBox(
|
||||
urwid.Pile([
|
||||
urwid.Text("Delete conversation with\n"+self.app.directory.simplest_display_str(bytes.fromhex(source_hash))+"\n", align="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.Text(
|
||||
"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="?"
|
||||
)
|
||||
dialog.delegate = self
|
||||
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("given", ConversationsDisplay.given_list_width)
|
||||
# options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
|
||||
options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
|
||||
self.columns_widget.contents[0] = (overlay, options)
|
||||
|
||||
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)
|
||||
if not dialog_pile.error_display:
|
||||
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(("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))
|
||||
if source_is_known:
|
||||
@@ -234,13 +253,23 @@ class ConversationsDisplay():
|
||||
def query_action(sender, user_data):
|
||||
self.close_conversation_by_hash(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 = [
|
||||
(urwid.Text("Query sent"), 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)
|
||||
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([
|
||||
selected_id_widget,
|
||||
@@ -253,7 +282,11 @@ class ConversationsDisplay():
|
||||
r_direct,
|
||||
r_propagated,
|
||||
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
|
||||
|
||||
@@ -261,10 +294,19 @@ class ConversationsDisplay():
|
||||
dialog.delegate = self
|
||||
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("given", ConversationsDisplay.given_list_width)
|
||||
# options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
|
||||
options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
|
||||
self.columns_widget.contents[0] = (overlay, options)
|
||||
|
||||
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)
|
||||
if not dialog_pile.error_display:
|
||||
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(("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([
|
||||
e_id,
|
||||
@@ -324,7 +372,11 @@ class ConversationsDisplay():
|
||||
r_unknown,
|
||||
r_trusted,
|
||||
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
|
||||
|
||||
@@ -332,10 +384,19 @@ class ConversationsDisplay():
|
||||
dialog.delegate = self
|
||||
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("given", ConversationsDisplay.given_list_width)
|
||||
# options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
|
||||
options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
|
||||
self.columns_widget.contents[0] = (overlay, options)
|
||||
|
||||
def ingest_lxm_uri(self):
|
||||
@@ -366,7 +427,10 @@ class ConversationsDisplay():
|
||||
rdialog_pile = urwid.Pile([
|
||||
urwid.Text("Message was decoded, decrypted successfully, and added to your conversation list."),
|
||||
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
|
||||
|
||||
@@ -374,16 +438,28 @@ class ConversationsDisplay():
|
||||
rdialog.delegate = self
|
||||
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)
|
||||
|
||||
elif ingest_result == duplicate_signal:
|
||||
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(""),
|
||||
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
|
||||
|
||||
@@ -391,9 +467,18 @@ class ConversationsDisplay():
|
||||
rdialog.delegate = self
|
||||
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)
|
||||
|
||||
else:
|
||||
@@ -405,7 +490,10 @@ class ConversationsDisplay():
|
||||
rdialog_pile = urwid.Pile([
|
||||
urwid.Text(propagation_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
|
||||
|
||||
@@ -413,23 +501,36 @@ class ConversationsDisplay():
|
||||
rdialog.delegate = self
|
||||
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)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could not ingest LXM URI. The contained exception was: "+str(e), RNS.LOG_VERBOSE)
|
||||
if not dialog_pile.error_display:
|
||||
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(("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([
|
||||
e_uri,
|
||||
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
|
||||
|
||||
@@ -437,9 +538,18 @@ class ConversationsDisplay():
|
||||
dialog.delegate = self
|
||||
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)
|
||||
|
||||
def delete_conversation(self, source_hash):
|
||||
@@ -471,7 +581,7 @@ class ConversationsDisplay():
|
||||
r_mall = urwid.RadioButton(max_messages_group, "Download all", state=True)
|
||||
r_mlim = urwid.RadioButton(max_messages_group, "Limit to", state=False)
|
||||
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):
|
||||
limit = None
|
||||
@@ -495,7 +605,11 @@ class ConversationsDisplay():
|
||||
else:
|
||||
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
|
||||
|
||||
pn_ident = None
|
||||
@@ -518,7 +632,7 @@ class ConversationsDisplay():
|
||||
|
||||
dialog = DialogLineBox(
|
||||
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"]),
|
||||
sync_progress,
|
||||
urwid.Divider(g["divider1"]),
|
||||
@@ -529,12 +643,23 @@ class ConversationsDisplay():
|
||||
]), title="Message Sync"
|
||||
)
|
||||
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(
|
||||
urwid.Pile([
|
||||
urwid.Text(""),
|
||||
urwid.Text("No trusted nodes found, cannot sync!\n", align="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("No trusted nodes found, cannot sync!\n", align=urwid.CENTER),
|
||||
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(""),
|
||||
button_columns
|
||||
]), title="Message Sync"
|
||||
@@ -550,10 +675,19 @@ class ConversationsDisplay():
|
||||
self.sync_dialog = dialog
|
||||
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("given", ConversationsDisplay.given_list_width)
|
||||
# options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
|
||||
options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
|
||||
self.columns_widget.contents[0] = (overlay, options)
|
||||
|
||||
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())
|
||||
|
||||
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:
|
||||
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)
|
||||
|
||||
@@ -574,13 +708,22 @@ class ConversationsDisplay():
|
||||
def update_conversation_list(self):
|
||||
ilb_position = self.ilb.get_selected_position()
|
||||
self.update_listbox()
|
||||
# options = self.columns_widget.options("weight", ConversationsDisplay.list_width)
|
||||
options = self.columns_widget.options("given", ConversationsDisplay.given_list_width)
|
||||
# options = self.columns_widget.options(urwid.WEIGHT, ConversationsDisplay.list_width)
|
||||
options = self.columns_widget.options(urwid.GIVEN, ConversationsDisplay.given_list_width)
|
||||
if not (self.dialog_open and self.sync_dialog != None):
|
||||
self.columns_widget.contents[0] = (self.listbox, options)
|
||||
else:
|
||||
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)
|
||||
|
||||
if ilb_position != None:
|
||||
@@ -606,17 +749,17 @@ class ConversationsDisplay():
|
||||
self.app.mark_conversation_read(self.currently_displayed_conversation)
|
||||
|
||||
self.currently_displayed_conversation = source_hash
|
||||
# options = self.widget.options("weight", 1-ConversationsDisplay.list_width)
|
||||
options = self.widget.options("weight", 1)
|
||||
# options = self.widget.options(urwid.WEIGHT, 1-ConversationsDisplay.list_width)
|
||||
options = self.widget.options(urwid.WEIGHT, 1)
|
||||
self.widget.contents[1] = (self.make_conversation_widget(source_hash), options)
|
||||
if source_hash == None:
|
||||
self.widget.set_focus_column(0)
|
||||
self.widget.focus_position = 0
|
||||
else:
|
||||
if self.app.conversation_is_unread(source_hash):
|
||||
self.app.mark_conversation_read(source_hash)
|
||||
self.update_conversation_list()
|
||||
|
||||
self.widget.set_focus_column(1)
|
||||
self.widget.focus_position = 1
|
||||
conversation_position = None
|
||||
index = 0
|
||||
for widget in self.list_widgets:
|
||||
@@ -756,9 +899,9 @@ class MessageEdit(urwid.Edit):
|
||||
y = self.get_cursor_coords(size)[1]
|
||||
if y == 0:
|
||||
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":
|
||||
self.delegate.frame.set_focus("body")
|
||||
self.delegate.frame.focus_position = "body"
|
||||
else:
|
||||
return super(MessageEdit, self).keypress(size, key)
|
||||
else:
|
||||
@@ -769,11 +912,11 @@ class MessageEdit(urwid.Edit):
|
||||
|
||||
class ConversationFrame(urwid.Frame):
|
||||
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:
|
||||
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:
|
||||
self.set_focus("footer")
|
||||
self.focus_position = "footer"
|
||||
else:
|
||||
return super(ConversationFrame, self).keypress(size, key)
|
||||
elif key == "ctrl k":
|
||||
@@ -788,7 +931,7 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
if source_hash == None:
|
||||
self.frame = None
|
||||
display_widget = urwid.LineBox(urwid.Filler(urwid.Text("\n No conversation selected"), "top"))
|
||||
urwid.WidgetWrap.__init__(self, display_widget)
|
||||
super().__init__(display_widget)
|
||||
else:
|
||||
if source_hash in ConversationsDisplay.cached_conversation_widgets:
|
||||
return ConversationsDisplay.cached_conversation_widgets[source_hash]
|
||||
@@ -815,7 +958,11 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
|
||||
header = None
|
||||
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.name = "minimal_editor"
|
||||
@@ -852,7 +999,7 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
self.frame
|
||||
)
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def clear_history_dialog(self):
|
||||
def dismiss_dialog(sender):
|
||||
@@ -867,17 +1014,30 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
|
||||
dialog = DialogLineBox(
|
||||
urwid.Pile([
|
||||
urwid.Text("Clear conversation history\n", align="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.Text("Clear conversation history\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="?"
|
||||
)
|
||||
dialog.delegate = self
|
||||
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.set_focus("body")
|
||||
self.frame.focus_position = "body"
|
||||
|
||||
def toggle_editor(self):
|
||||
if self.full_editor_active:
|
||||
@@ -894,7 +1054,17 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
if allowed:
|
||||
self.frame.contents["footer"] = (self.minimal_editor, None)
|
||||
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)
|
||||
|
||||
def toggle_focus_area(self):
|
||||
@@ -905,9 +1075,9 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
pass
|
||||
|
||||
if name == "messagelist":
|
||||
self.frame.set_focus("footer")
|
||||
self.frame.focus_position = "footer"
|
||||
elif name == "minimal_editor" or name == "full_editor":
|
||||
self.frame.set_focus("body")
|
||||
self.frame.focus_position = "body"
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key == "tab":
|
||||
@@ -974,7 +1144,30 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
else:
|
||||
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()
|
||||
title = self.title_editor.get_edit_text()
|
||||
if not content == "":
|
||||
@@ -983,6 +1176,70 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
else:
|
||||
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 dismiss_dialog(sender):
|
||||
self.dialog_open = False
|
||||
@@ -990,17 +1247,23 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
|
||||
dialog = DialogLineBox(
|
||||
urwid.Pile([
|
||||
urwid.Text("Could not output paper message,\ncheck your settings. See the log\nfile for any error messages.\n", align="center"),
|
||||
urwid.Columns([("weight", 0.6, urwid.Text("")), ("weight", 0.4, urwid.Button("OK", on_press=dismiss_dialog))])
|
||||
urwid.Text(
|
||||
"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="!"
|
||||
)
|
||||
dialog.delegate = self
|
||||
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.set_focus("body")
|
||||
self.frame.focus_position = "body"
|
||||
|
||||
def close(self):
|
||||
self.delegate.close_conversation(self)
|
||||
@@ -1060,7 +1323,7 @@ class LXMessageWidget(urwid.WidgetWrap):
|
||||
urwid.Text("")
|
||||
])
|
||||
|
||||
urwid.WidgetWrap.__init__(self, display_widget)
|
||||
super().__init__(display_widget)
|
||||
|
||||
class SyncProgressBar(urwid.ProgressBar):
|
||||
def get_text(self):
|
||||
|
||||
@@ -15,7 +15,7 @@ class DirectoryDisplay():
|
||||
])
|
||||
|
||||
self.shortcuts_display = DirectoryDisplayShortcuts(self.app)
|
||||
self.widget = urwid.Filler(pile, 'top')
|
||||
self.widget = urwid.Filler(pile, urwid.TOP)
|
||||
|
||||
def shortcuts(self):
|
||||
return self.shortcuts_display
|
||||
@@ -6,13 +6,13 @@ class IntroDisplay():
|
||||
font = urwid.font.HalfBlock5x4Font()
|
||||
|
||||
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([
|
||||
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.Text(("-= Starting =- "), align="center"),
|
||||
urwid.Text(("-= Starting =- "), align=urwid.CENTER),
|
||||
])
|
||||
|
||||
self.widget = urwid.Filler(intro)
|
||||
|
||||
@@ -10,7 +10,7 @@ class GuideDisplayShortcuts():
|
||||
self.app = app
|
||||
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):
|
||||
_selectable = True
|
||||
@@ -75,7 +75,7 @@ class GuideEntry(urwid.WidgetWrap):
|
||||
style = "topic_list_normal"
|
||||
focus_style = "list_focus"
|
||||
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):
|
||||
markup = TOPICS[topic]
|
||||
@@ -125,12 +125,12 @@ class TopicList(urwid.WidgetWrap):
|
||||
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):
|
||||
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)
|
||||
|
||||
@@ -141,16 +141,16 @@ class GuideDisplay():
|
||||
self.app = app
|
||||
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.right_area = urwid.LineBox(urwid.Filler(topic_text, "top"))
|
||||
self.right_area = urwid.LineBox(urwid.Filler(topic_text, urwid.TOP))
|
||||
|
||||
|
||||
self.columns = urwid.Columns(
|
||||
[
|
||||
("weight", GuideDisplay.list_width, self.left_area),
|
||||
("weight", 1-GuideDisplay.list_width, self.right_area)
|
||||
(urwid.WEIGHT, GuideDisplay.list_width, self.left_area),
|
||||
(urwid.WEIGHT, 1-GuideDisplay.list_width, self.right_area)
|
||||
],
|
||||
dividechars=0, focus_column=0
|
||||
)
|
||||
@@ -163,9 +163,8 @@ class GuideDisplay():
|
||||
entry.display_topic(entry.display_topic, entry.topic_name)
|
||||
|
||||
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)
|
||||
#content = urwid.LineBox(urwid.Filler(pile, "top"))
|
||||
content = urwid.LineBox(urwid.AttrMap(ScrollBar(Scrollable(pile), thumb_char="\u2503", trough_char=" "), "scrollbar"))
|
||||
|
||||
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
|
||||
|
||||
>>`!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
|
||||
- Ctrl-L Switch to Known Nodes list
|
||||
- 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-P Display peered LXMF Propagation Nodes
|
||||
|
||||
>>>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
|
||||
>>>Peered LXMF Propagation Nodes
|
||||
- Ctrl-L Switch to Announce Stream or Known Nodes
|
||||
- Ctrl-X Break peering with selected node entry
|
||||
- Ctrl-R Request immediate delivery sync of unhandled LXMs
|
||||
'''
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
@@ -335,7 +341,7 @@ By default, you can find the examples in `!~/.nomadnetwork/examples`!. If you bu
|
||||
|
||||
Sometimes, you don't want everyone to be able to view certain pages or execute certain scripts. In such cases, you can use `*authentication`* to control who gets to run certain requests.
|
||||
|
||||
To enable authentication for any page, simply add a new file to your pages directory with ".allowed" added to the file-name of the page. If your page is named "secret_page.mu", just add a file named "secret_page.mu.allowed".
|
||||
To enable authentication for any page, simply add a new file to your pages directory with ".allowed" added to the file-name of the page. If your page is named "secret_page.mu", just add a file named "secret_page.allowed".
|
||||
|
||||
For each user allowed to access the page, add a line to this file, containing the hash of that users primary identity. Users can find their own identity hash in the `![ Network ]`! part of the program, under `!Local Peer Info`!. If you want to allow access for three different users, your file would look like this:
|
||||
|
||||
@@ -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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -506,6 +512,12 @@ The number of minutes between each automatic sync. The default is equal to 6 hou
|
||||
On low-bandwidth networks, it can be useful to limit the amount of messages downloaded in each sync. The default is 8. Set to 0 to download all available messages every time a sync occurs.
|
||||
<
|
||||
|
||||
>>>
|
||||
`!compact_announce_stream = yes`!
|
||||
>>>>
|
||||
With this option enabled, Nomad Network will only display one entry in the announce stream per destination. Older announces are culled when a new one arrives.
|
||||
<
|
||||
|
||||
>> Text UI Section
|
||||
|
||||
This section hold configuration directives related to the look and feel of the text-based user interface of the program. It is delimited by the `![textui]`! header in the configuration file. Available directives, along with their default values, are as follows:
|
||||
@@ -608,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.
|
||||
<
|
||||
|
||||
>>>
|
||||
`!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`!
|
||||
>>>>
|
||||
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`!
|
||||
>>>>
|
||||
@@ -1125,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 += "\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 = {
|
||||
|
||||
@@ -10,12 +10,22 @@ class LogDisplayShortcuts():
|
||||
|
||||
class LogDisplay():
|
||||
def __init__(self, app):
|
||||
import urwid
|
||||
self.app = app
|
||||
self.log_term = LogTerminal(self.app)
|
||||
self.log_term = None
|
||||
|
||||
self.shortcuts_display = LogDisplayShortcuts(self.app)
|
||||
self.widget = urwid.LineBox(self.log_term)
|
||||
self.widget = None
|
||||
|
||||
def show(self):
|
||||
if self.log_term == None:
|
||||
self.log_term = LogTerminal(self.app)
|
||||
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):
|
||||
return self.shortcuts_display
|
||||
@@ -26,13 +36,17 @@ class LogTerminal(urwid.WidgetWrap):
|
||||
self.log_term = urwid.Terminal(
|
||||
("tail", "-fn50", self.app.logfilepath),
|
||||
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):
|
||||
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)
|
||||
@@ -1,5 +1,4 @@
|
||||
import RNS
|
||||
import time
|
||||
|
||||
from .Network import *
|
||||
from .Conversations import *
|
||||
@@ -116,6 +115,7 @@ class MainDisplay():
|
||||
|
||||
def show_log(self, user_data):
|
||||
self.sub_displays.active_display = self.sub_displays.log_display
|
||||
self.sub_displays.log_display.show()
|
||||
self.update_active_sub_display()
|
||||
|
||||
def show_guide(self, user_data):
|
||||
@@ -125,6 +125,8 @@ class MainDisplay():
|
||||
def update_active_sub_display(self):
|
||||
self.frame.contents["body"] = (self.sub_displays.active().widget, None)
|
||||
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):
|
||||
self.frame.contents["footer"] = (self.sub_displays.active().shortcuts().widget, None)
|
||||
@@ -155,7 +157,7 @@ class MainDisplay():
|
||||
class MenuColumns(urwid.Columns):
|
||||
def keypress(self, size, key):
|
||||
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)
|
||||
|
||||
@@ -169,7 +171,7 @@ class MenuDisplay():
|
||||
|
||||
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_conversations = (17, MenuButton("Conversations", on_press=handler.show_conversations))
|
||||
button_directory = (13, MenuButton("Directory", on_press=handler.show_directory))
|
||||
|
||||
@@ -15,7 +15,7 @@ class MapDisplay():
|
||||
])
|
||||
|
||||
self.shortcuts_display = MapDisplayShortcuts(self.app)
|
||||
self.widget = urwid.Filler(pile, 'top')
|
||||
self.widget = urwid.Filler(pile, urwid.TOP)
|
||||
|
||||
def shortcuts(self):
|
||||
return self.shortcuts_display
|
||||
@@ -4,7 +4,6 @@ import random
|
||||
import time
|
||||
from urwid.util import is_mouse_press
|
||||
from urwid.text_layout import calc_coords
|
||||
import re
|
||||
|
||||
DEFAULT_FG_DARK = "ddd"
|
||||
DEFAULT_FG_LIGHT = "222"
|
||||
@@ -162,7 +161,7 @@ def parse_line(line, state, url_delegate):
|
||||
else:
|
||||
tw = urwid.Text(o, align=state["align"])
|
||||
|
||||
widgets.append(("pack", tw))
|
||||
widgets.append((urwid.PACK, tw))
|
||||
else:
|
||||
if o["type"] == "field":
|
||||
fw = o["width"]
|
||||
@@ -608,7 +607,7 @@ class LinkSpec(urwid.AttrSpec):
|
||||
self.link_target = link_target
|
||||
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):
|
||||
@@ -618,7 +617,7 @@ class LinkableText(urwid.Text):
|
||||
signals = ["click", "change"]
|
||||
|
||||
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._cursor_position = 0
|
||||
self.key_timeout = 3
|
||||
@@ -729,7 +728,7 @@ class LinkableText(urwid.Text):
|
||||
|
||||
def render(self, size, focus=False):
|
||||
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):
|
||||
c = urwid.CompositeCanvas(c)
|
||||
@@ -751,41 +750,45 @@ class LinkableText(urwid.Text):
|
||||
return x, y
|
||||
|
||||
def mouse_event(self, size, event, button, x, y, focus):
|
||||
if button != 1 or not is_mouse_press(event):
|
||||
return False
|
||||
else:
|
||||
(maxcol,) = size
|
||||
translation = self.get_line_translation(maxcol)
|
||||
line_offset = 0
|
||||
|
||||
if self.align == "center":
|
||||
line_offset = translation[y][1][1]-translation[y][0][0]
|
||||
if x < translation[y][0][0]:
|
||||
x = translation[y][0][0]
|
||||
|
||||
if x > translation[y][1][0]+translation[y][0][0]:
|
||||
x = translation[y][1][0]+translation[y][0][0]
|
||||
|
||||
elif self.align == "right":
|
||||
line_offset = translation[y][1][1]-translation[y][0][0]
|
||||
if x < translation[y][0][0]:
|
||||
x = translation[y][0][0]
|
||||
|
||||
try:
|
||||
if button != 1 or not is_mouse_press(event):
|
||||
return False
|
||||
else:
|
||||
line_offset = translation[y][0][1]
|
||||
if x > translation[y][0][0]:
|
||||
x = translation[y][0][0]
|
||||
(maxcol,) = size
|
||||
translation = self.get_line_translation(maxcol)
|
||||
line_offset = 0
|
||||
|
||||
pos = line_offset+x
|
||||
if self.align == "center":
|
||||
line_offset = translation[y][1][1]-translation[y][0][0]
|
||||
if x < translation[y][0][0]:
|
||||
x = translation[y][0][0]
|
||||
|
||||
self._cursor_position = pos
|
||||
item = self.find_item_at_pos(self._cursor_position)
|
||||
if x > translation[y][1][0]+translation[y][0][0]:
|
||||
x = translation[y][1][0]+translation[y][0][0]
|
||||
|
||||
if item != None:
|
||||
if isinstance(item, LinkSpec):
|
||||
self.handle_link(item.link_target, item.link_fields)
|
||||
elif self.align == "right":
|
||||
line_offset = translation[y][1][1]-translation[y][0][0]
|
||||
if x < translation[y][0][0]:
|
||||
x = translation[y][0][0]
|
||||
|
||||
self._invalidate()
|
||||
self._emit("change")
|
||||
else:
|
||||
line_offset = translation[y][0][1]
|
||||
if x > translation[y][0][0]:
|
||||
x = translation[y][0][0]
|
||||
|
||||
return True
|
||||
pos = line_offset+x
|
||||
|
||||
self._cursor_position = pos
|
||||
item = self.find_item_at_pos(self._cursor_position)
|
||||
|
||||
if item != None:
|
||||
if isinstance(item, LinkSpec):
|
||||
self.handle_link(item.link_target, item.link_fields)
|
||||
|
||||
self._invalidate()
|
||||
self._emit("change")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
return False
|
||||
@@ -2,6 +2,7 @@ import RNS
|
||||
import urwid
|
||||
import nomadnet
|
||||
import time
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from nomadnet.Directory import DirectoryEntry
|
||||
from nomadnet.vendor.additional_urwid_widgets import IndicativeListBox, MODIFIER_KEY
|
||||
@@ -13,9 +14,7 @@ class NetworkDisplayShortcuts():
|
||||
self.app = app
|
||||
g = app.ui.glyphs
|
||||
|
||||
self.widget = urwid.AttrMap(urwid.Text("[C-l] Nodes/Announces [C-x] Remove [C-w] Disconnect [C-d] Back [C-f] Forward [C-r] Reload [C-u] URL [C-g] Fullscreen"), "shortcutbar")
|
||||
# "[C-"+g["arrow_u"]+g["arrow_d"]+"] Navigate Lists"
|
||||
|
||||
self.widget = urwid.AttrMap(urwid.Text("[C-l] Nodes/Announces [C-x] Remove [C-w] Disconnect [C-d] Back [C-f] Forward [C-r] Reload [C-u] URL [C-g] Fullscreen [C-s / C-b] Save Node"), "shortcutbar")
|
||||
|
||||
class DialogLineBox(urwid.LineBox):
|
||||
def keypress(self, size, key):
|
||||
@@ -53,7 +52,7 @@ class ListEntry(urwid.Text):
|
||||
class AnnounceInfo(urwid.WidgetWrap):
|
||||
def keypress(self, size, key):
|
||||
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)
|
||||
else:
|
||||
return super(AnnounceInfo, self).keypress(size, key)
|
||||
@@ -116,7 +115,7 @@ class AnnounceInfo(urwid.WidgetWrap):
|
||||
style = "list_untrusted"
|
||||
|
||||
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)
|
||||
|
||||
def connect(sender):
|
||||
@@ -188,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)
|
||||
|
||||
if is_node:
|
||||
type_button = ("weight", 0.45, urwid.Button("Connect", on_press=connect))
|
||||
msg_button = ("weight", 0.45, urwid.Button("Msg Op", on_press=msg_op))
|
||||
save_button = ("weight", 0.45, urwid.Button("Save", on_press=save_node))
|
||||
type_button = (urwid.WEIGHT, 0.45, urwid.Button("Connect", on_press=connect))
|
||||
msg_button = (urwid.WEIGHT, 0.45, urwid.Button("Msg Op", on_press=msg_op))
|
||||
save_button = (urwid.WEIGHT, 0.45, urwid.Button("Save", on_press=save_node))
|
||||
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
|
||||
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
|
||||
|
||||
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:
|
||||
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 = []
|
||||
|
||||
if is_pn:
|
||||
pile_widgets = [
|
||||
urwid.Text("Time : "+ts_string, align="left"),
|
||||
urwid.Text("Addr : "+addr_str, align="left"),
|
||||
urwid.Text("Type : "+type_string, align="left"),
|
||||
urwid.Text("Time : "+ts_string, align=urwid.LEFT),
|
||||
urwid.Text("Addr : "+addr_str, align=urwid.LEFT),
|
||||
urwid.Text("Type : "+type_string, align=urwid.LEFT),
|
||||
urwid.Divider(g["divider1"]),
|
||||
button_columns
|
||||
]
|
||||
|
||||
else:
|
||||
pile_widgets = [
|
||||
urwid.Text("Time : "+ts_string, align="left"),
|
||||
urwid.Text("Addr : "+addr_str, align="left"),
|
||||
urwid.Text("Type : "+type_string, align="left"),
|
||||
urwid.Text("Name : "+display_str, align="left"),
|
||||
urwid.Text(["Trust : ", (style, trust_str)], align="left"),
|
||||
urwid.Text("Time : "+ts_string, align=urwid.LEFT),
|
||||
urwid.Text("Addr : "+addr_str, align=urwid.LEFT),
|
||||
urwid.Text("Type : "+type_string, align=urwid.LEFT),
|
||||
urwid.Text("Name : "+display_str, align=urwid.LEFT),
|
||||
urwid.Text(["Trust : ", (style, trust_str)], align=urwid.LEFT),
|
||||
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"]),
|
||||
button_columns
|
||||
]
|
||||
|
||||
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 = 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):
|
||||
@@ -297,13 +308,13 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
|
||||
urwid.connect_signal(widget, "click", self.display_announce, announce)
|
||||
|
||||
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):
|
||||
try:
|
||||
parent = self.app.ui.main_display.sub_displays.network_display
|
||||
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)
|
||||
|
||||
except KeyError as e:
|
||||
@@ -314,17 +325,21 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
|
||||
def close_req(sender):
|
||||
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])
|
||||
|
||||
confirmed_button = urwid.Button("Request keys", on_press=confirmed)
|
||||
|
||||
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([
|
||||
("weight", 0.45, confirmed_button),
|
||||
("weight", 0.1, urwid.Text("")),
|
||||
("weight", 0.45, urwid.Button("Close", on_press=dismiss_dialog)),
|
||||
(urwid.WEIGHT, 0.45, confirmed_button),
|
||||
(urwid.WEIGHT, 0.1, urwid.Text("")),
|
||||
(urwid.WEIGHT, 0.45, urwid.Button("Close", on_press=dismiss_dialog)),
|
||||
])
|
||||
])
|
||||
|
||||
@@ -336,9 +351,18 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
|
||||
dialog.delegate = self.delegate.parent
|
||||
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)
|
||||
|
||||
def timestamp(self):
|
||||
@@ -366,11 +390,11 @@ class AnnounceStream(urwid.WidgetWrap):
|
||||
)
|
||||
|
||||
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):
|
||||
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":
|
||||
self.delete_selected_entry()
|
||||
|
||||
@@ -465,7 +489,7 @@ class ListDialogLineBox(urwid.LineBox):
|
||||
class KnownNodeInfo(urwid.WidgetWrap):
|
||||
def keypress(self, size, key):
|
||||
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)
|
||||
else:
|
||||
return super(KnownNodeInfo, self).keypress(size, key)
|
||||
@@ -482,6 +506,12 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
trust_level = self.app.directory.trust_level(source_hash)
|
||||
trust_str = ""
|
||||
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:
|
||||
display_str = self.app.directory.simplest_display_str(source_hash)
|
||||
else:
|
||||
@@ -542,6 +572,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
r_trusted = urwid.RadioButton(trust_button_group, "Trusted", state=trusted_selected)
|
||||
|
||||
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)
|
||||
op_hash = None
|
||||
@@ -553,7 +584,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
op_str = "Unknown"
|
||||
|
||||
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)
|
||||
|
||||
def connect(sender):
|
||||
@@ -588,7 +619,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
def ident_change(sender, userdata):
|
||||
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)
|
||||
|
||||
def save_node(sender):
|
||||
@@ -606,8 +637,16 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
trust_level = DirectoryEntry.TRUSTED
|
||||
|
||||
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.ui.main_display.sub_displays.network_display.directory_change_callback()
|
||||
|
||||
@@ -616,20 +655,21 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
|
||||
show_known_nodes(None)
|
||||
|
||||
back_button = ("weight", 0.2, urwid.Button("Back", on_press=show_known_nodes))
|
||||
connect_button = ("weight", 0.2, urwid.Button("Connect", on_press=connect))
|
||||
save_button = ("weight", 0.2, urwid.Button("Save", on_press=save_node))
|
||||
msg_button = ("weight", 0.2, urwid.Button("Msg Op", on_press=msg_op))
|
||||
bdiv = ("weight", 0.02, urwid.Text(""))
|
||||
back_button = (urwid.WEIGHT, 0.2, urwid.Button("Back", on_press=show_known_nodes))
|
||||
connect_button = (urwid.WEIGHT, 0.2, urwid.Button("Connect", on_press=connect))
|
||||
save_button = (urwid.WEIGHT, 0.2, urwid.Button("Save", on_press=save_node))
|
||||
msg_button = (urwid.WEIGHT, 0.2, urwid.Button("Msg Op", on_press=msg_op))
|
||||
bdiv = (urwid.WEIGHT, 0.02, urwid.Text(""))
|
||||
|
||||
button_columns = urwid.Columns([back_button, bdiv, connect_button, bdiv, msg_button, bdiv, save_button])
|
||||
|
||||
pile_widgets = [
|
||||
urwid.Text("Type : "+type_string, align="left"),
|
||||
urwid.Text("Type : "+type_string, align=urwid.LEFT),
|
||||
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.Text(lxmf_addr_str, align="center"),
|
||||
urwid.Text(lxmf_addr_str, align=urwid.CENTER),
|
||||
urwid.Divider(g["divider1"]),
|
||||
propagation_node_checkbox,
|
||||
connect_identify_checkbox,
|
||||
@@ -641,7 +681,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
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)
|
||||
|
||||
hops = RNS.Transport.hops_to(source_hash)
|
||||
@@ -655,7 +695,7 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
else:
|
||||
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 = urwid.Pile(pile_widgets)
|
||||
@@ -664,9 +704,9 @@ class KnownNodeInfo(urwid.WidgetWrap):
|
||||
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
|
||||
@@ -679,9 +719,9 @@ class ExceptionHandlingListBox(IndicativeListBox):
|
||||
|
||||
except Exception as e:
|
||||
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":
|
||||
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:
|
||||
RNS.log("An error occurred while processing an interface event. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
@@ -708,14 +748,23 @@ class KnownNodes(urwid.WidgetWrap):
|
||||
else:
|
||||
self.no_content = True
|
||||
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.display_widget = urwid.Filler(self.pile, valign="top", height="pack")
|
||||
self.pile = urwid.Pile([
|
||||
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):
|
||||
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":
|
||||
self.delete_selected_entry()
|
||||
|
||||
@@ -742,27 +791,27 @@ class KnownNodes(urwid.WidgetWrap):
|
||||
|
||||
def show_info(sender):
|
||||
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)
|
||||
|
||||
|
||||
dialog = ListDialogLineBox(
|
||||
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([
|
||||
("weight", 0.45, urwid.Button("Yes", on_press=confirmed)),
|
||||
("weight", 0.1, urwid.Text("")),
|
||||
("weight", 0.45, urwid.Button("No", on_press=dismiss_dialog)),
|
||||
("weight", 0.1, urwid.Text("")),
|
||||
("weight", 0.45, urwid.Button("Info", on_press=show_info))])
|
||||
(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)),
|
||||
(urwid.WEIGHT, 0.1, urwid.Text("")),
|
||||
(urwid.WEIGHT, 0.45, urwid.Button("Info", on_press=show_info))])
|
||||
]), title="?"
|
||||
)
|
||||
dialog.delegate = self.delegate
|
||||
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)
|
||||
|
||||
def delete_selected_entry(self):
|
||||
@@ -781,16 +830,29 @@ class KnownNodes(urwid.WidgetWrap):
|
||||
|
||||
dialog = ListDialogLineBox(
|
||||
urwid.Pile([
|
||||
urwid.Text("Delete Node\n"+self.app.directory.simplest_display_str(source_hash)+"\n", align="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.Text("Delete Node\n"+self.app.directory.simplest_display_str(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="?"
|
||||
)
|
||||
dialog.delegate = self.delegate
|
||||
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)
|
||||
|
||||
|
||||
@@ -854,7 +916,7 @@ class NodeEntry(urwid.WidgetWrap):
|
||||
|
||||
self.display_widget = urwid.AttrMap(widget, style, focus_style)
|
||||
self.display_widget.source_hash = source_hash
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
|
||||
class AnnounceTime(urwid.WidgetWrap):
|
||||
@@ -865,7 +927,7 @@ class AnnounceTime(urwid.WidgetWrap):
|
||||
self.display_widget = urwid.Text("")
|
||||
self.update_time()
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def update_time(self):
|
||||
self.last_announce_string = "Never"
|
||||
@@ -897,7 +959,7 @@ class NodeAnnounceTime(urwid.WidgetWrap):
|
||||
self.display_widget = urwid.Text("")
|
||||
self.update_time()
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def update_time(self):
|
||||
self.last_announce_string = "Never"
|
||||
@@ -928,7 +990,7 @@ class NodeActiveConnections(urwid.WidgetWrap):
|
||||
self.display_widget = urwid.Text("")
|
||||
self.update_stat()
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def update_stat(self):
|
||||
self.stat_string = "None"
|
||||
@@ -959,16 +1021,16 @@ class NodeStorageStats(urwid.WidgetWrap):
|
||||
self.display_widget = urwid.Text("")
|
||||
self.update_stat()
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def update_stat(self):
|
||||
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
|
||||
used = self.app.message_router.message_storage_size()
|
||||
|
||||
if limit != None:
|
||||
if limit != None and used != None:
|
||||
pct = round((used/limit)*100, 1)
|
||||
pct_str = str(pct)+"%, "
|
||||
limit_str = " of "+RNS.prettysize(limit)
|
||||
@@ -1002,7 +1064,7 @@ class NodeTotalConnections(urwid.WidgetWrap):
|
||||
self.display_widget = urwid.Text("")
|
||||
self.update_stat()
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def update_stat(self):
|
||||
self.stat_string = "None"
|
||||
@@ -1034,7 +1096,7 @@ class NodeTotalPages(urwid.WidgetWrap):
|
||||
self.display_widget = urwid.Text("")
|
||||
self.update_stat()
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def update_stat(self):
|
||||
self.stat_string = "None"
|
||||
@@ -1066,7 +1128,7 @@ class NodeTotalFiles(urwid.WidgetWrap):
|
||||
self.display_widget = urwid.Text("")
|
||||
self.update_stat()
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def update_stat(self):
|
||||
self.stat_string = "None"
|
||||
@@ -1115,45 +1177,45 @@ class LocalPeer(urwid.WidgetWrap):
|
||||
|
||||
dialog = DialogLineBox(
|
||||
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)
|
||||
]), title=g["info"]
|
||||
)
|
||||
dialog.delegate = 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
|
||||
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.parent.left_pile.contents[1] = (overlay, options)
|
||||
|
||||
def announce_query(sender):
|
||||
def dismiss_dialog(sender):
|
||||
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.app.announce_now()
|
||||
|
||||
dialog = DialogLineBox(
|
||||
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)
|
||||
]), title=g["info"]
|
||||
)
|
||||
dialog.delegate = 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
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
if LocalPeer.announce_timer == None:
|
||||
@@ -1174,11 +1236,15 @@ class LocalPeer(urwid.WidgetWrap):
|
||||
self.t_last_announce,
|
||||
announce_button,
|
||||
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):
|
||||
self.t_last_announce.start()
|
||||
@@ -1202,7 +1268,7 @@ class NodeInfo(urwid.WidgetWrap):
|
||||
widget_style = ""
|
||||
|
||||
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)
|
||||
|
||||
if self.app.enable_node:
|
||||
@@ -1226,25 +1292,25 @@ class NodeInfo(urwid.WidgetWrap):
|
||||
def announce_query(sender):
|
||||
def dismiss_dialog(sender):
|
||||
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.app.node.announce()
|
||||
|
||||
dialog = DialogLineBox(
|
||||
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)
|
||||
]), title=g["info"]
|
||||
)
|
||||
dialog.delegate = 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
|
||||
|
||||
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)
|
||||
|
||||
def connect_query(sender):
|
||||
@@ -1293,18 +1359,41 @@ class NodeInfo(urwid.WidgetWrap):
|
||||
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))
|
||||
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)
|
||||
connect_button = urwid.Button("Browse", on_press=connect_query)
|
||||
reset_button = urwid.Button("Rst Stats", on_press=stats_query)
|
||||
|
||||
pile = urwid.Pile([
|
||||
if not self.app.disable_propagation:
|
||||
pile = urwid.Pile([
|
||||
t_id,
|
||||
e_name,
|
||||
urwid.Divider(g["divider1"]),
|
||||
e_lxmf,
|
||||
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([
|
||||
t_id,
|
||||
e_name,
|
||||
urwid.Divider(g["divider1"]),
|
||||
e_lxmf,
|
||||
urwid.Divider(g["divider1"]),
|
||||
self.t_last_announce,
|
||||
self.t_storage_stats,
|
||||
self.t_active_links,
|
||||
@@ -1313,25 +1402,25 @@ class NodeInfo(urwid.WidgetWrap):
|
||||
self.t_total_files,
|
||||
urwid.Divider(g["divider1"]),
|
||||
urwid.Columns([
|
||||
("weight", 5, urwid.Button("Back", on_press=show_peer_info)),
|
||||
("weight", 0.5, urwid.Text("")),
|
||||
("weight", 6, connect_button),
|
||||
("weight", 0.5, urwid.Text("")),
|
||||
("weight", 8, reset_button),
|
||||
("weight", 0.5, urwid.Text("")),
|
||||
("weight", 7, announce_button),
|
||||
(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="center"),
|
||||
urwid.Text("\nThis instance is not hosting a node\n\n", align="center"),
|
||||
urwid.Padding(urwid.Button("Back", on_press=show_peer_info), "center", "pack")
|
||||
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
|
||||
|
||||
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):
|
||||
if self.app.node != None:
|
||||
@@ -1354,7 +1443,7 @@ class UpdatingText(urwid.WidgetWrap):
|
||||
self.append_text = append_text
|
||||
self.update()
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def update(self):
|
||||
self.value = self.value_method()
|
||||
@@ -1396,7 +1485,7 @@ class NetworkStats(urwid.WidgetWrap):
|
||||
|
||||
self.display_widget = urwid.LineBox(pile, title="Network Stats")
|
||||
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
def start(self):
|
||||
self.w_heard_peers.start()
|
||||
@@ -1448,9 +1537,9 @@ class NetworkDisplay():
|
||||
|
||||
self.list_display = 1
|
||||
self.left_pile = NetworkLeftPile([
|
||||
("weight", 1, self.known_nodes_display),
|
||||
# ("pack", self.network_stats_display),
|
||||
("pack", self.local_peer_display),
|
||||
(urwid.WEIGHT, 1, self.known_nodes_display),
|
||||
# (urwid.PACK, self.network_stats_display),
|
||||
(urwid.PACK, self.local_peer_display),
|
||||
])
|
||||
|
||||
self.left_pile.parent = self
|
||||
@@ -1461,10 +1550,10 @@ class NetworkDisplay():
|
||||
|
||||
self.columns = urwid.Columns(
|
||||
[
|
||||
# ("weight", NetworkDisplay.list_width, self.left_area),
|
||||
# ("weight", self.right_area_width, self.right_area)
|
||||
# (urwid.WEIGHT, NetworkDisplay.list_width, self.left_area),
|
||||
# (urwid.WEIGHT, self.right_area_width, self.right_area)
|
||||
(NetworkDisplay.given_list_width, self.left_area),
|
||||
("weight", 1, self.right_area)
|
||||
(urwid.WEIGHT, 1, self.right_area)
|
||||
],
|
||||
dividechars=0, focus_column=0
|
||||
)
|
||||
@@ -1474,11 +1563,11 @@ class NetworkDisplay():
|
||||
|
||||
def toggle_list(self):
|
||||
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.list_display = 0
|
||||
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.list_display = 1
|
||||
|
||||
@@ -1493,7 +1582,7 @@ class NetworkDisplay():
|
||||
self.widget.contents[0] = (self.left_area, options)
|
||||
|
||||
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)
|
||||
|
||||
if self.list_display != 0:
|
||||
@@ -1510,7 +1599,7 @@ class NetworkDisplay():
|
||||
|
||||
if selected_node_hash != None:
|
||||
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)
|
||||
|
||||
def focus_lists(self):
|
||||
@@ -1529,10 +1618,10 @@ class NetworkDisplay():
|
||||
|
||||
def close_list_dialogs(self):
|
||||
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)
|
||||
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)
|
||||
|
||||
def start(self):
|
||||
@@ -1575,16 +1664,25 @@ class LXMFPeers(urwid.WidgetWrap):
|
||||
else:
|
||||
self.no_content = True
|
||||
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.display_widget = urwid.Filler(self.pile, valign="top", height="pack")
|
||||
self.pile = urwid.Pile([
|
||||
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)
|
||||
|
||||
urwid.WidgetWrap.__init__(self, urwid.AttrMap(urwid.LineBox(self.display_widget, title="LXMF Propagation Peers"), widget_style))
|
||||
if hasattr(self, "peer_list") and self.peer_list:
|
||||
pl = len(self.peer_list)
|
||||
else:
|
||||
pl = 0
|
||||
super().__init__(urwid.AttrMap(urwid.LineBox(self.display_widget, title=f"LXMF Propagation Peers ({pl})"), widget_style))
|
||||
|
||||
def keypress(self, size, key):
|
||||
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":
|
||||
self.delete_selected_entry()
|
||||
elif key == "ctrl r":
|
||||
self.sync_selected_entry()
|
||||
|
||||
return super(LXMFPeers, self).keypress(size, key)
|
||||
|
||||
@@ -1600,6 +1698,48 @@ class LXMFPeers(urwid.WidgetWrap):
|
||||
self.delegate.reinit_lxmf_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):
|
||||
self.peer_list = self.app.message_router.peers
|
||||
@@ -1613,17 +1753,18 @@ class LXMFPeers(urwid.WidgetWrap):
|
||||
|
||||
def make_peer_widgets(self):
|
||||
widget_list = []
|
||||
for peer_id in self.peer_list:
|
||||
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:
|
||||
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
|
||||
widget_list.append(pe)
|
||||
|
||||
# TODO: Sort list
|
||||
return widget_list
|
||||
|
||||
class LXMFPeerEntry(urwid.WidgetWrap):
|
||||
def __init__(self, app, peer, delegate):
|
||||
def __init__(self, app, peer, delegate, trust_level):
|
||||
destination_hash = peer.destination_hash
|
||||
|
||||
self.app = app
|
||||
@@ -1635,7 +1776,7 @@ class LXMFPeerEntry(urwid.WidgetWrap):
|
||||
node_hash = RNS.Destination.hash_from_name_and_identity("nomadnetwork.node", node_identity)
|
||||
display_name = self.app.directory.alleged_display_str(node_hash)
|
||||
if display_name != None:
|
||||
display_str += " "+str(display_name)
|
||||
display_str = str(display_name)+"\n "+display_str
|
||||
|
||||
sym = g["sent"]
|
||||
style = "list_unknown"
|
||||
@@ -1645,15 +1786,21 @@ class LXMFPeerEntry(urwid.WidgetWrap):
|
||||
if hasattr(peer, "alive"):
|
||||
if peer.alive:
|
||||
alive_string = "Available"
|
||||
if trust_level == DirectoryEntry.TRUSTED:
|
||||
style = "list_trusted"
|
||||
focus_style = "list_focus_trusted"
|
||||
else:
|
||||
style = "list_normal"
|
||||
focus_style = "list_focus"
|
||||
else:
|
||||
alive_string = "Unresponsive"
|
||||
style = "list_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")
|
||||
# urwid.connect_signal(widget, "click", delegate.connect_node, node)
|
||||
|
||||
self.display_widget = urwid.AttrMap(widget, style, focus_style)
|
||||
self.display_widget.destination_hash = destination_hash
|
||||
urwid.WidgetWrap.__init__(self, self.display_widget)
|
||||
super().__init__(self.display_widget)
|
||||
|
||||
|
||||
def pretty_date(time=False):
|
||||
|
||||
6
nomadnet/vendor/Scrollable.py
vendored
6
nomadnet/vendor/Scrollable.py
vendored
@@ -268,10 +268,10 @@ class Scrollable(urwid.WidgetDecoration):
|
||||
def _get_original_widget_size(self, size):
|
||||
ow = self._original_widget
|
||||
sizing = ow.sizing()
|
||||
if FIXED in sizing:
|
||||
return ()
|
||||
elif FLOW in sizing:
|
||||
if FLOW in sizing:
|
||||
return (size[0],)
|
||||
elif FIXED in sizing:
|
||||
return ()
|
||||
|
||||
def get_scrollpos(self, size=None, focus=False):
|
||||
"""Current scrolling position
|
||||
|
||||
@@ -269,11 +269,13 @@ class IndicativeListBox(urwid.WidgetWrap):
|
||||
|
||||
# mousewheel up
|
||||
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
|
||||
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()
|
||||
|
||||
|
||||
2
setup.py
2
setup.py
@@ -30,6 +30,6 @@ setuptools.setup(
|
||||
entry_points= {
|
||||
'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
|
||||
},
|
||||
install_requires=["rns>=0.4.9", "lxmf>=0.3.1", "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",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user