summaryrefslogtreecommitdiff
path: root/internal/client/ui/core
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2025-02-28 21:23:43 +0200
committerKyren223 <Kyren223@proton.me>2025-02-28 21:23:43 +0200
commit0afb682db138d6c4b644426bccb5c93008d35094 (patch)
treedaa0f840aed7e9d80a37a8e67a9eb0f066620b29 /internal/client/ui/core
parent77163cac1d5ea722a677680d99c006da82ceb87f (diff)
Added the ability to view user profiles
Diffstat (limited to 'internal/client/ui/core')
-rw-r--r--internal/client/ui/core/banlist/banlist.go11
-rw-r--r--internal/client/ui/core/banview/banview.go6
-rw-r--r--internal/client/ui/core/chat/chat.go11
-rw-r--r--internal/client/ui/core/core.go20
-rw-r--r--internal/client/ui/core/memberlist/memberlist.go8
-rw-r--r--internal/client/ui/core/profile/profile.go145
-rw-r--r--internal/client/ui/core/signallist/signallist.go12
7 files changed, 202 insertions, 11 deletions
diff --git a/internal/client/ui/core/banlist/banlist.go b/internal/client/ui/core/banlist/banlist.go
index 286d453..7daff55 100644
--- a/internal/client/ui/core/banlist/banlist.go
+++ b/internal/client/ui/core/banlist/banlist.go
@@ -165,13 +165,20 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.SetIndex(m.index + m.height/2)
case "p":
- // TODO: profile
+ if 0 <= m.index && m.index < len(members) {
+ member := members[m.index]
+ return m, func() tea.Msg {
+ return ui.ProfilePopupMsg{
+ User: member.UserID,
+ }
+ }
+ }
case "v", "V":
if 0 <= m.index && m.index < len(members) {
member := members[m.index]
return m, func() tea.Msg {
- return ui.BanViewPopupmsg{
+ return ui.BanViewPopupMsg{
Network: m.networkId,
User: member.UserID,
}
diff --git a/internal/client/ui/core/banview/banview.go b/internal/client/ui/core/banview/banview.go
index a6ff91b..bcfd05b 100644
--- a/internal/client/ui/core/banview/banview.go
+++ b/internal/client/ui/core/banview/banview.go
@@ -13,12 +13,6 @@ import (
var width = 48
-const (
- BanReasonField = iota
- BanField
- FieldCount
-)
-
type Model struct {
banReason string
name string
diff --git a/internal/client/ui/core/chat/chat.go b/internal/client/ui/core/chat/chat.go
index c7735f7..9b0f240 100644
--- a/internal/client/ui/core/chat/chat.go
+++ b/internal/client/ui/core/chat/chat.go
@@ -413,6 +413,17 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.SetIndex(Unselected)
}
+ case "p":
+ if m.selectedMessage == nil {
+ return m, nil
+ }
+
+ return m, func() tea.Msg {
+ return ui.ProfilePopupMsg{
+ User: m.selectedMessage.SenderID,
+ }
+ }
+
case "x", "d":
if m.selectedMessage == nil {
return m, nil
diff --git a/internal/client/ui/core/core.go b/internal/client/ui/core/core.go
index fce8414..ba02ba9 100644
--- a/internal/client/ui/core/core.go
+++ b/internal/client/ui/core/core.go
@@ -27,6 +27,7 @@ import (
"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/profile"
"github.com/kyren223/eko/internal/client/ui/core/signaladd"
"github.com/kyren223/eko/internal/client/ui/core/signallist"
"github.com/kyren223/eko/internal/client/ui/core/state"
@@ -80,6 +81,7 @@ type Model struct {
banReasonPopup *banreason.Model
banViewPopup *banview.Model
signalAddPopup *signaladd.Model
+ profilePopup *profile.Model
networkList networklist.Model
signalList signallist.Model
frequencyList frequencylist.Model
@@ -171,6 +173,8 @@ func (m Model) View() string {
popup = m.banViewPopup.View()
} else if m.signalAddPopup != nil {
popup = m.signalAddPopup.View()
+ } else if m.profilePopup != nil {
+ popup = m.profilePopup.View()
} else {
assert.Never("missing handling of a popup!")
}
@@ -349,10 +353,14 @@ func (m *Model) updateConnected(message tea.Msg) tea.Cmd {
popup := banreason.New(msg.User, msg.Network)
m.banReasonPopup = &popup
- case ui.BanViewPopupmsg:
+ case ui.BanViewPopupMsg:
popup := banview.New(msg.User, msg.Network)
m.banViewPopup = &popup
+ case ui.ProfilePopupMsg:
+ popup := profile.New(msg.User)
+ m.profilePopup = &popup
+
case tea.KeyMsg:
switch msg.String() {
case "n":
@@ -482,6 +490,7 @@ func (m *Model) updateConnected(message tea.Msg) tea.Cmd {
m.banReasonPopup = nil
m.banViewPopup = nil
m.signalAddPopup = nil
+ m.profilePopup = nil
}
case "enter":
@@ -538,6 +547,8 @@ func (m *Model) updateConnected(message tea.Msg) tea.Cmd {
m.signalList.SetIndex(i)
}
return cmd
+ } else if m.profilePopup != nil {
+ m.profilePopup = nil
}
default:
@@ -689,6 +700,10 @@ func (m *Model) updatePopups(msg tea.Msg) tea.Cmd {
popup, cmd := m.signalAddPopup.Update(msg)
m.signalAddPopup = &popup
return cmd
+ } else if m.profilePopup != nil {
+ popup, cmd := m.profilePopup.Update(msg)
+ m.profilePopup = &popup
+ return cmd
}
return nil
}
@@ -703,7 +718,8 @@ func (m *Model) HasPopup() bool {
m.networkJoinPopup != nil ||
m.banReasonPopup != nil ||
m.banViewPopup != nil ||
- m.signalAddPopup != nil
+ m.signalAddPopup != nil ||
+ m.profilePopup != nil
}
func calculateNotifications() {
diff --git a/internal/client/ui/core/memberlist/memberlist.go b/internal/client/ui/core/memberlist/memberlist.go
index 3e07446..14ff342 100644
--- a/internal/client/ui/core/memberlist/memberlist.go
+++ b/internal/client/ui/core/memberlist/memberlist.go
@@ -200,7 +200,13 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.SetIndex(m.index + m.height/2)
case "p":
- // TODO: profile
+ member := m.Members()[m.index]
+
+ return m, func() tea.Msg {
+ return ui.ProfilePopupMsg{
+ User: member.UserID,
+ }
+ }
// Normal
case "T":
diff --git a/internal/client/ui/core/profile/profile.go b/internal/client/ui/core/profile/profile.go
new file mode 100644
index 0000000..601b99c
--- /dev/null
+++ b/internal/client/ui/core/profile/profile.go
@@ -0,0 +1,145 @@
+package profile
+
+import (
+ "crypto/ed25519"
+ "strconv"
+ "strings"
+
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+ "github.com/kyren223/eko/internal/client/ui/colors"
+ "github.com/kyren223/eko/internal/client/ui/core/state"
+ "github.com/kyren223/eko/pkg/assert"
+ "github.com/kyren223/eko/pkg/snowflake"
+ "golang.org/x/crypto/ssh"
+)
+
+var width = 70
+
+type Model struct {
+ id snowflake.ID
+}
+
+func New(userId snowflake.ID) Model {
+ return Model{
+ id: userId,
+ }
+}
+
+func (m Model) Init() tea.Cmd {
+ return nil
+}
+
+func (m Model) View() string {
+ user := state.State.Users[m.id]
+
+ var builder strings.Builder
+
+ headerStyle := lipgloss.NewStyle().
+ PaddingLeft(2).
+ Width(width).
+ Background(colors.Background).
+ Foreground(colors.Focus)
+
+ name := user.Name
+ name = lipgloss.NewStyle().
+ MarginBackground(colors.Background).
+ Background(colors.Gold).
+ Foreground(colors.Background).
+ Bold(true).
+ Render(name)
+ paddingLeft := lipgloss.NewStyle().
+ MarginLeft(2).
+ Background(colors.Background).
+ Foreground(colors.Gold).
+ Render("")
+ paddingRight := lipgloss.NewStyle().
+ MarginRight(2).
+ Background(colors.Background).
+ Foreground(colors.Gold).
+ Render("")
+ builder.WriteString(paddingLeft)
+ builder.WriteString(name)
+ builder.WriteString(paddingRight)
+ builder.WriteByte('\n')
+ builder.WriteByte('\n')
+
+ publicDMs := "Private DMs"
+ if user.IsPublicDM {
+ publicDMs = "Public DMs"
+ }
+
+ id := strconv.FormatInt(int64(user.ID), 10)
+ info := lipgloss.NewStyle().
+ PaddingLeft(2).
+ Width(width).
+ Background(colors.Background).
+ Foreground(colors.LightGray).
+ Render("ID: " + id + " | " + publicDMs)
+ builder.WriteString(info)
+ builder.WriteByte('\n')
+ builder.WriteByte('\n')
+
+ publicKeyHeader := headerStyle.Render("Public Key (ssh-ed25519)")
+ publicKey := lipgloss.NewStyle().
+ PaddingLeft(2).
+ Width(width).
+ Render(publicKeyToSshString(user.PublicKey))
+ builder.WriteString(publicKeyHeader)
+ builder.WriteByte('\n')
+ builder.WriteByte('\n')
+ builder.WriteString(publicKey)
+ builder.WriteByte('\n')
+
+ aboutMeHeader := headerStyle.Render("About me")
+ builder.WriteString(aboutMeHeader)
+ builder.WriteByte('\n')
+ builder.WriteByte('\n')
+
+ textBg := colors.BackgroundHighlight
+ textFg := colors.White
+ bg := colors.Background
+
+ description := user.Description
+ if description == "" {
+ description = lipgloss.NewStyle().
+ Foreground(colors.Purple).
+ Render("No description was provided")
+ }
+
+ excessWidth := 4 - 2
+ bgStyle := lipgloss.NewStyle().Background(textBg).Foreground(bg)
+ topMiddle := strings.Repeat(" ", width-excessWidth)
+ top := bgStyle.Render("🭠🭘" + topMiddle + "🭣🭕")
+ middle := lipgloss.NewStyle().Width(width+2).Padding(0, 2).
+ Background(textBg).Foreground(textFg).Render(description)
+ bgStyle2 := lipgloss.NewStyle().Foreground(textBg)
+ bottomMiddle := strings.Repeat("█", width-excessWidth)
+ bottom := bgStyle2.Render("🭥🭓" + bottomMiddle + "🭞🭚")
+ aboutMe := lipgloss.JoinVertical(lipgloss.Left, top, middle, bottom)
+
+ builder.WriteString(aboutMe)
+
+ content := builder.String()
+
+ return lipgloss.NewStyle().
+ Border(lipgloss.ThickBorder()).
+ Padding(1, 2).
+ Align(lipgloss.Left, lipgloss.Center).
+ BorderBackground(colors.Background).
+ BorderForeground(colors.White).
+ Background(colors.Background).
+ Foreground(colors.White).
+ Render(content)
+}
+
+func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
+ return m, nil
+}
+
+func publicKeyToSshString(pub ed25519.PublicKey) string {
+ sshPubKey, err := ssh.NewPublicKey(pub)
+ assert.NoError(err, "converting to ssh key should never fail")
+ sshPubKeyBytes := ssh.MarshalAuthorizedKey(sshPubKey)
+ return strings.Split(string(sshPubKeyBytes), " ")[1]
+}
diff --git a/internal/client/ui/core/signallist/signallist.go b/internal/client/ui/core/signallist/signallist.go
index f20724d..b8476ec 100644
--- a/internal/client/ui/core/signallist/signallist.go
+++ b/internal/client/ui/core/signallist/signallist.go
@@ -185,6 +185,18 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
case "i":
_ = clipboard.WriteAll(strconv.FormatInt(int64(*state.UserID), 10))
+ case "p":
+ if m.index == -1 {
+ return m, nil
+ }
+ userId := state.Data.Signals[m.index]
+
+ return m, func() tea.Msg {
+ return ui.ProfilePopupMsg{
+ User: userId,
+ }
+ }
+
case "c":
if m.index == -1 {
return m, nil