134 lines
4.4 KiB
GDScript
134 lines
4.4 KiB
GDScript
extends Node
|
|
|
|
signal session_started
|
|
signal progress_changed
|
|
signal session_completed(summary: Dictionary)
|
|
signal session_failed(message: String)
|
|
|
|
const EndpointConfig := preload("res://scripts/autoload/api_endpoint_config.gd")
|
|
const DEFAULT_URL := "https://us3q.ranaze.com/api/study-sessions"
|
|
|
|
var api_url := DEFAULT_URL
|
|
var request: HTTPRequest
|
|
var active_session_id := ""
|
|
var progress: Dictionary = {}
|
|
var summary: Dictionary = {}
|
|
var pending_action := ""
|
|
var pending_launch: Dictionary = {}
|
|
|
|
|
|
func _ready() -> void:
|
|
api_url = EndpointConfig.resolve(DEFAULT_URL, "/api/study-sessions")
|
|
request = HTTPRequest.new()
|
|
request.timeout = 25.0
|
|
request.request_completed.connect(_on_completed)
|
|
add_child(request)
|
|
AuthState.auth_changed.connect(_on_auth_changed)
|
|
|
|
|
|
func start(goal_type: String, target_value: int, mode: String, step3_system: String = "", launch: Dictionary = {}) -> void:
|
|
if not AuthState.can_send_authenticated_request():
|
|
session_failed.emit("Login is required to start a study session.")
|
|
return
|
|
pending_launch = launch.duplicate(true)
|
|
summary = {}
|
|
_send("create", HTTPClient.METHOD_POST, "", {
|
|
"clientSessionId": _new_uuid(), "goalType": goal_type, "targetValue": target_value,
|
|
"mode": mode, "step3System": step3_system
|
|
})
|
|
|
|
|
|
func finish_early() -> void:
|
|
if active_session_id.is_empty():
|
|
return
|
|
_send("finish", HTTPClient.METHOD_POST, "/%s/finish" % active_session_id.uri_encode())
|
|
|
|
|
|
func check_completion() -> void:
|
|
if active_session_id.is_empty():
|
|
return
|
|
_send("check", HTTPClient.METHOD_POST, "/%s/check" % active_session_id.uri_encode())
|
|
|
|
|
|
func apply_response(parsed: Dictionary, defer_completion: bool = false) -> void:
|
|
var next_progress: Variant = parsed.get("studySessionProgress", null)
|
|
if typeof(next_progress) == TYPE_DICTIONARY:
|
|
progress = next_progress
|
|
active_session_id = str(progress.get("id", active_session_id))
|
|
progress_changed.emit()
|
|
var next_summary: Variant = parsed.get("sessionSummary", null)
|
|
if typeof(next_summary) == TYPE_DICTIONARY and not next_summary.is_empty():
|
|
summary = next_summary
|
|
active_session_id = ""
|
|
if not defer_completion:
|
|
session_completed.emit(summary)
|
|
|
|
|
|
func emit_deferred_summary() -> void:
|
|
if not summary.is_empty():
|
|
session_completed.emit(summary)
|
|
|
|
|
|
func is_active() -> bool:
|
|
return not active_session_id.is_empty()
|
|
|
|
|
|
func seconds_remaining() -> int:
|
|
return int(progress.get("secondsRemaining", 0))
|
|
|
|
|
|
func _send(action: String, method: int, suffix: String, payload: Dictionary = {}) -> void:
|
|
if request.get_http_client_status() != HTTPClient.STATUS_DISCONNECTED:
|
|
return
|
|
pending_action = action
|
|
var headers := PackedStringArray(["Authorization: Bearer %s" % AuthState.access_token])
|
|
var body := ""
|
|
if not payload.is_empty():
|
|
headers.append("Content-Type: application/json")
|
|
body = JSON.stringify(payload)
|
|
var error := request.request(api_url + suffix, headers, method, body)
|
|
if error != OK:
|
|
pending_action = ""
|
|
session_failed.emit("The study session could not be started.")
|
|
|
|
|
|
func _on_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
|
var action := pending_action
|
|
pending_action = ""
|
|
if response_code == 401:
|
|
AuthState.handle_unauthorized(AuthState.access_token)
|
|
return
|
|
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
|
|
session_failed.emit("The study session request failed.")
|
|
return
|
|
var parsed: Variant = JSON.parse_string(body.get_string_from_utf8())
|
|
if typeof(parsed) != TYPE_DICTIONARY:
|
|
session_failed.emit("The study session response was invalid.")
|
|
return
|
|
var next_progress: Dictionary = parsed.get("progress", {})
|
|
progress = next_progress
|
|
active_session_id = str(progress.get("id", "")) if str(progress.get("state", "")) == "active" else ""
|
|
var next_summary: Variant = parsed.get("summary", null)
|
|
if typeof(next_summary) == TYPE_DICTIONARY:
|
|
summary = next_summary
|
|
progress_changed.emit()
|
|
if action == "create":
|
|
session_started.emit()
|
|
elif not summary.is_empty():
|
|
session_completed.emit(summary)
|
|
|
|
|
|
func _on_auth_changed() -> void:
|
|
if not AuthState.is_logged_in:
|
|
active_session_id = ""
|
|
progress = {}
|
|
summary = {}
|
|
|
|
|
|
func _new_uuid() -> String:
|
|
var bytes := Crypto.new().generate_random_bytes(16)
|
|
bytes[6] = (bytes[6] & 0x0f) | 0x40
|
|
bytes[8] = (bytes[8] & 0x3f) | 0x80
|
|
var value := bytes.hex_encode()
|
|
return "%s-%s-%s-%s-%s" % [value.substr(0, 8), value.substr(8, 4), value.substr(12, 4), value.substr(16, 4), value.substr(20, 12)]
|