async join
All checks were successful
checks-impure / test (pull_request) Successful in 1m36s
checks / test (pull_request) Successful in 2m17s

This commit is contained in:
Johannes Kirschbauer 2024-02-05 14:30:47 +07:00
parent 1a8a6acfb1
commit c52c83002c
Signed by: hsjobeki
SSH Key Fingerprint: SHA256:vX3utDqig7Ph5L0JPv87ZTPb/w7cMzREKVZzzLFg9qU
5 changed files with 67 additions and 41 deletions

View File

@ -4,8 +4,6 @@ from pathlib import Path
import gi
from clan_vm_manager.models.use_join import Join
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
@ -28,9 +26,6 @@ class MainApplication(Adw.Application):
self.config = config
self.connect("shutdown", self.on_shutdown)
if config.url:
Join.use().push(config.url)
def on_shutdown(self, app: Gtk.Application) -> None:
log.debug("Shutting down")
VMS.use().kill_all()

View File

@ -1,30 +1,45 @@
import logging
import threading
from collections.abc import Callable
from typing import Any
from typing import Any, ClassVar
import gi
from clan_cli import ClanError
from clan_cli.clan_uri import ClanURI
from clan_cli.history.add import HistoryEntry, add_history
from clan_cli.history.add import add_history
from clan_vm_manager.errors.show_error import show_error_dialog
from clan_vm_manager.models.use_vms import VMS, Clans
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gio, GObject
from gi.repository import Gio, GLib, GObject
log = logging.getLogger(__name__)
class JoinValue(GObject.Object):
# TODO: custom signals for async join
# __gsignals__: ClassVar = {}
__gsignals__: ClassVar = {
"join_finished": (GObject.SignalFlags.RUN_FIRST, None, [GObject.Object]),
}
url: ClanURI
def __init__(self, url: ClanURI) -> None:
def __init__(
self, url: ClanURI, on_join: Callable[["JoinValue", Any], None]
) -> None:
super().__init__()
self.url = url
self.connect("join_finished", on_join)
def __join(self) -> None:
add_history(self.url, all_machines=False)
GLib.idle_add(lambda: self.emit("join_finished", self))
def join(self) -> None:
threading.Thread(target=self.__join).start()
class Join:
@ -48,26 +63,27 @@ class Join:
return cls._instance
def push(self, url: ClanURI) -> None:
def push(self, url: ClanURI, on_join: Callable[[JoinValue], None]) -> None:
"""
Add a join request.
This method can add multiple join requests if called subsequently for each request.
"""
self.list_store.append(JoinValue(url))
def join(self, item: JoinValue, cb: Callable[[list[HistoryEntry]], None]) -> None:
# TODO: remove the item that was accepted join from this list
# and call the success function. (The caller is responsible for handling the success)
def after_join(item: JoinValue, _: Any) -> None:
self.discard(item)
Clans.use().refresh()
VMS.use().refresh()
print("Refreshed list after join")
on_join(item)
self.list_store.append(JoinValue(url, after_join))
def join(self, item: JoinValue) -> None:
try:
log.info(f"trying to join: {item.url}")
history = add_history(item.url)
cb(history)
self.discard(item)
item.join()
except ClanError as e:
show_error_dialog(e)
pass
def discard(self, item: JoinValue) -> None:
(has, idx) = self.list_store.find(item)

View File

@ -38,6 +38,18 @@ class ClanGroup(GObject.Object):
self.list_store.append(vm)
def init_grp_store(list_store: Gio.ListStore) -> None:
groups: dict[str | Path, list["VM"]] = {}
for vm in get_saved_vms():
ll = groups.get(vm.data.flake.flake_url, [])
ll.append(vm)
groups[vm.data.flake.flake_url] = ll
for url, vm_list in groups.items():
grp = ClanGroup(url, vm_list)
list_store.append(grp)
class Clans:
list_store: Gio.ListStore
_instance: "None | ClanGroup" = None
@ -51,19 +63,14 @@ class Clans:
if cls._instance is None:
cls._instance = cls.__new__(cls)
cls.list_store = Gio.ListStore.new(ClanGroup)
groups: dict[str | Path, list["VM"]] = {}
for vm in get_saved_vms():
ll = groups.get(vm.data.flake.flake_url, [])
ll.append(vm)
groups[vm.data.flake.flake_url] = ll
for url, vms in groups.items():
grp = ClanGroup(url, vms)
cls.list_store.append(grp)
init_grp_store(cls.list_store)
return cls._instance
def refresh(self) -> None:
self.list_store.remove_all()
init_grp_store(self.list_store)
class VM(GObject.Object):
# Define a custom signal with the name "vm_stopped" and a string argument for the message

View File

@ -3,8 +3,9 @@ from collections.abc import Callable
from functools import partial
import gi
from clan_cli.history.add import HistoryEntry
from clan_cli import history
from clan_vm_manager.models.interfaces import ClanConfig
from clan_vm_manager.models.use_join import Join, JoinValue
from clan_vm_manager.models.use_views import Views
@ -42,12 +43,16 @@ class ClanList(Gtk.Box):
# ------------------------#
"""
def __init__(self) -> None:
def __init__(self, config: ClanConfig) -> None:
super().__init__(orientation=Gtk.Orientation.VERTICAL)
groups = Clans.use()
join = Join.use()
if config.url:
join.push(config.url, self.after_join)
self.__init_machines = history.add.list_history()
self.join_boxed_list = create_boxed_list(
model=join.list_store, render_row=self.render_join_row
)
@ -152,6 +157,7 @@ class ClanList(Gtk.Box):
cancel_button = Gtk.Button(label="Cancel")
cancel_button.add_css_class("error")
cancel_button.connect("clicked", partial(self.on_discard_clicked, item))
self.cancel_button = cancel_button
trust_button = Gtk.Button(label="Join")
trust_button.add_css_class("success")
@ -178,18 +184,20 @@ class ClanList(Gtk.Box):
dialog.set_transient_for(p) # set the parent window of the dialog
dialog.choose()
def after_join(self, item: JoinValue) -> None:
# If the join request list is empty disable the shadow artefact
if not Join.use().list_store.get_n_items():
self.join_boxed_list.add_css_class("no-shadow")
print("after join in list")
def on_trust_clicked(self, item: JoinValue, widget: Gtk.Widget) -> None:
def on_join(_history: list[HistoryEntry]) -> None:
VMS.use().refresh()
widget.set_sensitive(False)
self.cancel_button.set_sensitive(False)
# TODO(@hsjobeki): Confirm and edit details
# Views.use().view.set_visible_child_name("details")
Join.use().join(item, cb=on_join)
# If the join request list is empty disable the shadow artefact
if not Join.use().list_store.get_n_items():
self.join_boxed_list.add_css_class("no-shadow")
Join.use().join(item)
def on_discard_clicked(self, item: JoinValue, widget: Gtk.Widget) -> None:
Join.use().discard(item)

View File

@ -29,7 +29,7 @@ class MainWindow(Adw.ApplicationWindow):
scroll = Gtk.ScrolledWindow()
scroll.set_propagate_natural_height(True)
scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scroll.set_child(ClanList())
scroll.set_child(ClanList(config))
stack_view.add_named(scroll, "list")
stack_view.add_named(Details(), "details")