Fix dashboard info-icon rendering and profile <null> display
Info badge: the icon was a PanelContainer using StyleBoxFlat corner radius to fake a circle, with the radius exceeding half the box size, causing visible artifacts. Replaced with a custom-drawn Control (draw_arc) for a clean ring at any size, and added a hover tooltip explaining what each stat card measures. Profile: Dictionary.get(key, default) only falls back when the key is missing, not when it's present with a null value, so a null displayName rendered as the literal string "<null>" (str(null) in GDScript) - including pre-filling the edit-profile dialog with it. Added a shared string_field() helper that treats null the same as missing, applied everywhere an account/preference string field is displayed or edited. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -258,19 +258,19 @@ func _render() -> void:
|
||||
return
|
||||
var account: Dictionary = AccountProfileService.account
|
||||
var prefs: Dictionary = AccountProfileService.preferences
|
||||
var username := str(account.get("username", AuthState.username))
|
||||
var display_name := str(account.get("displayName", "")).strip_edges()
|
||||
var username := string_field(account, "username", AuthState.username)
|
||||
var display_name := string_field(account, "displayName").strip_edges()
|
||||
profile_name.text = username if display_name.is_empty() else display_name
|
||||
var email := str(account.get("email", "")).strip_edges()
|
||||
var email := string_field(account, "email").strip_edges()
|
||||
profile_identity.text = "@%s" % username if email.is_empty() else "@%s | %s" % [username, email]
|
||||
var role := str(account.get("role", AuthState.role)).capitalize()
|
||||
var created := _date_only(str(account.get("createdUtc", "")))
|
||||
var role := string_field(account, "role", AuthState.role).capitalize()
|
||||
var created := _date_only(string_field(account, "createdUtc"))
|
||||
profile_meta.text = role if created.is_empty() else "%s | Member since %s" % [role, created]
|
||||
_render_connected_accounts(account.get("connectedProviders", []), account.get("availableProviders", []))
|
||||
(identity_values["exam"] as Label).text = _date_only(str(prefs.get("targetExamDate", "")), "Not scheduled")
|
||||
(identity_values["stage"] as Label).text = _friendly(str(prefs.get("preparationStage", "pre_dedicated")))
|
||||
(identity_values["exam"] as Label).text = _date_only(string_field(prefs, "targetExamDate"), "Not scheduled")
|
||||
(identity_values["stage"] as Label).text = _friendly(string_field(prefs, "preparationStage", "pre_dedicated"))
|
||||
(identity_values["goal"] as Label).text = "%d hours / week" % int(prefs.get("weeklyGoalHours", 10))
|
||||
(identity_values["mode"] as Label).text = _friendly(str(prefs.get("preferredStudyMode", "mixed")))
|
||||
(identity_values["mode"] as Label).text = _friendly(string_field(prefs, "preferredStudyMode", "mixed"))
|
||||
var data: Dictionary = DashboardService.data
|
||||
_set_metric("qbank", str(int(data.get("questionBankRating", QuestionBankService.overall_elo))))
|
||||
_set_metric("ccs", str(int(data.get("ccsRating", CCSService.rating.get("elo", 1000)))))
|
||||
@@ -353,7 +353,7 @@ func _render_outcome() -> void:
|
||||
outcome_delete.visible = false
|
||||
return
|
||||
outcome_score.value = int(outcome.get("officialScore", 200))
|
||||
outcome_date.text = _date_only(str(outcome.get("examDate", "")))
|
||||
outcome_date.text = _date_only(string_field(outcome, "examDate"))
|
||||
outcome_consent.button_pressed = true
|
||||
outcome_status.text = "Saved score %d for %s." % [int(outcome_score.value), outcome_date.text]
|
||||
outcome_delete.visible = true
|
||||
@@ -363,12 +363,12 @@ func _open_profile_editor() -> void:
|
||||
var dialog := _dialog("Edit profile")
|
||||
var display := LineEdit.new()
|
||||
display.placeholder_text = "Display name"
|
||||
display.text = str(AccountProfileService.account.get("displayName", ""))
|
||||
display.text = string_field(AccountProfileService.account, "displayName")
|
||||
_style_field(display)
|
||||
dialog.add_field(display)
|
||||
var email := LineEdit.new()
|
||||
email.placeholder_text = "Email address"
|
||||
email.text = str(AccountProfileService.account.get("email", ""))
|
||||
email.text = string_field(AccountProfileService.account, "email")
|
||||
_style_field(email)
|
||||
dialog.add_field(email)
|
||||
dialog.confirmed.connect(func(): AccountProfileService.update_account(display.text, email.text))
|
||||
@@ -381,18 +381,18 @@ func _open_study_editor() -> void:
|
||||
var dialog := _dialog("Edit study identity")
|
||||
var exam := LineEdit.new()
|
||||
exam.placeholder_text = "Target exam date: YYYY-MM-DD"
|
||||
exam.text = _date_only(str(prefs.get("targetExamDate", "")))
|
||||
exam.text = _date_only(string_field(prefs, "targetExamDate"))
|
||||
_style_field(exam)
|
||||
dialog.add_field(exam)
|
||||
var stage := OptionButton.new()
|
||||
_add_options(stage, [["Pre-dedicated", "pre_dedicated"], ["Dedicated", "dedicated"], ["Final review", "final_review"]], str(prefs.get("preparationStage", "pre_dedicated")))
|
||||
_add_options(stage, [["Pre-dedicated", "pre_dedicated"], ["Dedicated", "dedicated"], ["Final review", "final_review"]], string_field(prefs, "preparationStage", "pre_dedicated"))
|
||||
dialog.add_field(stage)
|
||||
var goal := OptionButton.new()
|
||||
for hours in [5, 10, 15, 20, 25]: goal.add_item("%d hours per week" % hours, hours)
|
||||
_select_id(goal, int(prefs.get("weeklyGoalHours", 10)))
|
||||
dialog.add_field(goal)
|
||||
var mode := OptionButton.new()
|
||||
_add_options(mode, [["Mixed", "mixed"], ["Question Bank", "question_bank"], ["CCS", "ccs"], ["Exam simulation", "exam_simulation"]], str(prefs.get("preferredStudyMode", "mixed")))
|
||||
_add_options(mode, [["Mixed", "mixed"], ["Question Bank", "question_bank"], ["CCS", "ccs"], ["Exam simulation", "exam_simulation"]], string_field(prefs, "preferredStudyMode", "mixed"))
|
||||
dialog.add_field(mode)
|
||||
dialog.confirmed.connect(func():
|
||||
AccountProfileService.update_preferences(exam.text, str(stage.get_item_metadata(stage.selected)), goal.get_selected_id(), str(mode.get_item_metadata(mode.selected))))
|
||||
@@ -603,8 +603,13 @@ func _clear(container: Container) -> void:
|
||||
child.free()
|
||||
|
||||
|
||||
static func string_field(source: Dictionary, key: String, fallback: String = "") -> String:
|
||||
var value = source.get(key)
|
||||
return fallback if value == null else str(value)
|
||||
|
||||
|
||||
func _date_only(value: String, fallback: String = "") -> String:
|
||||
if value.is_empty() or value == "<null>": return fallback
|
||||
if value.is_empty(): return fallback
|
||||
return value.left(10)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user