fix: currency validation, league mobile layout, settings 2-col, burnout ratio, add setusername
- Require both symbol+code in /setcurrency; add preset picker ($ USD, T Toman, IRR Rial, EUR, GBP) in settings inline menu - League: compact 2-line per entry for mobile; sort buttons in 2x2 grid - Settings: 2-column layout with grouped buttons (timezone+calendar, report+report time, break+accent, rpg+league, currency+rate) - Burnout: cap break ratio at 100% to prevent nonsense values - Add /setusername command (uses existing SanitizeDisplayName) - Update /help with new/changed commands
This commit is contained in:
@@ -18,7 +18,7 @@ const (
|
||||
LeagueSortHours LeagueSortOption = "hours"
|
||||
)
|
||||
|
||||
// buildLeagueText returns the formatted league rankings.
|
||||
// buildLeagueText returns the formatted league rankings (mobile-friendly narrow layout).
|
||||
func (h *Handler) buildLeagueText(orderBy LeagueSortOption) string {
|
||||
entries, err := h.DB.GetLeagueRankings(string(orderBy))
|
||||
if err != nil {
|
||||
@@ -33,37 +33,15 @@ func (h *Handler) buildLeagueText(orderBy LeagueSortOption) string {
|
||||
"xp": "XP",
|
||||
"level": "Level",
|
||||
"streak": "Streak",
|
||||
"hours": "Total Hours",
|
||||
"hours": "Hours",
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("WorkTime League — sorted by %s\n\n", orderLabel[string(orderBy)]))
|
||||
|
||||
// Determine column widths
|
||||
maxUserLen := 0
|
||||
for _, e := range entries {
|
||||
l := len(e.Username)
|
||||
if l > maxUserLen {
|
||||
maxUserLen = l
|
||||
}
|
||||
}
|
||||
if maxUserLen < 8 {
|
||||
maxUserLen = 8
|
||||
}
|
||||
if maxUserLen > 20 {
|
||||
maxUserLen = 20
|
||||
}
|
||||
|
||||
header := fmt.Sprintf("%-3s %-*s %8s %6s %6s %12s", "#", maxUserLen, "Username", "XP", "Level", "Streak", "Hours")
|
||||
b.WriteString(header + "\n")
|
||||
b.WriteString(strings.Repeat("-", len(header)) + "\n")
|
||||
b.WriteString(fmt.Sprintf("WorkTime League — %s\n\n", orderLabel[string(orderBy)]))
|
||||
|
||||
for i, e := range entries {
|
||||
rank := i + 1
|
||||
name := e.Username
|
||||
if len(name) > maxUserLen {
|
||||
name = name[:maxUserLen]
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%-3d %-*s %8d %6d %6d %12.1f\n",
|
||||
rank, maxUserLen, name, e.XP, e.Level, e.Streak, e.TotalHours))
|
||||
b.WriteString(fmt.Sprintf("%d %s\n", rank, name))
|
||||
b.WriteString(fmt.Sprintf(" XP:%d Lv:%d S:%d %.1fh\n", e.XP, e.Level, e.Streak, e.TotalHours))
|
||||
}
|
||||
|
||||
b.WriteString(fmt.Sprintf("\n%d participant(s)\n", len(entries)))
|
||||
@@ -128,12 +106,13 @@ func (h *Handler) handleLeagueCallback(ctx context.Context, chatID int64, msgID
|
||||
h.editText(ctx, chatID, msgID, text, &kb)
|
||||
}
|
||||
|
||||
// buildLeagueKeyboard creates the league navigation keyboard.
|
||||
// buildLeagueKeyboard creates the league navigation keyboard (2x2 sort grid).
|
||||
func (h *Handler) buildLeagueKeyboard(currentOrder string) models.InlineKeyboardMarkup {
|
||||
orders := []struct {
|
||||
type sortBtn struct {
|
||||
label string
|
||||
data string
|
||||
}{
|
||||
}
|
||||
orders := []sortBtn{
|
||||
{"XP", "league_xp"},
|
||||
{"Level", "league_level"},
|
||||
{"Streak", "league_streak"},
|
||||
@@ -141,14 +120,16 @@ func (h *Handler) buildLeagueKeyboard(currentOrder string) models.InlineKeyboard
|
||||
}
|
||||
|
||||
rows := [][]models.InlineKeyboardButton{}
|
||||
for _, o := range orders {
|
||||
label := o.label
|
||||
if o.data == "league_"+currentOrder {
|
||||
label = "> " + label
|
||||
for i := 0; i < len(orders); i += 2 {
|
||||
row := []models.InlineKeyboardButton{}
|
||||
for j := i; j < i+2 && j < len(orders); j++ {
|
||||
label := orders[j].label
|
||||
if orders[j].data == "league_"+currentOrder {
|
||||
label = "> " + label
|
||||
}
|
||||
row = append(row, models.InlineKeyboardButton{Text: label, CallbackData: orders[j].data})
|
||||
}
|
||||
rows = append(rows, []models.InlineKeyboardButton{
|
||||
{Text: label, CallbackData: o.data},
|
||||
})
|
||||
rows = append(rows, row)
|
||||
}
|
||||
rows = append(rows, []models.InlineKeyboardButton{
|
||||
{Text: "Back to Menu", CallbackData: "back_menu"},
|
||||
|
||||
Reference in New Issue
Block a user