forked from clan/clan-core
add join to list
This commit is contained in:
parent
3be697d1e9
commit
d0b075c217
@ -117,6 +117,11 @@ class ClanURI:
|
||||
def get_full_uri(self) -> str:
|
||||
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
|
||||
def from_path(
|
||||
cls, # noqa
|
||||
|
@ -28,7 +28,7 @@ def main() -> None:
|
||||
|
||||
def show_join(args: argparse.Namespace) -> None:
|
||||
app = MainApplication(
|
||||
config=ClanConfig(url=args.clan_uri, initial_view="join.trust"),
|
||||
config=ClanConfig(url=args.clan_uri, initial_view="list"),
|
||||
)
|
||||
return app.run()
|
||||
|
||||
|
@ -3,6 +3,8 @@ 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")
|
||||
|
||||
@ -23,6 +25,9 @@ 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:
|
||||
print("Shutting down")
|
||||
VMS.use().kill_all()
|
||||
|
@ -7,11 +7,6 @@ from clan_cli.clan_uri import ClanURI
|
||||
gi.require_version("Gtk", "4.0")
|
||||
|
||||
|
||||
@dataclass
|
||||
class InitialJoinValues:
|
||||
url: ClanURI | None
|
||||
|
||||
|
||||
@dataclass
|
||||
class ClanConfig:
|
||||
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():
|
||||
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]:
|
||||
vm_list = []
|
||||
|
@ -12,5 +12,20 @@ avatar {
|
||||
}
|
||||
|
||||
.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
|
||||
|
||||
import gi
|
||||
from clan_cli.history.add import HistoryEntry
|
||||
|
||||
from clan_vm_manager.models.use_join import Join, JoinValue
|
||||
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
The ClanList
|
||||
@ -25,52 +41,96 @@ class ClanList(Gtk.Box):
|
||||
def __init__(self) -> None:
|
||||
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()
|
||||
join = Join.use()
|
||||
|
||||
# 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_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:
|
||||
print("VM started", vm.data.flake.flake_attr)
|
||||
@ -89,6 +149,21 @@ class ClanList(Gtk.Box):
|
||||
dialog.run()
|
||||
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:
|
||||
print("Toggled", vm.data.flake.flake_attr, "active:", row.get_active())
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
from functools import partial
|
||||
|
||||
import gi
|
||||
from clan_cli.clan_uri import ClanURI
|
||||
from clan_cli.errors import ClanError
|
||||
from clan_cli.history.add import add_history
|
||||
|
||||
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("Adw", "1")
|
||||
@ -16,9 +15,9 @@ from gi.repository import Adw, Gio, GObject, Gtk
|
||||
|
||||
|
||||
class TrustValues(GObject.Object):
|
||||
data: InitialJoinValues
|
||||
data: JoinValue
|
||||
|
||||
def __init__(self, data: InitialJoinValues) -> None:
|
||||
def __init__(self, data: JoinValue) -> None:
|
||||
super().__init__()
|
||||
print("TrustValues", data)
|
||||
self.data = data
|
||||
@ -27,13 +26,11 @@ class TrustValues(GObject.Object):
|
||||
class Trust(Gtk.Box):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
initial_values: InitialJoinValues,
|
||||
) -> None:
|
||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
# self.on_trust = on_trust
|
||||
self.url: ClanURI | None = initial_values.url
|
||||
# self.url: ClanURI | None = Join.use().
|
||||
|
||||
def render(item: TrustValues) -> Gtk.Widget:
|
||||
row = Adw.ActionRow()
|
||||
@ -49,7 +46,7 @@ class Trust(Gtk.Box):
|
||||
cancel_button = Gtk.Button(label="Cancel")
|
||||
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.connect("clicked", partial(self.on_trust_clicked, item.data))
|
||||
|
||||
@ -68,7 +65,7 @@ class Trust(Gtk.Box):
|
||||
boxed_list.add_css_class("boxed-list")
|
||||
|
||||
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(
|
||||
# GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
||||
@ -117,7 +114,7 @@ class Trust(Gtk.Box):
|
||||
# trust_button.connect("clicked", self.on_trust_clicked)
|
||||
# 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:
|
||||
uri = item.url
|
||||
# or ClanURI(self.entry.get_text())
|
||||
|
@ -1,9 +1,8 @@
|
||||
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.views.list import ClanList
|
||||
from clan_vm_manager.views.trust_join import Trust
|
||||
|
||||
gi.require_version("Adw", "1")
|
||||
|
||||
@ -25,9 +24,6 @@ class MainWindow(Adw.ApplicationWindow):
|
||||
# Initialize all views
|
||||
stack_view = Views.use().view
|
||||
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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user