Fixed network and frequency list on the client

This commit is contained in:
2025-01-10 16:49:03 +02:00
parent 4bff6ccf4e
commit 97ca4e2d70
5 changed files with 237 additions and 206 deletions

View File

@@ -261,7 +261,7 @@ func (m *Model) ResetBeforeSwitch() {
network := networks[m.networkIndex]
frequencyId := network.Frequencies[m.frequencyIndex].ID
log.Println("Saving", frequencyId)
state.State.FrequencyState[frequencyId] = state.Frequency{
state.State.FrequencyState[frequencyId] = state.FrequencyState{
IncompleteMessage: m.vi.String(),
Offset: m.offset,
MaxHeight: m.maxMessagesHeight,

View File

@@ -3,15 +3,12 @@ package core
import (
"crypto/ed25519"
"fmt"
"log"
"slices"
"time"
"github.com/charmbracelet/bubbles/spinner"
"github.com/charmbracelet/bubbles/timer"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/google/btree"
"github.com/kyren223/eko/internal/client/gateway"
"github.com/kyren223/eko/internal/client/ui"
"github.com/kyren223/eko/internal/client/ui/core/chat"
@@ -22,7 +19,6 @@ import (
"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/loadscreen"
"github.com/kyren223/eko/internal/data"
"github.com/kyren223/eko/internal/packet"
"github.com/kyren223/eko/pkg/assert"
"github.com/kyren223/eko/pkg/snowflake"
@@ -186,124 +182,16 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
}
case *packet.NetworksInfo:
if msg.Set {
state.State.Networks = msg.Networks
} else {
networks := state.State.Networks
for _, newNetwork := range msg.Networks {
add := true
for i, existingNetwork := range networks {
if existingNetwork.ID == newNetwork.ID {
add = false
if newNetwork.Position == -1 {
newNetwork.Position = existingNetwork.Position
}
networks[i] = newNetwork
break
}
}
if add {
networks = append(networks, newNetwork)
}
}
networks = slices.DeleteFunc(networks, func(network packet.FullNetwork) bool {
return slices.Contains(msg.RemovedNetworks, network.ID)
})
slices.SortFunc(networks, func(a, b packet.FullNetwork) int {
return a.Position - b.Position
})
log.Println(networks)
state.State.Networks = networks
}
state.UpdateNetworks(msg)
case *packet.MembersInfo:
var network *packet.FullNetwork
for i, fullNetwork := range state.State.Networks {
if fullNetwork.ID == msg.Network {
network = &state.State.Networks[i]
}
}
members := network.Members
for _, updatedMember := range msg.Members {
add := true
for i, existingMember := range members {
if existingMember.User.ID == updatedMember.User.ID {
add = false
members[i] = updatedMember
break
}
}
if add {
members = append(members, updatedMember)
}
}
members = slices.DeleteFunc(members, func(member data.GetNetworkMembersRow) bool {
return slices.Contains(msg.RemovedMembers, member.User.ID)
})
log.Println(members)
network.Members = members
state.UpdateMembers(msg)
case *packet.FrequenciesInfo:
var network *packet.FullNetwork
for i, fullNetwork := range state.State.Networks {
if fullNetwork.ID == msg.Network {
network = &state.State.Networks[i]
}
}
frequencies := network.Frequencies
for _, newFrequency := range msg.Frequencies {
add := true
for i, existingFrequency := range frequencies {
if existingFrequency.ID == newFrequency.ID {
add = false
if newFrequency.Position == -1 {
newFrequency.Position = existingFrequency.Position
}
frequencies[i] = newFrequency
break
}
}
if add {
frequencies = append(frequencies, newFrequency)
}
}
frequencies = slices.DeleteFunc(frequencies, func(frequency data.Frequency) bool {
return slices.Contains(msg.RemovedFrequencies, frequency.ID)
})
slices.SortFunc(frequencies, func(a, b data.Frequency) int {
return int(a.Position - b.Position)
})
log.Println(frequencies)
network.Frequencies = frequencies
state.UpdateFrequencies(msg)
case *packet.MessagesInfo:
for _, id := range msg.RemovedMessages {
for _, btree := range state.State.Messages {
btree.Delete(data.Message{ID: id})
}
}
for _, message := range msg.Messages {
msgSource := message.FrequencyID
if msgSource == nil {
msgSource = message.ReceiverID
}
bt := state.State.Messages[*msgSource]
if bt == nil {
bt = btree.NewG(2, func(a, b data.Message) bool {
return a.ID < b.ID
})
state.State.Messages[*msgSource] = bt
}
bt.ReplaceOrInsert(message)
}
state.UpdateMessages(msg)
case tea.KeyMsg:
switch msg.String() {
@@ -317,9 +205,11 @@ func (m *Model) updateConnected(msg tea.Msg) tea.Cmd {
case FocusFrequencyList:
if m.frequencyCreationPopup == nil {
index := m.networkList.Index()
network := state.State.Networks[index]
popup := frequencycreation.New(network.ID)
m.frequencyCreationPopup = &popup
networkId := state.NetworkId(index)
if networkId != nil {
popup := frequencycreation.New(*networkId)
m.frequencyCreationPopup = &popup
}
}
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/kyren223/eko/internal/client/ui"
"github.com/kyren223/eko/internal/client/ui/colors"
"github.com/kyren223/eko/internal/client/ui/core/state"
"github.com/kyren223/eko/internal/data"
"github.com/kyren223/eko/internal/packet"
)
@@ -47,7 +48,7 @@ func (m Model) Init() tea.Cmd {
}
func (m Model) View() string {
if m.Network() == nil {
if state.NetworkId(m.networkIndex) == nil {
return ""
}
@@ -62,7 +63,7 @@ func (m Model) View() string {
builder.WriteString(nameStyle.Render(m.Network().Name))
builder.WriteString("\n")
for i, frequency := range m.Network().Frequencies {
for i, frequency := range m.Frequencies() {
frequencyStyle := frequencyStyle.Foreground(lipgloss.Color(frequency.HexColor))
if m.index == i {
frequencyStyle = frequencyStyle.Background(colors.BackgroundHighlight)
@@ -120,13 +121,13 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.index = min(m.FrequenciesLength()-1, m.index+1)
case "x":
frequenciesCount := len(m.Network().Frequencies)
frequenciesCount := len(m.Frequencies())
if frequenciesCount == 1 {
// Don't delete the last frequency!
return m, nil
}
// TODO: consider adding a confirmation popup
frequencyId := m.Network().Frequencies[m.index].ID
frequencyId := m.Frequencies()[m.index].ID
if m.index == frequenciesCount-1 {
m.index--
}
@@ -154,15 +155,15 @@ func (m Model) Swap(dir int) (Model, tea.Cmd) {
Pos1: m.index,
Pos2: m.index + dir,
})
tmp := m.Network().Frequencies[m.index]
m.Network().Frequencies[m.index] = m.Network().Frequencies[m.index+dir]
m.Network().Frequencies[m.index+dir] = tmp
tmp := m.Frequencies()[m.index]
m.Frequencies()[m.index] = m.Frequencies()[m.index+dir]
m.Frequencies()[m.index+dir] = tmp
m.index += dir
m.history = append(m.history, func(m *Model) {
m.index -= dir
tmp := m.Network().Frequencies[m.index]
m.Network().Frequencies[m.index] = m.Network().Frequencies[m.index+dir]
m.Network().Frequencies[m.index+dir] = tmp
tmp := m.Frequencies()[m.index]
m.Frequencies()[m.index] = m.Frequencies()[m.index+dir]
m.Frequencies()[m.index+dir] = tmp
})
return m, cmd
}
@@ -172,11 +173,12 @@ func (m *Model) SetNetworkIndex(networkIndex int) {
return
}
if 0 <= m.networkIndex && m.networkIndex < len(state.State.Networks) {
network := state.State.Networks[m.networkIndex]
if 0 <= m.index && m.index < len(network.Frequencies) {
frequencyId := network.Frequencies[m.index].ID
state.State.LastFrequency[network.ID] = frequencyId
networkId := state.NetworkId(m.networkIndex)
if networkId != nil {
frequencies := state.State.Frequencies[*networkId]
if 0 <= m.index && m.index < len(frequencies) {
frequencyId := frequencies[m.index].ID
state.State.LastFrequency[*networkId] = frequencyId
}
}
@@ -190,26 +192,46 @@ func (m *Model) SetNetworkIndex(networkIndex int) {
m.index = 0
// Try restoring last ID
network := state.State.Networks[m.networkIndex]
if id, ok := state.State.LastFrequency[network.ID]; ok {
for i, frequency := range network.Frequencies {
if frequency.ID == id {
m.index = i
break
}
networkId = state.NetworkId(m.networkIndex)
if networkId == nil {
return
}
id, ok := state.State.LastFrequency[*networkId]
if !ok {
return
}
frequencies := state.State.Frequencies[*networkId]
for i, frequency := range frequencies {
if frequency.ID == id {
m.index = i
break
}
}
}
func (m Model) FrequenciesLength() int {
return len(m.Network().Frequencies)
networkId := state.NetworkId(m.networkIndex)
if networkId == nil {
return 0
}
return len(state.State.Frequencies[*networkId])
}
func (m Model) Network() *packet.FullNetwork {
if m.networkIndex < 0 || m.networkIndex > len(state.State.Networks)-1 {
func (m Model) Network() *data.Network {
networkId := state.NetworkId(m.networkIndex)
if networkId == nil {
return nil
}
return &state.State.Networks[m.networkIndex]
network := state.State.Networks[*networkId]
return &network
}
func (m Model) Frequencies() []data.Frequency {
networkId := state.NetworkId(m.networkIndex)
if networkId == nil {
return nil
}
return state.State.Frequencies[*networkId]
}
func (m *Model) Index() int {

View File

@@ -1,7 +1,6 @@
package networklist
import (
"slices"
"strings"
tea "github.com/charmbracelet/bubbletea"
@@ -48,18 +47,17 @@ func IconStyle(icon string, fg, bg lipgloss.Color) lipgloss.Style {
return lipgloss.NewStyle().SetString(combined)
}
const TrustedIndex = -1
const PeersIndex = -1
type Model struct {
history []func(m *Model)
index int
focus bool
index int
focus bool
}
func New() Model {
return Model{
focus: false,
index: TrustedIndex,
index: PeersIndex,
}
}
@@ -70,7 +68,7 @@ func (m Model) Init() tea.Cmd {
func (m Model) View() string {
var builder strings.Builder
builder.WriteString("\n")
if m.index == TrustedIndex {
if m.index == PeersIndex {
builder.WriteString(trustedUsersButtonSelected)
} else {
builder.WriteString(trustedUsersButton)
@@ -81,7 +79,7 @@ func (m Model) View() string {
lipgloss.Color(network.FgHexColor),
lipgloss.Color(network.BgHexColor),
)
if m.index == i {
if *state.NetworkId(m.index) == i {
builder.WriteString(lipgloss.JoinHorizontal(
ui.Center,
selectedIndicator,
@@ -106,18 +104,6 @@ func (m Model) View() string {
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
switch msg := msg.(type) {
case *packet.SwapUserNetworks:
// Pop first by shifting to the left
copy(m.history, m.history[1:])
m.history = m.history[:len(m.history)-1]
case *packet.Error:
if msg.PktType == packet.PacketSwapUserNetworks {
// Server failed, revert!
undo := m.history[len(m.history)-1]
m.history = m.history[:len(m.history)-1]
undo(&m)
}
case tea.KeyMsg:
if !m.focus {
return m, nil
@@ -139,20 +125,14 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.index = min(len(state.State.Networks)-1, m.index+1)
case "Q":
if state.State.UserID == nil || m.index == TrustedIndex {
if state.State.UserID == nil || m.index == PeersIndex {
return m, nil
}
networks := state.State.Networks
network := networks[m.index]
// Remove network from the list so it's not visible
state.State.Networks = slices.Delete(networks, m.index, m.index+1)
// Set back to trusted bcz that's always valid
// Set back to peers icon bcz that's always valid
// Where if u were on the network u just left
// it'd be an issue (or if u left all networks)
m.index = TrustedIndex
m.index = PeersIndex
no := false
return m, gateway.Send(&packet.SetMember{
@@ -161,28 +141,22 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
Muted: nil,
Banned: nil,
BanReason: nil,
Network: network.ID,
Network: *state.NetworkId(m.index),
User: *state.State.UserID,
})
case "D":
if state.State.UserID == nil || m.index == TrustedIndex {
if state.State.UserID == nil || m.index == PeersIndex {
return m, nil
}
networks := state.State.Networks
network := networks[m.index]
// Remove network from the list so it's not visible
state.State.Networks = slices.Delete(networks, m.index, m.index+1)
// Set back to trusted bcz that's always valid
// Set back to peers icon bcz that's always valid
// Where if u were on the network u just left
// it'd be an issue (or if u left all networks)
m.index = TrustedIndex
m.index = PeersIndex
return m, gateway.Send(&packet.DeleteNetwork{
Network: network.ID,
Network: *state.NetworkId(m.index),
})
}
@@ -199,21 +173,13 @@ func (m *Model) Blur() {
}
func (m Model) Swap(dir int) (Model, tea.Cmd) {
cmd := gateway.Send(&packet.SwapUserNetworks{
Pos1: m.index,
Pos2: m.index + dir,
})
tmp := state.State.Networks[m.index]
state.State.Networks[m.index] = state.State.Networks[m.index+dir]
state.State.Networks[m.index+dir] = tmp
tmp := state.Data.Networks[m.index]
state.Data.Networks[m.index] = state.Data.Networks[m.index+dir]
state.Data.Networks[m.index+dir] = tmp
m.index += dir
m.history = append(m.history, func(m *Model) {
m.index -= dir
tmp := state.State.Networks[m.index]
state.State.Networks[m.index] = state.State.Networks[m.index+dir]
state.State.Networks[m.index+dir] = tmp
return m, gateway.Send(&packet.SetUserData{
Data: state.JsonUserData(),
})
return m, cmd
}
func (m Model) Index() int {

View File

@@ -1,13 +1,17 @@
package state
import (
"encoding/json"
"slices"
"github.com/google/btree"
"github.com/kyren223/eko/internal/data"
"github.com/kyren223/eko/internal/packet"
"github.com/kyren223/eko/pkg/assert"
"github.com/kyren223/eko/pkg/snowflake"
)
type Frequency struct {
type FrequencyState struct {
IncompleteMessage string
Offset int
MaxHeight int
@@ -17,17 +21,166 @@ type state struct {
UserID *snowflake.ID
// Key is either a frequency or receiver
FrequencyState map[snowflake.ID]Frequency
LastFrequency map[snowflake.ID]snowflake.ID // key is network
FrequencyState map[snowflake.ID]FrequencyState // key is frequency id
LastFrequency map[snowflake.ID]snowflake.ID // key is network id
Messages map[snowflake.ID]*btree.BTreeG[data.Message]
Networks []packet.FullNetwork
Messages map[snowflake.ID]*btree.BTreeG[data.Message] // key is frequency id or receiver id
Networks map[snowflake.ID]data.Network // key is network id
Frequencies map[snowflake.ID][]data.Frequency // key is network id
Members map[snowflake.ID]map[snowflake.ID]data.Member // key is network id then user id
Users map[snowflake.ID]data.User // key is user id
}
var State state = state{
UserID: nil,
FrequencyState: map[snowflake.ID]Frequency{},
FrequencyState: map[snowflake.ID]FrequencyState{},
LastFrequency: map[snowflake.ID]snowflake.ID{},
Messages: map[snowflake.ID]*btree.BTreeG[data.Message]{},
Networks: []packet.FullNetwork{},
Networks: map[snowflake.ID]data.Network{},
Frequencies: map[snowflake.ID][]data.Frequency{},
Members: map[snowflake.ID]map[snowflake.ID]data.Member{},
Users: map[snowflake.ID]data.User{},
}
type UserData struct {
Networks []snowflake.ID
}
var Data UserData = UserData{
Networks: []snowflake.ID{},
}
func UpdateNetworks(info *packet.NetworksInfo) {
networks := State.Networks
for _, removedNetworkId := range info.RemovedNetworks {
delete(networks, removedNetworkId)
delete(State.Frequencies, removedNetworkId)
delete(State.Members, removedNetworkId)
for i, network := range Data.Networks {
if network == removedNetworkId {
copy(Data.Networks[i:], Data.Networks[i+1:])
Data.Networks = Data.Networks[:len(Data.Networks)-1]
break
}
}
}
for _, network := range info.Networks {
if _, ok := networks[network.ID]; !ok {
Data.Networks = append(Data.Networks, network.ID)
}
networks[network.ID] = network.Network
State.Frequencies[network.ID] = network.Frequencies
for _, member := range network.Members {
State.Members[network.ID][member.UserID] = member
}
for _, user := range network.Users {
State.Users[user.ID] = user
}
}
}
func UpdateFrequencies(info *packet.FrequenciesInfo) {
// TODO: check this
frequencies := State.Frequencies[info.Network]
for _, newFrequency := range info.Frequencies {
add := true
for i, existingFrequency := range frequencies {
if existingFrequency.ID == newFrequency.ID {
add = false
if newFrequency.Position == -1 {
newFrequency.Position = existingFrequency.Position
}
frequencies[i] = newFrequency
break
}
}
if add {
frequencies = append(frequencies, newFrequency)
}
}
frequencies = slices.DeleteFunc(frequencies, func(frequency data.Frequency) bool {
return slices.Contains(info.RemovedFrequencies, frequency.ID)
})
slices.SortFunc(frequencies, func(a, b data.Frequency) int {
return int(a.Position - b.Position)
})
}
func UpdateMessages(info *packet.MessagesInfo) {
for _, id := range info.RemovedMessages {
for _, btree := range State.Messages {
btree.Delete(data.Message{ID: id})
}
}
for _, message := range info.Messages {
msgSource := message.FrequencyID
if msgSource == nil {
msgSource = message.ReceiverID
}
bt := State.Messages[*msgSource]
if bt == nil {
bt = btree.NewG(2, func(a, b data.Message) bool {
return a.ID < b.ID
})
State.Messages[*msgSource] = bt
}
bt.ReplaceOrInsert(message)
}
}
func UpdateMembers(info *packet.MembersInfo) {
members := State.Members[info.Network]
for _, member := range info.Members {
members[member.UserID] = member
}
for _, removedMember := range info.RemovedMembers {
delete(members, removedMember)
if removedMember != *State.UserID {
continue
}
delete(State.Networks, info.Network)
delete(State.Frequencies, info.Network)
delete(State.Members, info.Network)
for i, network := range Data.Networks {
if network == info.Network {
copy(Data.Networks[i:], Data.Networks[i+1:])
Data.Networks = Data.Networks[:len(Data.Networks)-1]
break
}
}
}
for _, user := range info.Users {
State.Users[user.ID] = user
}
}
func NetworkId(index int) *snowflake.ID {
if 0 <= index && index < len(Data.Networks) {
return &Data.Networks[index]
}
return nil
}
func JsonUserData() string {
bytes, err := json.Marshal(Data)
assert.NoError(err, "marshling should never fail")
return string(bytes)
}
func FromJsonUserDat(s string) {
var data UserData
err := json.Unmarshal([]byte(s), &data)
if err != nil {
return
}
Data = data
}