view: refactor edit into its own self-contained file and struct.

This commit is contained in:
fiatjaf
2025-11-26 23:37:11 -03:00
parent 530b484662
commit 2b189756d1
3 changed files with 190 additions and 164 deletions

181
view/event.go Normal file
View File

@@ -0,0 +1,181 @@
package main
import (
"context"
"encoding/json"
"slices"
"strings"
"fiatjaf.com/nostr"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/widgets"
)
var event struct {
kindSpin *widgets.QSpinBox
kindNameLabel *widgets.QLabel
tagRows [][]*widgets.QLineEdit
contentEdit *widgets.QTextEdit
createdAtEdit *widgets.QDateTimeEdit
outputEdit *widgets.QTextEdit
}
func updateEvent() {
kind := nostr.Kind(event.kindSpin.Value())
kindName := kind.Name()
if kindName != "unknown" {
event.kindNameLabel.SetText(kindName)
} else {
event.kindNameLabel.SetText("")
}
tags := make(nostr.Tags, 0, len(event.tagRows))
for y, tagItems := range event.tagRows {
if y == len(event.tagRows)-1 && strings.TrimSpace(tagItems[0].Text()) == "" {
continue
}
tag := make(nostr.Tag, 0, len(tagItems))
for x, edit := range tagItems {
text := strings.TrimSpace(edit.Text())
if x == len(tagItems)-1 && text == "" {
continue
}
text = decodeTagValue(text)
tag = append(tag, text)
}
if len(tag) > 0 {
tags = append(tags, tag)
}
}
final := nostr.Event{
Kind: kind,
Content: event.contentEdit.ToPlainText(),
CreatedAt: nostr.Timestamp(event.createdAtEdit.DateTime().ToMSecsSinceEpoch() / 1000),
Tags: tags,
}
if currentKeyer != nil {
currentKeyer.SignEvent(context.Background(), &final)
}
jsonBytes, _ := json.MarshalIndent(event, "", " ")
event.outputEdit.SetPlainText(string(jsonBytes))
}
func setupEventTab() *widgets.QWidget {
tab := widgets.NewQWidget(nil, 0)
// set up event tab
layout := widgets.NewQVBoxLayout()
tab.SetLayout(layout)
// kind input
kindHBox := widgets.NewQHBoxLayout()
layout.AddLayout(kindHBox, 0)
kindLabel := widgets.NewQLabel2("kind:", nil, 0)
kindHBox.AddWidget(kindLabel, 0, 0)
event.kindSpin = widgets.NewQSpinBox(nil)
event.kindSpin.SetValue(1)
event.kindSpin.SetMinimum(0)
event.kindSpin.SetMaximum(1<<16 - 1)
kindHBox.AddWidget(event.kindSpin, 0, 0)
event.kindSpin.ConnectValueChanged(func(int) {
updateEvent()
})
event.kindNameLabel = widgets.NewQLabel2("", nil, 0)
kindHBox.AddWidget(event.kindNameLabel, 0, 0)
// content input
contentLabel := widgets.NewQLabel2("content:", nil, 0)
layout.AddWidget(contentLabel, 0, 0)
event.contentEdit = widgets.NewQTextEdit(nil)
layout.AddWidget(event.contentEdit, 0, 0)
event.contentEdit.ConnectTextChanged(updateEvent)
// created_at input
createdAtLabel := widgets.NewQLabel2("created at:", nil, 0)
layout.AddWidget(createdAtLabel, 0, 0)
event.createdAtEdit = widgets.NewQDateTimeEdit(nil)
event.createdAtEdit.SetDateTime(core.QDateTime_CurrentDateTime())
layout.AddWidget(event.createdAtEdit, 0, 0)
event.createdAtEdit.ConnectDateTimeChanged(func(*core.QDateTime) {
updateEvent()
})
// tags input
tagsLabel := widgets.NewQLabel2("tags:", nil, 0)
layout.AddWidget(tagsLabel, 0, 0)
tagsLayout := widgets.NewQVBoxLayout()
tagRowHBoxes := make([]widgets.QLayout_ITF, 0, 2)
event.tagRows = make([][]*widgets.QLineEdit, 0, 2)
layout.AddLayout(tagsLayout, 0)
var addTagRow func()
addTagRow = func() {
hbox := widgets.NewQHBoxLayout()
tagRowHBoxes = append(tagRowHBoxes, hbox)
tagsLayout.AddLayout(hbox, 0)
tagItems := []*widgets.QLineEdit{}
y := len(event.tagRows)
event.tagRows = append(event.tagRows, tagItems)
var addItem func()
addItem = func() {
edit := widgets.NewQLineEdit(nil)
hbox.AddWidget(edit, 0, 0)
x := len(tagItems)
tagItems = append(tagItems, edit)
event.tagRows[y] = tagItems
edit.ConnectTextChanged(func(text string) {
if strings.TrimSpace(text) != "" {
// when an item input has been filled check if we have to show more
if y == len(event.tagRows)-1 {
addTagRow()
}
if x == len(tagItems)-1 {
addItem()
}
} else {
// do this when an item input has been emptied: check if we need to remove an item from this row
nItems := len(tagItems)
if nItems >= 2 && strings.TrimSpace(tagItems[nItems-1].Text()) == "" && strings.TrimSpace(tagItems[nItems-2].Text()) == "" {
// remove last item if the last 2 are empty
hbox.Layout().RemoveWidget(tagItems[nItems-1])
tagItems[nItems-1].DeleteLater()
tagItems = tagItems[0 : nItems-1]
event.tagRows[y] = tagItems
}
// check if we need to remove rows
nRows := len(event.tagRows)
itemIsFilled := func(edit *widgets.QLineEdit) bool { return strings.TrimSpace(edit.Text()) != "" }
if nRows >= 2 && !slices.ContainsFunc(event.tagRows[nRows-1], itemIsFilled) && !slices.ContainsFunc(event.tagRows[nRows-2], itemIsFilled) {
// remove the last row if the last 2 are empty
tagsLayout.RemoveItem(tagRowHBoxes[nRows-1])
for _, tagItem := range event.tagRows[nRows-1] {
tagItem.DeleteLater()
}
tagRowHBoxes[nRows-1].QLayout_PTR().DeleteLater()
event.tagRows = event.tagRows[0 : nRows-1]
tagRowHBoxes = tagRowHBoxes[0 : nRows-1]
}
}
updateEvent()
})
}
addItem()
}
// first
addTagRow()
// output JSON
outputLabel := widgets.NewQLabel2("event:", nil, 0)
layout.AddWidget(outputLabel, 0, 0)
event.outputEdit = widgets.NewQTextEdit(nil)
event.outputEdit.SetReadOnly(true)
layout.AddWidget(event.outputEdit, 0, 0)
return tab
}

View File

@@ -1,21 +1,16 @@
package main
import (
"encoding/json"
"os"
"slices"
"strings"
"fiatjaf.com/nostr"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/widgets"
)
var (
currentSec nostr.SecretKey
currentKeyer nostr.Keyer
tagRows [][]*widgets.QLineEdit
updateEvent func()
statusLabel *widgets.QLabel
)
func main() {
@@ -41,22 +36,25 @@ func main() {
if text == "" {
currentSec = nostr.SecretKey{}
currentKeyer = nil
statusLabel.SetText("")
return
}
sk, bunker, err := handleSecretKeyOrBunker(text)
if err != nil {
// TODO: have a field somewhere at the bottom of the screen, below the tabs view, to display errors and other messages
statusLabel.SetText(err.Error())
currentSec = nostr.SecretKey{}
currentKeyer = nil
return
}
currentSec = sk
currentKeyer = bunker
statusLabel.SetText("")
updateEvent()
})
tabWidget := widgets.NewQTabWidget(nil)
eventTab := widgets.NewQWidget(nil, 0)
eventTab := setupEventTab()
reqTab := widgets.NewQWidget(nil, 0)
tabWidget.AddTab(eventTab, "event")
@@ -64,162 +62,8 @@ func main() {
mainLayout.AddWidget(tabWidget, 0, 0)
// set up event tab
layout := widgets.NewQVBoxLayout()
eventTab.SetLayout(layout)
// kind input
kindHBox := widgets.NewQHBoxLayout()
layout.AddLayout(kindHBox, 0)
kindLabel := widgets.NewQLabel2("kind:", nil, 0)
kindHBox.AddWidget(kindLabel, 0, 0)
kindSpin := widgets.NewQSpinBox(nil)
// TODO: set default value to 1
kindSpin.SetMinimum(0)
kindSpin.SetMaximum(1<<16 - 1)
kindHBox.AddWidget(kindSpin, 0, 0)
kindSpin.ConnectValueChanged(func(int) {
updateEvent()
})
kindNameLabel := widgets.NewQLabel2("", nil, 0)
kindHBox.AddWidget(kindNameLabel, 0, 0)
// content input
contentLabel := widgets.NewQLabel2("content:", nil, 0)
layout.AddWidget(contentLabel, 0, 0)
contentEdit := widgets.NewQTextEdit(nil)
layout.AddWidget(contentEdit, 0, 0)
contentEdit.ConnectTextChanged(updateEvent)
// created_at input
createdAtLabel := widgets.NewQLabel2("created at:", nil, 0)
layout.AddWidget(createdAtLabel, 0, 0)
createdAtEdit := widgets.NewQDateTimeEdit(nil)
createdAtEdit.SetDateTime(core.QDateTime_CurrentDateTime())
layout.AddWidget(createdAtEdit, 0, 0)
createdAtEdit.ConnectDateTimeChanged(func(*core.QDateTime) {
updateEvent()
})
// tags input
tagsLabel := widgets.NewQLabel2("tags:", nil, 0)
layout.AddWidget(tagsLabel, 0, 0)
tagsLayout := widgets.NewQVBoxLayout()
tagRowHBoxes := make([]widgets.QLayout_ITF, 0, 2)
layout.AddLayout(tagsLayout, 0)
var addTagRow func()
addTagRow = func() {
hbox := widgets.NewQHBoxLayout()
tagRowHBoxes = append(tagRowHBoxes, hbox)
tagsLayout.AddLayout(hbox, 0)
tagItems := []*widgets.QLineEdit{}
y := len(tagRows)
tagRows = append(tagRows, tagItems)
var addItem func()
addItem = func() {
edit := widgets.NewQLineEdit(nil)
hbox.AddWidget(edit, 0, 0)
x := len(tagItems)
tagItems = append(tagItems, edit)
tagRows[y] = tagItems
edit.ConnectTextChanged(func(text string) {
if strings.TrimSpace(text) != "" {
// when an item input has been filled check if we have to show more
if y == len(tagRows)-1 {
addTagRow()
}
if x == len(tagItems)-1 {
addItem()
}
} else {
// do this when an item input has been emptied: check if we need to remove an item from this row
nItems := len(tagItems)
if nItems >= 2 && strings.TrimSpace(tagItems[nItems-1].Text()) == "" && strings.TrimSpace(tagItems[nItems-2].Text()) == "" {
// remove last item if the last 2 are empty
hbox.Layout().RemoveWidget(tagItems[nItems-1])
tagItems[nItems-1].DeleteLater()
tagItems = tagItems[0 : nItems-1]
tagRows[y] = tagItems
}
// check if we need to remove rows
nRows := len(tagRows)
itemIsFilled := func(edit *widgets.QLineEdit) bool { return strings.TrimSpace(edit.Text()) != "" }
if nRows >= 2 && !slices.ContainsFunc(tagRows[nRows-1], itemIsFilled) && !slices.ContainsFunc(tagRows[nRows-2], itemIsFilled) {
// remove the last row if the last 2 are empty
tagsLayout.RemoveItem(tagRowHBoxes[nRows-1])
for _, tagItem := range tagRows[nRows-1] {
tagItem.DeleteLater()
}
tagRowHBoxes[nRows-1].QLayout_PTR().DeleteLater()
tagRows = tagRows[0 : nRows-1]
tagRowHBoxes = tagRowHBoxes[0 : nRows-1]
}
}
updateEvent()
})
}
addItem()
}
// first
addTagRow()
// output JSON
outputLabel := widgets.NewQLabel2("event:", nil, 0)
layout.AddWidget(outputLabel, 0, 0)
outputEdit := widgets.NewQTextEdit(nil)
outputEdit.SetReadOnly(true)
layout.AddWidget(outputEdit, 0, 0)
// function to update the display
updateEvent = func() {
kind := nostr.Kind(kindSpin.Value())
kindName := kind.Name()
if kindName != "unknown" {
kindNameLabel.SetText(kindName)
} else {
kindNameLabel.SetText("")
}
tags := make(nostr.Tags, 0, len(tagRows))
for y, tagItems := range tagRows {
if y == len(tagRows)-1 && strings.TrimSpace(tagItems[0].Text()) == "" {
continue
}
tag := make(nostr.Tag, 0, len(tagItems))
for x, edit := range tagItems {
text := strings.TrimSpace(edit.Text())
if x == len(tagItems)-1 && text == "" {
continue
}
text = decodeTagValue(text)
tag = append(tag, text)
}
if len(tag) > 0 {
tags = append(tags, tag)
}
}
event := nostr.Event{
Kind: kind,
Content: contentEdit.ToPlainText(),
CreatedAt: nostr.Timestamp(createdAtEdit.DateTime().ToMSecsSinceEpoch() / 1000),
Tags: tags,
}
if currentKeyer != nil {
// TODO:
// call debounced function that calls:
// currentKeyer.SignEvent(event)
// then the json.Marshal / outputEdit.SetPlainText
}
jsonBytes, _ := json.MarshalIndent(event, "", " ")
outputEdit.SetPlainText(string(jsonBytes))
}
statusLabel = widgets.NewQLabel2("", nil, 0)
mainLayout.AddWidget(statusLabel, 0, 0)
// initial render
updateEvent()

1
view/req.go Normal file
View File

@@ -0,0 +1 @@
package main