Implement profile management functionality with UI integration

This commit is contained in:
Hakan Sariman 2025-05-22 15:08:59 +03:00
parent 6600958c26
commit f65f0473bf
3 changed files with 64 additions and 44 deletions

32
client/server/profile.go Normal file
View File

@ -0,0 +1,32 @@
package server
import (
"context"
"github.com/netbirdio/netbird/client/proto"
)
// GetProfiles returns a list of all available profiles.
func (s *Server) GetProfiles(context.Context, *proto.GetProfilesRequest) (*proto.GetProfilesResponse, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
mockProfiles := []*proto.Profile{
{
Name: "default",
Selected: true,
},
{
Name: "work",
Selected: false,
},
{
Name: "home",
Selected: false,
},
}
return &proto.GetProfilesResponse{
Profiles: mockProfiles,
}, nil
}

View File

@ -828,10 +828,10 @@ func (s *serviceClient) listenEvents() {
defer s.mNetworks.Enable() defer s.mNetworks.Enable()
s.runSelfCommand("networks", "true") s.runSelfCommand("networks", "true")
}() }()
case <-s.mProfiles.manageItem.ClickedCh: case <-s.mProfiles.menu.ClickedCh:
s.mProfiles.manageItem.Disable() s.mProfiles.menu.Disable()
go func() { go func() {
defer s.mProfiles.manageItem.Enable() defer s.mProfiles.menu.Enable()
s.runSelfCommand("profiles", "true") s.runSelfCommand("profiles", "true")
}() }()
case <-s.mNotifications.ClickedCh: case <-s.mNotifications.ClickedCh:

View File

@ -17,20 +17,25 @@ import (
"github.com/netbirdio/netbird/client/proto" "github.com/netbirdio/netbird/client/proto"
) )
var mockProfiles = []profile{
{name: "Default", selected: false},
{name: "Home", selected: true},
{name: "Work", selected: false},
}
// showProfilesUI creates and displays the Profiles window with a list of existing profiles, // showProfilesUI creates and displays the Profiles window with a list of existing profiles,
// a button to add new profiles, allows removal, and lets the user switch the active profile. // a button to add new profiles, allows removal, and lets the user switch the active profile.
func (s *serviceClient) showProfilesUI() { func (s *serviceClient) showProfilesUI() {
conn, err := s.getSrvClient(defaultFailTimeout)
if err != nil {
log.Errorf("get client: %v", err)
return
}
profiles, err := s.mProfiles.getProfiles(s.ctx, conn)
if err != nil {
log.Errorf("get profiles: %v", err)
return
}
var idx int var idx int
// List widget for profiles // List widget for profiles
list := widget.NewList( list := widget.NewList(
func() int { return len(mockProfiles) }, func() int { return len(profiles) },
func() fyne.CanvasObject { func() fyne.CanvasObject {
// Each item: Selected indicator, Name, spacer, Select & Remove buttons // Each item: Selected indicator, Name, spacer, Select & Remove buttons
return container.NewHBox( return container.NewHBox(
@ -49,30 +54,30 @@ func (s *serviceClient) showProfilesUI() {
selectBtn := row.Objects[3].(*widget.Button) selectBtn := row.Objects[3].(*widget.Button)
removeBtn := row.Objects[4].(*widget.Button) removeBtn := row.Objects[4].(*widget.Button)
profile := mockProfiles[i] profile := profiles[i]
// Show a checkmark if selected // Show a checkmark if selected
if profile.selected { if profile.Selected {
indicator.SetText("✓") indicator.SetText("✓")
} else { } else {
indicator.SetText("") indicator.SetText("")
} }
nameLabel.SetText(profile.name) nameLabel.SetText(profile.Name)
// Configure Select/Active button // Configure Select/Active button
selectBtn.SetText(func() string { selectBtn.SetText(func() string {
if profile.selected { if profile.Selected {
return "Active" return "Active"
} }
return "Select" return "Select"
}()) }())
selectBtn.OnTapped = func() { selectBtn.OnTapped = func() {
if profile.selected { if profile.Selected {
return // already active return // already active
} }
// confirm switch // confirm switch
dialog.ShowConfirm( dialog.ShowConfirm(
"Switch Profile", "Switch Profile",
fmt.Sprintf("Are you sure you want to switch to '%s'?", profile.name), fmt.Sprintf("Are you sure you want to switch to '%s'?", profile.Name),
func(confirm bool) { func(confirm bool) {
if !confirm { if !confirm {
return return
@ -86,7 +91,7 @@ func (s *serviceClient) showProfilesUI() {
if idx%2 == 1 { if idx%2 == 1 {
dialog.ShowInformation( dialog.ShowInformation(
"Profile Switched", "Profile Switched",
fmt.Sprintf("Profile '%s' switched successfully", profile.name), fmt.Sprintf("Profile '%s' switched successfully", profile.Name),
s.wProfiles, s.wProfiles,
) )
} }
@ -109,7 +114,7 @@ func (s *serviceClient) showProfilesUI() {
removeBtn.OnTapped = func() { removeBtn.OnTapped = func() {
dialog.ShowConfirm( dialog.ShowConfirm(
"Delete Profile", "Delete Profile",
fmt.Sprintf("Are you sure you want to delete '%s'?", profile.name), fmt.Sprintf("Are you sure you want to delete '%s'?", profile.Name),
func(confirm bool) { func(confirm bool) {
if !confirm { if !confirm {
return return
@ -182,12 +187,7 @@ func (s *serviceClient) updateProfiles() {
type profileMenu struct { type profileMenu struct {
mtx sync.Mutex mtx sync.Mutex
menu *systray.MenuItem menu *systray.MenuItem
profiles []profileMenuItem profiles []*proto.Profile
manageItem *systray.MenuItem
}
type profileMenuItem struct {
menuItem *systray.MenuItem
} }
type profile struct { type profile struct {
@ -198,6 +198,7 @@ type profile struct {
func newProfileMenu(menu *systray.MenuItem) *profileMenu { func newProfileMenu(menu *systray.MenuItem) *profileMenu {
p := &profileMenu{ p := &profileMenu{
menu: menu, menu: menu,
profiles: make([]*proto.Profile, 0),
} }
return p return p
} }
@ -209,28 +210,15 @@ func (p *profileMenu) loadProfiles(profiles []*proto.Profile) {
p.mtx.Lock() p.mtx.Lock()
defer p.mtx.Unlock() defer p.mtx.Unlock()
for _, profile := range profiles { // Add new profiles
menuItem := p.menu.AddSubMenuItem(profile.Name, "Switch to "+profile.Name) p.profiles = append(p.profiles, profiles...)
if profile.Selected {
menuItem.Check()
}
p.profiles = append(p.profiles, profileMenuItem{menuItem: menuItem})
}
// add manage profiles item
p.menu.AddSeparator()
p.manageItem = p.menu.AddSubMenuItem("Manage Profiles", "Manage your profiles")
} }
func (p *profileMenu) clearProfiles() { func (p *profileMenu) clearProfiles() {
p.mtx.Lock() p.mtx.Lock()
defer p.mtx.Unlock() defer p.mtx.Unlock()
// Remove all existing profile menu items
for _, item := range p.profiles { p.profiles = make([]*proto.Profile, 0)
item.menuItem.Remove()
}
p.profiles = nil
} }
func (p *profileMenu) updateProfiles(ctx context.Context, conn proto.DaemonServiceClient) { func (p *profileMenu) updateProfiles(ctx context.Context, conn proto.DaemonServiceClient) {