Danro-Jump/Assets/Scripts/Runtime/UI/MainMenu/QrCodeSettingsModalWindow.cs
Danil Iarosh d39d160d92 Implement physical prize dispensing, service stats, and QR screen timeout
- Добавлен асинхронный процесс аппаратной выдачи физических призов (хоппер и витрина) в GameplayHudController.
- Реализована блокировка выдачи по 10-секундному таймауту датчика хоппера с гарантированной отправкой команды отключения двигателя '300'.
- Реализован автоматический таймаут неактивности на экране QR-кода с последующим переходом в главное меню.
- Расширен сервис статистики PrizeSessionStateService новыми долгосрочными счетчиками (сыгранные игры, очки, монеты, сбои связи COM, физ. выдачи и ошибки).
- Обновлен построитель сводки статистики в меню настроек для отображения новых счетчиков и списка последних 5 сессий.
- Написаны EditMode тесты для проверки инкремента счетчиков и запуска выдачи призов.
2026-06-23 10:36:13 +03:00

198 lines
6.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using DanroJump.Prizes.Qr;
using DanroJump.Settings;
using DanroJump.UI.Modals;
using UnityEngine;
using UnityEngine.UIElements;
namespace DanroJump.UI.MainMenu
{
/// <summary>
/// Сервисная модалка настройки короткого текста QR-приза по рангам.
/// </summary>
public sealed class QrCodeSettingsModalWindow : ModalWindowBase, IDisposable
{
private static readonly QrPrizeTier[] EditableTiers =
{
QrPrizeTier.Bronze,
QrPrizeTier.Silver,
QrPrizeTier.Gold
};
private readonly Label tierLabel;
private readonly Label prizeTextLabel;
private readonly Label statusLabel;
private readonly Button previousTierButton;
private readonly Button nextTierButton;
private readonly Button editTextButton;
private readonly Button closeButton;
private IServiceSettingsService settingsService;
private int tierIndex;
public QrCodeSettingsModalWindow(VisualElement root, IServiceSettingsService settingsService)
: base(root)
{
this.settingsService = settingsService;
tierLabel = root?.Q<Label>("QrSettingsTierValue");
prizeTextLabel = root?.Q<Label>("QrSettingsPrizeTextValue");
statusLabel = root?.Q<Label>("QrSettingsStatus");
previousTierButton = root?.Q<Button>("QrSettingsTierPreviousButton");
nextTierButton = root?.Q<Button>("QrSettingsTierNextButton");
editTextButton = root?.Q<Button>("QrSettingsEditTextButton");
closeButton = root?.Q<Button>("QrSettingsCloseButton");
if (previousTierButton != null)
{
previousTierButton.clicked += SelectPreviousTier;
}
if (nextTierButton != null)
{
nextTierButton.clicked += SelectNextTier;
}
if (editTextButton != null)
{
editTextButton.clicked += RequestTextEdit;
}
if (closeButton != null)
{
closeButton.clicked += RequestClose;
}
}
public event Action<QrPrizeTier, string> TextEditRequested;
private QrPrizeTier CurrentTier => EditableTiers[Mathf.Clamp(tierIndex, 0, EditableTiers.Length - 1)];
public void SetSettingsService(IServiceSettingsService service)
{
settingsService = service;
RefreshView();
}
public override void Open()
{
base.Open();
RefreshView();
}
public void Dispose()
{
if (previousTierButton != null)
{
previousTierButton.clicked -= SelectPreviousTier;
}
if (nextTierButton != null)
{
nextTierButton.clicked -= SelectNextTier;
}
if (editTextButton != null)
{
editTextButton.clicked -= RequestTextEdit;
}
if (closeButton != null)
{
closeButton.clicked -= RequestClose;
}
}
public override bool HandleNavigationMove(VisualElement current, NavigationMoveEvent.Direction direction)
{
switch (direction)
{
case NavigationMoveEvent.Direction.Left:
SelectPreviousTier();
return true;
case NavigationMoveEvent.Direction.Right:
SelectNextTier();
return true;
default:
return false;
}
}
public override bool HandleNavigationKey(VisualElement current, KeyCode keyCode)
{
switch (keyCode)
{
case KeyCode.LeftArrow:
case KeyCode.A:
SelectPreviousTier();
return true;
case KeyCode.RightArrow:
case KeyCode.D:
SelectNextTier();
return true;
case KeyCode.Return:
case KeyCode.KeypadEnter:
case KeyCode.JoystickButton0:
if (current == editTextButton)
{
RequestTextEdit();
return true;
}
if (current == closeButton)
{
RequestClose();
return true;
}
return false;
default:
return false;
}
}
protected override VisualElement GetDefaultFocusElement()
{
return editTextButton ?? nextTierButton ?? closeButton;
}
private void SelectPreviousTier()
{
tierIndex = (tierIndex - 1 + EditableTiers.Length) % EditableTiers.Length;
RefreshView();
}
private void SelectNextTier()
{
tierIndex = (tierIndex + 1) % EditableTiers.Length;
RefreshView();
}
private void RequestTextEdit()
{
TextEditRequested?.Invoke(CurrentTier, QrPrizeTextSettings.GetSettingKey(CurrentTier));
}
private void RefreshView()
{
var tier = CurrentTier;
var text = QrPrizeTextSettings.GetPrizeText(settingsService, tier);
if (tierLabel != null)
{
tierLabel.text = QrPrizeTextSettings.GetDisplayName(tier);
}
if (prizeTextLabel != null)
{
prizeTextLabel.text = string.IsNullOrWhiteSpace(text) ? "<пусто>" : text;
}
if (statusLabel != null)
{
statusLabel.text = "В QR вводится только текст приза. Телефон и ссылка Telegram собираются автоматически.";
}
}
}
}