state

package
v0.1.0-rc3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 8, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package state — StateReader is a read-only view of the central Store. Pane constructors accept StateReader so they cannot inadvertently call write methods (Set*, Clear*, Stamp*). The root app holds *Store directly and remains the sole writer via Update() handlers.

Package state provides the central Store — the single source of truth for all application data. Panes read from the store via accessor methods. Only the root app.Update() writes to the store via data-carrying Msg payloads, never Commands, pane Update(), or View() directly.

Index

Constants

View Source
const (
	// PlaylistsTTL is the cache lifetime for the user's playlist list.
	PlaylistsTTL = 5 * time.Minute
	// AlbumsTTL is the cache lifetime for the user's saved albums.
	AlbumsTTL = 5 * time.Minute
	// LikedTracksTTL is the cache lifetime for the user's liked tracks.
	LikedTracksTTL = 5 * time.Minute
	// RecentlyPlayedTTL is the cache lifetime for recently played tracks.
	// Shorter than library data because it changes with every playback event.
	RecentlyPlayedTTL = 2 * time.Minute
	// StatsTTL is the cache lifetime for user stats (top tracks/artists).
	// Long because Spotify updates these slowly.
	StatsTTL = 10 * time.Minute
	// DevicesTTL is the cache lifetime for the available device list.
	// Short cooldown prevents rapid-fire API calls while ensuring fresh data on user request.
	DevicesTTL = 5 * time.Second
)

TTL constants define how long each data domain is considered fresh. After the TTL expires, Update() should trigger a re-fetch from the Spotify API.

Variables

This section is empty.

Functions

func IsStale

func IsStale(fetchedAt time.Time, ttl time.Duration) bool

IsStale returns true if fetchedAt is zero (never fetched) or older than ttl. Use this to decide whether to re-fetch cached data from the Spotify API.

Types

type GatewayEventLog

type GatewayEventLog struct {
	// contains filtered or unexported fields
}

GatewayEventLog is a fixed-size ring buffer of GatewayEvent values with cursor-based reads. Multiple consumers can independently track their position using monotonically increasing sequence numbers.

Thread-safe: Add() takes a write lock, ReadFrom() takes a read lock.

func NewGatewayEventLog

func NewGatewayEventLog(capacity int) *GatewayEventLog

NewGatewayEventLog creates an event log with the given capacity. If capacity is <= 0 it defaults to defaultEventLogCapacity (500).

func (*GatewayEventLog) Add

func (l *GatewayEventLog) Add(event domain.GatewayEvent)

Add appends an event to the ring buffer, overwriting the oldest if full.

func (*GatewayEventLog) Len

func (l *GatewayEventLog) Len() int

Len returns the number of events currently stored.

func (*GatewayEventLog) ReadFrom

func (l *GatewayEventLog) ReadFrom(cursor uint64) (uint64, []domain.GatewayEvent)

ReadFrom returns events added since the given cursor position. Returns the new cursor and the slice of new events. First call should use cursor=0.

If the cursor is older than the oldest retained event (due to ring buffer wraparound), all currently stored events are returned.

type StateReader

type StateReader interface {

	// PlaybackState returns the current playback state, or nil if nothing is playing.
	PlaybackState() *domain.PlaybackState
	// ActiveDevice returns the currently active Spotify device, or nil if unknown.
	ActiveDevice() *domain.Device
	// UserID returns the Spotify user ID of the authenticated user.
	UserID() string

	// UserProfile returns the full authenticated user profile.
	// Returns a zero-value UserProfile before profile is loaded.
	UserProfile() domain.UserProfile
	// IsPremium returns true only when the authenticated user's Product is "premium".
	// Returns false for free users, unknown tier, or when profile not yet loaded.
	IsPremium() bool

	// Queue returns the upcoming tracks in the user's play queue.
	Queue() []domain.Track

	// Playlists returns the user's saved playlists.
	Playlists() []domain.SimplePlaylist
	// PlaylistsTotal returns the total number of playlists (for pagination).
	PlaylistsTotal() int
	// PlaylistTracks returns cached tracks for a given playlist ID, or nil if not loaded.
	PlaylistTracks(playlistID string) []domain.Track
	// PlayingPlaylistID returns the Spotify playlist ID that is currently playing.
	PlayingPlaylistID() string

	// SavedAlbums returns the user's saved albums.
	SavedAlbums() []domain.SavedAlbum
	// AlbumsLoaded returns true if saved albums have been fetched at least once.
	AlbumsLoaded() bool

	// LikedTracks returns the user's liked tracks.
	LikedTracks() []domain.SavedTrack
	// LikedTotal returns the total number of liked tracks (for pagination).
	LikedTotal() int
	// LikedLoaded returns true if liked tracks have been fetched at least once.
	LikedLoaded() bool

	// RecentlyPlayed returns the recently played track history.
	RecentlyPlayed() []domain.PlayHistory

	// TopTracks returns cached top tracks for the given time range.
	TopTracks(timeRange string) []domain.Track
	// TopArtists returns cached top artists for the given time range.
	TopArtists(timeRange string) []domain.FullArtist

	// Devices returns the most recently fetched list of Spotify Connect devices.
	Devices() []domain.Device

	// StatsStale returns true if stats for the given time range should be re-fetched.
	// Other staleness methods (PlaylistsStale, AlbumsStale, LikedTracksStale,
	// RecentlyPlayedStale, DevicesStale) are omitted because only handlers.go
	// calls them on the concrete *Store — no pane reads them via StateReader.
	StatsStale(timeRange string) bool

	// PlaylistsFetching returns true while a playlists fetch is in-flight.
	PlaylistsFetching() bool
	// AlbumsFetching returns true while a saved-albums fetch is in-flight.
	AlbumsFetching() bool
	// LikedFetching returns true while a liked-tracks fetch is in-flight.
	LikedFetching() bool
	// RecentFetching returns true while a recently-played fetch is in-flight.
	RecentFetching() bool

	// PlaylistsFetchedAt returns the time playlists were last successfully fetched.
	PlaylistsFetchedAt() time.Time
	// AlbumsFetchedAt returns the time saved albums were last successfully fetched.
	AlbumsFetchedAt() time.Time
	// LikedTracksFetchedAt returns the time liked tracks were last successfully fetched.
	LikedTracksFetchedAt() time.Time
	// RecentPlayedFetchedAt returns the time recently played was last successfully fetched.
	RecentPlayedFetchedAt() time.Time

	// ReadEventsFrom returns gateway events added since the given cursor.
	// Pass cursor=0 on the first call.
	ReadEventsFrom(cursor uint64) (uint64, []domain.GatewayEvent)

	// IsThrottled returns true if the gateway is currently in a 429 backoff period.
	IsThrottled() bool
	// ThrottleRetryAfterSecs returns the Retry-After seconds from the last 429 response.
	ThrottleRetryAfterSecs() int
}

StateReader is the read-only subset of *Store that panes are permitted to call. It contains every accessor method that pane View() and Update() bodies invoke, grouped by domain area.

Write-only methods (Set*, Clear*, Stamp*, SetDevicesFetchedAt, etc.) are intentionally omitted — only the root App.Update() may call those on the concrete *Store.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the central application state. All panes read from here; only the root app.Update() writes to it via Msg payloads. Fields are never accessed directly — use the accessor methods to ensure safe concurrent access.

func New

func New() *Store

New returns an empty Store with no playback state. statsFetchedAt is pre-allocated so callers never encounter a nil map panic when reading stats staleness before any fetch has completed.

func (*Store) ActiveDevice

func (s *Store) ActiveDevice() *domain.Device

ActiveDevice returns the currently active Spotify device, or nil if unknown.

func (*Store) AlbumsFetchError

func (s *Store) AlbumsFetchError() error

AlbumsFetchError returns the last saved albums fetch error, or nil.

func (*Store) AlbumsFetchedAt

func (s *Store) AlbumsFetchedAt() time.Time

AlbumsFetchedAt returns the time when saved albums were last successfully fetched. Returns the zero time if albums have never been fetched.

func (*Store) AlbumsFetching

func (s *Store) AlbumsFetching() bool

AlbumsFetching returns true while a saved-albums fetch is in-flight.

func (*Store) AlbumsLoaded

func (s *Store) AlbumsLoaded() bool

AlbumsLoaded returns true if saved albums have been fetched at least once. Replaces the former albumsLoaded boolean sentinel — derived from albumsFetchedAt.

func (*Store) AlbumsStale

func (s *Store) AlbumsStale() bool

AlbumsStale returns true if the saved albums are stale and should be re-fetched.

func (*Store) ClearAlbumsFetchError

func (s *Store) ClearAlbumsFetchError()

ClearAlbumsFetchError clears the saved albums fetch error on successful retry.

func (*Store) ClearDevicesError

func (s *Store) ClearDevicesError()

ClearDevicesError clears the devices error state on successful retry.

func (*Store) ClearLikedTracksFetchError

func (s *Store) ClearLikedTracksFetchError()

ClearLikedTracksFetchError clears the liked tracks fetch error on successful retry.

func (*Store) ClearPlaylistsError

func (s *Store) ClearPlaylistsError()

ClearPlaylistsError clears the playlists error state on successful retry.

func (*Store) ClearPlaylistsFetchError

func (s *Store) ClearPlaylistsFetchError()

ClearPlaylistsFetchError clears the playlists list fetch error on successful retry.

func (*Store) ClearQueueError

func (s *Store) ClearQueueError()

ClearQueueError clears the queue error state on successful retry.

func (*Store) ClearRecentPlayedFetchError

func (s *Store) ClearRecentPlayedFetchError()

ClearRecentPlayedFetchError clears the recently played fetch error on successful retry.

func (*Store) ClearStatsError

func (s *Store) ClearStatsError()

ClearStatsError clears the stats error state on successful retry.

func (*Store) Devices

func (s *Store) Devices() []domain.Device

Devices returns the most recently fetched list of Spotify Connect devices, or nil if the list has not been fetched yet.

func (*Store) DevicesError

func (s *Store) DevicesError() error

DevicesError returns the last devices fetch error, or nil if successful.

func (*Store) DevicesFetchedAt

func (s *Store) DevicesFetchedAt() time.Time

DevicesFetchedAt returns the time when the device list was last successfully fetched. Returns the zero time if devices have never been fetched.

func (*Store) DevicesFetching

func (s *Store) DevicesFetching() bool

DevicesFetching returns true while a devices fetch is in-flight.

func (*Store) DevicesStale

func (s *Store) DevicesStale() bool

DevicesStale returns true if the device list is stale and should be re-fetched.

func (*Store) IsPremium

func (s *Store) IsPremium() bool

IsPremium returns true only when Product == "premium". Returns false for free users, unknown tier, or when profile not yet loaded.

func (*Store) IsThrottled

func (s *Store) IsThrottled() bool

IsThrottled returns true if the gateway is currently in a 429 backoff period.

func (*Store) LikedFetching

func (s *Store) LikedFetching() bool

LikedFetching returns true while a liked-tracks fetch is in-flight.

func (*Store) LikedLoaded

func (s *Store) LikedLoaded() bool

LikedLoaded returns true if liked tracks have been fetched at least once. Replaces the former likedLoaded boolean sentinel — derived from likedTracksFetchedAt.

func (*Store) LikedTotal

func (s *Store) LikedTotal() int

LikedTotal returns the total number of liked tracks (for pagination).

func (*Store) LikedTracks

func (s *Store) LikedTracks() []domain.SavedTrack

LikedTracks returns the user's liked tracks.

func (*Store) LikedTracksFetchError

func (s *Store) LikedTracksFetchError() error

LikedTracksFetchError returns the last liked tracks fetch error, or nil.

func (*Store) LikedTracksFetchedAt

func (s *Store) LikedTracksFetchedAt() time.Time

LikedTracksFetchedAt returns the time when liked tracks were last successfully fetched. Returns the zero time if liked tracks have never been fetched.

func (*Store) LikedTracksStale

func (s *Store) LikedTracksStale() bool

LikedTracksStale returns true if the liked tracks are stale and should be re-fetched.

func (*Store) PlaybackState

func (s *Store) PlaybackState() *domain.PlaybackState

PlaybackState returns the current playback state, or nil if nothing is playing.

func (*Store) PlayingPlaylistID

func (s *Store) PlayingPlaylistID() string

PlayingPlaylistID returns the Spotify playlist ID that is currently playing. Returns "" if no playlist is active or the ID is unknown.

func (*Store) PlaylistTracks

func (s *Store) PlaylistTracks(playlistID string) []domain.Track

PlaylistTracks returns the cached tracks for a given playlist ID, or nil if the playlist has not been loaded yet.

func (*Store) Playlists

func (s *Store) Playlists() []domain.SimplePlaylist

Playlists returns the user's saved playlists.

func (*Store) PlaylistsError

func (s *Store) PlaylistsError() error

PlaylistsError returns the last playlists fetch error, or nil if successful.

func (*Store) PlaylistsFetchError

func (s *Store) PlaylistsFetchError() error

PlaylistsFetchError returns the last playlists list fetch error, or nil.

func (*Store) PlaylistsFetchedAt

func (s *Store) PlaylistsFetchedAt() time.Time

PlaylistsFetchedAt returns the time when playlists were last successfully fetched. Returns the zero time if playlists have never been fetched.

func (*Store) PlaylistsFetching

func (s *Store) PlaylistsFetching() bool

PlaylistsFetching returns true while a playlists fetch is in-flight.

func (*Store) PlaylistsStale

func (s *Store) PlaylistsStale() bool

PlaylistsStale returns true if the playlists list is stale and should be re-fetched.

func (*Store) PlaylistsTotal

func (s *Store) PlaylistsTotal() int

PlaylistsTotal returns the total number of playlists (for pagination).

func (*Store) Queue

func (s *Store) Queue() []domain.Track

Queue returns the upcoming tracks in the user's play queue.

func (*Store) QueueError

func (s *Store) QueueError() error

QueueError returns the last queue fetch error, or nil if successful.

func (*Store) ReadEventsFrom

func (s *Store) ReadEventsFrom(cursor uint64) (uint64, []domain.GatewayEvent)

ReadEventsFrom returns gateway events added since the given cursor. Returns the new cursor and the slice of new events. Pass cursor=0 on the first call.

func (*Store) RecentFetching

func (s *Store) RecentFetching() bool

RecentFetching returns true while a recently-played fetch is in-flight.

func (*Store) RecentPlayedFetchError

func (s *Store) RecentPlayedFetchError() error

RecentPlayedFetchError returns the last recently played fetch error, or nil.

func (*Store) RecentPlayedFetchedAt

func (s *Store) RecentPlayedFetchedAt() time.Time

RecentPlayedFetchedAt returns the time when recently played was last successfully fetched. Returns the zero time if recently played has never been fetched.

func (*Store) RecentlyPlayed

func (s *Store) RecentlyPlayed() []domain.PlayHistory

RecentlyPlayed returns the recently played track history.

func (*Store) RecentlyPlayedStale

func (s *Store) RecentlyPlayedStale() bool

RecentlyPlayedStale returns true if the recently played list is stale and should be re-fetched.

func (*Store) RecordEvent

func (s *Store) RecordEvent(event domain.GatewayEvent)

RecordEvent records a gateway lifecycle event. Implements domain.GatewayEventRecorder.

func (*Store) SavedAlbums

func (s *Store) SavedAlbums() []domain.SavedAlbum

SavedAlbums returns the user's saved albums.

func (*Store) SetActiveDevice

func (s *Store) SetActiveDevice(device *domain.Device)

SetActiveDevice updates the active device independently of playback state.

func (*Store) SetAlbumsFetchError

func (s *Store) SetAlbumsFetchError(err error)

SetAlbumsFetchError records a saved albums fetch failure.

func (*Store) SetAlbumsFetchedAt

func (s *Store) SetAlbumsFetchedAt(t time.Time)

SetAlbumsFetchedAt stamps the time when saved albums were last successfully loaded.

func (*Store) SetAlbumsFetching

func (s *Store) SetAlbumsFetching(f bool)

SetAlbumsFetching sets or clears the in-flight albums fetch sentinel.

func (*Store) SetDevices

func (s *Store) SetDevices(devices []domain.Device)

SetDevices replaces the cached device list. Called by app.Update() after a successful DevicesLoadedMsg.

func (*Store) SetDevicesError

func (s *Store) SetDevicesError(err error)

SetDevicesError records a devices fetch failure.

func (*Store) SetDevicesFetchedAt

func (s *Store) SetDevicesFetchedAt(t time.Time)

SetDevicesFetchedAt stamps the time when devices were last successfully loaded. Called by root app.Update() after a successful DevicesLoadedMsg.

func (*Store) SetDevicesFetching

func (s *Store) SetDevicesFetching(f bool)

SetDevicesFetching sets or clears the in-flight devices fetch sentinel.

func (*Store) SetLikedFetching

func (s *Store) SetLikedFetching(f bool)

SetLikedFetching sets or clears the in-flight liked-tracks fetch sentinel.

func (*Store) SetLikedTotal

func (s *Store) SetLikedTotal(total int)

SetLikedTotal updates the total liked tracks count for pagination.

func (*Store) SetLikedTracks

func (s *Store) SetLikedTracks(tracks []domain.SavedTrack)

SetLikedTracks updates the liked tracks in the store. fetchedAt is only stamped when the slice is non-empty to avoid resetting the TTL on empty/error responses and blocking retries prematurely.

func (*Store) SetLikedTracksFetchError

func (s *Store) SetLikedTracksFetchError(err error)

SetLikedTracksFetchError records a liked tracks fetch failure.

func (*Store) SetLikedTracksFetchedAt

func (s *Store) SetLikedTracksFetchedAt(t time.Time)

SetLikedTracksFetchedAt stamps the time when liked tracks were last successfully loaded.

func (*Store) SetPlaybackState

func (s *Store) SetPlaybackState(state *domain.PlaybackState)

SetPlaybackState updates the playback state. Pass nil to clear (204 response). Also updates the active device from the state's Device field.

func (*Store) SetPlayingPlaylistID

func (s *Store) SetPlayingPlaylistID(id string)

SetPlayingPlaylistID records the currently playing playlist ID. This is set by the root app when a PlayContextMsg plays a playlist.

func (*Store) SetPlaylistTracks

func (s *Store) SetPlaylistTracks(playlistID string, tracks []domain.Track)

SetPlaylistTracks caches the tracks for a specific playlist in the store.

func (*Store) SetPlaylists

func (s *Store) SetPlaylists(playlists []domain.SimplePlaylist)

SetPlaylists updates the saved playlists in the store. fetchedAt is only stamped when the slice is non-empty, preventing an empty (nil-client or error-fallback) response from resetting the TTL and blocking retries for the full cache duration.

func (*Store) SetPlaylistsError

func (s *Store) SetPlaylistsError(err error)

SetPlaylistsError records a playlists fetch failure.

func (*Store) SetPlaylistsFetchError

func (s *Store) SetPlaylistsFetchError(err error)

SetPlaylistsFetchError records a playlists list fetch failure.

func (*Store) SetPlaylistsFetchedAt

func (s *Store) SetPlaylistsFetchedAt(t time.Time)

SetPlaylistsFetchedAt stamps the time when playlists were last successfully loaded. Used by tests and import flows that need precise staleness control.

func (*Store) SetPlaylistsFetching

func (s *Store) SetPlaylistsFetching(f bool)

SetPlaylistsFetching sets or clears the in-flight playlists fetch sentinel.

func (*Store) SetPlaylistsTotal

func (s *Store) SetPlaylistsTotal(total int)

SetPlaylistsTotal updates the total playlists count for pagination.

func (*Store) SetQueue

func (s *Store) SetQueue(tracks []domain.Track)

SetQueue updates the queue tracks in the store.

func (*Store) SetQueueError

func (s *Store) SetQueueError(err error)

SetQueueError records a queue fetch failure.

func (*Store) SetRecentFetching

func (s *Store) SetRecentFetching(f bool)

SetRecentFetching sets or clears the in-flight recently-played fetch sentinel.

func (*Store) SetRecentPlayedFetchError

func (s *Store) SetRecentPlayedFetchError(err error)

SetRecentPlayedFetchError records a recently played fetch failure.

func (*Store) SetRecentPlayedFetchedAt

func (s *Store) SetRecentPlayedFetchedAt(t time.Time)

SetRecentPlayedFetchedAt stamps the time when recently played was last successfully loaded.

func (*Store) SetRecentlyPlayed

func (s *Store) SetRecentlyPlayed(items []domain.PlayHistory)

SetRecentlyPlayed updates the recently played history in the store. fetchedAt is only stamped when the slice is non-empty to avoid resetting the TTL on empty/error responses and blocking retries prematurely.

func (*Store) SetSavedAlbums

func (s *Store) SetSavedAlbums(albums []domain.SavedAlbum)

SetSavedAlbums updates the saved albums in the store. fetchedAt is only stamped when the slice is non-empty to avoid resetting the TTL on empty/error responses and blocking retries prematurely.

func (*Store) SetStatsError

func (s *Store) SetStatsError(err error)

SetStatsError records a stats fetch failure.

func (*Store) SetStatsFetching

func (s *Store) SetStatsFetching(timeRange string, f bool)

SetStatsFetching sets or clears the in-flight stats fetch sentinel for a time range.

func (*Store) SetThrottle

func (s *Store) SetThrottle(isThrottled bool, retryAfterSecs int, at time.Time)

SetThrottle records the current rate-limit state from the API Gateway. Called after a 429 response; cleared when the backoff period expires.

func (*Store) SetTopArtists

func (s *Store) SetTopArtists(timeRange string, artists []domain.FullArtist)

SetTopArtists caches top artists for a specific time range in the store. It does NOT stamp statsFetchedAt — call StampStatsFetchedAt after both SetTopTracks and SetTopArtists succeed so the range is only marked fresh when both datasets are written (avoids partial-data false-fresh state).

func (*Store) SetTopTracks

func (s *Store) SetTopTracks(timeRange string, tracks []domain.Track)

SetTopTracks caches top tracks for a specific time range in the store. It does NOT stamp statsFetchedAt — call StampStatsFetchedAt after both SetTopTracks and SetTopArtists succeed so the range is only marked fresh when both datasets are written (avoids partial-data false-fresh state).

func (*Store) SetUserProfile

func (s *Store) SetUserProfile(p domain.UserProfile)

SetUserProfile stores the authenticated user's full Spotify profile. Called once at startup after GET /v1/me succeeds.

func (*Store) StampStatsFetchedAt

func (s *Store) StampStatsFetchedAt(timeRange string)

StampStatsFetchedAt records the time when stats for a time range were fully loaded. Call this once after both SetTopTracks and SetTopArtists succeed so that StatsStale only returns false when both datasets are present.

func (*Store) StatsError

func (s *Store) StatsError() error

StatsError returns the last stats fetch error, or nil if successful.

func (*Store) StatsFetchedAt

func (s *Store) StatsFetchedAt(timeRange string) time.Time

StatsFetchedAt returns the time when stats for the given time range were last fetched. Returns the zero time if that range has never been fetched.

func (*Store) StatsFetching

func (s *Store) StatsFetching(timeRange string) bool

StatsFetching returns true while a stats fetch for the given time range is in-flight.

func (*Store) StatsStale

func (s *Store) StatsStale(timeRange string) bool

StatsStale returns true if stats for the given time range are stale and should be re-fetched.

func (*Store) ThrottleLast429At

func (s *Store) ThrottleLast429At() time.Time

ThrottleLast429At returns the time of the most recent 429 response.

func (*Store) ThrottleRetryAfterSecs

func (s *Store) ThrottleRetryAfterSecs() int

ThrottleRetryAfterSecs returns the Retry-After seconds from the last 429 response.

func (*Store) TopArtists

func (s *Store) TopArtists(timeRange string) []domain.FullArtist

TopArtists returns the cached top artists for the given time range, or nil if that range has not been fetched yet. timeRange should be "short_term", "medium_term", or "long_term".

func (*Store) TopTracks

func (s *Store) TopTracks(timeRange string) []domain.Track

TopTracks returns the cached top tracks for the given time range, or nil if that range has not been fetched yet. timeRange should be "short_term", "medium_term", or "long_term".

func (*Store) UserID

func (s *Store) UserID() string

UserID returns the Spotify user ID. Returns "" before profile is loaded. Preserved for call-site compatibility — delegates to userProfile.ID.

func (*Store) UserProfile

func (s *Store) UserProfile() domain.UserProfile

UserProfile returns the full authenticated user profile. Returns a zero-value UserProfile before profile is loaded.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL