add join to list
This commit is contained in:
@@ -117,6 +117,11 @@ class ClanURI:
|
|||||||
def get_full_uri(self) -> str:
|
def get_full_uri(self) -> str:
|
||||||
return self._full_uri
|
return self._full_uri
|
||||||
|
|
||||||
|
# TODO(@Qubasa): return a comparable id e.g. f"{url}#{attr}"
|
||||||
|
# This should be our standard.
|
||||||
|
def get_id(self) -> str:
|
||||||
|
return f"{self._components.path}#{self._components.fragment}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_path(
|
def from_path(
|
||||||
cls, # noqa
|
cls, # noqa
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def main() -> None:
|
|||||||
|
|
||||||
def show_join(args: argparse.Namespace) -> None:
|
def show_join(args: argparse.Namespace) -> None:
|
||||||
app = MainApplication(
|
app = MainApplication(
|
||||||
config=ClanConfig(url=args.clan_uri, initial_view="join.trust"),
|
config=ClanConfig(url=args.clan_uri, initial_view="list"),
|
||||||
)
|
)
|
||||||
return app.run()
|
return app.run()
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ from pathlib import Path
|
|||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
|
from clan_vm_manager.models.use_join import Join
|
||||||
|
|
||||||
gi.require_version("Gtk", "4.0")
|
gi.require_version("Gtk", "4.0")
|
||||||
gi.require_version("Adw", "1")
|
gi.require_version("Adw", "1")
|
||||||
|
|
||||||
@@ -23,6 +25,9 @@ class MainApplication(Adw.Application):
|
|||||||
self.config = config
|
self.config = config
|
||||||
self.connect("shutdown", self.on_shutdown)
|
self.connect("shutdown", self.on_shutdown)
|
||||||
|
|
||||||
|
if config.url:
|
||||||
|
Join.use().push(config.url)
|
||||||
|
|
||||||
def on_shutdown(self, app: Gtk.Application) -> None:
|
def on_shutdown(self, app: Gtk.Application) -> None:
|
||||||
print("Shutting down")
|
print("Shutting down")
|
||||||
VMS.use().kill_all()
|
VMS.use().kill_all()
|
||||||
|
|||||||
@@ -7,11 +7,6 @@ from clan_cli.clan_uri import ClanURI
|
|||||||
gi.require_version("Gtk", "4.0")
|
gi.require_version("Gtk", "4.0")
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class InitialJoinValues:
|
|
||||||
url: ClanURI | None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ClanConfig:
|
class ClanConfig:
|
||||||
initial_view: str
|
initial_view: str
|
||||||
|
|||||||
73
pkgs/clan-vm-manager/clan_vm_manager/models/use_join.py
Normal file
73
pkgs/clan-vm-manager/clan_vm_manager/models/use_join.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
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_vm_manager.errors.show_error import show_error_dialog
|
||||||
|
|
||||||
|
gi.require_version("Gtk", "4.0")
|
||||||
|
gi.require_version("Adw", "1")
|
||||||
|
from gi.repository import Gio, GObject
|
||||||
|
|
||||||
|
|
||||||
|
class JoinValue(GObject.Object):
|
||||||
|
# TODO: custom signals for async join
|
||||||
|
# __gsignals__: ClassVar = {}
|
||||||
|
|
||||||
|
url: ClanURI
|
||||||
|
|
||||||
|
def __init__(self, url: ClanURI) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
|
||||||
|
class Join:
|
||||||
|
"""
|
||||||
|
This is a singleton.
|
||||||
|
It is initialized with the first call of use()
|
||||||
|
"""
|
||||||
|
|
||||||
|
_instance: "None | Join" = None
|
||||||
|
list_store: Gio.ListStore
|
||||||
|
|
||||||
|
# Make sure the VMS class is used as a singleton
|
||||||
|
def __init__(self) -> None:
|
||||||
|
raise RuntimeError("Call use() instead")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def use(cls: Any) -> "Join":
|
||||||
|
if cls._instance is None:
|
||||||
|
print("Creating new instance")
|
||||||
|
cls._instance = cls.__new__(cls)
|
||||||
|
cls.list_store = Gio.ListStore.new(JoinValue)
|
||||||
|
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def push(self, url: ClanURI) -> 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)
|
||||||
|
try:
|
||||||
|
print(f"trying to join: {item.url}")
|
||||||
|
|
||||||
|
history = add_history(item.url)
|
||||||
|
cb(history)
|
||||||
|
self.discard(item)
|
||||||
|
|
||||||
|
except ClanError as e:
|
||||||
|
show_error_dialog(e)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def discard(self, item: JoinValue) -> None:
|
||||||
|
(has, idx) = self.list_store.find(item)
|
||||||
|
if has:
|
||||||
|
self.list_store.remove(idx)
|
||||||
@@ -142,6 +142,11 @@ class VMS:
|
|||||||
for vm in self.get_running_vms():
|
for vm in self.get_running_vms():
|
||||||
vm.stop()
|
vm.stop()
|
||||||
|
|
||||||
|
def refresh(self) -> None:
|
||||||
|
self.list_store.remove_all()
|
||||||
|
for vm in get_initial_vms():
|
||||||
|
self.list_store.append(vm)
|
||||||
|
|
||||||
|
|
||||||
def get_initial_vms() -> list[VM]:
|
def get_initial_vms() -> list[VM]:
|
||||||
vm_list = []
|
vm_list = []
|
||||||
|
|||||||
@@ -12,5 +12,20 @@ avatar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.trust {
|
.trust {
|
||||||
padding: 25px;
|
padding-top: 25px;
|
||||||
}
|
padding-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm-list {
|
||||||
|
margin-top: 25px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-shadow {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Disable shadow for empty lists */
|
||||||
|
/* list:empty {
|
||||||
|
box-shadow: none;
|
||||||
|
} */
|
||||||
@@ -1,13 +1,29 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
from clan_cli.history.add import HistoryEntry
|
||||||
|
|
||||||
|
from clan_vm_manager.models.use_join import Join, JoinValue
|
||||||
|
|
||||||
gi.require_version("Adw", "1")
|
gi.require_version("Adw", "1")
|
||||||
from gi.repository import Adw, Gdk, Gtk
|
from gi.repository import Adw, Gdk, Gio, GObject, Gtk
|
||||||
|
|
||||||
from clan_vm_manager.models.use_vms import VM, VMS
|
from clan_vm_manager.models.use_vms import VM, VMS
|
||||||
|
|
||||||
|
|
||||||
|
def create_boxed_list(
|
||||||
|
model: Gio.ListStore, render_row: Callable[[Gtk.ListBox, GObject], Gtk.Widget]
|
||||||
|
) -> Gtk.ListBox:
|
||||||
|
boxed_list = Gtk.ListBox()
|
||||||
|
boxed_list.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||||
|
boxed_list.add_css_class("boxed-list")
|
||||||
|
boxed_list.add_css_class("no-shadow")
|
||||||
|
|
||||||
|
boxed_list.bind_model(model, create_widget_func=partial(render_row, boxed_list))
|
||||||
|
return boxed_list
|
||||||
|
|
||||||
|
|
||||||
class ClanList(Gtk.Box):
|
class ClanList(Gtk.Box):
|
||||||
"""
|
"""
|
||||||
The ClanList
|
The ClanList
|
||||||
@@ -25,52 +41,96 @@ class ClanList(Gtk.Box):
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
boxed_list = Gtk.ListBox()
|
|
||||||
boxed_list.set_selection_mode(Gtk.SelectionMode.NONE)
|
|
||||||
boxed_list.add_css_class("boxed-list")
|
|
||||||
|
|
||||||
def create_widget(item: VM) -> Gtk.Widget:
|
|
||||||
flake = item.data.flake
|
|
||||||
row = Adw.ActionRow()
|
|
||||||
|
|
||||||
print("Creating", item.data.flake.flake_attr)
|
|
||||||
# Title
|
|
||||||
row.set_title(flake.clan_name)
|
|
||||||
row.set_title_lines(1)
|
|
||||||
row.set_title_selectable(True)
|
|
||||||
|
|
||||||
# Subtitle
|
|
||||||
row.set_subtitle(flake.flake_attr)
|
|
||||||
row.set_subtitle_lines(1)
|
|
||||||
|
|
||||||
# Avatar
|
|
||||||
avatar = Adw.Avatar()
|
|
||||||
avatar.set_custom_image(Gdk.Texture.new_from_filename(flake.icon))
|
|
||||||
avatar.set_text(flake.clan_name + " " + flake.flake_attr)
|
|
||||||
avatar.set_show_initials(True)
|
|
||||||
avatar.set_size(50)
|
|
||||||
row.add_prefix(avatar)
|
|
||||||
|
|
||||||
# Switch
|
|
||||||
switch = Gtk.Switch()
|
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
||||||
box.set_valign(Gtk.Align.CENTER)
|
|
||||||
box.append(switch)
|
|
||||||
|
|
||||||
switch.connect("notify::active", partial(self.on_row_toggle, item))
|
|
||||||
row.add_suffix(box)
|
|
||||||
|
|
||||||
return row
|
|
||||||
|
|
||||||
vms = VMS.use()
|
vms = VMS.use()
|
||||||
|
join = Join.use()
|
||||||
|
|
||||||
# TODO: Move this up to create_widget and connect every VM signal to its corresponding switch
|
# TODO: Move this up to create_widget and connect every VM signal to its corresponding switch
|
||||||
vms.handle_vm_stopped(self.stopped_vm)
|
vms.handle_vm_stopped(self.stopped_vm)
|
||||||
vms.handle_vm_started(self.started_vm)
|
vms.handle_vm_started(self.started_vm)
|
||||||
|
|
||||||
boxed_list.bind_model(vms.list_store, create_widget_func=create_widget)
|
self.join_boxed_list = create_boxed_list(
|
||||||
|
model=join.list_store, render_row=self.render_join_row
|
||||||
|
)
|
||||||
|
|
||||||
self.append(boxed_list)
|
self.vm_boxed_list = create_boxed_list(
|
||||||
|
model=vms.list_store, render_row=self.render_vm_row
|
||||||
|
)
|
||||||
|
self.vm_boxed_list.add_css_class("vm-list")
|
||||||
|
|
||||||
|
self.append(self.join_boxed_list)
|
||||||
|
self.append(self.vm_boxed_list)
|
||||||
|
|
||||||
|
def render_vm_row(self, boxed_list: Gtk.ListBox, item: VM) -> Gtk.Widget:
|
||||||
|
if boxed_list.has_css_class("no-shadow"):
|
||||||
|
boxed_list.remove_css_class("no-shadow")
|
||||||
|
flake = item.data.flake
|
||||||
|
row = Adw.ActionRow()
|
||||||
|
|
||||||
|
print("Creating", item.data.flake.flake_attr)
|
||||||
|
# Title
|
||||||
|
row.set_title(flake.clan_name)
|
||||||
|
|
||||||
|
row.set_title_lines(1)
|
||||||
|
row.set_title_selectable(True)
|
||||||
|
|
||||||
|
# Subtitle
|
||||||
|
row.set_subtitle(flake.flake_attr)
|
||||||
|
row.set_subtitle_lines(1)
|
||||||
|
|
||||||
|
# Avatar
|
||||||
|
avatar = Adw.Avatar()
|
||||||
|
avatar.set_custom_image(Gdk.Texture.new_from_filename(flake.icon))
|
||||||
|
avatar.set_text(flake.clan_name + " " + flake.flake_attr)
|
||||||
|
avatar.set_show_initials(True)
|
||||||
|
avatar.set_size(50)
|
||||||
|
row.add_prefix(avatar)
|
||||||
|
|
||||||
|
# Switch
|
||||||
|
switch = Gtk.Switch()
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
box.set_valign(Gtk.Align.CENTER)
|
||||||
|
box.append(switch)
|
||||||
|
|
||||||
|
switch.connect("notify::active", partial(self.on_row_toggle, item))
|
||||||
|
row.add_suffix(box)
|
||||||
|
|
||||||
|
return row
|
||||||
|
|
||||||
|
def render_join_row(self, boxed_list: Gtk.ListBox, item: JoinValue) -> Gtk.Widget:
|
||||||
|
if boxed_list.has_css_class("no-shadow"):
|
||||||
|
boxed_list.remove_css_class("no-shadow")
|
||||||
|
|
||||||
|
row = Adw.ActionRow()
|
||||||
|
|
||||||
|
row.set_title(str(item.url))
|
||||||
|
row.add_css_class("trust")
|
||||||
|
|
||||||
|
# TODO: figure out how to detect that
|
||||||
|
if True:
|
||||||
|
row.set_subtitle("Clan already exists. Joining again will update it")
|
||||||
|
|
||||||
|
avatar = Adw.Avatar()
|
||||||
|
avatar.set_text(str(item.url))
|
||||||
|
avatar.set_show_initials(True)
|
||||||
|
avatar.set_size(50)
|
||||||
|
row.add_prefix(avatar)
|
||||||
|
|
||||||
|
cancel_button = Gtk.Button(label="Cancel")
|
||||||
|
cancel_button.add_css_class("error")
|
||||||
|
cancel_button.connect("clicked", partial(self.on_discard_clicked, item))
|
||||||
|
|
||||||
|
trust_button = Gtk.Button(label="Join")
|
||||||
|
trust_button.add_css_class("success")
|
||||||
|
trust_button.connect("clicked", partial(self.on_trust_clicked, item))
|
||||||
|
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||||
|
box.set_valign(Gtk.Align.CENTER)
|
||||||
|
box.append(cancel_button)
|
||||||
|
box.append(trust_button)
|
||||||
|
|
||||||
|
row.add_suffix(box)
|
||||||
|
|
||||||
|
return row
|
||||||
|
|
||||||
def started_vm(self, vm: VM, _vm: VM) -> None:
|
def started_vm(self, vm: VM, _vm: VM) -> None:
|
||||||
print("VM started", vm.data.flake.flake_attr)
|
print("VM started", vm.data.flake.flake_attr)
|
||||||
@@ -89,6 +149,21 @@ class ClanList(Gtk.Box):
|
|||||||
dialog.run()
|
dialog.run()
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
|
||||||
|
def on_trust_clicked(self, item: JoinValue, widget: Gtk.Widget) -> None:
|
||||||
|
def on_join(_history: list[HistoryEntry]) -> None:
|
||||||
|
VMS.use().refresh()
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
def on_discard_clicked(self, item: JoinValue, widget: Gtk.Widget) -> None:
|
||||||
|
Join.use().discard(item)
|
||||||
|
if not Join.use().list_store.get_n_items():
|
||||||
|
self.join_boxed_list.add_css_class("no-shadow")
|
||||||
|
|
||||||
def on_row_toggle(self, vm: VM, row: Adw.SwitchRow, state: bool) -> None:
|
def on_row_toggle(self, vm: VM, row: Adw.SwitchRow, state: bool) -> None:
|
||||||
print("Toggled", vm.data.flake.flake_attr, "active:", row.get_active())
|
print("Toggled", vm.data.flake.flake_attr, "active:", row.get_active())
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
from clan_cli.clan_uri import ClanURI
|
|
||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
from clan_cli.history.add import 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.errors.show_error import show_error_dialog
|
||||||
from clan_vm_manager.models.interfaces import InitialJoinValues
|
from clan_vm_manager.models.use_join import JoinValue
|
||||||
|
|
||||||
gi.require_version("Gtk", "4.0")
|
gi.require_version("Gtk", "4.0")
|
||||||
gi.require_version("Adw", "1")
|
gi.require_version("Adw", "1")
|
||||||
@@ -16,9 +15,9 @@ from gi.repository import Adw, Gio, GObject, Gtk
|
|||||||
|
|
||||||
|
|
||||||
class TrustValues(GObject.Object):
|
class TrustValues(GObject.Object):
|
||||||
data: InitialJoinValues
|
data: JoinValue
|
||||||
|
|
||||||
def __init__(self, data: InitialJoinValues) -> None:
|
def __init__(self, data: JoinValue) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
print("TrustValues", data)
|
print("TrustValues", data)
|
||||||
self.data = data
|
self.data = data
|
||||||
@@ -27,13 +26,11 @@ class TrustValues(GObject.Object):
|
|||||||
class Trust(Gtk.Box):
|
class Trust(Gtk.Box):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
|
||||||
initial_values: InitialJoinValues,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
# self.on_trust = on_trust
|
# self.on_trust = on_trust
|
||||||
self.url: ClanURI | None = initial_values.url
|
# self.url: ClanURI | None = Join.use().
|
||||||
|
|
||||||
def render(item: TrustValues) -> Gtk.Widget:
|
def render(item: TrustValues) -> Gtk.Widget:
|
||||||
row = Adw.ActionRow()
|
row = Adw.ActionRow()
|
||||||
@@ -49,7 +46,7 @@ class Trust(Gtk.Box):
|
|||||||
cancel_button = Gtk.Button(label="Cancel")
|
cancel_button = Gtk.Button(label="Cancel")
|
||||||
cancel_button.add_css_class("error")
|
cancel_button.add_css_class("error")
|
||||||
|
|
||||||
trust_button = Gtk.Button(label="Trust")
|
trust_button = Gtk.Button(label="Join")
|
||||||
trust_button.add_css_class("success")
|
trust_button.add_css_class("success")
|
||||||
trust_button.connect("clicked", partial(self.on_trust_clicked, item.data))
|
trust_button.connect("clicked", partial(self.on_trust_clicked, item.data))
|
||||||
|
|
||||||
@@ -68,7 +65,7 @@ class Trust(Gtk.Box):
|
|||||||
boxed_list.add_css_class("boxed-list")
|
boxed_list.add_css_class("boxed-list")
|
||||||
|
|
||||||
list_store = Gio.ListStore.new(TrustValues)
|
list_store = Gio.ListStore.new(TrustValues)
|
||||||
list_store.append(TrustValues(data=initial_values))
|
# list_store.append(TrustValues(data=initial_values))
|
||||||
|
|
||||||
# icon = Gtk.Image.new_from_pixbuf(
|
# icon = Gtk.Image.new_from_pixbuf(
|
||||||
# GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
# GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
||||||
@@ -117,7 +114,7 @@ class Trust(Gtk.Box):
|
|||||||
# trust_button.connect("clicked", self.on_trust_clicked)
|
# trust_button.connect("clicked", self.on_trust_clicked)
|
||||||
# layout.append(trust_button)
|
# layout.append(trust_button)
|
||||||
|
|
||||||
def on_trust_clicked(self, item: InitialJoinValues, widget: Gtk.Widget) -> None:
|
def on_trust_clicked(self, item: JoinValue, widget: Gtk.Widget) -> None:
|
||||||
try:
|
try:
|
||||||
uri = item.url
|
uri = item.url
|
||||||
# or ClanURI(self.entry.get_text())
|
# or ClanURI(self.entry.get_text())
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import gi
|
import gi
|
||||||
|
|
||||||
from clan_vm_manager.models.interfaces import ClanConfig, InitialJoinValues
|
from clan_vm_manager.models.interfaces import ClanConfig
|
||||||
from clan_vm_manager.models.use_views import Views
|
from clan_vm_manager.models.use_views import Views
|
||||||
from clan_vm_manager.views.list import ClanList
|
from clan_vm_manager.views.list import ClanList
|
||||||
from clan_vm_manager.views.trust_join import Trust
|
|
||||||
|
|
||||||
gi.require_version("Adw", "1")
|
gi.require_version("Adw", "1")
|
||||||
|
|
||||||
@@ -25,9 +24,6 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
# Initialize all views
|
# Initialize all views
|
||||||
stack_view = Views.use().view
|
stack_view = Views.use().view
|
||||||
stack_view.add_named(ClanList(), "list")
|
stack_view.add_named(ClanList(), "list")
|
||||||
stack_view.add_named(
|
|
||||||
Trust(initial_values=InitialJoinValues(url=config.url)), "join.trust"
|
|
||||||
)
|
|
||||||
|
|
||||||
stack_view.set_visible_child_name(config.initial_view)
|
stack_view.set_visible_child_name(config.initial_view)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user