class_name ReportModal extends Control ## In-game reporting modal (Trust & Safety). ## ## Allows players to submit moderation reports for worlds, players, or messages. signal report_submitted(target_type: String, target_id: int) signal closed var target_type: String = "world" var target_id: int = 0 var target_name: String = "" var api_client: Node = null var _category_option: OptionButton var _details_edit: TextEdit var _status_label: Label var _submit_button: Button var _cancel_button: Button func _init() -> void: name = "ReportModal" set_anchors_preset(Control.PRESET_FULL_RECT) func setup_report(p_type: String, p_id: int, p_name: String) -> void: target_type = p_type target_id = p_id target_name = p_name func _ready() -> void: mouse_filter = Control.MOUSE_FILTER_STOP var overlay := ColorRect.new() overlay.color = Color(0, 0, 0, 0.8) overlay.set_anchors_preset(Control.PRESET_FULL_RECT) add_child(overlay) var center := CenterContainer.new() center.set_anchors_preset(Control.PRESET_FULL_RECT) add_child(center) var panel := PanelContainer.new() panel.custom_minimum_size = Vector2(460, 0) var style := StyleBoxFlat.new() style.bg_color = Color(0.16, 0.16, 0.2, 1.0) style.border_color = Color(0.9, 0.35, 0.35, 1.0) style.set_border_width_all(2) style.set_corner_radius_all(10) style.content_margin_left = 20 style.content_margin_right = 20 style.content_margin_top = 18 style.content_margin_bottom = 18 panel.add_theme_stylebox_override("panel", style) center.add_child(panel) var vbox := VBoxContainer.new() vbox.add_theme_constant_override("separation", 10) panel.add_child(vbox) var title := Label.new() title.text = "🚩 Submit Report" title.add_theme_font_size_override("font_size", 18) title.add_theme_color_override("font_color", Color(1.0, 0.5, 0.5)) title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER vbox.add_child(title) var target_lbl := Label.new() target_lbl.name = "TargetLabel" target_lbl.text = "Reporting %s: \"%s\" (ID: %d)" % [target_type.capitalize(), target_name, target_id] target_lbl.add_theme_font_size_override("font_size", 12) target_lbl.add_theme_color_override("font_color", Color(0.8, 0.8, 0.8)) vbox.add_child(target_lbl) var cat_lbl := Label.new() cat_lbl.text = "Category:" cat_lbl.add_theme_font_size_override("font_size", 12) vbox.add_child(cat_lbl) _category_option = OptionButton.new() _category_option.name = "CategoryOption" _category_option.add_item("Inappropriate Content / Behavior", 0) _category_option.set_item_metadata(0, "inappropriate_content") _category_option.add_item("Harassment / Toxicity", 1) _category_option.set_item_metadata(1, "harassment") _category_option.add_item("World Rules Violation", 2) _category_option.set_item_metadata(2, "rules_violation") _category_option.add_item("Misleading Tags / Content", 3) _category_option.set_item_metadata(3, "misleading") _category_option.add_item("Spam / Exploiting", 4) _category_option.set_item_metadata(4, "spam") _category_option.add_item("Other Safety Concern", 5) _category_option.set_item_metadata(5, "other") vbox.add_child(_category_option) var det_lbl := Label.new() det_lbl.text = "Details / Explanation:" det_lbl.add_theme_font_size_override("font_size", 12) vbox.add_child(det_lbl) _details_edit = TextEdit.new() _details_edit.name = "DetailsEdit" _details_edit.custom_minimum_size = Vector2(0, 80) _details_edit.placeholder_text = "Please describe what happened..." vbox.add_child(_details_edit) _status_label = Label.new() _status_label.name = "StatusLabel" _status_label.text = "" _status_label.add_theme_font_size_override("font_size", 11) vbox.add_child(_status_label) var btn_row := HBoxContainer.new() btn_row.alignment = BoxContainer.ALIGNMENT_END btn_row.add_theme_constant_override("separation", 10) _cancel_button = Button.new() _cancel_button.name = "CancelButton" _cancel_button.text = "Cancel" _cancel_button.pressed.connect(_on_cancel_pressed) btn_row.add_child(_cancel_button) _submit_button = Button.new() _submit_button.name = "SubmitButton" _submit_button.text = "Submit Report" _submit_button.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4)) _submit_button.pressed.connect(_on_submit_pressed) btn_row.add_child(_submit_button) vbox.add_child(btn_row) func _on_cancel_pressed() -> void: closed.emit() queue_free() func _on_submit_pressed() -> void: var details := _details_edit.text.strip_edges() if details.is_empty(): _status_label.text = "Please provide details explaining your report." _status_label.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4)) return var cat_meta: String = _category_option.get_item_metadata(_category_option.selected) _submit_button.disabled = true _cancel_button.disabled = true _status_label.text = "Submitting report..." _status_label.add_theme_color_override("font_color", Color(0.8, 0.8, 0.8)) var api: Node = api_client if api == null and has_node("/root/ApiClient"): api = get_node("/root/ApiClient") if api != null: var res: Dictionary = await api.submit_moderation_report(target_type, target_id, details, cat_meta) if res.get("ok", false) or res.get("status", 0) == 201: _status_label.text = "Report submitted to moderation queue. Thank you." _status_label.add_theme_color_override("font_color", Color(0.4, 1.0, 0.5)) report_submitted.emit(target_type, target_id) await get_tree().create_timer(1.2).timeout queue_free() return else: _status_label.text = "Failed to submit: %s" % str(res.get("detail", "Error")) _status_label.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4)) _submit_button.disabled = false _cancel_button.disabled = false else: _status_label.text = "Report recorded locally." _status_label.add_theme_color_override("font_color", Color(0.4, 1.0, 0.5)) report_submitted.emit(target_type, target_id) await get_tree().create_timer(1.0).timeout queue_free() func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("ui_cancel"): get_viewport().set_input_as_handled() closed.emit() queue_free()