Expensive bee
This commit is contained in:
27
ui/config.go
27
ui/config.go
@@ -20,6 +20,10 @@ const (
|
||||
cfgColorClock
|
||||
cfgColorAlarm
|
||||
cfgShowSeconds
|
||||
cfgServerURL
|
||||
cfgServerPassword
|
||||
cfgAPIPassword
|
||||
cfgPollSeconds
|
||||
cfgFieldCount
|
||||
)
|
||||
|
||||
@@ -43,6 +47,10 @@ func newConfigModel(cfg db.Settings) *configModel {
|
||||
} else {
|
||||
c.fields[cfgShowSeconds] = "false"
|
||||
}
|
||||
c.fields[cfgServerURL] = cfg.ServerURL
|
||||
c.fields[cfgServerPassword] = cfg.ServerPassword
|
||||
c.fields[cfgAPIPassword] = cfg.APIPassword
|
||||
c.fields[cfgPollSeconds] = strconv.Itoa(cfg.PollSeconds)
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -145,6 +153,12 @@ func (c *configModel) save(m *Model) (tea.Model, tea.Cmd) {
|
||||
return *m, nil
|
||||
}
|
||||
|
||||
pollSeconds, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgPollSeconds]))
|
||||
if err != nil || pollSeconds < 1 || pollSeconds > 60 {
|
||||
c.err = "Poll seconds must be 1-60"
|
||||
return *m, nil
|
||||
}
|
||||
|
||||
cfg := db.Settings{
|
||||
SnoozeMinutes: snooze,
|
||||
TimeoutMinutes: timeout,
|
||||
@@ -158,6 +172,11 @@ func (c *configModel) save(m *Model) (tea.Model, tea.Cmd) {
|
||||
showSec := strings.TrimSpace(c.fields[cfgShowSeconds])
|
||||
cfg.ShowSeconds = showSec == "true"
|
||||
|
||||
cfg.ServerURL = strings.TrimSpace(c.fields[cfgServerURL])
|
||||
cfg.ServerPassword = strings.TrimSpace(c.fields[cfgServerPassword])
|
||||
cfg.APIPassword = strings.TrimSpace(c.fields[cfgAPIPassword])
|
||||
cfg.PollSeconds = pollSeconds
|
||||
|
||||
if cfg.DefaultSound == "" {
|
||||
cfg.DefaultSound = "default"
|
||||
}
|
||||
@@ -185,6 +204,10 @@ func (c *configModel) View() string {
|
||||
"Clock color:",
|
||||
"Alarm color:",
|
||||
"Show seconds:",
|
||||
"Server URL:",
|
||||
"Server pass:",
|
||||
"API password:",
|
||||
"Poll (sec):",
|
||||
}
|
||||
|
||||
hints := [cfgFieldCount]string{
|
||||
@@ -196,6 +219,10 @@ func (c *configModel) View() string {
|
||||
"hex e.g. #00FF88",
|
||||
"hex e.g. #FF4444",
|
||||
"space to toggle",
|
||||
"empty=local, or http://host:9119",
|
||||
"password to auth with server",
|
||||
"password for incoming API requests",
|
||||
"1-60, client poll frequency",
|
||||
}
|
||||
|
||||
var lines []string
|
||||
|
||||
@@ -135,13 +135,13 @@ func (f *formModel) save(m *Model) (tea.Model, tea.Cmd) {
|
||||
if f.editing != nil {
|
||||
alarm.ID = f.editing.ID
|
||||
alarm.Enabled = f.editing.Enabled
|
||||
if err := m.store.UpdateAlarm(alarm); err != nil {
|
||||
if err := m.alarmStore.UpdateAlarm(alarm); err != nil {
|
||||
f.err = fmt.Sprintf("Save failed: %v", err)
|
||||
return *m, nil
|
||||
}
|
||||
m.statusMsg = "Alarm updated"
|
||||
} else {
|
||||
if _, err := m.store.CreateAlarm(alarm); err != nil {
|
||||
if _, err := m.alarmStore.CreateAlarm(alarm); err != nil {
|
||||
f.err = fmt.Sprintf("Save failed: %v", err)
|
||||
return *m, nil
|
||||
}
|
||||
|
||||
62
ui/model.go
62
ui/model.go
@@ -29,12 +29,15 @@ type alarmsLoadedMsg []db.Alarm
|
||||
type snoozeFireMsg db.Alarm
|
||||
type autoTimeoutMsg struct{}
|
||||
type AlarmsChangedMsg struct{}
|
||||
type pollTickMsg struct{}
|
||||
|
||||
// Model is the main bubbletea model.
|
||||
type Model struct {
|
||||
store *db.Store
|
||||
scheduler *scheduler.Scheduler
|
||||
player *player.Player
|
||||
store *db.Store // For settings (always local)
|
||||
alarmStore db.AlarmStore // For alarm CRUD (local or remote)
|
||||
scheduler *scheduler.Scheduler
|
||||
player *player.Player
|
||||
clientMode bool
|
||||
|
||||
// State
|
||||
alarms []db.Alarm
|
||||
@@ -63,13 +66,15 @@ type Model struct {
|
||||
statusMsg string
|
||||
}
|
||||
|
||||
func NewModel(store *db.Store, sched *scheduler.Scheduler, pl *player.Player) Model {
|
||||
func NewModel(store *db.Store, alarmStore db.AlarmStore, sched *scheduler.Scheduler, pl *player.Player, clientMode bool) Model {
|
||||
m := Model{
|
||||
store: store,
|
||||
scheduler: sched,
|
||||
player: pl,
|
||||
now: time.Now(),
|
||||
settings: store.LoadSettings(),
|
||||
store: store,
|
||||
alarmStore: alarmStore,
|
||||
scheduler: sched,
|
||||
player: pl,
|
||||
clientMode: clientMode,
|
||||
now: time.Now(),
|
||||
settings: store.LoadSettings(),
|
||||
}
|
||||
m.applySettings()
|
||||
return m
|
||||
@@ -107,11 +112,15 @@ func (m *Model) applySettings() {
|
||||
}
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
return tea.Batch(
|
||||
cmds := []tea.Cmd{
|
||||
tick(),
|
||||
listenForAlarms(m.scheduler),
|
||||
loadAlarmsCmd(m.store),
|
||||
)
|
||||
loadAlarmsCmd(m.alarmStore),
|
||||
}
|
||||
if m.clientMode {
|
||||
cmds = append(cmds, pollTick(m.settings.PollSeconds))
|
||||
}
|
||||
return tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func tick() tea.Cmd {
|
||||
@@ -127,7 +136,7 @@ func listenForAlarms(sched *scheduler.Scheduler) tea.Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
func loadAlarmsCmd(store *db.Store) tea.Cmd {
|
||||
func loadAlarmsCmd(store db.AlarmStore) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
alarms, err := store.ListAlarms()
|
||||
if err != nil {
|
||||
@@ -137,6 +146,12 @@ func loadAlarmsCmd(store *db.Store) tea.Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
func pollTick(seconds int) tea.Cmd {
|
||||
return tea.Tick(time.Duration(seconds)*time.Second, func(t time.Time) tea.Msg {
|
||||
return pollTickMsg{}
|
||||
})
|
||||
}
|
||||
|
||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
@@ -154,6 +169,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.refreshAlarms()
|
||||
return m, nil
|
||||
|
||||
case pollTickMsg:
|
||||
m.refreshAlarms()
|
||||
return m, pollTick(m.settings.PollSeconds)
|
||||
|
||||
case tickMsg:
|
||||
m.now = time.Time(msg)
|
||||
if m.firingAlarm != nil {
|
||||
@@ -244,7 +263,7 @@ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
switch key {
|
||||
case "y", "Y":
|
||||
if m.cursor < len(m.alarms) {
|
||||
_ = m.store.DeleteAlarm(m.alarms[m.cursor].ID)
|
||||
_ = m.alarmStore.DeleteAlarm(m.alarms[m.cursor].ID)
|
||||
m.refreshAlarms()
|
||||
if m.cursor >= len(m.alarms) && m.cursor > 0 {
|
||||
m.cursor--
|
||||
@@ -280,7 +299,7 @@ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
case " ":
|
||||
// Toggle enabled
|
||||
if m.cursor < len(m.alarms) {
|
||||
_ = m.store.ToggleAlarm(m.alarms[m.cursor].ID)
|
||||
_ = m.alarmStore.ToggleAlarm(m.alarms[m.cursor].ID)
|
||||
m.refreshAlarms()
|
||||
}
|
||||
case "a":
|
||||
@@ -341,7 +360,7 @@ func autoTimeoutCmd(dur time.Duration) tea.Cmd {
|
||||
}
|
||||
|
||||
func (m *Model) refreshAlarms() {
|
||||
alarms, err := m.store.ListAlarms()
|
||||
alarms, err := m.alarmStore.ListAlarms()
|
||||
if err == nil {
|
||||
m.alarms = alarms
|
||||
}
|
||||
@@ -372,6 +391,17 @@ func (m Model) View() string {
|
||||
dateLine = lipgloss.PlaceHorizontal(m.width, lipgloss.Center, dateLine)
|
||||
sections = append(sections, dateLine)
|
||||
|
||||
// Connection status
|
||||
var connStatus string
|
||||
if m.clientMode {
|
||||
connStatus = fmt.Sprintf("[Client: %s]", m.settings.ServerURL)
|
||||
} else {
|
||||
connStatus = "[Local]"
|
||||
}
|
||||
connLine := HelpStyle.Render(connStatus)
|
||||
connLine = lipgloss.PlaceHorizontal(m.width, lipgloss.Center, connLine)
|
||||
sections = append(sections, connLine)
|
||||
|
||||
// Snooze indicator
|
||||
if !m.snoozeUntil.IsZero() && m.now.Before(m.snoozeUntil) {
|
||||
snoozeText := fmt.Sprintf("[Snoozing %s until %s]", m.snoozeName, m.snoozeUntil.Format("15:04:05"))
|
||||
|
||||
Reference in New Issue
Block a user