From 2b189756d1bd6f239b7f10ff9d52ac9546db3dee Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 26 Nov 2025 23:37:11 -0300 Subject: [PATCH] view: refactor edit into its own self-contained file and struct. --- view/event.go | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++ view/main.go | 172 +++-------------------------------------------- view/req.go | 1 + 3 files changed, 190 insertions(+), 164 deletions(-) create mode 100644 view/event.go create mode 100644 view/req.go diff --git a/view/event.go b/view/event.go new file mode 100644 index 0000000..f12ed8e --- /dev/null +++ b/view/event.go @@ -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 +} diff --git a/view/main.go b/view/main.go index 37d2456..cf12214 100644 --- a/view/main.go +++ b/view/main.go @@ -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() diff --git a/view/req.go b/view/req.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/view/req.go @@ -0,0 +1 @@ +package main