Add basic desktop UI - systray

This PR adds a basic UI for desktop
applications that support Linux, Max
and Windows.
This commit is contained in:
Givi Khojanashvili
2022-03-20 20:36:35 +04:00
committed by GitHub
parent bd61be24be
commit 4ef3c7a637
15 changed files with 469 additions and 9 deletions

216
client/ui/client_ui.go Normal file
View File

@@ -0,0 +1,216 @@
package main
import (
"context"
"flag"
"fmt"
"runtime"
"strings"
"time"
_ "embed"
"github.com/getlantern/systray"
log "github.com/sirupsen/logrus"
"github.com/skratchdot/open-golang/open"
"github.com/wiretrustee/wiretrustee/client/internal"
"github.com/wiretrustee/wiretrustee/client/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
var daemonAddr string
defaultDaemonAddr := "unix:///var/run/wiretrustee.sock"
if runtime.GOOS == "windows" {
defaultDaemonAddr = "tcp://127.0.0.1:41731"
}
flag.StringVar(
&daemonAddr, "daemon-addr",
defaultDaemonAddr,
"Daemon service address to serve CLI requests [unix|tcp]://[path|host:port]")
flag.Parse()
client := newServiceClient(daemonAddr)
systray.Run(client.onTrayReady, client.onTrayExit)
}
//go:embed connected.ico
var iconConnected []byte
//go:embed disconnected.ico
var iconDisconnected []byte
type serviceClient struct {
ctx context.Context
addr string
conn proto.DaemonServiceClient
mStatus *systray.MenuItem
mUp *systray.MenuItem
mDown *systray.MenuItem
}
func newServiceClient(addr string) *serviceClient {
s := &serviceClient{
ctx: context.Background(),
addr: addr,
}
return s
}
func (s *serviceClient) up() error {
conn, err := s.client()
if err != nil {
log.Errorf("get client: %v", err)
return err
}
status, err := conn.Status(s.ctx, &proto.StatusRequest{})
if err != nil {
log.Errorf("get service status: %v", err)
return err
}
if status.Status != string(internal.StatusIdle) {
log.Warnf("already connected")
return nil
}
if _, err := s.conn.Up(s.ctx, &proto.UpRequest{}); err != nil {
log.Errorf("up service: %v", err)
return err
}
return nil
}
func (s *serviceClient) down() error {
conn, err := s.client()
if err != nil {
log.Errorf("get client: %v", err)
return err
}
status, err := conn.Status(s.ctx, &proto.StatusRequest{})
if err != nil {
log.Errorf("get service status: %v", err)
return err
}
if status.Status != string(internal.StatusConnected) {
log.Warnf("already down")
return nil
}
if _, err := s.conn.Down(s.ctx, &proto.DownRequest{}); err != nil {
log.Errorf("down service: %v", err)
return err
}
return nil
}
func (s *serviceClient) updateStatus() {
conn, err := s.client()
if err != nil {
log.Errorf("get client: %v", err)
return
}
status, err := conn.Status(s.ctx, &proto.StatusRequest{})
if err != nil {
log.Errorf("get service status: %v", err)
return
}
if status.Status == string(internal.StatusConnected) {
systray.SetTemplateIcon(iconConnected, iconConnected)
s.mStatus.SetTitle("Connected")
s.mUp.Disable()
s.mDown.Enable()
} else {
systray.SetTemplateIcon(iconDisconnected, iconDisconnected)
s.mStatus.SetTitle("Disconnected")
s.mDown.Disable()
s.mUp.Enable()
}
}
func (s *serviceClient) onTrayReady() {
systray.SetTemplateIcon(iconDisconnected, iconDisconnected)
go func() {
s.mStatus = systray.AddMenuItem("Disconnected", "Disconnected")
s.mStatus.Disable()
systray.AddSeparator()
s.mUp = systray.AddMenuItem("Up", "Up")
s.mDown = systray.AddMenuItem("Down", "Down")
s.mDown.Disable()
mURL := systray.AddMenuItem("Open UI", "wiretrustee website")
systray.AddSeparator()
mQuit := systray.AddMenuItem("Quit", "Quit the whole app")
s.updateStatus()
ticker := time.NewTicker(time.Second * 3)
defer ticker.Stop()
var err error
for {
select {
case <-mURL.ClickedCh:
err = open.Run("https://app.wiretrustee.com")
case <-s.mUp.ClickedCh:
s.mUp.Disable()
if err = s.up(); err != nil {
s.mUp.Enable()
}
case <-s.mDown.ClickedCh:
s.mDown.Disable()
if err = s.down(); err != nil {
s.mDown.Enable()
}
case <-mQuit.ClickedCh:
systray.Quit()
return
case <-ticker.C:
s.updateStatus()
}
if err != nil {
log.Errorf("process connection: %v", err)
}
}
}()
}
func (s *serviceClient) onTrayExit() {}
func (s *serviceClient) client() (proto.DaemonServiceClient, error) {
if s.conn != nil {
return s.conn, nil
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
conn, err := grpc.DialContext(
ctx,
strings.TrimPrefix(s.addr, "tcp://"),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
)
if err != nil {
return nil, fmt.Errorf("dial service: %w", err)
}
s.conn = proto.NewDaemonServiceClient(conn)
return s.conn, nil
}

View File

@@ -0,0 +1,46 @@
package config
import (
"os"
"runtime"
)
// ClientConfig basic settings for the UI application.
type ClientConfig struct {
configPath string
logFile string
daemonAddr string
}
// Config object with default settings.
//
// We are creating this package to extract utility functions from the cmd package
// reading and parsing the configurations for the client should be done here
func Config() *ClientConfig {
defaultConfigPath := "/etc/wiretrustee/config.json"
defaultLogFile := "/var/log/wiretrustee/client.log"
if runtime.GOOS == "windows" {
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "config.json"
defaultLogFile = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "client.log"
}
defaultDaemonAddr := "unix:///var/run/wiretrustee.sock"
if runtime.GOOS == "windows" {
defaultDaemonAddr = "tcp://127.0.0.1:41731"
}
return &ClientConfig{
configPath: defaultConfigPath,
logFile: defaultLogFile,
daemonAddr: defaultDaemonAddr,
}
}
// DaemonAddr of the gRPC API.
func (c *ClientConfig) DaemonAddr() string {
return c.daemonAddr
}
// LogFile path.
func (c *ClientConfig) LogFile() string {
return c.logFile
}

BIN
client/ui/connected.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
client/ui/disconnected.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=Wiretrustee UI
Exec=/usr/bin/wiretrustee-ui
Icon=/usr/share/icons/hicolor/256x256/wiretrustee.png
Comment=Wiretrustee client UI.

BIN
client/ui/wiretrustee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB