Lucky manta ray
This commit is contained in:
103
ui/model.go
103
ui/model.go
@@ -19,6 +19,7 @@ const (
|
||||
viewMain viewMode = iota
|
||||
viewForm
|
||||
viewConfirmDelete
|
||||
viewConfig
|
||||
)
|
||||
|
||||
// Messages
|
||||
@@ -53,17 +54,55 @@ type Model struct {
|
||||
// Form state
|
||||
form *formModel
|
||||
|
||||
// Config state
|
||||
config *configModel
|
||||
settings db.Settings
|
||||
|
||||
// Error/status
|
||||
statusMsg string
|
||||
}
|
||||
|
||||
func NewModel(store *db.Store, sched *scheduler.Scheduler, pl *player.Player) Model {
|
||||
return Model{
|
||||
m := Model{
|
||||
store: store,
|
||||
scheduler: sched,
|
||||
player: pl,
|
||||
now: time.Now(),
|
||||
settings: store.LoadSettings(),
|
||||
}
|
||||
m.applySettings()
|
||||
return m
|
||||
}
|
||||
|
||||
// applySettings updates runtime styles and durations from current settings.
|
||||
func (m *Model) applySettings() {
|
||||
ClockStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(m.settings.ColorClock)).
|
||||
Bold(true)
|
||||
|
||||
ClockAlarmStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(m.settings.ColorAlarm)).
|
||||
Bold(true)
|
||||
|
||||
AlarmEnabledStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(m.settings.ColorClock))
|
||||
|
||||
TitleStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(m.settings.ColorClock)).
|
||||
Bold(true).
|
||||
Underline(true)
|
||||
|
||||
FormActiveStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(m.settings.ColorClock)).
|
||||
Bold(true)
|
||||
|
||||
AlarmFiringStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(m.settings.ColorAlarm)).
|
||||
Bold(true).
|
||||
Blink(true)
|
||||
|
||||
FormErrorStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(m.settings.ColorAlarm))
|
||||
}
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
@@ -123,7 +162,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.refreshAlarms()
|
||||
return m, tea.Batch(
|
||||
listenForAlarms(m.scheduler),
|
||||
autoTimeoutCmd(),
|
||||
autoTimeoutCmd(m.autoTimeoutDuration()),
|
||||
)
|
||||
|
||||
case snoozeFireMsg:
|
||||
@@ -132,12 +171,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.snoozeUntil = time.Time{}
|
||||
m.snoozeName = ""
|
||||
m.startFiring(&alarm)
|
||||
return m, autoTimeoutCmd()
|
||||
return m, autoTimeoutCmd(m.autoTimeoutDuration())
|
||||
|
||||
case autoTimeoutMsg:
|
||||
if m.firingAlarm != nil {
|
||||
m.player.Stop()
|
||||
m.statusMsg = "Alarm auto-dismissed after 5 minutes"
|
||||
m.statusMsg = fmt.Sprintf("Alarm auto-dismissed after %d minutes", m.settings.TimeoutMinutes)
|
||||
m.firingAlarm = nil
|
||||
m.snoozeCount = 0
|
||||
}
|
||||
@@ -175,11 +214,12 @@ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
case "s":
|
||||
m.player.Stop()
|
||||
alarm := *m.firingAlarm
|
||||
dur := m.snoozeDuration()
|
||||
m.firingAlarm = nil
|
||||
m.snoozeUntil = time.Now().Add(snoozeDuration)
|
||||
m.snoozeUntil = time.Now().Add(dur)
|
||||
m.snoozeName = alarm.Name
|
||||
m.statusMsg = fmt.Sprintf("Snoozed '%s' for 5 minutes", alarm.Name)
|
||||
return m, snoozeCmd(alarm, snoozeDuration)
|
||||
m.statusMsg = fmt.Sprintf("Snoozed '%s' for %d minutes", alarm.Name, m.settings.SnoozeMinutes)
|
||||
return m, snoozeCmd(alarm, dur)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -189,6 +229,11 @@ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
return m.form.HandleKey(msg, &m)
|
||||
}
|
||||
|
||||
// Config mode
|
||||
if m.mode == viewConfig && m.config != nil {
|
||||
return m.config.HandleKey(msg, &m)
|
||||
}
|
||||
|
||||
// Confirm delete mode
|
||||
if m.mode == viewConfirmDelete {
|
||||
switch key {
|
||||
@@ -249,18 +294,31 @@ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
m.mode = viewConfirmDelete
|
||||
m.statusMsg = fmt.Sprintf("Delete '%s'? (y/n)", m.alarms[m.cursor].Name)
|
||||
}
|
||||
case "c":
|
||||
m.config = newConfigModel(m.settings)
|
||||
m.mode = viewConfig
|
||||
m.statusMsg = ""
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
const snoozeDuration = 5 * time.Minute
|
||||
const autoTimeoutDuration = 5 * time.Minute
|
||||
func (m *Model) snoozeDuration() time.Duration {
|
||||
return time.Duration(m.settings.SnoozeMinutes) * time.Minute
|
||||
}
|
||||
|
||||
func (m *Model) autoTimeoutDuration() time.Duration {
|
||||
return time.Duration(m.settings.TimeoutMinutes) * time.Minute
|
||||
}
|
||||
|
||||
func (m *Model) startFiring(alarm *db.Alarm) {
|
||||
m.firingAlarm = alarm
|
||||
m.firingBlink = true
|
||||
m.firingStart = time.Now()
|
||||
m.player.PlayLoop(alarm.SoundPath)
|
||||
sound := alarm.SoundPath
|
||||
if sound == "default" || sound == "" {
|
||||
sound = m.settings.DefaultSound
|
||||
}
|
||||
m.player.PlayLoop(sound)
|
||||
}
|
||||
|
||||
func snoozeCmd(alarm db.Alarm, after time.Duration) tea.Cmd {
|
||||
@@ -270,9 +328,9 @@ func snoozeCmd(alarm db.Alarm, after time.Duration) tea.Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
func autoTimeoutCmd() tea.Cmd {
|
||||
func autoTimeoutCmd(dur time.Duration) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
time.Sleep(autoTimeoutDuration)
|
||||
time.Sleep(dur)
|
||||
return autoTimeoutMsg{}
|
||||
}
|
||||
}
|
||||
@@ -292,7 +350,7 @@ func (m Model) View() string {
|
||||
var sections []string
|
||||
|
||||
// Big clock
|
||||
clockStr := RenderBigClock(m.now)
|
||||
clockStr := RenderBigClock(m.now, m.settings.BlinkOnMs, m.settings.BlinkOffMs)
|
||||
if m.firingAlarm != nil && m.firingBlink {
|
||||
clockStr = ClockAlarmStyle.Render(clockStr)
|
||||
} else {
|
||||
@@ -326,7 +384,7 @@ func (m Model) View() string {
|
||||
if m.snoozeCount > 0 {
|
||||
firingText += fmt.Sprintf("\n(snoozed %d time(s))", m.snoozeCount)
|
||||
}
|
||||
firingText += "\n\n[Enter/Space/d] Dismiss [s] Snooze 5min"
|
||||
firingText += fmt.Sprintf("\n\n[Enter/Space/d] Dismiss [s] Snooze %dmin", m.settings.SnoozeMinutes)
|
||||
styled := AlarmFiringStyle.Render(firingText)
|
||||
styled = lipgloss.PlaceHorizontal(m.width, lipgloss.Center, styled)
|
||||
sections = append(sections, "", styled)
|
||||
@@ -337,8 +395,12 @@ func (m Model) View() string {
|
||||
divider = lipgloss.PlaceHorizontal(m.width, lipgloss.Center, divider)
|
||||
sections = append(sections, "", divider, "")
|
||||
|
||||
// Form view
|
||||
if m.mode == viewForm && m.form != nil {
|
||||
// Form / Config view
|
||||
if m.mode == viewConfig && m.config != nil {
|
||||
configView := m.config.View()
|
||||
configView = lipgloss.PlaceHorizontal(m.width, lipgloss.Center, configView)
|
||||
sections = append(sections, configView)
|
||||
} else if m.mode == viewForm && m.form != nil {
|
||||
formView := m.form.View()
|
||||
formView = lipgloss.PlaceHorizontal(m.width, lipgloss.Center, formView)
|
||||
sections = append(sections, formView)
|
||||
@@ -438,13 +500,14 @@ func (m Model) renderAlarmList() string {
|
||||
}
|
||||
|
||||
func (m Model) renderHelp() string {
|
||||
if m.mode == viewForm {
|
||||
switch m.mode {
|
||||
case viewForm, viewConfig:
|
||||
return HelpStyle.Render("Tab: next field Shift+Tab: prev field Enter: save Esc: cancel")
|
||||
}
|
||||
if m.mode == viewConfirmDelete {
|
||||
case viewConfirmDelete:
|
||||
return HelpStyle.Render("y: confirm delete n: cancel")
|
||||
default:
|
||||
return HelpStyle.Render("j/k: navigate a: add e: edit d: delete space: toggle c: config q: quit")
|
||||
}
|
||||
return HelpStyle.Render("j/k: navigate a: add e: edit d: delete space: toggle q: quit")
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
|
||||
Reference in New Issue
Block a user