extends Node const EndpointConfig := preload("res://scripts/autoload/api_endpoint_config.gd") const DEFAULT_API_URL := "https://us3q.ranaze.com/api/app-feedback" const REQUEST_TIMEOUT_SECONDS := 30.0 signal feedback_submitted(receipt_id: String) signal feedback_failed(message: String) var api_url := DEFAULT_API_URL var request: HTTPRequest var is_submitting := false var request_access_token := "" func _ready() -> void: api_url = EndpointConfig.resolve( str(ProjectSettings.get_setting("step3/app_feedback_api_url", DEFAULT_API_URL)), "/api/app-feedback") request = HTTPRequest.new() request.timeout = REQUEST_TIMEOUT_SECONDS request.request_completed.connect(_on_request_completed) add_child(request) func submit(category: String, details: String, context: Dictionary) -> void: if is_submitting: return if not AuthState.can_send_authenticated_request(): feedback_failed.emit("Log in before sending feedback.") return is_submitting = true request_access_token = AuthState.access_token var headers := PackedStringArray([ "Content-Type: application/json", "Authorization: Bearer %s" % request_access_token ]) var payload := {"category": category, "details": details, "context": context} var error := request.request(api_url, headers, HTTPClient.METHOD_POST, JSON.stringify(payload)) if error != OK: is_submitting = false feedback_failed.emit("The feedback request could not be started.") func build_context(screen_name: String, viewport_size: Vector2) -> Dictionary: var session_type := "" if BlockSessionService.block_started: session_type = BlockSessionService.session_kind return { "screen": screen_name.left(120), "sessionType": session_type.left(120), "platform": OS.get_name().left(120), "viewportWidth": maxi(0, int(round(viewport_size.x))), "viewportHeight": maxi(0, int(round(viewport_size.y))), "appVersion": str(ProjectSettings.get_setting("application/config/version", "pre-alpha")).left(120), "buildRevision": str(ProjectSettings.get_setting("step3/build_revision", "development")).left(120), "clientUtc": "%sZ" % Time.get_datetime_string_from_system(true) } func _on_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void: is_submitting = false if response_code == 401: AuthState.handle_unauthorized(request_access_token) if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300: var detail := body.get_string_from_utf8().strip_edges().left(240) var message := "Feedback submission failed with status %d." % response_code if result != HTTPRequest.RESULT_SUCCESS and response_code == 0: message = "The connection was interrupted. Your feedback is still in the form." elif not detail.is_empty(): message += " %s" % detail feedback_failed.emit(message) return var parsed: Variant = JSON.parse_string(body.get_string_from_utf8()) if typeof(parsed) != TYPE_DICTIONARY or str(parsed.get("id", "")).is_empty(): feedback_failed.emit("The feedback service returned an invalid receipt.") return feedback_submitted.emit(str(parsed.get("id", "")))