summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/client/ui/core/core.go57
-rw-r--r--internal/client/ui/core/networkupdate/networkupdate.go355
-rw-r--r--internal/client/ui/core/state/state.go7
-rw-r--r--internal/data/networks.sql.go40
-rw-r--r--internal/packet/models.go1
-rw-r--r--internal/packet/types.go1
-rw-r--r--internal/server/api/api.go75
-rw-r--r--internal/server/server.go2
8 files changed, 526 insertions, 12 deletions
diff --git a/internal/client/ui/core/core.go b/internal/client/ui/core/core.go
index 9e8bd93..d8e4c1f 100644
--- a/internal/client/ui/core/core.go
+++ b/internal/client/ui/core/core.go
@@ -19,6 +19,7 @@ import (
"github.com/kyren223/eko/internal/client/ui/core/networkcreation"
"github.com/kyren223/eko/internal/client/ui/core/networkjoin"
"github.com/kyren223/eko/internal/client/ui/core/networklist"
+ "github.com/kyren223/eko/internal/client/ui/core/networkupdate"
"github.com/kyren223/eko/internal/client/ui/core/state"
"github.com/kyren223/eko/internal/client/ui/loadscreen"
"github.com/kyren223/eko/internal/packet"
@@ -51,6 +52,7 @@ type Model struct {
connected bool
networkCreationPopup *networkcreation.Model
+ networkUpdatePopup *networkupdate.Model
networkJoinPopup *networkjoin.Model
frequencyCreationPopup *frequencycreation.Model
networkList networklist.Model
@@ -68,6 +70,7 @@ func New(privKey ed25519.PrivateKey, name string) Model {
timeout: initialTimeout,
connected: false,
networkCreationPopup: nil,
+ networkUpdatePopup: nil,
networkJoinPopup: nil,
frequencyCreationPopup: nil,
networkList: networklist.New(),
@@ -103,6 +106,8 @@ func (m Model) View() string {
var popup string
if m.networkCreationPopup != nil {
popup = m.networkCreationPopup.View()
+ } else if m.networkUpdatePopup != nil {
+ popup = m.networkUpdatePopup.View()
} else if m.frequencyCreationPopup != nil {
popup = m.frequencyCreationPopup.View()
} else if m.networkJoinPopup != nil {
@@ -220,7 +225,7 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
case "n":
switch m.focus {
case FocusNetworkList:
- if m.networkCreationPopup == nil && m.networkJoinPopup == nil {
+ if !m.HasPopup() {
popup := networkcreation.New()
m.networkCreationPopup = &popup
} else {
@@ -230,7 +235,7 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
}
}
case FocusFrequencyList:
- if m.frequencyCreationPopup == nil {
+ if !m.HasPopup() {
networkId := state.NetworkId(m.networkList.Index())
if networkId == nil {
return nil
@@ -256,7 +261,7 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
}
case "a":
- if m.focus == FocusNetworkList && m.networkJoinPopup == nil && m.networkCreationPopup == nil {
+ if m.focus == FocusNetworkList && !m.HasPopup() {
popup := networkjoin.New()
m.networkJoinPopup = &popup
} else {
@@ -265,7 +270,8 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
case "i":
index := m.networkList.Index()
- if m.focus == FocusNetworkList && index != networklist.PeersIndex {
+ networkFocus := m.focus == FocusNetworkList
+ if !m.HasPopup() && networkFocus && index != networklist.PeersIndex {
networkId := state.NetworkId(index)
if networkId != nil {
_ = clipboard.WriteAll(networkId.String())
@@ -274,12 +280,27 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
m.updatePopups(msg)
}
+ case "u":
+ index := m.networkList.Index()
+ networkFocus := m.focus == FocusNetworkList
+ if !m.HasPopup() && networkFocus && index != networklist.PeersIndex {
+ networkId := state.NetworkId(index)
+ if networkId != nil {
+ popup := networkupdate.New(*networkId)
+ m.networkUpdatePopup = &popup
+ }
+ } else {
+ cmd := m.updatePopups(msg)
+ if cmd != nil {
+ return cmd
+ }
+ }
+
case "esc":
- if m.networkCreationPopup != nil {
+ if m.HasPopup() {
m.networkCreationPopup = nil
- } else if m.frequencyCreationPopup != nil {
+ m.networkUpdatePopup = nil
m.frequencyCreationPopup = nil
- } else if m.networkJoinPopup != nil {
m.networkJoinPopup = nil
}
@@ -290,6 +311,12 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
m.networkCreationPopup = nil
}
return cmd
+ } else if m.networkUpdatePopup != nil {
+ cmd := m.networkUpdatePopup.Select()
+ if cmd != nil {
+ m.networkUpdatePopup = nil
+ }
+ return cmd
} else if m.frequencyCreationPopup != nil {
cmd := m.frequencyCreationPopup.Select()
if cmd != nil {
@@ -324,10 +351,7 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
}
}
- isPopup := m.networkCreationPopup != nil ||
- m.frequencyCreationPopup != nil ||
- m.networkJoinPopup != nil
- if isPopup {
+ if m.HasPopup() {
return nil
}
@@ -385,6 +409,10 @@ func (m *Model) updatePopups(msg tea.Msg) tea.Cmd {
popup, cmd := m.networkCreationPopup.Update(msg)
m.networkCreationPopup = &popup
return cmd
+ } else if m.networkUpdatePopup != nil {
+ popup, cmd := m.networkUpdatePopup.Update(msg)
+ m.networkUpdatePopup = &popup
+ return cmd
} else if m.frequencyCreationPopup != nil {
popup, cmd := m.frequencyCreationPopup.Update(msg)
m.frequencyCreationPopup = &popup
@@ -396,3 +424,10 @@ func (m *Model) updatePopups(msg tea.Msg) tea.Cmd {
}
return nil
}
+
+func (m *Model) HasPopup() bool {
+ return m.networkCreationPopup != nil ||
+ m.networkUpdatePopup != nil ||
+ m.frequencyCreationPopup != nil ||
+ m.networkJoinPopup != nil
+}
diff --git a/internal/client/ui/core/networkupdate/networkupdate.go b/internal/client/ui/core/networkupdate/networkupdate.go
new file mode 100644
index 0000000..9bea5ff
--- /dev/null
+++ b/internal/client/ui/core/networkupdate/networkupdate.go
@@ -0,0 +1,355 @@
+package networkupdate
+
+import (
+ "errors"
+ "strings"
+
+ "github.com/charmbracelet/bubbles/textinput"
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+ "github.com/kyren223/eko/internal/client/gateway"
+ "github.com/kyren223/eko/internal/client/ui/colors"
+ "github.com/kyren223/eko/internal/client/ui/core/networklist"
+ "github.com/kyren223/eko/internal/client/ui/core/state"
+ "github.com/kyren223/eko/internal/client/ui/field"
+ "github.com/kyren223/eko/internal/client/ui/layouts/flex"
+ "github.com/kyren223/eko/internal/packet"
+ "github.com/kyren223/eko/pkg/assert"
+ "github.com/kyren223/eko/pkg/snowflake"
+)
+
+var (
+ width = 48
+
+ style = lipgloss.NewStyle().
+ Border(lipgloss.ThickBorder()).
+ Padding(1, 4).
+ Align(lipgloss.Center, lipgloss.Center)
+
+ headerStyle = lipgloss.NewStyle().Foreground(colors.Turquoise)
+
+ fieldBlurredStyle = lipgloss.NewStyle().
+ PaddingLeft(1).
+ Border(lipgloss.RoundedBorder()).
+ BorderForeground(colors.DarkCyan)
+ fieldFocusedStyle = fieldBlurredStyle.
+ BorderForeground(colors.Focus).
+ Border(lipgloss.ThickBorder())
+
+ underlineStyle = func(s string, width int, color lipgloss.Color) string {
+ underline := lipgloss.NewStyle().Foreground(color).
+ Render(strings.Repeat(lipgloss.ThickBorder().Bottom, width))
+ return lipgloss.JoinVertical(lipgloss.Left, s, underline)
+ }
+
+ iconHeader = headerStyle.Bold(true).Render("Icon: ")
+ bgColorHeader = headerStyle.Bold(true).Render("BG # ")
+ fgColorHeader = headerStyle.Bold(true).Render(" FG # ")
+
+ blurredUpdate = lipgloss.NewStyle().
+ Background(colors.Gray).Padding(0, 1).Render("Update Network")
+ focusedUpdate = lipgloss.NewStyle().
+ Background(colors.Blue).Padding(0, 1).Render("Update Network")
+)
+
+const (
+ MaxIconLength = 2
+ MaxHexDigits = 6
+)
+
+const (
+ NameField = iota
+ FgColorField
+ BgColorField
+ IconField
+ PrivateField
+ UpdateField
+ FieldCount
+)
+
+type Model struct {
+ precomputedStyle lipgloss.Style
+
+ name field.Model
+ icon textinput.Model
+ bgColor textinput.Model
+ fgColor textinput.Model
+ private bool
+ update string
+
+ selected int
+ nameWidth int
+
+ lastFg lipgloss.Color
+ lastBg lipgloss.Color
+
+ networkId snowflake.ID
+}
+
+func New(networkId snowflake.ID) Model {
+ network := state.State.Networks[networkId]
+
+ name := field.New(width)
+ name.Header = "Network Name"
+ name.HeaderStyle = headerStyle
+ name.FocusedStyle = fieldFocusedStyle
+ name.BlurredStyle = fieldBlurredStyle
+ name.ErrorStyle = lipgloss.NewStyle().Foreground(colors.Error)
+ name.Input.CharLimit = width
+ name.Focus()
+ name.Input.Validate = func(s string) error {
+ if strings.TrimSpace(s) == "" {
+ return errors.New("cannot be empty")
+ }
+ return nil
+ }
+ name.Input.SetValue(network.Name)
+
+ nameWidth := lipgloss.Width(name.View())
+
+ icon := textinput.New()
+ icon.Prompt = ""
+ icon.CharLimit = MaxIconLength
+ icon.Placeholder = "ic"
+ icon.Validate = func(s string) error {
+ if len(s) == 0 {
+ return errors.New("err")
+ }
+ return nil
+ }
+ icon.SetValue(network.Icon)
+
+ bgColor := textinput.New()
+ bgColor.Prompt = ""
+ bgColor.CharLimit = MaxHexDigits
+ bgColor.Placeholder = "000000"
+ bgColor.Validate = func(s string) error {
+ if len(s) != MaxHexDigits {
+ return errors.New("err")
+ }
+ return nil
+ }
+ bgColor.SetValue(network.BgHexColor[1:])
+
+ fgColor := textinput.New()
+ fgColor.Prompt = ""
+ fgColor.CharLimit = MaxHexDigits
+ fgColor.Placeholder = "000000"
+ fgColor.Validate = func(s string) error {
+ if len(s) != MaxHexDigits {
+ return errors.New("err")
+ }
+ return nil
+ }
+ fgColor.SetValue(network.FgHexColor[1:])
+
+ return Model{
+ name: name,
+ icon: icon,
+ bgColor: bgColor,
+ fgColor: fgColor,
+ lastBg: lipgloss.Color("#" + bgColor.Value()),
+ lastFg: lipgloss.Color("#" + fgColor.Value()),
+ update: blurredUpdate,
+
+ nameWidth: nameWidth,
+ precomputedStyle: lipgloss.NewStyle().Width(nameWidth / 3),
+
+ networkId: networkId,
+ }
+}
+
+func (m Model) Init() tea.Cmd {
+ return nil
+}
+
+func (m Model) View() string {
+ name := m.name.View()
+
+ iconPreview := networklist.IconStyle(m.icon.Value(), m.lastFg, m.lastBg).String()
+ iconPreview = lipgloss.NewStyle().Width(m.nameWidth).Align(lipgloss.Center).Render(iconPreview)
+
+ color := colors.Gray
+ if m.icon.Err != nil {
+ color = colors.Error
+ } else if m.selected == IconField {
+ color = colors.Focus
+ }
+ iconInput := underlineStyle(m.icon.View(), MaxIconLength, color)
+ iconText := m.precomputedStyle.Render(lipgloss.JoinHorizontal(lipgloss.Top, iconHeader, iconInput))
+
+ color = colors.Gray
+ if m.bgColor.Err != nil {
+ color = colors.Error
+ } else if m.selected == BgColorField {
+ color = colors.Focus
+ }
+ bgColorInput := underlineStyle(m.bgColor.View(), MaxHexDigits, color)
+ bgColorInput = lipgloss.NewStyle().Width(MaxHexDigits + 1).Render(bgColorInput)
+ bgColorIndicator := lipgloss.NewStyle().Foreground(m.lastBg).Render("■")
+ bgColorText := lipgloss.JoinHorizontal(lipgloss.Top, bgColorHeader, bgColorInput, bgColorIndicator)
+ bgColorText = m.precomputedStyle.Render(bgColorText)
+
+ color = colors.Gray
+ if m.fgColor.Err != nil {
+ color = colors.Error
+ } else if m.selected == FgColorField {
+ color = colors.Focus
+ }
+ fgColorInput := underlineStyle(m.fgColor.View(), MaxHexDigits, color)
+ fgColorInput = lipgloss.NewStyle().Width(MaxHexDigits + 1).Render(fgColorInput)
+ fgColorIndicator := lipgloss.NewStyle().Foreground(m.lastFg).Render("■")
+ fgColorText := lipgloss.JoinHorizontal(lipgloss.Top, fgColorHeader, fgColorInput, fgColorIndicator)
+ fgColorText = m.precomputedStyle.Render(fgColorText)
+
+ icon := lipgloss.JoinHorizontal(lipgloss.Top, fgColorText, bgColorText, iconText)
+
+ privateStyle := lipgloss.NewStyle().PaddingLeft(1)
+ if m.selected == PrivateField {
+ privateStyle = privateStyle.Foreground(colors.Focus)
+ }
+ private := "[ ] Private"
+ if m.private {
+ private = "[x] Private"
+ }
+ private = privateStyle.Render(private)
+
+ update := lipgloss.NewStyle().Width(m.nameWidth).Align(lipgloss.Center).Render(m.update)
+
+ content := flex.NewVertical(iconPreview, name, icon, private, update).WithGap(1).View()
+ return style.Render(content)
+}
+
+func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
+ switch msg := msg.(type) {
+ case tea.KeyMsg:
+ key := msg.Type
+ switch key {
+ case tea.KeyTab:
+ return m, m.cycle(1)
+ case tea.KeyShiftTab:
+ return m, m.cycle(-1)
+
+ default:
+ var cmd tea.Cmd
+ switch m.selected {
+ case NameField:
+ m.name, cmd = m.name.Update(msg)
+ case IconField:
+ m.icon, cmd = m.icon.Update(msg)
+ case BgColorField:
+ oldValue := m.bgColor.Value()
+ position := m.bgColor.Position()
+ m.bgColor, cmd = m.bgColor.Update(msg)
+ newValue := m.bgColor.Value()
+
+ hex := "0123456789abcdefABCDEF"
+ invalid := false
+ for _, c := range newValue {
+ if !strings.ContainsRune(hex, c) {
+ invalid = true
+ break
+ }
+ }
+
+ if invalid {
+ m.bgColor.SetValue(oldValue)
+ m.bgColor.SetCursor(position)
+ } else if len(m.bgColor.Value()) == 6 {
+ m.lastBg = lipgloss.Color("#" + m.bgColor.Value())
+ }
+ case FgColorField:
+ oldValue := m.fgColor.Value()
+ position := m.fgColor.Position()
+ m.fgColor, cmd = m.fgColor.Update(msg)
+ newValue := m.fgColor.Value()
+
+ hex := "0123456789abcdefABCDEF"
+ invalid := false
+ for _, c := range newValue {
+ if !strings.ContainsRune(hex, c) {
+ invalid = true
+ break
+ }
+ }
+
+ if invalid {
+ m.fgColor.SetValue(oldValue)
+ m.fgColor.SetCursor(position)
+ } else if len(m.fgColor.Value()) == 6 {
+ m.lastFg = lipgloss.Color("#" + m.fgColor.Value())
+ }
+
+ }
+ return m, cmd
+ }
+ }
+
+ return m, nil
+}
+
+func (m *Model) cycle(step int) tea.Cmd {
+ m.selected += step
+ if m.selected < 0 {
+ m.selected = FieldCount - 1
+ } else {
+ m.selected %= FieldCount
+ }
+ return m.updateFocus()
+}
+
+func (m *Model) updateFocus() tea.Cmd {
+ m.name.Blur()
+ m.icon.Blur()
+ m.bgColor.Blur()
+ m.fgColor.Blur()
+ m.update = blurredUpdate
+ switch m.selected {
+ case NameField:
+ return m.name.Focus()
+ case IconField:
+ return m.icon.Focus()
+ case BgColorField:
+ return m.bgColor.Focus()
+ case FgColorField:
+ return m.fgColor.Focus()
+ case PrivateField:
+ return nil
+ case UpdateField:
+ m.update = focusedUpdate
+ return nil
+ default:
+ assert.Never("missing switch statement field in update focus", "selected", m.selected)
+ return nil
+ }
+}
+
+func (m *Model) Select() tea.Cmd {
+ if m.selected == PrivateField {
+ m.private = !m.private
+ return nil
+ }
+
+ if m.selected != UpdateField {
+ return nil
+ }
+
+ m.name.Input.Err = m.name.Input.Validate(m.name.Input.Value())
+ m.icon.Err = m.icon.Validate(m.icon.Value())
+ m.bgColor.Err = m.bgColor.Validate(m.bgColor.Value())
+ m.fgColor.Err = m.fgColor.Validate(m.fgColor.Value())
+ if m.name.Input.Err != nil || m.icon.Err != nil || m.bgColor.Err != nil || m.fgColor.Err != nil {
+ return nil
+ }
+
+ return gateway.Send(&packet.UpdateNetwork{
+ CreateNetwork: packet.CreateNetwork{
+ Name: m.name.Input.Value(),
+ Icon: m.icon.Value(),
+ BgHexColor: "#" + m.bgColor.Value(),
+ FgHexColor: "#" + m.fgColor.Value(),
+ IsPublic: !m.private,
+ },
+ Network: m.networkId,
+ })
+}
diff --git a/internal/client/ui/core/state/state.go b/internal/client/ui/core/state/state.go
index d8d47b9..b2f80fe 100644
--- a/internal/client/ui/core/state/state.go
+++ b/internal/client/ui/core/state/state.go
@@ -73,14 +73,19 @@ func UpdateNetworks(info *packet.NetworksInfo) {
}
networks[network.ID] = network.Network
+
+ if info.Partial {
+ continue
+ }
+
State.Frequencies[network.ID] = network.Frequencies
+
for _, member := range network.Members {
if State.Members[network.ID] == nil {
State.Members[network.ID] = map[snowflake.ID]data.Member{}
}
State.Members[network.ID][member.UserID] = member
}
-
for _, user := range network.Users {
State.Users[user.ID] = user
}
diff --git a/internal/data/networks.sql.go b/internal/data/networks.sql.go
index 5091602..af3c273 100644
--- a/internal/data/networks.sql.go
+++ b/internal/data/networks.sql.go
@@ -110,3 +110,43 @@ func (q *Queries) TransferNetwork(ctx context.Context, arg TransferNetworkParams
)
return i, err
}
+
+const updateNetwork = `-- name: UpdateNetwork :one
+UPDATE networks SET
+ name = ?, icon = ?,
+ bg_hex_color = ?, fg_hex_color = ?,
+ is_public = ?
+WHERE id = ?
+RETURNING id, owner_id, name, icon, bg_hex_color, fg_hex_color, is_public
+`
+
+type UpdateNetworkParams struct {
+ Name string
+ Icon string
+ BgHexColor string
+ FgHexColor string
+ IsPublic bool
+ ID snowflake.ID
+}
+
+func (q *Queries) UpdateNetwork(ctx context.Context, arg UpdateNetworkParams) (Network, error) {
+ row := q.db.QueryRowContext(ctx, updateNetwork,
+ arg.Name,
+ arg.Icon,
+ arg.BgHexColor,
+ arg.FgHexColor,
+ arg.IsPublic,
+ arg.ID,
+ )
+ var i Network
+ err := row.Scan(
+ &i.ID,
+ &i.OwnerID,
+ &i.Name,
+ &i.Icon,
+ &i.BgHexColor,
+ &i.FgHexColor,
+ &i.IsPublic,
+ )
+ return i, err
+}
diff --git a/internal/packet/models.go b/internal/packet/models.go
index e1ffdc5..27b6300 100644
--- a/internal/packet/models.go
+++ b/internal/packet/models.go
@@ -1,6 +1,7 @@
package packet
const (
+ MaxNetworkNameBytes = 32
MaxIconBytes = 16
DefaultFrequencyName = "main"
DefaultFrequencyColor = "#FFFFFF"
diff --git a/internal/packet/types.go b/internal/packet/types.go
index c73902b..a790cda 100644
--- a/internal/packet/types.go
+++ b/internal/packet/types.go
@@ -76,6 +76,7 @@ type FullNetwork struct {
type NetworksInfo struct {
Networks []FullNetwork
RemovedNetworks []snowflake.ID
+ Partial bool
}
func (m *NetworksInfo) Type() PacketType {
diff --git a/internal/server/api/api.go b/internal/server/api/api.go
index 9a0ef76..b11bf36 100644
--- a/internal/server/api/api.go
+++ b/internal/server/api/api.go
@@ -166,6 +166,11 @@ func CreateNetwork(ctx context.Context, sess *session.Session, request *packet.C
if name == "" {
return &packet.Error{Error: "server name must not be blank"}
}
+ if len(name) > packet.MaxNetworkNameBytes {
+ return &packet.Error{Error: fmt.Sprintf(
+ "network name may not exceed %v bytes", packet.MaxNetworkNameBytes,
+ )}
+ }
if len(request.Icon) > packet.MaxIconBytes {
return &packet.Error{Error: fmt.Sprintf(
@@ -251,6 +256,7 @@ func CreateNetwork(ctx context.Context, sess *session.Session, request *packet.C
return &packet.NetworksInfo{
Networks: []packet.FullNetwork{fullNetwork},
RemovedNetworks: nil,
+ Partial: false,
}
}
@@ -299,6 +305,7 @@ func GetNetworksInfo(ctx context.Context, sess *session.Session) (packet.Payload
return &packet.NetworksInfo{
Networks: fullNetworks,
RemovedNetworks: nil,
+ Partial: false,
}, nil
}
@@ -460,6 +467,7 @@ func DeleteNetwork(ctx context.Context, sess *session.Session, request *packet.D
return NetworkPropagate(ctx, sess, network.ID, &packet.NetworksInfo{
Networks: nil,
RemovedNetworks: []snowflake.ID{request.Network},
+ Partial: false,
})
}
@@ -527,6 +535,7 @@ func SetMember(ctx context.Context, sess *session.Session, request *packet.SetMe
Users: users,
}},
RemovedNetworks: nil,
+ Partial: false,
}
}
@@ -603,6 +612,7 @@ func SetMember(ctx context.Context, sess *session.Session, request *packet.SetMe
return &packet.NetworksInfo{
Networks: nil,
RemovedNetworks: []snowflake.ID{request.Network},
+ Partial: false,
}
}
@@ -642,6 +652,7 @@ func SetMember(ctx context.Context, sess *session.Session, request *packet.SetMe
Users: users,
}},
RemovedNetworks: nil,
+ Partial: false,
}
}
@@ -686,3 +697,67 @@ func GetUserData(ctx context.Context, sess *session.Session, request *packet.Get
Data: data,
}
}
+
+func UpdateNetwork(ctx context.Context, sess *session.Session, request *packet.UpdateNetwork) packet.Payload {
+ queries := data.New(db)
+
+ network, err := queries.GetNetworkById(ctx, request.Network)
+ if err == sql.ErrNoRows {
+ return &packet.Error{Error: "network doesn't exist"}
+ }
+ if err != nil {
+ log.Println("database error 0:", err)
+ return &ErrInternalError
+ }
+
+ if network.OwnerID != sess.ID() {
+ return &ErrPermissionDenied
+ }
+
+ name := strings.TrimSpace(request.Name)
+ if name == "" {
+ return &packet.Error{Error: "server name must not be blank"}
+ }
+ if len(name) > packet.MaxNetworkNameBytes {
+ return &packet.Error{Error: fmt.Sprintf(
+ "network name may not exceed %v bytes", packet.MaxNetworkNameBytes,
+ )}
+ }
+
+ if len(request.Icon) > packet.MaxIconBytes {
+ return &packet.Error{Error: fmt.Sprintf(
+ "exceeded allowed icon size in bytes: %v", packet.MaxIconBytes,
+ )}
+ }
+
+ if ok, err := isValidHexColor(request.BgHexColor); !ok {
+ return &packet.Error{Error: err}
+ }
+ if ok, err := isValidHexColor(request.FgHexColor); !ok {
+ return &packet.Error{Error: err}
+ }
+
+ network, err = queries.UpdateNetwork(ctx, data.UpdateNetworkParams{
+ Name: name,
+ Icon: request.Icon,
+ BgHexColor: request.BgHexColor,
+ FgHexColor: request.FgHexColor,
+ IsPublic: request.IsPublic,
+ ID: network.ID,
+ })
+ if err != nil {
+ log.Println("database error 1:", err)
+ return &ErrInternalError
+ }
+
+ return NetworkPropagate(ctx, sess, network.ID, &packet.NetworksInfo{
+ Networks: []packet.FullNetwork{{
+ Network: network,
+ Frequencies: nil,
+ Members: nil,
+ Users: nil,
+ }},
+ RemovedNetworks: nil,
+ Partial: true,
+ })
+}
diff --git a/internal/server/server.go b/internal/server/server.go
index 327eaa8..3bce2de 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -345,6 +345,8 @@ func processRequest(ctx context.Context, sess *session.Session, request packet.P
case *packet.CreateNetwork:
response = timeout(10*time.Millisecond, api.CreateNetwork, ctx, sess, request)
+ case *packet.UpdateNetwork:
+ response = timeout(5*time.Millisecond, api.UpdateNetwork, ctx, sess, request)
case *packet.DeleteNetwork:
response = timeout(500*time.Millisecond, api.DeleteNetwork, ctx, sess, request)