mirror of
https://github.com/quexten/goldwarden.git
synced 2024-09-11 19:17:33 +03:00
Improve UI
This commit is contained in:
parent
000f9e515d
commit
cff906922a
@ -192,6 +192,8 @@ func handleVaultStatus(request messages.IPCMessage, cfg *config.Config, vault *v
|
||||
vaultStatus.NumberOfNotes = len(vault.GetNotes())
|
||||
vaultStatus.LastSynced = vault.GetLastSynced()
|
||||
vaultStatus.WebsockedConnected = vault.IsWebsocketConnected()
|
||||
vaultStatus.PinSet = cfg.HasPin()
|
||||
vaultStatus.LoggedIn = cfg.IsLoggedIn()
|
||||
response, err = messages.IPCMessageFromPayload(vaultStatus)
|
||||
return
|
||||
}
|
||||
|
@ -113,7 +113,9 @@ var statusCmd = &cobra.Command{
|
||||
fmt.Println(" \"loginEntries\":", status.NumberOfLogins, ",")
|
||||
fmt.Println(" \"noteEntries\":", status.NumberOfNotes, ",")
|
||||
fmt.Println(" \"lastSynced\": \"" + time.Unix(status.LastSynced, 0).String() + "\",")
|
||||
fmt.Println(" \"websocketConnected\":", status.WebsockedConnected)
|
||||
fmt.Println(" \"websocketConnected\":", status.WebsockedConnected, ",")
|
||||
fmt.Println(" \"pinSet\":", status.PinSet, ",")
|
||||
fmt.Println(" \"loggedIn\":", status.LoggedIn)
|
||||
fmt.Println("}")
|
||||
default:
|
||||
println("Wrong response type")
|
||||
|
@ -22,6 +22,8 @@ type VaultStatusRequest struct {
|
||||
|
||||
type VaultStatusResponse struct {
|
||||
Locked bool
|
||||
LoggedIn bool
|
||||
PinSet bool
|
||||
NumberOfLogins int
|
||||
NumberOfNotes int
|
||||
LastSynced int64
|
||||
|
62
ui/components.py
Normal file
62
ui/components.py
Normal file
@ -0,0 +1,62 @@
|
||||
from gi.repository import Gtk
|
||||
|
||||
def status_icon_ok(icon_name):
|
||||
imagebox = Gtk.Box()
|
||||
imagebox.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
imagebox.set_halign(Gtk.Align.CENTER)
|
||||
imagebox.set_valign(Gtk.Align.CENTER)
|
||||
image = Gtk.Image()
|
||||
image.get_style_context().add_class("status-icon")
|
||||
image.get_style_context().add_class("ok-icon")
|
||||
image.set_from_icon_name(icon_name)
|
||||
imagebox.append(image)
|
||||
return imagebox
|
||||
|
||||
def status_icon_error(icon_name):
|
||||
imagebox = Gtk.Box()
|
||||
imagebox.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
imagebox.set_halign(Gtk.Align.CENTER)
|
||||
imagebox.set_valign(Gtk.Align.CENTER)
|
||||
image = Gtk.Image()
|
||||
image.get_style_context().add_class("status-icon")
|
||||
image.get_style_context().add_class("error-icon")
|
||||
image.set_from_icon_name(icon_name)
|
||||
imagebox.append(image)
|
||||
return imagebox
|
||||
|
||||
def status_icon_warning(icon_name):
|
||||
imagebox = Gtk.Box()
|
||||
imagebox.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
imagebox.set_halign(Gtk.Align.CENTER)
|
||||
imagebox.set_valign(Gtk.Align.CENTER)
|
||||
image = Gtk.Image()
|
||||
image.get_style_context().add_class("status-icon")
|
||||
image.get_style_context().add_class("warning-icon")
|
||||
image.set_from_icon_name(icon_name)
|
||||
imagebox.append(image)
|
||||
|
||||
return imagebox
|
||||
|
||||
class StatusIcon(Gtk.Box):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.icon_name = None
|
||||
self.status = None
|
||||
|
||||
def set_icon(self, icon_name, status):
|
||||
if self.icon_name == icon_name and self.status == status:
|
||||
return
|
||||
self.icon_name = icon_name
|
||||
self.status = status
|
||||
|
||||
while self.get_first_child() != None:
|
||||
self.remove(self.get_first_child())
|
||||
|
||||
if status == "ok":
|
||||
self.append(status_icon_ok(icon_name))
|
||||
elif status == "error":
|
||||
self.append(status_icon_error(icon_name))
|
||||
elif status == "warning":
|
||||
self.append(status_icon_warning(icon_name))
|
||||
else:
|
||||
raise Exception("Invalid status", status)
|
12
ui/main.py
12
ui/main.py
@ -6,9 +6,13 @@ import monitors.dbus_autofill_monitor
|
||||
import sys
|
||||
import goldwarden
|
||||
from threading import Thread
|
||||
import os
|
||||
|
||||
isflatpak = os.path.exists("/.flatpak-info")
|
||||
pathprefix = "/app/bin/" if isflatpak else "./"
|
||||
|
||||
try:
|
||||
subprocess.Popen(["python3", "/app/bin/background.py"], start_new_session=True)
|
||||
subprocess.Popen(["python3", f'{pathprefix}background.py'], start_new_session=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -16,9 +20,9 @@ is_hidden = "--hidden" in sys.argv
|
||||
|
||||
if not is_hidden:
|
||||
try:
|
||||
subprocess.Popen(["python3", "/app/bin/settings.py"], start_new_session=True)
|
||||
subprocess.Popen(["python3", f'{pathprefix}settings.py'], start_new_session=True)
|
||||
except:
|
||||
subprocess.Popen(["python3", "./settings.py"], start_new_session=True)
|
||||
subprocess.Popen(["python3", f'{pathprefix}settings.py'], start_new_session=True)
|
||||
pass
|
||||
|
||||
try:
|
||||
@ -40,7 +44,7 @@ thread = Thread(target=run_daemon)
|
||||
thread.start()
|
||||
|
||||
def on_autofill():
|
||||
subprocess.Popen(["python3", "/app/bin/autofill.py"], start_new_session=True)
|
||||
subprocess.Popen(["python3", f'{pathprefix}autofill.py'], start_new_session=True)
|
||||
|
||||
monitors.dbus_autofill_monitor.on_autofill = lambda: on_autofill()
|
||||
monitors.dbus_autofill_monitor.run_daemon()
|
||||
|
@ -5,10 +5,11 @@ gi.require_version('Gtk', '4.0')
|
||||
gi.require_version('Adw', '1')
|
||||
import gc
|
||||
|
||||
from gi.repository import Gtk, Adw, GLib
|
||||
from gi.repository import Gtk, Adw, GLib, Gdk
|
||||
import goldwarden
|
||||
from threading import Thread
|
||||
import subprocess
|
||||
import components
|
||||
|
||||
class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -31,6 +32,9 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
self.ssh_row.set_subtitle("Listening at ~/.goldwarden-ssh-agent.sock")
|
||||
self.preferences_group.add(self.ssh_row)
|
||||
|
||||
self.icon = components.status_icon_ok("emblem-default")
|
||||
self.ssh_row.add_prefix(self.icon)
|
||||
|
||||
self.login_with_device = Adw.ActionRow()
|
||||
self.login_with_device.set_title("Login with device")
|
||||
self.login_with_device.set_subtitle("Waiting for requests...")
|
||||
@ -50,6 +54,10 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
self.autofill_row.set_subtitle("Unavailable, please set up a shortcut in your desktop environment (README)")
|
||||
self.shortcut_preferences_group.add(self.autofill_row)
|
||||
|
||||
self.autofill_icon = components.StatusIcon()
|
||||
self.autofill_icon.set_icon("dialog-warning", "warning")
|
||||
self.autofill_row.add_prefix(self.autofill_icon)
|
||||
|
||||
self.copy_username_shortcut_row = Adw.ActionRow()
|
||||
self.copy_username_shortcut_row.set_title("Copy Username Shortcut")
|
||||
self.copy_username_shortcut_row.set_subtitle("U")
|
||||
@ -69,9 +77,14 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
self.status_row.set_subtitle("Locked")
|
||||
self.vault_status_preferences_group.add(self.status_row)
|
||||
|
||||
self.vault_status_icon = components.StatusIcon()
|
||||
self.vault_status_icon.set_icon("dialog-error", "error")
|
||||
self.status_row.add_prefix(self.vault_status_icon)
|
||||
|
||||
self.last_sync_row = Adw.ActionRow()
|
||||
self.last_sync_row.set_title("Last Sync")
|
||||
self.last_sync_row.set_subtitle("Never")
|
||||
self.last_sync_row.set_icon_name("emblem-synchronizing-symbolic")
|
||||
self.vault_status_preferences_group.add(self.last_sync_row)
|
||||
|
||||
self.websocket_connected_row = Adw.ActionRow()
|
||||
@ -79,14 +92,20 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
self.websocket_connected_row.set_subtitle("False")
|
||||
self.vault_status_preferences_group.add(self.websocket_connected_row)
|
||||
|
||||
self.websocket_connected_status_icon = components.StatusIcon()
|
||||
self.websocket_connected_status_icon.set_icon("dialog-error", "error")
|
||||
self.websocket_connected_row.add_prefix(self.websocket_connected_status_icon)
|
||||
|
||||
self.login_row = Adw.ActionRow()
|
||||
self.login_row.set_title("Vault Login Entries")
|
||||
self.login_row.set_subtitle("0")
|
||||
self.login_row.set_icon_name("dialog-password-symbolic")
|
||||
self.vault_status_preferences_group.add(self.login_row)
|
||||
|
||||
self.notes_row = Adw.ActionRow()
|
||||
self.notes_row.set_title("Vault Notes")
|
||||
self.notes_row.set_subtitle("0")
|
||||
self.notes_row.set_icon_name("emblem-documents-symbolic")
|
||||
self.vault_status_preferences_group.add(self.notes_row)
|
||||
|
||||
self.action_preferences_group = Adw.PreferencesGroup()
|
||||
@ -142,21 +161,55 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
pin_set = goldwarden.is_pin_enabled()
|
||||
status = goldwarden.get_vault_status()
|
||||
if status != None:
|
||||
if pin_set:
|
||||
self.unlock_button.set_sensitive(True)
|
||||
else:
|
||||
self.unlock_button.set_sensitive(False)
|
||||
logged_in = status["loggedIn"]
|
||||
if logged_in:
|
||||
self.preferences_group.set_visible(True)
|
||||
self.shortcut_preferences_group.set_visible(True)
|
||||
self.autotype_button.set_visible(True)
|
||||
self.login_row.set_sensitive(True)
|
||||
self.notes_row.set_sensitive(True)
|
||||
self.websocket_connected_row.set_sensitive(True)
|
||||
else:
|
||||
self.preferences_group.set_visible(False)
|
||||
self.shortcut_preferences_group.set_visible(False)
|
||||
self.autotype_button.set_visible(False)
|
||||
self.websocket_connected_row.set_sensitive(False)
|
||||
self.login_row.set_sensitive(False)
|
||||
self.notes_row.set_sensitive(False)
|
||||
|
||||
locked = status["locked"]
|
||||
self.login_button.set_sensitive(pin_set and not locked)
|
||||
self.set_pin_button.set_sensitive(not pin_set or not locked)
|
||||
self.autotype_button.set_sensitive(not locked)
|
||||
self.status_row.set_subtitle(str("Unlocked" if not locked else "Locked"))
|
||||
self.status_row.set_subtitle(str("Logged in" if (logged_in and not locked) else "Logged out") if not locked else "Locked")
|
||||
if locked or not logged_in:
|
||||
self.vault_status_icon.set_icon("dialog-warning", "warning")
|
||||
else:
|
||||
self.vault_status_icon.set_icon("emblem-default", "ok")
|
||||
if not logged_in:
|
||||
self.logout_button.set_sensitive(False)
|
||||
else:
|
||||
self.logout_button.set_sensitive(True)
|
||||
self.login_row.set_subtitle(str(status["loginEntries"]))
|
||||
self.notes_row.set_subtitle(str(status["noteEntries"]))
|
||||
self.websocket_connected_row.set_subtitle("Connected" if status["websocketConnected"] else "Disconnected")
|
||||
if status["websocketConnected"]:
|
||||
self.websocket_connected_status_icon.set_icon("emblem-default", "ok")
|
||||
else:
|
||||
self.websocket_connected_status_icon.set_icon("dialog-error", "error")
|
||||
self.last_sync_row.set_subtitle(str(status["lastSynced"]))
|
||||
self.unlock_button.set_sensitive(True)
|
||||
if status["lastSynced"].startswith("1970"):
|
||||
self.last_sync_row.set_subtitle("Never")
|
||||
self.unlock_button.set_label("Unlock" if locked else "Lock")
|
||||
else:
|
||||
is_daemon_running = goldwarden.is_daemon_running()
|
||||
if not is_daemon_running:
|
||||
self.status_row.set_subtitle("Daemon not running")
|
||||
self.vault_status_icon.set_icon("dialog-error", "error")
|
||||
GLib.timeout_add(1000, update_labels)
|
||||
|
||||
GLib.timeout_add(1000, update_labels)
|
||||
@ -177,23 +230,26 @@ class MyApp(Adw.Application):
|
||||
self.settings_win = SettingsWinvdow(application=app)
|
||||
self.settings_win.present()
|
||||
|
||||
app = MyApp(application_id="com.quexten.Goldwarden")
|
||||
|
||||
def show_login():
|
||||
dialog = Gtk.Dialog(title="Goldwarden")
|
||||
preference_group = Adw.PreferencesGroup()
|
||||
preference_group.set_title("Config")
|
||||
preference_group.set_margin_top(10)
|
||||
preference_group.set_margin_bottom(10)
|
||||
preference_group.set_margin_start(10)
|
||||
preference_group.set_margin_end(10)
|
||||
|
||||
dialog.get_content_area().append(preference_group)
|
||||
|
||||
api_url_entry = Adw.EntryRow()
|
||||
api_url_entry.set_title("API Url")
|
||||
# set value
|
||||
api_url_entry.set_text("https://api.bitwarden.com/")
|
||||
api_url_entry.set_text("https://vault.bitwarden.com/api")
|
||||
preference_group.add(api_url_entry)
|
||||
|
||||
identity_url_entry = Adw.EntryRow()
|
||||
identity_url_entry.set_title("Identity Url")
|
||||
identity_url_entry.set_text("https://identity.bitwarden.com/")
|
||||
identity_url_entry.set_text("https://vault.bitwarden.com/identity")
|
||||
preference_group.add(identity_url_entry)
|
||||
|
||||
notification_url_entry = Adw.EntryRow()
|
||||
@ -239,5 +295,14 @@ def show_login():
|
||||
dialog.set_modal(True)
|
||||
dialog.present()
|
||||
|
||||
css_provider = Gtk.CssProvider()
|
||||
css_provider.load_from_path("style.css")
|
||||
Gtk.StyleContext.add_provider_for_display(
|
||||
Gdk.Display.get_default(),
|
||||
css_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||
)
|
||||
|
||||
|
||||
app = MyApp(application_id="com.quexten.Goldwarden.settings")
|
||||
app.run(sys.argv)
|
26
ui/style.css
Normal file
26
ui/style.css
Normal file
@ -0,0 +1,26 @@
|
||||
.status-icon {
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.ok-icon {
|
||||
color: @green_5;
|
||||
background-color: alpha(@green_3, .25);
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
color: #ae7b03;
|
||||
background: alpha(@yellow_5, .25);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
color: @red_4;
|
||||
background-color: alpha(@red_2, .25);
|
||||
}
|
||||
|
||||
.accent-icon {
|
||||
padding: 9px;
|
||||
color: @blue_4;
|
||||
background-color: alpha(@blue_3, .25);
|
||||
}
|
Loading…
Reference in New Issue
Block a user