208 lines
6.8 KiB
GDScript
208 lines
6.8 KiB
GDScript
extends Control
|
|
|
|
const Style := preload("res://scripts/ui/ui_style.gd")
|
|
const MIN_DETAILS := 10
|
|
const MAX_DETAILS := 4000
|
|
const CATEGORIES := [
|
|
{"id": "bug", "label": "Bug / technical issue"},
|
|
{"id": "usability", "label": "Usability"},
|
|
{"id": "feature_request", "label": "Feature request"},
|
|
{"id": "account", "label": "Account"},
|
|
{"id": "other", "label": "Other feedback"}
|
|
]
|
|
|
|
var panel: PanelContainer
|
|
var form: VBoxContainer
|
|
var success: VBoxContainer
|
|
var group: ButtonGroup
|
|
var details: TextEdit
|
|
var count: Label
|
|
var error_label: Label
|
|
var submit_button: Button
|
|
var cancel_button: Button
|
|
var receipt_label: Label
|
|
var context_label: Label
|
|
var current_context := {}
|
|
|
|
|
|
func _ready() -> void:
|
|
visible = false
|
|
top_level = true
|
|
mouse_filter = Control.MOUSE_FILTER_STOP
|
|
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
var dim := ColorRect.new()
|
|
dim.color = Color(0, 0, 0, 0.72)
|
|
dim.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
add_child(dim)
|
|
var center := CenterContainer.new()
|
|
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
add_child(center)
|
|
panel = PanelContainer.new()
|
|
panel.custom_minimum_size = Vector2(700, 590)
|
|
panel.add_theme_stylebox_override("panel", Style.panel_box(Style.SURFACE_DARK, Style.CYAN))
|
|
center.add_child(panel)
|
|
var root := VBoxContainer.new()
|
|
root.add_theme_constant_override("separation", 12)
|
|
panel.add_child(root)
|
|
var title := Label.new()
|
|
title.text = "Send feedback"
|
|
Style.set_label(title, 26, Style.TEXT)
|
|
root.add_child(title)
|
|
form = VBoxContainer.new()
|
|
form.add_theme_constant_override("separation", 10)
|
|
root.add_child(form)
|
|
var prompt := Label.new()
|
|
prompt.text = "What would you like to report?"
|
|
Style.set_label(prompt, 17, Style.CYAN)
|
|
form.add_child(prompt)
|
|
group = ButtonGroup.new()
|
|
group.allow_unpress = false
|
|
var category_grid := GridContainer.new()
|
|
category_grid.columns = 2
|
|
form.add_child(category_grid)
|
|
for category in CATEGORIES:
|
|
var option := CheckBox.new()
|
|
option.text = str(category["label"])
|
|
option.set_meta("category", str(category["id"]))
|
|
option.button_group = group
|
|
option.toggled.connect(func(_pressed: bool): _refresh())
|
|
category_grid.add_child(option)
|
|
var detail_header := HBoxContainer.new()
|
|
form.add_child(detail_header)
|
|
var detail_title := Label.new()
|
|
detail_title.text = "Details"
|
|
detail_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
detail_header.add_child(detail_title)
|
|
count = Label.new()
|
|
detail_header.add_child(count)
|
|
details = TextEdit.new()
|
|
details.placeholder_text = "Describe what happened, what you expected, and how to reproduce it when applicable."
|
|
details.custom_minimum_size = Vector2(0, 210)
|
|
details.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
details.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY
|
|
details.text_changed.connect(_refresh)
|
|
form.add_child(details)
|
|
context_label = Label.new()
|
|
context_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
Style.set_label(context_label, 13, Style.MUTED)
|
|
form.add_child(context_label)
|
|
error_label = Label.new()
|
|
error_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
Style.set_label(error_label, 14, Style.RED)
|
|
form.add_child(error_label)
|
|
var actions := HBoxContainer.new()
|
|
actions.add_theme_constant_override("separation", 10)
|
|
form.add_child(actions)
|
|
cancel_button = Button.new()
|
|
cancel_button.text = "Cancel"
|
|
cancel_button.pressed.connect(hide_dialog)
|
|
actions.add_child(cancel_button)
|
|
submit_button = Button.new()
|
|
submit_button.text = "Send feedback"
|
|
submit_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
Style.style_primary_button(submit_button)
|
|
submit_button.pressed.connect(_submit)
|
|
actions.add_child(submit_button)
|
|
success = VBoxContainer.new()
|
|
success.visible = false
|
|
success.alignment = BoxContainer.ALIGNMENT_CENTER
|
|
success.add_theme_constant_override("separation", 16)
|
|
root.add_child(success)
|
|
receipt_label = Label.new()
|
|
receipt_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
receipt_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
Style.set_label(receipt_label, 21, Style.LIME)
|
|
success.add_child(receipt_label)
|
|
var close := Button.new()
|
|
close.text = "Close"
|
|
Style.style_primary_button(close)
|
|
close.pressed.connect(_close_success)
|
|
success.add_child(close)
|
|
FeedbackService.feedback_submitted.connect(_submitted)
|
|
FeedbackService.feedback_failed.connect(_failed)
|
|
resized.connect(_refresh_size)
|
|
_refresh_size()
|
|
_refresh()
|
|
|
|
|
|
func show_dialog(context: Dictionary) -> void:
|
|
current_context = context.duplicate(true)
|
|
form.visible = true
|
|
success.visible = false
|
|
error_label.text = ""
|
|
context_label.text = "Included context: %s, %s, %dx%d, version %s (%s)." % [
|
|
context.get("screen", "unknown"), context.get("platform", "unknown"),
|
|
context.get("viewportWidth", 0), context.get("viewportHeight", 0),
|
|
context.get("appVersion", "unknown"), context.get("buildRevision", "unknown")]
|
|
visible = true
|
|
move_to_front()
|
|
BlockSessionService.pause_current_question_timer()
|
|
_refresh()
|
|
|
|
|
|
func hide_dialog() -> void:
|
|
if FeedbackService.is_submitting:
|
|
return
|
|
visible = false
|
|
BlockSessionService.resume_current_question_timer()
|
|
|
|
|
|
func _close_success() -> void:
|
|
details.text = ""
|
|
for button in group.get_buttons():
|
|
button.button_pressed = false
|
|
hide_dialog()
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if visible and event.is_action_pressed("ui_cancel"):
|
|
hide_dialog()
|
|
get_viewport().set_input_as_handled()
|
|
|
|
|
|
func _refresh_size() -> void:
|
|
if panel != null:
|
|
panel.custom_minimum_size = Vector2(minf(700.0, maxf(300.0, size.x - 32.0)), minf(590.0, maxf(430.0, size.y - 32.0)))
|
|
|
|
|
|
func _refresh() -> void:
|
|
if details == null:
|
|
return
|
|
var length := details.text.strip_edges().length()
|
|
count.text = "%d / %d" % [length, MAX_DETAILS]
|
|
var selected := group.get_pressed_button()
|
|
var category := "" if selected == null else str(selected.get_meta("category", ""))
|
|
submit_button.disabled = FeedbackService.is_submitting or not is_valid_submission(category, details.text)
|
|
cancel_button.disabled = FeedbackService.is_submitting
|
|
|
|
|
|
static func is_valid_submission(category: String, value: String) -> bool:
|
|
var valid_categories := ["bug", "usability", "feature_request", "account", "other"]
|
|
var length := value.strip_edges().length()
|
|
return valid_categories.has(category) and length >= MIN_DETAILS and length <= MAX_DETAILS
|
|
|
|
|
|
func _submit() -> void:
|
|
var selected := group.get_pressed_button()
|
|
if selected == null:
|
|
return
|
|
error_label.text = ""
|
|
submit_button.text = "Sending..."
|
|
_refresh()
|
|
FeedbackService.submit(str(selected.get_meta("category", "")), details.text.strip_edges(), current_context)
|
|
|
|
|
|
func _submitted(receipt_id: String) -> void:
|
|
submit_button.text = "Send feedback"
|
|
form.visible = false
|
|
success.visible = true
|
|
receipt_label.text = "Feedback sent\nReceipt: %s" % receipt_id
|
|
UISoundService.play_sound("confirmation")
|
|
|
|
|
|
func _failed(message: String) -> void:
|
|
submit_button.text = "Send feedback"
|
|
error_label.text = message
|
|
_refresh()
|
|
UISoundService.play_sound("error")
|