mirror of
https://github.com/marcusDenslow/terminalShop.git
synced 2026-10-25 10:02:21 +01:00
fix(cards): advance tui after duplicate card detection by pollin on updated_at instead of count
This commit is contained in:
@@ -244,6 +244,11 @@ func (h *WebhookHandler) handleCheckoutSessionCompleted(ctx context.Context, eve
|
||||
if _, derr := paymentmethod.Detach(pm.ID, nil); derr != nil {
|
||||
webhookLog().Warn("failed to detach duplicate pm", "user_id", user.ID, "error", derr)
|
||||
}
|
||||
// Bump UpdatedAt so the TUI poll signal can detect that the webhook
|
||||
// fired even though no new row was inserted.
|
||||
if uerr := db.Model(&dup).Update("updated_at", time.Now()).Error; uerr != nil {
|
||||
webhookLog().Warn("failed to bump dup card updated_at", "card_id", dup.ID, "error", uerr)
|
||||
}
|
||||
webhookLog().Info("skipped duplicate card via local-db match", "user_id", user.ID, "existing_card_id", dup.ID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -132,7 +132,11 @@ func (m Model) generateReviewContent() string {
|
||||
view := strings.Builder{}
|
||||
|
||||
if m.review.cardJustAdded {
|
||||
view.WriteString(m.theme.TextSuccess().Bold(true).Render("card added successfully") + "\n\n")
|
||||
if m.review.cardWasDuplicate {
|
||||
view.WriteString(m.theme.TextDim().Render("identical card already saved — using existing card") + "\n\n")
|
||||
} else {
|
||||
view.WriteString(m.theme.TextSuccess().Bold(true).Render("card added successfully") + "\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
if m.ShippingInfo != nil {
|
||||
|
||||
@@ -170,6 +170,7 @@ type paymentState struct {
|
||||
cardCursor int
|
||||
collectURL *string
|
||||
collectCardCount int
|
||||
collectBaseline time.Time
|
||||
viewport viewport.Model
|
||||
viewportReady bool
|
||||
}
|
||||
@@ -191,8 +192,9 @@ type accountState struct {
|
||||
}
|
||||
|
||||
type reviewState struct {
|
||||
cardJustAdded bool
|
||||
success bool
|
||||
cardJustAdded bool
|
||||
cardWasDuplicate bool
|
||||
success bool
|
||||
}
|
||||
|
||||
type confirmState struct {
|
||||
@@ -951,18 +953,24 @@ func (m Model) pollOrderCmd() tea.Cmd {
|
||||
})
|
||||
}
|
||||
|
||||
func (m Model) pollCardsCmd(cardCount int) tea.Cmd {
|
||||
func (m Model) pollCardsCmd(baseline time.Time, baselineCount int) tea.Cmd {
|
||||
return tea.Tick(time.Second, func(_ time.Time) tea.Msg {
|
||||
if m.APIClient == nil {
|
||||
return PollPaymentStatusMsg{CardCount: cardCount}
|
||||
return PollPaymentStatusMsg{Baseline: baseline, BaselineCount: baselineCount}
|
||||
}
|
||||
cards, err := m.APIClient.GetCards()
|
||||
if err != nil {
|
||||
return PollPaymentStatusMsg{CardCount: cardCount}
|
||||
return PollPaymentStatusMsg{Baseline: baseline, BaselineCount: baselineCount}
|
||||
}
|
||||
if len(cards) > cardCount {
|
||||
return PollPaymentCompleteMsg{Cards: cards}
|
||||
var newest time.Time
|
||||
for _, card := range cards {
|
||||
if card.UpdatedAt.After(newest) {
|
||||
newest = card.UpdatedAt
|
||||
}
|
||||
}
|
||||
return PollPaymentStatusMsg{CardCount: cardCount}
|
||||
if newest.After(baseline) {
|
||||
return PollPaymentCompleteMsg{Cards: cards, Duplicate: len(cards) == baselineCount}
|
||||
}
|
||||
return PollPaymentStatusMsg{Baseline: baseline, BaselineCount: baselineCount}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package tui
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
@@ -59,13 +60,15 @@ type PollPaymentInitMsg struct {
|
||||
// PollPaymentStatusMsg is sent each polling tick while waiting for the browser
|
||||
// card entry to complete.
|
||||
type PollPaymentStatusMsg struct {
|
||||
CardCount int
|
||||
Baseline time.Time
|
||||
BaselineCount int
|
||||
}
|
||||
|
||||
// PollPaymentCompleteMsg is sent when the card count increases, meaning the
|
||||
// webhook saved the new card
|
||||
type PollPaymentCompleteMsg struct {
|
||||
Cards []models.Card
|
||||
Cards []models.Card
|
||||
Duplicate bool
|
||||
}
|
||||
|
||||
// buildPaymentForm creates the huh form bound to state's fields.
|
||||
@@ -436,13 +439,13 @@ func (m Model) PaymentUpdate(msg tea.Msg) (Model, tea.Cmd) {
|
||||
case PollPaymentInitMsg:
|
||||
url := msg.URL
|
||||
m.payment.collectURL = &url
|
||||
return m, m.pollCardsCmd(m.payment.collectCardCount)
|
||||
return m, m.pollCardsCmd(m.payment.collectBaseline, m.payment.collectCardCount)
|
||||
|
||||
case PollPaymentStatusMsg:
|
||||
if m.payment.view != 2 {
|
||||
return m, nil // user navigated away, stop polling
|
||||
}
|
||||
return m, m.pollCardsCmd(msg.CardCount)
|
||||
return m, m.pollCardsCmd(msg.Baseline, msg.BaselineCount)
|
||||
|
||||
case PollPaymentCompleteMsg:
|
||||
if m.payment.view != 2 {
|
||||
@@ -451,9 +454,19 @@ func (m Model) PaymentUpdate(msg tea.Msg) (Model, tea.Cmd) {
|
||||
m.SavedCards = msg.Cards
|
||||
m.payment.view = 0
|
||||
m.payment.collectURL = nil
|
||||
selected := m.SavedCards[len(m.SavedCards)-1]
|
||||
var selected models.Card
|
||||
if msg.Duplicate {
|
||||
for _, card := range m.SavedCards {
|
||||
if selected.ID == 0 || card.UpdatedAt.After(selected.UpdatedAt) {
|
||||
selected = card
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selected = m.SavedCards[len(m.SavedCards)-1]
|
||||
}
|
||||
m.SelectedCard = &selected
|
||||
m.review.cardJustAdded = true
|
||||
m.review.cardWasDuplicate = msg.Duplicate
|
||||
return m.ReviewSwitch()
|
||||
}
|
||||
|
||||
@@ -496,6 +509,13 @@ func (m Model) PaymentUpdate(msg tea.Msg) (Model, tea.Cmd) {
|
||||
m.payment.view = 2
|
||||
m.payment.collectURL = nil
|
||||
m.payment.collectCardCount = len(m.SavedCards)
|
||||
var maxT time.Time
|
||||
for _, card := range m.SavedCards {
|
||||
if card.UpdatedAt.After(maxT) {
|
||||
maxT = card.UpdatedAt
|
||||
}
|
||||
}
|
||||
m.payment.collectBaseline = maxT
|
||||
m.footer = []footerCommand{
|
||||
{key: "esc", value: "back"},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user