diff --git a/widgets/news.go b/widgets/news.go index 45e999a..579e44f 100644 --- a/widgets/news.go +++ b/widgets/news.go @@ -100,15 +100,15 @@ func NewNewsText(opts ...text.Option) (*NewsText, error) { return &NewsText{Text: t}, nil } -func (t *NewsText) Keyboard(k *terminalapi.Keyboard, _ *widgetapi.EventMeta) error { +func (widget *NewsText) Keyboard(k *terminalapi.Keyboard, _ *widgetapi.EventMeta) error { if k.Key == keyboard.KeyArrowLeft { - if t.Selected == 0 { - t.Selected = len(t.newsArticles.Articles) - 1 + if widget.Selected == 0 { + widget.Selected = len(widget.newsArticles.Articles) - 1 } else { - t.Selected = t.Selected - 1 + widget.Selected = widget.Selected - 1 } } else if k.Key == keyboard.KeyArrowRight { - t.Selected = (t.Selected + 1) % len(t.newsArticles.Articles) + widget.Selected = (widget.Selected + 1) % len(widget.newsArticles.Articles) } - return t.drawNews() + return widget.drawNews() } diff --git a/widgets/yearMood.go b/widgets/yearMood.go index 2243f17..09e84ab 100644 --- a/widgets/yearMood.go +++ b/widgets/yearMood.go @@ -3,15 +3,22 @@ package widgets import ( "ArinDash/util" "context" + "encoding/json" "fmt" + "os" + "strconv" + "strings" "time" "github.com/mum4k/termdash/cell" + "github.com/mum4k/termdash/keyboard" "github.com/mum4k/termdash/terminal/terminalapi" "github.com/mum4k/termdash/widgetapi" "github.com/mum4k/termdash/widgets/text" ) +const moodsFile = "moods.json" + type YearMoodOptions struct { } @@ -20,97 +27,302 @@ func YearMood() YearMoodOptions { return YearMoodOptions{} } -var days = map[int]map[int]map[int]int{ - 2025: { - 1: { - 1: -1, - 2: 1, - 3: 3, - 4: 2, - }, - }, -} - func createYearMood(ctx context.Context, _ terminalapi.Terminal, _ interface{}) widgetapi.Widget { - widget := util.PanicOnErrorWithResult(text.New()) - var selectedMonth = time.Now().Month() - var selectedDay = time.Now().Day() + widget := util.PanicOnErrorWithResult(NewMoodText()) + + widget.selectedMonth = time.Now().Month() + widget.selectedDay = time.Now().Day() go util.Periodic(ctx, 1*time.Hour, func() error { - widget.Reset() - - if err := widget.Write(" Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\n\n", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { - return err - } - - for day := range 31 { - for month := range 12 { - date := time.Date(time.Now().Year(), time.Month(month+1), day+1, 0, 0, 0, 0, time.UTC) - if date.Month() != time.Month(month+1) { - if err := widget.Write(" "); err != nil { - return err - } - continue - } - cellOptions := make([]cell.Option, 0) - weekday := time.Date(time.Now().Year(), time.Month(month+1), day+1, 0, 0, 0, 0, time.UTC).Weekday() - if weekday == time.Saturday || weekday == time.Sunday { - cellOptions = append(cellOptions, cell.Dim()) - } - cellOptions = append(cellOptions, cell.FgColor(cell.ColorWhite)) - cellOptions = append(cellOptions, cell.Bold()) - mood := days[time.Now().Year()][month+1][day+1] - if mood == -1 { - cellOptions = append(cellOptions, cell.BgColor(cell.ColorRed)) - } else if mood == 1 { - cellOptions = append(cellOptions, cell.BgColor(cell.ColorPurple)) - } else if mood == 2 { - cellOptions = append(cellOptions, cell.BgColor(cell.ColorYellow)) - } else if mood == 3 { - cellOptions = append(cellOptions, cell.BgColor(cell.ColorGreen)) - } else { - cellOptions = append(cellOptions, cell.BgColor(cell.ColorGray)) - } - - if day+1 == selectedDay && time.Month(month+1) == selectedMonth { - if err := widget.Write(">", text.WriteCellOpts(append(cellOptions, cell.Blink())...)); err != nil { - return err - } - if err := widget.Write(fmt.Sprintf("%2d", day+1), text.WriteCellOpts(cellOptions...)); err != nil { - return err - } - } else { - if err := widget.Write(fmt.Sprintf(" %2d", day+1), text.WriteCellOpts(cellOptions...)); err != nil { - return err - } - } - if time.Now().Day() == day+1 && time.Now().Month() == time.Month(month+1) { - if err := widget.Write("<", text.WriteCellOpts(cellOptions...)); err != nil { - return err - } - } else { - if err := widget.Write(" ", text.WriteCellOpts(cellOptions...)); err != nil { - return err - } - } - - if month+1 < 12 { - if err := widget.Write(" "); err != nil { - return err - } - } - } - if err := widget.Write("\n"); err != nil { - return err - } - } - - if err := widget.Write("\nKeys:\nd=delete 1=idk(purple) 2=meh(yellow) 3=great(green) other=bad(red)", text.WriteCellOpts(cell.FgColor(cell.ColorGray))); err != nil { - return err - } - - return nil + return widget.drawTable() }) return widget } + +func (widget *MoodText) drawTable() error { + days := moods() + + widget.Reset() + + if err := widget.Write(" "); err != nil { + return err + } + for m := range 12 { + mood := moodToColor(widget.moodMonth(m + 1)) + if err := widget.Write(fmt.Sprintf("%2s", time.Month(m + 1).String()[0:3]), text.WriteCellOpts(cell.BgColor(mood), cell.FgColor(cell.ColorWhite))); err != nil { + return err + } + if m < 11 { + if err := widget.Write(" "); err != nil { + return err + } + } else { + if err := widget.Write("\n"); err != nil { + return err + } + } + } + + for day := range 31 { + for month := range 12 { + date := time.Date(time.Now().Year(), time.Month(month+1), day+1, 0, 0, 0, 0, time.UTC) + if date.Month() != time.Month(month+1) { + if err := widget.Write(" "); err != nil { + return err + } + continue + } + cellOptions := make([]cell.Option, 0) + weekday := time.Date(time.Now().Year(), time.Month(month+1), day+1, 0, 0, 0, 0, time.UTC).Weekday() + if weekday == time.Saturday || weekday == time.Sunday { + cellOptions = append(cellOptions, cell.Dim()) + } + cellOptions = append(cellOptions, cell.FgColor(cell.ColorWhite)) + cellOptions = append(cellOptions, cell.Bold()) + mood := days[strconv.Itoa(time.Now().Year())][strconv.Itoa(month+1)][strconv.Itoa(day+1)] + cellOptions = append(cellOptions, cell.BgColor(moodToColor(mood))) + + if day+1 == widget.selectedDay && time.Month(month+1) == widget.selectedMonth { + if err := widget.Write(">", text.WriteCellOpts(append(cellOptions, cell.Blink())...)); err != nil { + return err + } + if err := widget.Write(fmt.Sprintf("%2d", day+1), text.WriteCellOpts(cellOptions...)); err != nil { + return err + } + } else { + if err := widget.Write(fmt.Sprintf(" %2d", day+1), text.WriteCellOpts(cellOptions...)); err != nil { + return err + } + } + if time.Now().Day() == day+1 && time.Now().Month() == time.Month(month+1) { + if err := widget.Write("<", text.WriteCellOpts(cellOptions...)); err != nil { + return err + } + } else { + if err := widget.Write(" ", text.WriteCellOpts(cellOptions...)); err != nil { + return err + } + } + + if month+1 < 12 { + if err := widget.Write(" "); err != nil { + return err + } + } + } + if err := widget.Write("\n"); err != nil { + return err + } + } + + if err := widget.Write("\n"); err != nil { + return err + } + for k, v := range totalMoodPercents() { + if v == 0 { + continue + } + amount := int(59 * v) + if amount == 0 { + amount = 1 + } + if err := widget.Write(strings.Repeat("█", amount), text.WriteCellOpts(cell.BgColor(cell.ColorGray), cell.FgColor(moodToColor(k)))); err != nil { + return err + } + } + + if err := widget.Write("\n\n"); err != nil { + return err + } + if err := widget.Write("<-=delete 1=idk(purple) 2=meh(yellow) 3=great(green) other=bad(red)", text.WriteCellOpts(cell.FgColor(cell.ColorGray))); err != nil { + return err + } + return nil +} + +func moods() map[string]map[string]map[string]int { + moods := make(map[string]map[string]map[string]int) + file, err := os.ReadFile(moodsFile) + if err == nil { + if err := json.Unmarshal(file, &moods); err != nil { + panic(err) + } + } else { + initMoods, _ := json.Marshal(moods) + if err := os.WriteFile(moodsFile, initMoods, 0644); err != nil { + // ignore + } + } + return moods +} + +func moodToColor(mood int) cell.Color { + switch mood { + case 1: + return cell.ColorPurple + case 2: + return cell.ColorYellow + case 3: + return cell.ColorGreen + case -1: + return cell.ColorRed + default: + return cell.ColorGray + } +} + +func totalMoodPercents() map[int]float64 { + m := moods() + now := time.Now() + currentYear := strconv.Itoa(now.Year()) + + // Initialize percentages for all known moods + percents := map[int]float64{ + 1: 0.0, // idk + 2: 0.0, // meh + 3: 0.0, // great + -1: 0.0, // bad + 0: 0.0, // no mood recorded + } + + // Calculate total days in the current year + firstDay := time.Date(now.Year(), time.January, 1, 0, 0, 0, 0, time.UTC) + nextYear := time.Date(now.Year()+1, time.January, 1, 0, 0, 0, 0, time.UTC) + totalDaysInYear := int(nextYear.Sub(firstDay).Hours() / 24) + + yearData, ok := m[currentYear] + if !ok { + percents[0] = 1.0 + return percents + } + + counts := make(map[int]int) + recordedDaysCount := 0 + + for _, monthData := range yearData { + for _, mood := range monthData { + if mood == 0 { + continue + } + counts[mood]++ + recordedDaysCount++ + } + } + + // Any day that doesn't have a recorded mood (1, 2, 3, or -1) is counted as 0 + counts[0] = totalDaysInYear - recordedDaysCount + + for mood := range percents { + if count, exists := counts[mood]; exists { + percents[mood] = float64(count) / float64(totalDaysInYear) + } + } + + return percents +} + +func (widget *MoodText) moodMonth(month int) int { + days := moods()[strconv.Itoa(time.Now().Year())][strconv.Itoa(month)] + + counts := make(map[int]int) + mostFrequentMood := 0 + maxCount := 0 + + for _, mood := range days { + if mood == 0 { + continue + } + + counts[mood]++ + if counts[mood] > maxCount { + maxCount = counts[mood] + mostFrequentMood = mood + } + } + return mostFrequentMood +} + +type MoodText struct { + *text.Text + selectedMonth time.Month + selectedDay int +} + +func NewMoodText(opts ...text.Option) (*MoodText, error) { + t, err := text.New(opts...) + if err != nil { + return nil, err + } + return &MoodText{Text: t}, nil +} + +func (widget *MoodText) Keyboard(k *terminalapi.Keyboard, _ *widgetapi.EventMeta) error { + switch k.Key { + case keyboard.KeyEsc, keyboard.KeyCtrlC: + break + case keyboard.KeyArrowUp: + if widget.selectedDay == 1 { + widget.selectedDay = 31 + } else { + widget.selectedDay-- + } + break + case keyboard.KeyArrowDown: + if widget.selectedDay == 31 { + widget.selectedDay = 1 + } else { + widget.selectedDay++ + } + break + case keyboard.KeyArrowLeft: + if widget.selectedMonth == 1 { + widget.selectedMonth = 12 + } else { + widget.selectedMonth-- + } + break + case keyboard.KeyArrowRight: + if widget.selectedMonth == 12 { + widget.selectedMonth = 1 + } else { + widget.selectedMonth++ + } + break + case keyboard.KeyBackspace, keyboard.KeyBackspace2: + widget.WriteMood(0) + break + case '1': + widget.WriteMood(1) + break + case '2': + widget.WriteMood(2) + break + case '3': + widget.WriteMood(3) + break + default: + widget.WriteMood(-1) + break + } + return widget.drawTable() +} + +func (widget *MoodText) WriteMood(mood int) { + m := moods() + if m[strconv.Itoa(time.Now().Year())] == nil { + m[strconv.Itoa(time.Now().Year())] = make(map[string]map[string]int) + } + if m[strconv.Itoa(time.Now().Year())][strconv.Itoa(int(widget.selectedMonth))] == nil { + m[strconv.Itoa(time.Now().Year())][strconv.Itoa(int(widget.selectedMonth))] = make(map[string]int) + } + m[strconv.Itoa(time.Now().Year())][strconv.Itoa(int(widget.selectedMonth))][strconv.Itoa(widget.selectedDay)] = mood + newMoods, err := json.MarshalIndent(m, "", " ") + if err != nil { + widget.Write(fmt.Sprintf("Error: %s", err.Error()), text.WriteCellOpts(cell.FgColor(cell.ColorRed))) + } + if err := os.WriteFile(moodsFile, newMoods, 0644); err != nil { + widget.Write(fmt.Sprintf("Error: %s", err.Error()), text.WriteCellOpts(cell.FgColor(cell.ColorRed))) + // ignore + } +}