summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2025-01-16 19:11:15 +0200
committerKyren223 <Kyren223@proton.me>2025-01-16 19:11:15 +0200
commit15067a81c5ee1ddc1fc6c4760ca13cd8e64c08f7 (patch)
treea04ccb749be2bdb22ffa365cfeb18df47696fae7 /internal
parent9ebd017160ceac890b8e03b213064ae00c9fc158 (diff)
Added client and server side edit message functionality
Diffstat (limited to 'internal')
-rw-r--r--internal/client/gateway/gateway.go2
-rw-r--r--internal/client/ui/core/chat/chat.go77
-rw-r--r--internal/client/ui/viminput/viminput.go12
-rw-r--r--internal/server/api/api.go68
-rw-r--r--internal/server/server.go2
5 files changed, 157 insertions, 4 deletions
diff --git a/internal/client/gateway/gateway.go b/internal/client/gateway/gateway.go
index 16a3176..4d7c9ed 100644
--- a/internal/client/gateway/gateway.go
+++ b/internal/client/gateway/gateway.go
@@ -229,6 +229,8 @@ func Send(request packet.Payload) tea.Cmd {
err := send(request)
if err != nil {
log.Println("request send error:", err)
+ } else {
+ log.Println("request sent successfully:", request)
}
return RequestSentMsg{
request: request,
diff --git a/internal/client/ui/core/chat/chat.go b/internal/client/ui/core/chat/chat.go
index ef9de7e..78109dd 100644
--- a/internal/client/ui/core/chat/chat.go
+++ b/internal/client/ui/core/chat/chat.go
@@ -26,6 +26,7 @@ var (
focusStyle = lipgloss.NewStyle().Foreground(colors.Focus)
readOnlyStyle = lipgloss.NewStyle().Foreground(colors.Gray)
mutedStyle = lipgloss.NewStyle().Foreground(colors.Red)
+ editStyle = lipgloss.NewStyle().Foreground(colors.Gold)
ViBlurredBorder = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder(), true, true, false).
@@ -33,6 +34,7 @@ var (
ViFocusedBorder = ViBlurredBorder.BorderForeground(colors.Focus)
ViReadOnlyBorder = ViBlurredBorder.BorderForeground(colors.Gray)
ViMutedBorder = ViBlurredBorder.BorderForeground(colors.Red)
+ ViEditBorder = ViBlurredBorder.BorderForeground(colors.Gold)
VimModeStyle = lipgloss.NewStyle().Bold(true)
@@ -84,7 +86,8 @@ type Model struct {
base int
index int
- selectedMessage *snowflake.ID
+ selectedMessage *data.Message
+ editingMessage *data.Message
messagesHeight int
maxMessagesHeight int
@@ -107,16 +110,22 @@ func New(width int) Model {
vi: vi,
focus: false,
locked: false,
+ hasReadAccess: false,
+ hasWriteAccess: false,
networkIndex: -1,
receiverIndex: -1,
frequencyIndex: -1,
base: SnapToBottom,
index: Unselected,
+ selectedMessage: nil,
+ editingMessage: nil,
messagesHeight: 0,
maxMessagesHeight: -1,
messagesCache: nil,
prerender: "",
width: width,
+ style: blurStyle,
+ borderStyle: ViBlurredBorder,
}
}
@@ -174,6 +183,10 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.borderStyle = ViFocusedBorder
m.style = focusStyle
m.vi.SetInactive(false)
+ if m.editingMessage != nil {
+ m.borderStyle = ViEditBorder
+ m.style = editStyle
+ }
} else {
m.vi.Placeholder = SendMessagePlaceholder
m.borderStyle = ViBlurredBorder
@@ -193,14 +206,38 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
if key, ok := msg.(tea.KeyMsg); ok {
inNormalQ := key.String() == "q" && m.vi.Mode() == viminput.NormalMode
inInsertCtrlQ := key.String() == "ctrl+q" && m.vi.Mode() == viminput.InsertMode
+
if inNormalQ || inInsertCtrlQ {
m.locked = false
+
+ if m.editingMessage != nil {
+ m.editingMessage = nil
+ m.borderStyle = ViBlurredBorder
+ m.style = blurStyle
+ m.vi.Reset()
+ m.vi, _ = m.vi.Update(msg)
+ }
+
m.Prerender()
return m, nil
}
if key.Type == tea.KeyEnter {
- cmd := m.sendMessage()
+ var cmd tea.Cmd
+
+ if m.editingMessage != nil {
+ cmd = m.editMessage()
+ m.locked = false
+ m.editingMessage = nil
+
+ m.borderStyle = ViBlurredBorder
+ m.style = blurStyle
+ m.vi.Reset()
+ m.vi, _ = m.vi.Update(msg)
+ } else {
+ cmd = m.sendMessage()
+ }
+
m.Prerender()
return m, cmd
}
@@ -251,9 +288,26 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
if m.selectedMessage != nil {
log.Println("deleting message:", m.selectedMessage)
cmd = gateway.Send(&packet.DeleteMessage{
- Message: *m.selectedMessage,
+ Message: m.selectedMessage.ID,
})
}
+
+ case "e":
+ if m.selectedMessage != nil {
+ log.Println("editing message:", m.selectedMessage)
+ m.editingMessage = m.selectedMessage
+
+ m.vi.Reset()
+ m.vi.SetString(m.selectedMessage.Content)
+
+ m.locked = true
+ m.vi.SetMode(viminput.InsertMode)
+ m.vi.SetCursorLine(len(m.vi.Lines()) - 1)
+ m.vi.SetCursorColumn(len(m.vi.Lines()[m.vi.CursorLine()]))
+
+ m.borderStyle = ViEditBorder
+ m.style = editStyle
+ }
}
}
@@ -627,7 +681,7 @@ func (m *Model) renderMessageGroup(group []data.Message, remaining *int, height
*remaining-- // For the header
if selectedIndex != -1 {
- m.selectedMessage = &group[selectedIndex].ID
+ m.selectedMessage = &group[selectedIndex]
if selectedIndex == len(group)-1 {
buf = m.renderHeader(group[selectedIndex], true)
@@ -752,3 +806,18 @@ func (m *Model) SetIndex(index int) {
m.index = Unselected
}
}
+
+func (m *Model) editMessage() tea.Cmd {
+ message := m.vi.String()
+ if len(message) > MaxCharCount {
+ return nil
+ }
+ if len(strings.TrimSpace(message)) == 0 {
+ return nil
+ }
+
+ return gateway.Send(&packet.EditMessage{
+ Message: m.editingMessage.ID,
+ Content: message,
+ })
+}
diff --git a/internal/client/ui/viminput/viminput.go b/internal/client/ui/viminput/viminput.go
index 296ce6d..6bc66d1 100644
--- a/internal/client/ui/viminput/viminput.go
+++ b/internal/client/ui/viminput/viminput.go
@@ -2104,3 +2104,15 @@ func (m *Model) SetInactive(inactive bool) {
func (m *Model) Inactive() bool {
return m.inactive
}
+
+func (m *Model) Lines() [][]rune {
+ return m.lines
+}
+
+func (m *Model) CursorLine() int {
+ return m.cursorLine
+}
+
+func (m *Model) CursorColumn() int {
+ return m.cursorColumn
+}
diff --git a/internal/server/api/api.go b/internal/server/api/api.go
index f0d1b1f..24112cf 100644
--- a/internal/server/api/api.go
+++ b/internal/server/api/api.go
@@ -891,3 +891,71 @@ func DeleteMessage(ctx context.Context, sess *session.Session, request *packet.D
assert.Never("unreachable")
return nil
}
+
+func EditMessage(ctx context.Context, sess *session.Session, request *packet.EditMessage) packet.Payload {
+ if len(request.Content) > packet.MaxMessageBytes {
+ return &packet.Error{Error: fmt.Sprintf(
+ "message conent must not exceed %v bytes",
+ packet.MaxMessageBytes,
+ )}
+ }
+
+ content := strings.TrimSpace(request.Content)
+ if content == "" {
+ return &packet.Error{Error: "message content must not be blank"}
+ }
+
+ queries := data.New(db)
+
+ message, err := queries.GetMessageById(ctx, request.Message)
+ if err == sql.ErrNoRows {
+ return &packet.Error{Error: "message doesn't exist"}
+ }
+ if err != nil {
+ log.Println("database error 0:", err)
+ return &ErrInternalError
+ }
+
+ if message.FrequencyID != nil {
+ frequency, err := queries.GetFrequencyById(ctx, *message.FrequencyID)
+ if err != nil {
+ log.Println("database error 1:", err)
+ return &ErrInternalError
+ }
+
+ // Note: it is possible to edit your messages in any frequency
+ // regardless if you are in the network or if you have access to
+ // the frequency, as long as you know the message ID
+ // This should be fine but may be changed later to be more strict
+ if message.SenderID != sess.ID() {
+ return &ErrPermissionDenied
+ }
+
+ editedMessage, err := queries.EditMessage(ctx, data.EditMessageParams{
+ Content: content,
+ ID: message.ID,
+ })
+ if err != nil {
+ log.Println("database error 4:", err)
+ return &ErrInternalError
+ }
+
+ return NetworkPropagateWithFilter(ctx, sess, frequency.NetworkID, &packet.MessagesInfo{
+ Messages: []data.Message{editedMessage},
+ RemovedMessages: nil,
+ }, func(userId snowflake.ID) (pass bool) {
+ if frequency.Perms != packet.PermNoAccess {
+ return true
+ }
+ isAdmin, _ := IsNetworkAdmin(ctx, queries, userId, frequency.NetworkID)
+ return isAdmin
+ })
+ }
+
+ if message.ReceiverID != nil {
+ return &ErrNotImplemented
+ }
+
+ assert.Never("unreachable")
+ return nil
+}
diff --git a/internal/server/server.go b/internal/server/server.go
index 95c22f6..8e69d70 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -361,6 +361,8 @@ func processRequest(ctx context.Context, sess *session.Session, request packet.P
case *packet.SendMessage:
response = timeout(20*time.Millisecond, api.SendMessage, ctx, sess, request)
+ case *packet.EditMessage:
+ response = timeout(5*time.Millisecond, api.EditMessage, ctx, sess, request)
case *packet.DeleteMessage:
response = timeout(5*time.Millisecond, api.DeleteMessage, ctx, sess, request)
case *packet.RequestMessages: