Added the ability to view user profiles

This commit is contained in:
2025-02-28 21:23:43 +02:00
parent 77163cac1d
commit 0afb682db1
8 changed files with 207 additions and 12 deletions

View File

@@ -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,
}

View File

@@ -13,12 +13,6 @@ import (
var width = 48
const (
BanReasonField = iota
BanField
FieldCount
)
type Model struct {
banReason string
name string

View File

@@ -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

View File

@@ -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() {

View File

@@ -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":

View File

@@ -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]
}

View File

@@ -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

View File

@@ -57,7 +57,11 @@ func Transition(model tea.Model) tea.Cmd {
type QuitMsg struct{}
type BanViewPopupmsg struct {
type ProfilePopupMsg struct {
User snowflake.ID
}
type BanViewPopupMsg struct {
Network snowflake.ID
User snowflake.ID
}