feat: added a splash page

This commit is contained in:
2026-03-26 18:34:19 +01:00
parent 4bd4dfabd2
commit 4eeb3eda9a
5 changed files with 70 additions and 3 deletions

View File

@@ -31,5 +31,4 @@ var AccountMenuItems = []string{
"order history",
"faq",
"about",
"something",
}

View File

@@ -38,6 +38,7 @@ const (
paymentPage
confirmPage
accountPage
splashPage
)
// This msg is sent after a debounce delay to apply a pending resize.
@@ -117,6 +118,11 @@ type Model struct {
FAQs []FAQ // loaded from embedded faq.json
FaqFocused bool // true when FAQ detail is focused
// Splash screen
splashDataReady bool // true when products have loaded
splashDelayDone bool // true when minimum display time has elapsed
splashCursor bool // toggles for blinking cursor animation
}
// SwitchPage changed the active page and marks that a switch happened
@@ -217,7 +223,16 @@ func (m Model) fetchProductsCmd() tea.Msg {
func (m Model) Init() tea.Cmd {
// Fetch products and cart from API on startup, and schedule token refresh
cmds := []tea.Cmd{m.fetchProductsCmd, m.fetchCartCmd}
cmds := []tea.Cmd {
m.fetchProductsCmd,
m.fetchCartCmd,
tea.Tick(1500*time.Millisecond, func(t time.Time) tea.Msg {
return DelayCompleteMsg{}
}),
tea.Tick(700*time.Microsecond, func(t time.Time) tea.Msg {
return splashCursorTickMsg{}
}),
}
if m.User != nil && m.AuthFingerprintKey != "" {
cmds = append(cmds, m.scheduleTokenRefreshCmd())
}
@@ -233,7 +248,6 @@ func (m Model) resetPageState() Model {
return m
}
type StripeTokenMsg struct {
TokenID string
Last4 string
@@ -642,6 +656,7 @@ func (m Model) GetCartItemsSlice() []*models.CartItem {
func NewModel(username string) Model {
m := Model{
Username: username,
currentPage: splashPage,
Coffees: []models.Coffee{
{
Name: "Espresso",

34
pkg/tui/splash.go Normal file
View File

@@ -0,0 +1,34 @@
package tui
import (
"github.com/charmbracelet/lipgloss"
)
type DelayCompleteMsg struct{}
type splashCursorTickMsg struct{}
func (m Model) SplashView() string {
accent := lipgloss.NewStyle().Foreground(lipgloss.Color("#AAAAAA")).Bold(true)
var cursor string
if m.splashCursor {
cursor = lipgloss.NewStyle().Background(lipgloss.Color("#4682B4")).Render(" ")
} else {
cursor = " "
}
logo := accent.Render("terminal coffe") + cursor
content := lipgloss.JoinVertical(
lipgloss.Center,
logo,
)
return lipgloss.Place(
m.viewportWidth,
m.viewportHeight,
lipgloss.Center,
lipgloss.Center,
content,
)
}

View File

@@ -47,6 +47,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.Coffees = msg.Products
m.ErrorMsg = ""
}
m.splashDataReady = true
if m.currentPage == splashPage && m.splashDelayDone {
return m.SwitchPage(shopPage), nil
}
return m, nil
case CartFetchedMsg:
if msg.Err == nil && msg.Cart != nil {
@@ -88,6 +92,17 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.ErrorMsg = fmt.Sprintf("failed to delete card: %v", msg.Err)
}
return m, nil
case DelayCompleteMsg:
m.splashDelayDone = true
if m.currentPage == splashPage && m.splashDataReady {
return m.SwitchPage(shopPage), nil
}
return m, nil
case splashCursorTickMsg:
m.splashCursor = !m.splashCursor
return m, tea.Tick(700*time.Millisecond, func(t time.Time) tea.Msg {
return splashCursorTickMsg{}
})
case tea.KeyMsg:
// If a form is active, skip global keys — let the page handler own input
if m.ShippingForm != nil || m.PaymentForm != nil {

View File

@@ -32,6 +32,10 @@ func (m Model) View() string {
return m.ResizeView()
}
if m.currentPage == splashPage {
return m.SplashView()
}
// If the menu modal is showing, render it full-screen (bypass container)
if m.ShowingMenu {
return m.BuildMenuView()