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:
2026-06-25 01:46:07 +03:30
parent 344c615666
commit 605c41ab88
5 changed files with 136 additions and 66 deletions

View File

@@ -215,6 +215,8 @@ func (h *Handler) HandleMessage(ctx context.Context, msg *models.Message) {
h.handleRPGSettingsMsg(ctx, msg)
case "/leaguetoggle":
h.handleLeagueSettingsMsg(ctx, msg)
case "/setusername":
h.handleSetUsername(ctx, msg)
default:
h.sendText(ctx, msg.Chat.ID, "Unknown command")
}
@@ -326,6 +328,10 @@ func (h *Handler) handlePrefixedCallback(ctx context.Context, chatID int64, msgI
h.selectBreakThreshold(ctx, chatID, msgID, callbackID, data[15:])
return
}
if len(data) > 9 && data[:9] == "currency_" {
h.selectCurrency(ctx, chatID, msgID, callbackID, data[9:])
return
}
switch data {
case "rpg_enable":
h.handleSelectCallback(ctx, chatID, msgID, callbackID, "rpg_enable", true)
@@ -399,13 +405,14 @@ func (h *Handler) handleHelp(ctx context.Context, msg *models.Message) {
/setaccent - Select accent color
/settimezone <name> - Set timezone (e.g. Asia/Tehran)
/setbreak <minutes> - Set break threshold in minutes
/setusername <name> - Set your display name
/rpg - Show RPG stats and XP
/achievements - View unlocked achievements
/salary - Show salary estimate
/burnout - View burnout assessment
/league - View WorkTime League rankings
/setrate <amount> - Set hourly rate (e.g. 25.50)
/setcurrency <symbol> [code] - Set currency (e.g. $ USD)
/setcurrency <symbol> <code> - Set currency (e.g. $ USD)
/rpgtoggle - Enable/disable RPG system
/leaguetoggle - Enable/disable league participation
@@ -414,6 +421,36 @@ Buttons on the main menu also work.`
h.sendText(ctx, msg.Chat.ID, text)
}
// handleSetUsername handles /setusername <name>.
func (h *Handler) handleSetUsername(ctx context.Context, msg *models.Message) {
name := strings.TrimSpace(strings.TrimPrefix(msg.Text, "/setusername"))
if name == "" {
user, err := h.getOrCreateUser(msg.Chat.ID)
if err != nil {
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
return
}
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Current username: %s\nUsage: /setusername <name>", user.Username))
return
}
name = SanitizeDisplayName(name)
if name == "" {
h.sendText(ctx, msg.Chat.ID, "Invalid username.")
return
}
user, err := h.getOrCreateUser(msg.Chat.ID)
if err != nil {
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
return
}
user.Username = name
if err := h.DB.UpdateUser(user); err != nil {
h.sendText(ctx, msg.Chat.ID, "Error saving username")
return
}
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Username set to: %s", name))
}
// handleNoteMsg processes the /note command: /note [YYYY-MM-DD] HH:MM <text>
func (h *Handler) handleNoteMsg(ctx context.Context, msg *models.Message) {
user, err := h.getOrCreateUser(msg.Chat.ID)