mirror of
https://git.srhtnin.garden/sarah/SaraSuite.git
synced 2026-10-13 18:18:12 +02:00
130 lines
5.6 KiB
Python
130 lines
5.6 KiB
Python
"""One card per program: name, status, versions, and its action buttons."""
|
|
|
|
from typing import Callable, Optional
|
|
|
|
import flet as ft
|
|
|
|
from Core import ChipButtonStyle, Palette
|
|
from Installer import RemovalPlan, Status
|
|
from Manifests import Program
|
|
|
|
|
|
class ProgramCard(ft.Container):
|
|
def __init__(
|
|
self,
|
|
Program: Program,
|
|
OnInstall: Callable[["ProgramCard"], None],
|
|
OnUpdate: Callable[["ProgramCard"], None],
|
|
OnRemove: Callable[["ProgramCard"], None],
|
|
OnOptions: Callable[["ProgramCard"], None],
|
|
) -> None:
|
|
self.Program = Program
|
|
self.CurrentStatus: Optional[Status] = None
|
|
|
|
self.StatusText = ft.Text("Checking...", size=12, color=Palette.Overlay0)
|
|
self.StatusChip = ft.Container(
|
|
content=self.StatusText,
|
|
bgcolor=Palette.Surface0,
|
|
border=ft.Border.all(1, Palette.Overlay0),
|
|
border_radius=4,
|
|
padding=ft.Padding.symmetric(horizontal=8, vertical=2),
|
|
)
|
|
self.DetailText = ft.Text("", size=12, color=Palette.Overlay0, selectable=True)
|
|
self.Spinner = ft.ProgressRing(width=16, height=16, stroke_width=2, visible=False)
|
|
|
|
self.InstallButton = ft.OutlinedButton(
|
|
content="Install", style=ChipButtonStyle(), visible=False, on_click=lambda e: OnInstall(self),
|
|
)
|
|
self.UpdateButton = ft.OutlinedButton(
|
|
content="Update", style=ChipButtonStyle(), visible=False, on_click=lambda e: OnUpdate(self),
|
|
)
|
|
self.OptionsButton = ft.OutlinedButton(
|
|
content="Options", style=ChipButtonStyle(), visible=False, on_click=lambda e: OnOptions(self),
|
|
)
|
|
self.RemoveButton = ft.OutlinedButton(
|
|
content="Remove", style=ChipButtonStyle(Palette.Red), visible=False, on_click=lambda e: OnRemove(self),
|
|
)
|
|
|
|
super().__init__(
|
|
content=ft.Column(
|
|
spacing=6,
|
|
controls=[
|
|
ft.Row(
|
|
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
|
controls=[
|
|
ft.Text(Program.Name, size=20, color=Palette.Mauve),
|
|
self.StatusChip,
|
|
ft.Container(expand=True),
|
|
self.Spinner,
|
|
self.InstallButton,
|
|
self.UpdateButton,
|
|
self.OptionsButton,
|
|
self.RemoveButton,
|
|
],
|
|
),
|
|
ft.Text(Program.Description, size=13, color=Palette.Text),
|
|
self.DetailText,
|
|
],
|
|
),
|
|
bgcolor=Palette.Mantle,
|
|
border=ft.Border.all(1, Palette.Surface1),
|
|
border_radius=8,
|
|
padding=16,
|
|
)
|
|
|
|
def _SetChip(self, Text: str, Color: str) -> None:
|
|
self.StatusText.value = Text
|
|
self.StatusText.color = Color
|
|
self.StatusChip.border = ft.Border.all(1, Color)
|
|
|
|
def ShowStatus(self, Current: Status, Leftovers: RemovalPlan, Busy: bool) -> None:
|
|
self.CurrentStatus = Current
|
|
Receipt = Current.Receipt
|
|
|
|
if Current.Broken:
|
|
self._SetChip("Incomplete", Palette.Peach)
|
|
elif Current.UpdateAvailable:
|
|
self._SetChip("Update available", Palette.Yellow)
|
|
elif Current.Installed:
|
|
self._SetChip("Installed", Palette.Green)
|
|
elif Current.Error:
|
|
self._SetChip("Unreachable", Palette.Red)
|
|
else:
|
|
self._SetChip("Not installed", Palette.Overlay0)
|
|
|
|
Details = []
|
|
if Receipt is not None:
|
|
Installed = f"Installed {Receipt.Version} ({Receipt.Commit[:7]})".replace(" ", " ")
|
|
Details.append(f"{Installed} on {Receipt.UpdatedAt.replace('T', ' ')}")
|
|
if Current.LatestCommit and (Receipt is None or Current.LatestCommit != Receipt.Commit):
|
|
Details.append(f"Latest commit {Current.LatestCommit[:7]}")
|
|
Details.append(f"From {self.Program.GitUrl}")
|
|
if Current.Error:
|
|
Details.append(Current.Error)
|
|
if Receipt is None and Leftovers.Items:
|
|
Details.append(f"{len(Leftovers.Items)} file(s)/folder(s) it made are still around")
|
|
self.DetailText.value = " · ".join(Details)
|
|
|
|
self.InstallButton.visible = not Current.Installed
|
|
self.InstallButton.disabled = Busy or not Current.LatestCommit
|
|
# Only there when there's something to update (or repair).
|
|
self.UpdateButton.visible = Current.UpdateAvailable
|
|
self.UpdateButton.disabled = Busy or not Current.LatestCommit
|
|
self.UpdateButton.content = "Repair" if Current.Broken else "Update"
|
|
# Change options (e.g. the file picker) after installing. Needs the
|
|
# git host, since it reapplies the installed commit.
|
|
self.OptionsButton.visible = Current.Installed and bool(self.Program.Options)
|
|
self.OptionsButton.disabled = Busy or not Current.LatestCommit
|
|
# Not installed but configs/runtime files were kept: still removable.
|
|
self.RemoveButton.visible = Current.Installed or bool(Leftovers.Items)
|
|
self.RemoveButton.content = "Remove" if Current.Installed else "Clean up"
|
|
self.RemoveButton.disabled = Busy
|
|
self.Spinner.visible = False
|
|
|
|
def SetBusy(self, Busy: bool, Working: bool = False) -> None:
|
|
"""Busy greys out the buttons (only one operation runs at a time);
|
|
Working also shows this card's spinner."""
|
|
for Button in (self.InstallButton, self.UpdateButton, self.OptionsButton, self.RemoveButton):
|
|
Button.disabled = Busy
|
|
self.Spinner.visible = Working
|