mirror of
https://github.com/mr0xb/godot-flashcards.git
synced 2026-08-27 20:44:56 -04:00
321 lines
8.6 KiB
GDScript
321 lines
8.6 KiB
GDScript
extends Node
|
|
|
|
signal decks_loaded()
|
|
signal deck_saved(deck: Deck)
|
|
signal deck_deleted(deck_id: String)
|
|
signal progress_updated(deck_id: String)
|
|
|
|
const DECKS_DIR = "user://data/decks/"
|
|
const PROGRESS_FILE = "user://data/progress/progress.json"
|
|
|
|
var decks: Array[Deck] = []
|
|
var progress_data: Dictionary = {}
|
|
var user_id: String = "local"
|
|
|
|
func _ready() -> void:
|
|
_ensure_directories_exist()
|
|
load_all_decks()
|
|
load_progress()
|
|
|
|
func _ensure_directories_exist() -> void:
|
|
DirAccess.make_dir_recursive_absolute("user://data/decks/")
|
|
DirAccess.make_dir_recursive_absolute("user://data/progress/")
|
|
|
|
func load_all_decks() -> void:
|
|
decks.clear()
|
|
var dir = DirAccess.open(DECKS_DIR)
|
|
|
|
if not dir:
|
|
push_warning("Could not open decks directory, creating it...")
|
|
_ensure_directories_exist()
|
|
decks_loaded.emit()
|
|
return
|
|
|
|
dir.list_dir_begin()
|
|
var file_name = dir.get_next()
|
|
|
|
while file_name != "":
|
|
if file_name.ends_with(".json"):
|
|
var deck = load_deck_from_file(DECKS_DIR + file_name)
|
|
if deck:
|
|
decks.append(deck)
|
|
file_name = dir.get_next()
|
|
|
|
dir.list_dir_end()
|
|
|
|
decks.sort_custom(func(a, b): return a.updated_at > b.updated_at)
|
|
|
|
print("Loaded %d deck(s)" % decks.size())
|
|
decks_loaded.emit()
|
|
|
|
func load_deck_from_file(file_path: String) -> Deck:
|
|
print("Loading Deck From File: ", file_path)
|
|
var file = FileAccess.open(file_path, FileAccess.READ)
|
|
if not file:
|
|
push_error("Failed to open deck file: " + file_path)
|
|
return null
|
|
|
|
var json_string = file.get_as_text()
|
|
file.close()
|
|
|
|
var json = JSON.new()
|
|
var error = json.parse(json_string)
|
|
|
|
if error != OK:
|
|
push_error("Failed to parse JSON in file: %s (Error: %s)" % [file_path, json.get_error_message()])
|
|
return null
|
|
|
|
var deck = Deck.new()
|
|
deck.from_dict(json.data)
|
|
return deck
|
|
|
|
func save_deck(deck: Deck) -> bool:
|
|
if not deck:
|
|
push_error("Cannot save null deck")
|
|
return false
|
|
|
|
deck.updated_at = Time.get_datetime_string_from_system(true, true) + "Z"
|
|
|
|
var file_path = DECKS_DIR + deck.id + ".json"
|
|
print("Saving Deck to: ", file_path)
|
|
var file = FileAccess.open(file_path, FileAccess.WRITE)
|
|
|
|
if not file:
|
|
push_error("Failed to create deck file: " + file_path)
|
|
return false
|
|
|
|
var deck_dict = deck.to_dict()
|
|
var json_string = JSON.stringify(deck_dict, "\t")
|
|
file.store_string(json_string)
|
|
file.close()
|
|
|
|
var found = false
|
|
for i in range(decks.size()):
|
|
if decks[i].id == deck.id:
|
|
decks[i] = deck
|
|
found = true
|
|
break
|
|
|
|
if not found:
|
|
decks.append(deck)
|
|
|
|
decks.sort_custom(func(a, b): return a.updated_at > b.updated_at)
|
|
|
|
print("Saved deck: %s (%d cards)" % [deck.name, deck.get_card_count()])
|
|
deck_saved.emit(deck)
|
|
return true
|
|
|
|
func delete_deck(deck_id: String) -> bool:
|
|
var file_path = DECKS_DIR + deck_id + ".json"
|
|
|
|
if FileAccess.file_exists(file_path):
|
|
var error = DirAccess.remove_absolute(file_path)
|
|
if error != OK:
|
|
push_error("Failed to delete deck file: " + file_path)
|
|
return false
|
|
|
|
for i in range(decks.size()):
|
|
if decks[i].id == deck_id:
|
|
decks.remove_at(i)
|
|
break
|
|
|
|
if progress_data.has(deck_id):
|
|
progress_data.erase(deck_id)
|
|
save_progress()
|
|
|
|
print("Deleted deck: " + deck_id)
|
|
deck_deleted.emit(deck_id)
|
|
return true
|
|
|
|
func get_deck_by_id(deck_id: String) -> Deck:
|
|
for deck in decks:
|
|
if deck.id == deck_id:
|
|
return deck
|
|
return null
|
|
|
|
func load_progress() -> void:
|
|
if not FileAccess.file_exists(PROGRESS_FILE):
|
|
print("No progress file found, starting fresh")
|
|
return
|
|
|
|
var file = FileAccess.open(PROGRESS_FILE, FileAccess.READ)
|
|
if not file:
|
|
push_error("Failed to open progress file")
|
|
return
|
|
|
|
var json_string = file.get_as_text()
|
|
file.close()
|
|
|
|
var json = JSON.new()
|
|
var error = json.parse(json_string)
|
|
|
|
if error != OK:
|
|
push_error("Failed to parse progress JSON")
|
|
return
|
|
|
|
var data = json.data
|
|
user_id = data.get("user_id", "local")
|
|
|
|
var deck_progress = data.get("deck_progress", {})
|
|
for deck_id in deck_progress.keys():
|
|
var progress = Progress.new()
|
|
progress.from_dict(deck_progress[deck_id])
|
|
progress_data[deck_id] = progress
|
|
|
|
print("Loaded progress for %d deck(s)" % progress_data.size())
|
|
|
|
func save_progress() -> void:
|
|
var deck_progress = {}
|
|
for deck_id in progress_data.keys():
|
|
deck_progress[deck_id] = progress_data[deck_id].to_dict()
|
|
|
|
var data = {
|
|
"version": "1.0",
|
|
"user_id": user_id,
|
|
"deck_progress": deck_progress
|
|
}
|
|
|
|
var file = FileAccess.open(PROGRESS_FILE, FileAccess.WRITE)
|
|
if not file:
|
|
push_error("Failed to save progress file")
|
|
return
|
|
|
|
var json_string = JSON.stringify(data, "\t")
|
|
file.store_string(json_string)
|
|
file.close()
|
|
|
|
print("Saved progress for %d deck(s)" % progress_data.size())
|
|
|
|
func get_or_create_progress(deck_id: String) -> Progress:
|
|
if not progress_data.has(deck_id):
|
|
var progress = Progress.new()
|
|
progress.deck_id = deck_id
|
|
progress_data[deck_id] = progress
|
|
|
|
return progress_data[deck_id]
|
|
|
|
func record_quiz_result(deck_id: String, card_id: String, is_correct: bool) -> void:
|
|
var progress = get_or_create_progress(deck_id)
|
|
progress.record_answer(card_id, is_correct)
|
|
save_progress()
|
|
progress_updated.emit(deck_id)
|
|
|
|
func get_progress(deck_id: String) -> Progress:
|
|
return progress_data.get(deck_id, null)
|
|
|
|
|
|
func download_deck_from_api(api_url: String) -> void:
|
|
print("Downloading deck from API: " + api_url)
|
|
|
|
var http_request = HTTPRequest.new()
|
|
add_child(http_request)
|
|
|
|
http_request.request_completed.connect(_on_api_deck_downloaded.bind(http_request))
|
|
var error = http_request.request(api_url)
|
|
|
|
if error != OK:
|
|
push_error("Failed to start HTTP request: " + str(error))
|
|
http_request.queue_free()
|
|
|
|
func _on_api_deck_downloaded(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
|
|
if not http_request:
|
|
return
|
|
|
|
if result != HTTPRequest.RESULT_SUCCESS:
|
|
push_error("HTTP request failed with result: " + str(result))
|
|
http_request.queue_free()
|
|
return
|
|
|
|
if response_code != 200:
|
|
push_error("API returned error code: " + str(response_code))
|
|
http_request.queue_free()
|
|
return
|
|
|
|
var json_string = body.get_string_from_utf8()
|
|
var json = JSON.new()
|
|
var parse_error = json.parse(json_string)
|
|
|
|
if parse_error != OK:
|
|
push_error("Failed to parse API response JSON")
|
|
http_request.queue_free()
|
|
return
|
|
|
|
var deck = Deck.new()
|
|
deck.from_dict(json.data)
|
|
save_deck(deck)
|
|
|
|
print("Successfully downloaded and saved deck: " + deck.name)
|
|
http_request.queue_free()
|
|
|
|
func upload_deck_to_api(deck: Deck, api_url: String, auth_token: String = "") -> void:
|
|
print("Uploading deck to API: " + deck.name)
|
|
|
|
var http_request = HTTPRequest.new()
|
|
add_child(http_request)
|
|
|
|
http_request.request_completed.connect(_on_api_deck_uploaded.bind(http_request))
|
|
|
|
var headers = ["Content-Type: application/json"]
|
|
if auth_token != "":
|
|
headers.append("Authorization: Bearer " + auth_token)
|
|
|
|
var deck_dict = deck.to_dict()
|
|
var json_string = JSON.stringify(deck_dict)
|
|
|
|
var error = http_request.request(api_url, headers, HTTPClient.METHOD_POST, json_string)
|
|
|
|
if error != OK:
|
|
push_error("Failed to start HTTP upload request: " + str(error))
|
|
http_request.queue_free()
|
|
|
|
func _on_api_deck_uploaded(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
|
|
if not http_request:
|
|
return
|
|
|
|
if result != HTTPRequest.RESULT_SUCCESS:
|
|
push_error("HTTP upload failed with result: " + str(result))
|
|
http_request.queue_free()
|
|
return
|
|
|
|
if response_code == 200 or response_code == 201:
|
|
print("Deck successfully uploaded to API!")
|
|
# Optionally parse response to get the deck ID from server
|
|
var json_string = body.get_string_from_utf8()
|
|
print("Server response: " + json_string)
|
|
else:
|
|
push_error("API returned error code: " + str(response_code))
|
|
|
|
http_request.queue_free()
|
|
|
|
func browse_decks_from_api(api_url: String) -> void:
|
|
print("Browsing decks from API: " + api_url)
|
|
|
|
var http_request = HTTPRequest.new()
|
|
add_child(http_request)
|
|
http_request.request_completed.connect(_on_api_decks_browsed.bind(http_request))
|
|
|
|
var error = http_request.request(api_url)
|
|
if error != OK:
|
|
push_error("Failed to start browse request: " + str(error))
|
|
http_request.queue_free()
|
|
|
|
func _on_api_decks_browsed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
|
|
if not http_request:
|
|
return
|
|
|
|
if result != HTTPRequest.RESULT_SUCCESS or response_code != 200:
|
|
push_error("Browse request failed")
|
|
http_request.queue_free()
|
|
return
|
|
|
|
var json_string = body.get_string_from_utf8()
|
|
var json = JSON.new()
|
|
|
|
if json.parse(json_string) == OK:
|
|
var decks_list = json.data
|
|
print("Found %d decks from API" % decks_list.size())
|
|
|
|
for deck_data in decks_list:
|
|
print(" - " + deck_data.get("deck", {}).get("name", "Unknown"))
|
|
|
|
http_request.queue_free()
|