mirror of
https://github.com/Kyren223/eko.git
synced 2026-08-28 18:31:30 +00:00
Added a way to change network properties both server side and client
side
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
355
internal/client/ui/core/networkupdate/networkupdate.go
Normal file
355
internal/client/ui/core/networkupdate/networkupdate.go
Normal file
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package packet
|
||||
|
||||
const (
|
||||
MaxNetworkNameBytes = 32
|
||||
MaxIconBytes = 16
|
||||
DefaultFrequencyName = "main"
|
||||
DefaultFrequencyColor = "#FFFFFF"
|
||||
|
||||
@@ -76,6 +76,7 @@ type FullNetwork struct {
|
||||
type NetworksInfo struct {
|
||||
Networks []FullNetwork
|
||||
RemovedNetworks []snowflake.ID
|
||||
Partial bool
|
||||
}
|
||||
|
||||
func (m *NetworksInfo) Type() PacketType {
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -18,5 +18,13 @@ UPDATE networks SET
|
||||
WHERE id = ?
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateNetwork :one
|
||||
UPDATE networks SET
|
||||
name = ?, icon = ?,
|
||||
bg_hex_color = ?, fg_hex_color = ?,
|
||||
is_public = ?
|
||||
WHERE id = ?
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteNetwork :exec
|
||||
DELETE FROM networks WHERE id = ?;
|
||||
|
||||
Reference in New Issue
Block a user