add year navigation: year row at top of calendar, clickable year in export, shared buildYearPicker with 12-year grid
This commit is contained in:
@@ -79,6 +79,37 @@ func (h *Handler) handleHistoryCallback(ctx context.Context, chatID int64, msgID
|
|||||||
h.editCalendar(ctx, chatID, msgID, y, m)
|
h.editCalendar(ctx, chatID, msgID, y, m)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Navigate to previous year
|
||||||
|
if n, _ := fmt.Sscanf(data, "cal_prev_year_%d_%d", &y, &m); n == 2 {
|
||||||
|
h.editCalendar(ctx, chatID, msgID, y, m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Navigate to next year
|
||||||
|
if n, _ := fmt.Sscanf(data, "cal_next_year_%d_%d", &y, &m); n == 2 {
|
||||||
|
h.editCalendar(ctx, chatID, msgID, y, m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Show year picker from calendar
|
||||||
|
if n, _ := fmt.Sscanf(data, "cal_show_years_%d_%d", &y, &m); n == 2 {
|
||||||
|
h.editCalendarYearPicker(ctx, chatID, msgID, y, m, y)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Navigate year block in picker
|
||||||
|
var startY, refY, refM int
|
||||||
|
if n, _ := fmt.Sscanf(data, "cal_years_%d_%d_%d", &startY, &refY, &refM); n == 3 {
|
||||||
|
h.editCalendarYearPicker(ctx, chatID, msgID, startY+6, refM, refY)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Select year from picker
|
||||||
|
if n, _ := fmt.Sscanf(data, "cal_year_%d", &y); n == 1 {
|
||||||
|
h.editCalendar(ctx, chatID, msgID, y, 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Back from year picker to calendar
|
||||||
|
if n, _ := fmt.Sscanf(data, "cal_years_back_%d_%d", &y, &m); n == 2 {
|
||||||
|
h.editCalendar(ctx, chatID, msgID, y, m)
|
||||||
|
return
|
||||||
|
}
|
||||||
// Open a specific day
|
// Open a specific day
|
||||||
if n, _ := fmt.Sscanf(data, "cal_day_%s", &date); n == 1 && len(date) == 10 {
|
if n, _ := fmt.Sscanf(data, "cal_day_%s", &date); n == 1 && len(date) == 10 {
|
||||||
h.editDayView(ctx, chatID, msgID, user, date, loc)
|
h.editDayView(ctx, chatID, msgID, user, date, loc)
|
||||||
@@ -148,6 +179,36 @@ func (h *Handler) sendCalendar(ctx context.Context, chatID int64, msgID int, yea
|
|||||||
h.editCalendar(ctx, chatID, msgID, year, month)
|
h.editCalendar(ctx, chatID, msgID, year, month)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildYearPicker returns a 12-year grid keyboard centered on centerYear.
|
||||||
|
// cbPrefix is "cal_" or "exp_". backData is the callback for the Back button.
|
||||||
|
// navSuffix is appended to the nav callbacks to carry reference info (e.g. "_2026_3").
|
||||||
|
func buildYearPicker(cbPrefix, backData, navSuffix string, centerYear int) models.InlineKeyboardMarkup {
|
||||||
|
blockStart := centerYear - 6
|
||||||
|
kbRows := [][]models.InlineKeyboardButton{
|
||||||
|
{
|
||||||
|
{Text: "< 12", CallbackData: fmt.Sprintf("%syears_%d%s", cbPrefix, blockStart-12, navSuffix)},
|
||||||
|
{Text: fmt.Sprintf("%d", centerYear), CallbackData: "noop"},
|
||||||
|
{Text: "12 >", CallbackData: fmt.Sprintf("%syears_%d%s", cbPrefix, blockStart+12, navSuffix)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := 0; i < 12; i += 4 {
|
||||||
|
row := []models.InlineKeyboardButton{}
|
||||||
|
for j := i; j < i+4; j++ {
|
||||||
|
y := blockStart + j
|
||||||
|
row = append(row, models.InlineKeyboardButton{
|
||||||
|
Text: fmt.Sprintf("%d", y), CallbackData: fmt.Sprintf("%syear_%d", cbPrefix, y),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
kbRows = append(kbRows, row)
|
||||||
|
}
|
||||||
|
if backData != "" {
|
||||||
|
kbRows = append(kbRows, []models.InlineKeyboardButton{
|
||||||
|
{Text: "Back", CallbackData: backData},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return models.InlineKeyboardMarkup{InlineKeyboard: kbRows}
|
||||||
|
}
|
||||||
|
|
||||||
// editCalendar renders a monthly calendar grid with event indicators and navigation.
|
// editCalendar renders a monthly calendar grid with event indicators and navigation.
|
||||||
func (h *Handler) editCalendar(ctx context.Context, chatID int64, msgID int, year, month int) {
|
func (h *Handler) editCalendar(ctx context.Context, chatID int64, msgID int, year, month int) {
|
||||||
user, err := h.getOrCreateUser(chatID)
|
user, err := h.getOrCreateUser(chatID)
|
||||||
@@ -193,6 +254,13 @@ func (h *Handler) editCalendar(ctx context.Context, chatID int64, msgID int, yea
|
|||||||
|
|
||||||
kbRows := [][]models.InlineKeyboardButton{}
|
kbRows := [][]models.InlineKeyboardButton{}
|
||||||
|
|
||||||
|
// Year navigation row
|
||||||
|
kbRows = append(kbRows, []models.InlineKeyboardButton{
|
||||||
|
{Text: fmt.Sprintf("< %d", year-1), CallbackData: fmt.Sprintf("cal_prev_year_%d_%d", year-1, month)},
|
||||||
|
{Text: fmt.Sprintf("%d", year), CallbackData: fmt.Sprintf("cal_show_years_%d_%d", year, month)},
|
||||||
|
{Text: fmt.Sprintf("%d >", year+1), CallbackData: fmt.Sprintf("cal_next_year_%d_%d", year+1, month)},
|
||||||
|
})
|
||||||
|
|
||||||
// Weekday header row
|
// Weekday header row
|
||||||
headerRow := []models.InlineKeyboardButton{}
|
headerRow := []models.InlineKeyboardButton{}
|
||||||
for _, wn := range cm.weekDays {
|
for _, wn := range cm.weekDays {
|
||||||
@@ -241,6 +309,17 @@ func (h *Handler) editCalendar(ctx context.Context, chatID int64, msgID int, yea
|
|||||||
h.sendOrEdit(ctx, chatID, msgID, text, &kb)
|
h.sendOrEdit(ctx, chatID, msgID, text, &kb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// editCalendarYearPicker shows a 12-year grid for year selection.
|
||||||
|
// refY/refM encode the view to return to via the Back button.
|
||||||
|
func (h *Handler) editCalendarYearPicker(ctx context.Context, chatID int64, msgID int, centerYear, refM, refY int) {
|
||||||
|
backData := fmt.Sprintf("cal_years_back_%d_%d", refY, refM)
|
||||||
|
navSuffix := fmt.Sprintf("_%d_%d", refY, refM)
|
||||||
|
kb := buildYearPicker("cal_", backData, navSuffix, centerYear)
|
||||||
|
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
|
||||||
|
ChatID: chatID, MessageID: msgID, Text: "Select year:", ReplyMarkup: &kb,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// editDayView opens an existing message as a day view.
|
// editDayView opens an existing message as a day view.
|
||||||
func (h *Handler) editDayView(ctx context.Context, chatID int64, msgID int, user *db.User, date string, loc *time.Location) {
|
func (h *Handler) editDayView(ctx context.Context, chatID int64, msgID int, user *db.User, date string, loc *time.Location) {
|
||||||
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
|
h.sendDayView(ctx, chatID, msgID, user, date, loc, false)
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ func (h *Handler) editExportMonthPicker(ctx context.Context, chatID int64, msgID
|
|||||||
// Year navigation
|
// Year navigation
|
||||||
{
|
{
|
||||||
{Text: "<", CallbackData: fmt.Sprintf("exp_year_prev_%d", year)},
|
{Text: "<", CallbackData: fmt.Sprintf("exp_year_prev_%d", year)},
|
||||||
{Text: fmt.Sprintf("%d", year), CallbackData: "noop"},
|
{Text: fmt.Sprintf("%d", year), CallbackData: fmt.Sprintf("exp_show_years_%d", year)},
|
||||||
{Text: ">", CallbackData: fmt.Sprintf("exp_year_next_%d", year)},
|
{Text: ">", CallbackData: fmt.Sprintf("exp_year_next_%d", year)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -405,6 +405,17 @@ func (h *Handler) editExportMonthPicker(ctx context.Context, chatID int64, msgID
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// editExportYearPicker shows a 12-year grid for year selection in the export flow.
|
||||||
|
// refY is the year to return to via the Back button.
|
||||||
|
func (h *Handler) editExportYearPicker(ctx context.Context, chatID int64, msgID int, centerYear, refY int) {
|
||||||
|
backData := fmt.Sprintf("exp_years_back_%d", refY)
|
||||||
|
navSuffix := fmt.Sprintf("_%d", refY)
|
||||||
|
kb := buildYearPicker("exp_", backData, navSuffix, centerYear)
|
||||||
|
h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{
|
||||||
|
ChatID: chatID, MessageID: msgID, Text: "Select year:", ReplyMarkup: &kb,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// formatMonthTitle returns the localized month name and year for a given calendar.
|
// formatMonthTitle returns the localized month name and year for a given calendar.
|
||||||
func formatMonthTitle(cal string, year, month int) string {
|
func formatMonthTitle(cal string, year, month int) string {
|
||||||
switch cal {
|
switch cal {
|
||||||
@@ -437,6 +448,27 @@ func (h *Handler) handleExportCallback(ctx context.Context, chatID int64, msgID
|
|||||||
h.editExportMonthPicker(ctx, chatID, msgID, y+1, 0, user)
|
h.editExportMonthPicker(ctx, chatID, msgID, y+1, 0, user)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Show year picker from month picker
|
||||||
|
if n, _ := fmt.Sscanf(data, "exp_show_years_%d", &y); n == 1 {
|
||||||
|
h.editExportYearPicker(ctx, chatID, msgID, y, y)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Navigate year block in picker
|
||||||
|
var refY int
|
||||||
|
if n, _ := fmt.Sscanf(data, "exp_years_%d_%d", &y, &refY); n == 2 {
|
||||||
|
h.editExportYearPicker(ctx, chatID, msgID, y+6, refY)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Select year from picker
|
||||||
|
if n, _ := fmt.Sscanf(data, "exp_year_%d", &y); n == 1 {
|
||||||
|
h.editExportMonthPicker(ctx, chatID, msgID, y, 0, user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Back from year picker to month picker
|
||||||
|
if n, _ := fmt.Sscanf(data, "exp_years_back_%d", &y); n == 1 {
|
||||||
|
h.editExportMonthPicker(ctx, chatID, msgID, y, 0, user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Export a specific month
|
// Export a specific month
|
||||||
if n, _ := fmt.Sscanf(data, "exp_do_%d_%d", &y, &m); n == 2 {
|
if n, _ := fmt.Sscanf(data, "exp_do_%d_%d", &y, &m); n == 2 {
|
||||||
|
|||||||
Reference in New Issue
Block a user