Skip to content

Commit

Permalink
Improve UI
Browse files Browse the repository at this point in the history
  • Loading branch information
quexten committed Dec 28, 2023
1 parent 000f9e5 commit cff9069
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 12 deletions.
2 changes: 2 additions & 0 deletions agent/actions/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 3 additions & 1 deletion cmd/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 2 additions & 0 deletions ipc/messages/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type VaultStatusRequest struct {

type VaultStatusResponse struct {
Locked bool
LoggedIn bool
PinSet bool
NumberOfLogins int
NumberOfNotes int
LastSynced int64
Expand Down
62 changes: 62 additions & 0 deletions ui/components.py
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 4 deletions ui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@
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

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:
Expand All @@ -40,7 +44,7 @@ def 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()
Expand Down
79 changes: 72 additions & 7 deletions ui/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
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):
Expand All @@ -31,6 +32,9 @@ def __init__(self, *args, **kwargs):
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...")
Expand All @@ -50,6 +54,10 @@ def __init__(self, *args, **kwargs):
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")
Expand All @@ -69,24 +77,35 @@ def __init__(self, *args, **kwargs):
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()
self.websocket_connected_row.set_title("Websocket Connected")
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()
Expand Down Expand Up @@ -142,21 +161,55 @@ def update_labels():
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)
Expand All @@ -177,23 +230,26 @@ def on_activate(self, app):
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()
Expand Down Expand Up @@ -239,5 +295,14 @@ def handle_res():
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 changes: 26 additions & 0 deletions ui/style.css
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit cff9069

Please sign in to comment.