diff --git a/dev.deedles.Trayscale.metainfo.xml b/dev.deedles.Trayscale.metainfo.xml index ec53d4d..a27f561 100644 --- a/dev.deedles.Trayscale.metainfo.xml +++ b/dev.deedles.Trayscale.metainfo.xml @@ -54,6 +54,12 @@ + + +
    Change tray icon when exit node is in use.
+
    Bug fixes and performance improvements.
+
+
    Reorder tray menu items.
diff --git a/go.mod b/go.mod index 338feb3..6fbcfa5 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,20 @@ go 1.23.0 require ( deedles.dev/mk v0.1.0 - deedles.dev/xiter v0.0.0-20240815221547-ba5e081b01e3 + deedles.dev/xiter v0.0.0-20240823045626-63c91578a1b7 fyne.io/systray v1.11.0 github.com/diamondburned/gotk4-adwaita/pkg v0.0.0-20240712143708-824c3ce8a5f4 github.com/diamondburned/gotk4/pkg v0.3.1 github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf golang.org/x/net v0.28.0 gotest.tools/v3 v3.5.1 - honnef.co/go/tools v0.4.6 - tailscale.com v1.72.0 + honnef.co/go/tools v0.5.1 + tailscale.com v1.72.1 ) require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/BurntSushi/toml v1.3.2 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240615085220-eb727477b3f7 // indirect github.com/KarpelesLab/weak v0.1.1 // indirect github.com/akutz/memconn v0.1.0 // indirect github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect @@ -29,7 +29,7 @@ require ( github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect + github.com/google/nftables v0.2.1-0.20240815064735-6ddeb7caed5d // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/csrf v1.7.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -46,20 +46,19 @@ require ( github.com/peterbourgon/ff/v3 v3.4.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect - github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect - github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect + github.com/tailscale/goupnp v1.0.1-0.20210804030727-66b27ba4e403 // indirect + github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect github.com/tcnksm/go-httpstat v0.2.0 // indirect github.com/toqueteos/webbrowser v1.2.0 // indirect - github.com/vishvananda/netlink v1.2.1-beta.2 // indirect github.com/vishvananda/netns v0.0.4 // indirect github.com/x448/float16 v0.8.4 // indirect go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect golang.org/x/crypto v0.26.0 // indirect - golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect - golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a // indirect + golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect + golang.org/x/exp/typeparams v0.0.0-20240823005443-9b4947da3948 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/go.sum b/go.sum index e4d6444..cf7e08f 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,15 @@ deedles.dev/mk v0.1.0 h1:xrvuJA3+R/j6/6AZPc+o31I1rotdKLrAYJxhZJwdOuc= deedles.dev/mk v0.1.0/go.mod h1:TSFsz0T+BvhNqJae0yrj+KadkN4elx248PCpq2Ol4ME= -deedles.dev/xiter v0.0.0-20240815221547-ba5e081b01e3 h1:aoM+rT4PLB+NMqh7dDGPtT2+6BdsCEbw21pXxLhj+ek= -deedles.dev/xiter v0.0.0-20240815221547-ba5e081b01e3/go.mod h1:59997UHUsKAy/8bHUClTfeXdyuLZ6z/+yF++vIpxfx8= +deedles.dev/xiter v0.0.0-20240823045626-63c91578a1b7 h1:wWGMKcFAyBmkzZ0Sxmd/HAa1a8LAPg9jwHuYifHWe1o= +deedles.dev/xiter v0.0.0-20240823045626-63c91578a1b7/go.mod h1:59997UHUsKAy/8bHUClTfeXdyuLZ6z/+yF++vIpxfx8= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc= filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA= fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.1-0.20240615085220-eb727477b3f7 h1:GwfNF1ZrlhC1rccPhl176V0DcyO+1Gwd9zqbGTZP2zg= +github.com/BurntSushi/toml v1.4.1-0.20240615085220-eb727477b3f7/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/KarpelesLab/weak v0.1.1 h1:fNnlPo3aypS9tBzoEQluY13XyUfd/eWaSE/vMvo9s4g= github.com/KarpelesLab/weak v0.1.1/go.mod h1:pzXsWs5f2bf+fpgHayTlBE1qJpO3MpJKo5sRaLu1XNw= github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= @@ -51,8 +51,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI= -github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= +github.com/google/nftables v0.2.1-0.20240815064735-6ddeb7caed5d h1:oBc6aRXtBgtYSG6y8ceSK2PU9TDaDT+I6Qt+qo7uvws= +github.com/google/nftables v0.2.1-0.20240815064735-6ddeb7caed5d/go.mod h1:Fo/xFnOxWlRQtnHdNi46KbIjufTDzbKhtghpWrmsSUg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= @@ -102,10 +102,10 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= -github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio= -github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= -github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk= -github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= +github.com/tailscale/goupnp v1.0.1-0.20210804030727-66b27ba4e403 h1:tB2UwtefWtWjIOp5UjU2eHPdP1EY3JZyAkes6WOsvIo= +github.com/tailscale/goupnp v1.0.1-0.20210804030727-66b27ba4e403/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= +github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= +github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g= github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= github.com/tailscale/wireguard-go v0.0.0-20240731203015-71393c576b98 h1:RNpJrXfI5u6e+uzyIzvmnXbhmhdRkVf//90sMBH3lso= @@ -116,8 +116,6 @@ github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9r github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw= github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= -github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= -github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= @@ -131,10 +129,10 @@ go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 h1:lGdhQUN go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a h1:8qmSSA8Gz/1kTrCe0nqR0R3Gb/NDhykzWw2q2mWZydM= -golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/exp/typeparams v0.0.0-20240823005443-9b4947da3948 h1:mwwJFsdsQzu/zhRdxEXmpMvRMnAR6QpbAXx3cNooEf0= +golang.org/x/exp/typeparams v0.0.0-20240823005443-9b4947da3948/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= @@ -168,8 +166,8 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 h1:TU8z2Lh3Bbq77w0t1eG8yRlLcNHzZu3x6mhoH2Mk0c8= gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU= -honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8= -honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= +honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= +honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= @@ -178,5 +176,5 @@ sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= -tailscale.com v1.72.0 h1:emsPxupFM72zJLt2wvSzpa1vymqPYbL0WVVO+170/s0= -tailscale.com v1.72.0/go.mod h1:v7OHtg0KLAnhOVf81Z8WrjNefj238QbFhgkWJQoKxbs= +tailscale.com v1.72.1 h1:hk82jek36ph2S3Tfsh57NVWKEm/pZ9nfUonvlowpfaA= +tailscale.com v1.72.1/go.mod h1:v7OHtg0KLAnhOVf81Z8WrjNefj238QbFhgkWJQoKxbs= diff --git a/internal/tsutil/poller.go b/internal/tsutil/poller.go index 7bf51ca..2e8ab38 100644 --- a/internal/tsutil/poller.go +++ b/internal/tsutil/poller.go @@ -59,6 +59,7 @@ func (p *Poller) Run(ctx context.Context) { if interval < 0 { interval = 5 * time.Second } + retry := interval check := time.NewTicker(interval) defer check.Stop() @@ -70,7 +71,15 @@ func (p *Poller) Run(ctx context.Context) { return } slog.Error("get Tailscale status", "err", err) - continue + select { + case <-ctx.Done(): + return + case <-time.After(retry): + if retry < 30*time.Second { + retry *= 2 + } + continue + } } prefs, err := Prefs(ctx) @@ -79,9 +88,19 @@ func (p *Poller) Run(ctx context.Context) { return } slog.Error("get Tailscale prefs", "err", err) - continue + select { + case <-ctx.Done(): + return + case <-time.After(retry): + if retry < 30*time.Second { + retry *= 2 + } + continue + } } + retry = interval + var files []apitype.WaitingFile if status.Self.HasCap(tailcfg.CapabilityFileSharing) { files, err = WaitingFiles(ctx) diff --git a/internal/ui/app.go b/internal/ui/app.go index 270a665..8a85e62 100644 --- a/internal/ui/app.go +++ b/internal/ui/app.go @@ -2,7 +2,6 @@ package ui import ( "context" - "iter" "log/slog" "os" "slices" @@ -11,7 +10,6 @@ import ( "deedles.dev/mk" "deedles.dev/trayscale/internal/tray" "deedles.dev/trayscale/internal/tsutil" - "deedles.dev/xiter" "github.com/diamondburned/gotk4-adwaita/pkg/adw" "github.com/diamondburned/gotk4/pkg/gdk/v4" "github.com/diamondburned/gotk4/pkg/gio/v2" @@ -129,11 +127,14 @@ func (a *App) updatePeers(status tsutil.Status) { } peerMap := status.Status.Peer - peers := slices.SortedFunc(iter.Seq[key.NodePublic](xiter.Filter(xiter.MapKeys(status.Status.Peer), - func(peer key.NodePublic) bool { - return !tsutil.IsMullvad(peerMap[peer]) - })), - key.NodePublic.Compare) + peers := make([]key.NodePublic, 0, len(status.Status.Peer)) + for k, p := range peerMap { + if tsutil.IsMullvad(p) { + continue + } + peers = append(peers, k) + } + slices.SortFunc(peers, key.NodePublic.Compare) for key, page := range a.peerPages { if _, ok := peerMap[key]; !ok { diff --git a/internal/ui/peerpage.go b/internal/ui/peerpage.go index 85712fb..3cbacde 100644 --- a/internal/ui/peerpage.go +++ b/internal/ui/peerpage.go @@ -4,7 +4,6 @@ import ( "cmp" "context" _ "embed" - "iter" "log/slog" "net/netip" "slices" @@ -225,23 +224,46 @@ func (page *PeerPage) Update(a *App, peer *ipnstate.PeerStatus, status tsutil.St slices.SortFunc(peer.TailscaleIPs, netip.Addr.Compare) page.addrRows.Update(peer.TailscaleIPs) - if peer.PrimaryRoutes != nil { - page.routes = peer.PrimaryRoutes.AsSlice() + routes := func(yield func(netip.Prefix) bool) { + if peer.PrimaryRoutes == nil { + return + } + for i := 0; i < peer.PrimaryRoutes.Len(); i++ { + r := peer.PrimaryRoutes.At(i) + if r.Bits() == 0 { + continue + } + if !yield(r) { + return + } + } } - page.routes = slices.SortedFunc(iter.Seq[netip.Prefix](xiter.Filter(xiter.OfSlice(page.routes), - func(p netip.Prefix) bool { return p.Bits() != 0 })), + routes = xiter.Or( + routes, + xiter.Of(netip.Prefix{}), + ) + + clear(page.routes) + page.routes = page.routes[:0] + page.routes = slices.AppendSeq(page.routes, routes) + slices.SortFunc( + page.routes, func(p1, p2 netip.Prefix) int { - return cmp.Or(p1.Addr().Compare(p2.Addr()), p1.Bits()-p2.Bits()) - }) - if len(page.routes) == 0 { - page.routes = append(page.routes, netip.Prefix{}) - } - eroutes := make([]enum[netip.Prefix], 0, len(page.routes)) - for i, r := range page.routes { - i = -1 - eroutes = append(eroutes, enumerate(i, r)) + return cmp.Or( + p1.Addr().Compare(p2.Addr()), + cmp.Compare(p1.Bits(), p2.Bits()), + ) + }, + ) + + eroutes := func(yield func(enum[netip.Prefix]) bool) { + for _, r := range page.routes { + if !yield(enumerate(-1, r)) { + return + } + } } - page.routeRows.Update(eroutes) + page.routeRows.UpdateFromSeq(eroutes, len(page.routes)) page.ExitNodeRow.SetVisible(peer.ExitNodeOption) page.ExitNodeRow.ActivatableWidget().(*gtk.Switch).SetState(peer.ExitNode) diff --git a/internal/ui/rowmanager.go b/internal/ui/rowmanager.go index ca24415..a5ab1bf 100644 --- a/internal/ui/rowmanager.go +++ b/internal/ui/rowmanager.go @@ -1,8 +1,10 @@ package ui import ( + "iter" "slices" + "deedles.dev/xiter" "github.com/diamondburned/gotk4/pkg/gtk/v4" ) @@ -31,9 +33,14 @@ func (m *rowManager[Data]) resize(size int) { } func (m *rowManager[Data]) Update(data []Data) { - m.resize(len(data)) + m.UpdateFromSeq(slices.Values(data), len(data)) +} + +func (m *rowManager[Data]) UpdateFromSeq(data iter.Seq[Data], size int) { + m.resize(size) - for i, d := range data { + edata := xiter.Enumerate(xiter.Seq[Data](data)) + for i, d := range edata { if i < len(m.rows) { m.rows[i].Update(d) continue diff --git a/internal/ui/selfpage.go b/internal/ui/selfpage.go index 2eff473..3701b39 100644 --- a/internal/ui/selfpage.go +++ b/internal/ui/selfpage.go @@ -4,7 +4,6 @@ import ( "cmp" "context" _ "embed" - "iter" "log/slog" "net/netip" "slices" @@ -354,12 +353,16 @@ func (page *SelfPage) init(a *App, peer *ipnstate.PeerStatus, status tsutil.Stat page.PreferredDERP.SetText(dm.Regions[r.PreferredDERP].RegionName) page.DERPLatencies.SetVisible(true) - namedLats := slices.SortedFunc(iter.Seq[xiter.Pair[string, time.Duration]](xiter.Map(xiter.ToPair(xiter.OfMap(r.RegionLatency)), - func(p xiter.Pair[int, time.Duration]) xiter.Pair[string, time.Duration] { - return xiter.P(dm.Regions[p.V1].RegionName, p.V2) - })), - func(p1, p2 xiter.Pair[string, time.Duration]) int { return cmp.Compare(p1.V2, p2.V2) }) - latencyRows.Update(namedLats) + namedLats := func(yield func(latencyEntry) bool) { + for id, latency := range r.RegionLatency { + named := xiter.P(dm.Regions[id].RegionName, latency) + if !yield(named) { + return + } + } + } + sortedLats := slices.SortedFunc(namedLats, func(p1, p2 latencyEntry) int { return cmp.Compare(p1.V2, p2.V2) }) + latencyRows.Update(sortedLats) }) } @@ -383,19 +386,39 @@ func (page *SelfPage) Update(a *App, peer *ipnstate.PeerStatus, status tsutil.St page.fileRows.Update(status.Files) page.FilesGroup.SetVisible(len(status.Files) > 0) - page.routes = slices.SortedFunc(iter.Seq[netip.Prefix](xiter.Filter(xiter.OfSlice(status.Prefs.AdvertiseRoutes), - func(p netip.Prefix) bool { return p.Bits() != 0 })), // Filter - func(p1, p2 netip.Prefix) int { // SortedFunc - return cmp.Or(p1.Addr().Compare(p2.Addr()), p1.Bits()-p2.Bits()) - }) - if len(page.routes) == 0 { - page.routes = append(page.routes, netip.Prefix{}) + routes := func(yield func(netip.Prefix) bool) { + for _, r := range status.Prefs.AdvertiseRoutes { + if r.Bits() == 0 { + continue + } + if !yield(r) { + return + } + } } - eroutes := make([]enum[netip.Prefix], 0, len(page.routes)) - for i, r := range page.routes { - eroutes = append(eroutes, enumerate(i, r)) + routes = xiter.Or( + routes, + xiter.Of(netip.Prefix{}), + ) + + clear(page.routes) + page.routes = page.routes[:0] + page.routes = slices.AppendSeq(page.routes, routes) + slices.SortFunc(page.routes, func(p1, p2 netip.Prefix) int { + return cmp.Or( + p1.Addr().Compare(p2.Addr()), + cmp.Compare(p1.Bits(), p2.Bits()), + ) + }) + + eroutes := func(yield func(enum[netip.Prefix]) bool) { + for i, r := range page.routes { + if !yield(enumerate(i, r)) { + return + } + } } - page.routeRows.Update(eroutes) + page.routeRows.UpdateFromSeq(eroutes, len(page.routes)) } type addrRow struct {