From 5903d96d14d4b17da3f6236198a82b220e8b9233 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Tue, 2 Dec 2025 21:41:41 -0300 Subject: [PATCH] req: improve layout, perform auth when key is set. --- main.go | 27 +++++++ req.go | 216 +++++++++++++++++++++++++++++++------------------------- 2 files changed, 145 insertions(+), 98 deletions(-) diff --git a/main.go b/main.go index d17a001..f24f0ca 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,8 @@ import ( "context" "encoding/hex" "flag" + "fmt" + "net/http" "os" "strings" "time" @@ -41,6 +43,31 @@ var ( func main() { flag.Parse() + + // nostr setup + httpHeader := http.Header{} + httpHeader.Set("User-Agent", "vnak") + sys.Pool = nostr.NewPool(nostr.PoolOptions{ + AuthorKindQueryMiddleware: sys.TrackQueryAttempts, + EventMiddleware: sys.TrackEventHintsAndRelays, + DuplicateMiddleware: sys.TrackEventRelaysD, + PenaltyBox: false, + AuthHandler: func(ctx context.Context, evt *nostr.Event) error { + if currentKeyer != nil { + err := currentKeyer.SignEvent(ctx, evt) + if err != nil { + return fmt.Errorf("failed to sign auth event: %w", err) + } + return nil + } + return fmt.Errorf("can't sign auth event, no key") + }, + RelayOptions: nostr.RelayOptions{ + RequestHeader: httpHeader, + }, + }) + + // UI setup app = qt.NewQApplication(os.Args) window = qt.NewQMainWindow2() diff --git a/req.go b/req.go index c4be482..0873a3a 100644 --- a/req.go +++ b/req.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/json" "fmt" "strconv" @@ -59,15 +60,6 @@ func setupReqTab() *qt.QWidget { layout := qt.NewQVBoxLayout2() req.tab.SetLayout(layout.QLayout) - // authors - authorsLabel := qt.NewQLabel2() - authorsLabel.SetText("authors:") - layout.AddWidget(authorsLabel.QWidget) - req.authorsVBox = qt.NewQVBoxLayout2() - layout.AddLayout(req.authorsVBox.QLayout) - req.authorsEdits = []*qt.QLineEdit{} - req.addAuthor("") - // ids idsLabel := qt.NewQLabel2() idsLabel.SetText("ids:") @@ -77,6 +69,15 @@ func setupReqTab() *qt.QWidget { req.idsEdits = []*qt.QLineEdit{} req.addId("") + // authors + authorsLabel := qt.NewQLabel2() + authorsLabel.SetText("authors:") + layout.AddWidget(authorsLabel.QWidget) + req.authorsVBox = qt.NewQVBoxLayout2() + layout.AddLayout(req.authorsVBox.QLayout) + req.authorsEdits = []*qt.QLineEdit{} + req.addAuthor("") + // kinds kindsLabel := qt.NewQLabel2() kindsLabel.SetText("kinds:") @@ -95,79 +96,26 @@ func setupReqTab() *qt.QWidget { req.tagRows = make([]reqTagRow, 0, 2) req.addTagRow("", []string{""}) - // since - sinceHBox := qt.NewQHBoxLayout2() - layout.AddLayout(sinceHBox.QLayout) - sinceLabel := qt.NewQLabel2() - sinceLabel.SetText("since:") - sinceHBox.AddWidget(sinceLabel.QWidget) - req.sinceCheck = qt.NewQCheckBox(req.tab) - req.sinceCheck.SetChecked(false) - sinceHBox.AddWidget(req.sinceCheck.QWidget) - req.sinceEdit = qt.NewQDateTimeEdit(req.tab) - req.sinceEdit.SetEnabled(false) - { - qtime := qt.NewQDateTime() - qtime.SetMSecsSinceEpoch(time.Now().UnixMilli()) - req.sinceEdit.SetDateTime(qtime) - } - sinceHBox.AddWidget(req.sinceEdit.QWidget) - req.sinceCheck.OnStateChanged(func(state int) { - req.sinceEdit.SetEnabled(state == 2) // 2 is checked - if state == 2 { - qtime := qt.NewQDateTime() - qtime.SetMSecsSinceEpoch(time.Now().UnixMilli()) - req.sinceEdit.SetDateTime(qtime) - } - req.updateReq() - }) - req.sinceEdit.OnDateTimeChanged(func(*qt.QDateTime) { - req.updateReq() - }) - - // until - untilHBox := qt.NewQHBoxLayout2() - layout.AddLayout(untilHBox.QLayout) - untilLabel := qt.NewQLabel2() - untilLabel.SetText("until:") - untilHBox.AddWidget(untilLabel.QWidget) - req.untilCheck = qt.NewQCheckBox(req.tab) - req.untilCheck.SetChecked(false) - untilHBox.AddWidget(req.untilCheck.QWidget) - req.untilEdit = qt.NewQDateTimeEdit(req.tab) - req.untilEdit.SetEnabled(false) - { - qtime := qt.NewQDateTime() - qtime.SetMSecsSinceEpoch(time.Now().UnixMilli()) - req.untilEdit.SetDateTime(qtime) - } - untilHBox.AddWidget(req.untilEdit.QWidget) - req.untilCheck.OnStateChanged(func(state int) { - req.untilEdit.SetEnabled(state == 2) // 2 is checked - if state == 2 { - qtime := qt.NewQDateTime() - qtime.SetMSecsSinceEpoch(time.Now().UnixMilli()) - req.untilEdit.SetDateTime(qtime) - } - req.updateReq() - }) - req.untilEdit.OnDateTimeChanged(func(*qt.QDateTime) { - req.updateReq() - }) + // numeric and boolean inputs + qtime := qt.NewQDateTime() + qtime.SetMSecsSinceEpoch(time.Now().UnixMilli()) + inputsHBox := qt.NewQHBoxLayout2() + layout.AddLayout(inputsHBox.QLayout) // limit limitHBox := qt.NewQHBoxLayout2() - layout.AddLayout(limitHBox.QLayout) + inputsHBox.AddLayout(limitHBox.QLayout) limitLabel := qt.NewQLabel2() limitLabel.SetText("limit:") limitHBox.AddWidget(limitLabel.QWidget) req.limitCheck = qt.NewQCheckBox(req.tab) - req.limitCheck.SetChecked(false) + req.limitCheck.SetChecked(true) limitHBox.AddWidget(req.limitCheck.QWidget) req.limitSpin = qt.NewQSpinBox(req.tab) - req.limitSpin.SetEnabled(false) + req.limitSpin.SetEnabled(true) req.limitSpin.SetMinimum(0) req.limitSpin.SetMaximum(1000) + req.limitSpin.SetValue(10) limitHBox.AddWidget(req.limitSpin.QWidget) req.limitCheck.OnStateChanged(func(state int) { req.limitSpin.SetEnabled(state == 2) // 2 is checked @@ -177,27 +125,73 @@ func setupReqTab() *qt.QWidget { req.updateReq() }) + // since + sinceHBox := qt.NewQHBoxLayout2() + sinceHBox.AddStretch() + inputsHBox.AddLayout(sinceHBox.QLayout) + sinceLabel := qt.NewQLabel2() + sinceLabel.SetText("since:") + sinceHBox.AddWidget(sinceLabel.QWidget) + req.sinceCheck = qt.NewQCheckBox(req.tab) + req.sinceCheck.SetChecked(false) + sinceHBox.AddWidget(req.sinceCheck.QWidget) + req.sinceEdit = qt.NewQDateTimeEdit(req.tab) + req.sinceEdit.SetEnabled(false) + req.sinceEdit.SetDateTime(qtime) + sinceHBox.AddWidget(req.sinceEdit.QWidget) + req.sinceCheck.OnStateChanged(func(state int) { + req.sinceEdit.SetEnabled(state == 2) // 2 is checked + req.updateReq() + }) + req.sinceEdit.OnDateTimeChanged(func(*qt.QDateTime) { + req.updateReq() + }) + + // until + untilHBox := qt.NewQHBoxLayout2() + untilHBox.AddStretch() + inputsHBox.AddLayout(untilHBox.QLayout) + untilLabel := qt.NewQLabel2() + untilLabel.SetText("until:") + untilHBox.AddWidget(untilLabel.QWidget) + req.untilCheck = qt.NewQCheckBox(req.tab) + req.untilCheck.SetChecked(false) + untilHBox.AddWidget(req.untilCheck.QWidget) + req.untilEdit = qt.NewQDateTimeEdit(req.tab) + req.untilEdit.SetEnabled(false) + req.untilEdit.SetDateTime(qtime) + untilHBox.AddWidget(req.untilEdit.QWidget) + req.untilCheck.OnStateChanged(func(state int) { + req.untilEdit.SetEnabled(state == 2) // 2 is checked + req.updateReq() + }) + req.untilEdit.OnDateTimeChanged(func(*qt.QDateTime) { + req.updateReq() + }) + // output + outputHBox := qt.NewQHBoxLayout2() + layout.AddLayout(outputHBox.QLayout) outputLabel := qt.NewQLabel2() outputLabel.SetText("filter:") - layout.AddWidget(outputLabel.QWidget) + outputHBox.AddWidget(outputLabel.QWidget) req.outputEdit = qt.NewQTextEdit(req.tab) req.outputEdit.SetReadOnly(true) req.outputEdit.SetMaximumHeight(100) - layout.AddWidget(req.outputEdit.QWidget) + outputHBox.AddWidget(req.outputEdit.QWidget) - // send button - buttonHBox := qt.NewQHBoxLayout2() sendButton := qt.NewQPushButton5("send request", req.tab) - buttonHBox.AddWidget(sendButton.QWidget) - buttonHBox.AddStretch() + sendButton.OnClicked(func() { + req.subscribe() + }) // relays - relaysLabel := qt.NewQLabel2() - relaysLabel.SetText("relays:") - layout.AddWidget(relaysLabel.QWidget) relaysHBox := qt.NewQHBoxLayout2() layout.AddLayout(relaysHBox.QLayout) + relaysLabel := qt.NewQLabel2() + relaysLabel.SetText("relays:") + relaysHBox.AddWidget(relaysLabel.QWidget) + req.relaysEdits = []*qt.QLineEdit{} var addRelayEdit func() addRelayEdit = func() { @@ -224,15 +218,18 @@ func setupReqTab() *qt.QWidget { } addRelayEdit() - layout.AddLayout(buttonHBox.QLayout) - // results + resultsVBox := qt.NewQVBoxLayout2() resultsLabel := qt.NewQLabel2() resultsLabel.SetText("results:") - layout.AddWidget(resultsLabel.QWidget) req.resultsList = qt.NewQListWidget(req.tab) - req.resultsList.SetMinimumHeight(200) - layout.AddWidget(req.resultsList.QWidget) + resultsVBox.AddWidget(resultsLabel.QWidget) + resultsVBox.AddWidget(req.resultsList.QWidget) + + subscribeHBox := qt.NewQHBoxLayout2() + layout.AddLayout(subscribeHBox.QLayout) + subscribeHBox.AddWidget(sendButton.QWidget) + subscribeHBox.AddLayout(resultsVBox.QLayout) // double-click to show pretty JSON req.resultsList.OnItemDoubleClicked(func(item *qt.QListWidgetItem) { @@ -257,10 +254,6 @@ func setupReqTab() *qt.QWidget { dialog.Exec() }) - sendButton.OnClicked(func() { - req.subscribe() - }) - return req.tab } @@ -293,6 +286,7 @@ func (req *reqVars) updateReq() { kinds := []nostr.Kind{} for _, kindRow := range req.kindRows { text := strings.TrimSpace(kindRow.edit.Text()) + kindRow.label.SetText("") if k, err := strconv.Atoi(text); err == nil { kind := nostr.Kind(k) kinds = append(kinds, kind) @@ -301,8 +295,6 @@ func (req *reqVars) updateReq() { name := kind.Name() if name != "unknown" { kindRow.label.SetText(name) - } else { - kindRow.label.SetText("") } } } @@ -383,6 +375,16 @@ func (req *reqVars) subscribe() { return } + if currentKeyer != nil { + err = relay.Auth(ctx, func(ctx context.Context, evt *nostr.Event) error { + return currentKeyer.SignEvent(ctx, evt) + }) + if err != nil { + statusLabel.SetText(fmt.Sprintf("failed to auth to %s: %s", niceRelayURL(relay.URL), err)) + return + } + } + statusLabel.SetText("subscribed to " + niceRelayURL(relay.URL)) sub, err := relay.Subscribe(ctx, req.filter, nostr.SubscriptionOptions{ Label: "vnak-req-1", @@ -515,7 +517,7 @@ func (req *reqVars) populate(filter nostr.Filter) { dt.SetMSecsSinceEpoch(int64(filter.Since) * 1000) req.sinceEdit.SetDateTime(dt) } else { - req.sinceCheck.SetChecked(false) + req.sinceCheck.SetChecked(true) } if filter.Until != 0 { @@ -672,9 +674,13 @@ func (req *reqVars) addTagRow(name string, values []string) { } } else { nItems := len(valuesEdits) - if nItems >= 2 && strings.TrimSpace(valuesEdits[nItems-1].Text()) == "" && strings.TrimSpace(valuesEdits[nItems-2].Text()) == "" { - hbox.RemoveWidget(valuesEdits[nItems-1].QWidget) - valuesEdits[nItems-1].DeleteLater() + if nItems >= 2 && + strings.TrimSpace(valuesEdits[nItems-1].Text()) == "" && + strings.TrimSpace(valuesEdits[nItems-2].Text()) == "" { + // when the last values of a tag row are empty remove the last one + last := valuesEdits[nItems-1] + hbox.RemoveWidget(last.QWidget) + last.DeleteLater() valuesEdits = valuesEdits[0 : nItems-1] req.tagRows[y].vals = valuesEdits } @@ -688,9 +694,23 @@ func (req *reqVars) addTagRow(name string, values []string) { perhapsDeleteRow = func() { nRows := len(req.tagRows) hasValue := func(e *qt.QLineEdit) bool { return strings.TrimSpace(e.Text()) != "" } - if nRows >= 2 && !slices.ContainsFunc(req.tagRows[nRows-1].vals, hasValue) && !slices.ContainsFunc(req.tagRows[nRows-2].vals, hasValue) { - req.tagsLayout.RemoveItem(hbox.QLayoutItem) - hbox.DeleteLater() + if nRows >= 2 && + !slices.ContainsFunc(req.tagRows[nRows-1].vals, hasValue) && + !slices.ContainsFunc(req.tagRows[nRows-2].vals, hasValue) && + strings.TrimSpace(req.tagRows[nRows-1].key.Text()) == "" && + strings.TrimSpace(req.tagRows[nRows-2].key.Text()) == "" { + // when the two last rows are completely empty remove the last one: + last := req.tagRows[nRows-1] + last.hbox.RemoveWidget(last.separator.QWidget) + last.separator.DeleteLater() + last.hbox.RemoveWidget(last.key.QWidget) + last.key.DeleteLater() + for _, val := range last.vals { + last.hbox.RemoveWidget(val.QWidget) + val.DeleteLater() + } + + req.tagsLayout.RemoveItem(last.hbox.QLayoutItem) req.tagRows = req.tagRows[0 : nRows-1] } }