Launch question bank directly from navigation
This commit is contained in:
@@ -8,6 +8,7 @@ signal stage_changed(stage_name: String)
|
||||
var main: Control
|
||||
var exam_view: VBoxContainer
|
||||
var mode_view: VBoxContainer
|
||||
var question_bank_launch_view: VBoxContainer
|
||||
var stage_scroll: ScrollContainer
|
||||
var stage_content: VBoxContainer
|
||||
var exam_grid: VBoxContainer
|
||||
@@ -17,6 +18,9 @@ var question_bank_button: Button
|
||||
var bank_status_label: Label
|
||||
var bank_error_label: Label
|
||||
var retry_button: Button
|
||||
var launch_status_label: Label
|
||||
var launch_error_label: Label
|
||||
var launch_retry_button: Button
|
||||
var ccs_button: Button
|
||||
var ccs_status_label: Label
|
||||
var pending_ccs_launch := false
|
||||
@@ -51,12 +55,16 @@ func _ready() -> void:
|
||||
|
||||
_build_exam_view()
|
||||
_build_mode_view()
|
||||
_build_question_bank_launch_view()
|
||||
_show_stage("exam")
|
||||
|
||||
|
||||
func on_show(payload: Variant = null) -> void:
|
||||
if str(payload) == "step3":
|
||||
_show_stage("step3")
|
||||
elif str(payload) == "question_bank":
|
||||
_show_stage("question_bank")
|
||||
call_deferred("_select_question_bank")
|
||||
elif str(payload) == "exam":
|
||||
_show_stage("exam")
|
||||
_refresh_bank_state()
|
||||
@@ -202,6 +210,84 @@ func _build_mode_view() -> void:
|
||||
ccs_status_label = ccs_card.status
|
||||
ccs_button.pressed.connect(_select_ccs)
|
||||
|
||||
|
||||
func _build_question_bank_launch_view() -> void:
|
||||
question_bank_launch_view = VBoxContainer.new()
|
||||
question_bank_launch_view.name = "QuestionBankLaunch"
|
||||
question_bank_launch_view.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
question_bank_launch_view.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
question_bank_launch_view.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
stage_content.add_child(question_bank_launch_view)
|
||||
|
||||
var center := CenterContainer.new()
|
||||
center.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
center.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
question_bank_launch_view.add_child(center)
|
||||
|
||||
var panel := PanelContainer.new()
|
||||
panel.custom_minimum_size = Vector2(560, 300)
|
||||
panel.set_meta("medmind_wide_minimum", Vector2(560, 300))
|
||||
panel.add_theme_stylebox_override("panel", Brand.content_card_box(UIThemeService.is_dark_mode(), true, Brand.TEAL))
|
||||
panel.set_meta("medmind_role", "question_bank_launch")
|
||||
themed_controls.append(panel)
|
||||
responsive_cards.append(panel)
|
||||
center.add_child(panel)
|
||||
|
||||
var content := VBoxContainer.new()
|
||||
content.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
content.add_theme_constant_override("separation", 18)
|
||||
panel.add_child(content)
|
||||
|
||||
var icon := TextureRect.new()
|
||||
icon.texture = preload("res://assets/icons/lucide/clipboard-list.svg")
|
||||
icon.custom_minimum_size = Vector2(54, 54)
|
||||
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
icon.modulate = Brand.TEAL if UIThemeService.is_dark_mode() else Brand.ROYAL_BLUE
|
||||
icon.set_meta("medmind_role", "question_bank_launch_icon")
|
||||
themed_controls.append(icon)
|
||||
content.add_child(icon)
|
||||
|
||||
var title := Label.new()
|
||||
title.text = "Preparing your question bank"
|
||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
title.add_theme_font_override("font", Brand.font)
|
||||
title.add_theme_font_size_override("font_size", 27)
|
||||
content.add_child(title)
|
||||
|
||||
launch_status_label = Label.new()
|
||||
launch_status_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
launch_status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
launch_status_label.add_theme_font_override("font", Brand.font)
|
||||
launch_status_label.add_theme_font_size_override("font_size", 16)
|
||||
launch_status_label.add_theme_color_override("font_color", Brand.muted_color(UIThemeService.is_dark_mode()))
|
||||
launch_status_label.set_meta("medmind_role", "muted")
|
||||
themed_controls.append(launch_status_label)
|
||||
content.add_child(launch_status_label)
|
||||
|
||||
launch_error_label = Label.new()
|
||||
launch_error_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
launch_error_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
launch_error_label.add_theme_font_override("font", Brand.font)
|
||||
launch_error_label.add_theme_font_size_override("font_size", 15)
|
||||
launch_error_label.add_theme_color_override("font_color", Style.RED)
|
||||
content.add_child(launch_error_label)
|
||||
|
||||
var actions := HBoxContainer.new()
|
||||
actions.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
actions.add_theme_constant_override("separation", 12)
|
||||
content.add_child(actions)
|
||||
launch_retry_button = Button.new()
|
||||
launch_retry_button.text = "Retry"
|
||||
launch_retry_button.custom_minimum_size = Vector2(120, 44)
|
||||
launch_retry_button.pressed.connect(_retry_question_bank)
|
||||
actions.add_child(launch_retry_button)
|
||||
var cancel := Button.new()
|
||||
cancel.text = "Back to Home"
|
||||
cancel.custom_minimum_size = Vector2(150, 44)
|
||||
cancel.pressed.connect(func(): main.show_screen("selection", "exam"))
|
||||
actions.add_child(cancel)
|
||||
|
||||
func _add_mode_card(
|
||||
title_text: String,
|
||||
subtitle_text: String,
|
||||
@@ -527,6 +613,8 @@ func refresh_theme_controls() -> void:
|
||||
match role:
|
||||
"mode_card": control.add_theme_stylebox_override("panel", _mode_card_box(enabled, accent))
|
||||
"exam_card": control.add_theme_stylebox_override("panel", _exam_card_box(enabled))
|
||||
"question_bank_launch": control.add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.TEAL))
|
||||
"question_bank_launch_icon": control.modulate = Brand.TEAL if dark else Brand.ROYAL_BLUE
|
||||
"icon_circle": control.add_theme_stylebox_override("panel", _circle_box(visual_accent, 36))
|
||||
"arrow_circle": control.add_theme_stylebox_override("panel", _circle_box(visual_accent, 24, enabled))
|
||||
"exam_icon_circle": control.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.ROYAL_BLUE if enabled else Brand.PURPLE, 39))
|
||||
@@ -542,7 +630,7 @@ func refresh_theme_controls() -> void:
|
||||
"arrow_icon": control.modulate = Color.WHITE if dark else Brand.ROYAL_BLUE
|
||||
"exam_arrow": control.modulate = Brand.TEAL if dark and enabled else Brand.ROYAL_BLUE
|
||||
"back_bar": _style_back_bar(control as Button)
|
||||
for node in [exam_view, mode_view]:
|
||||
for node in [exam_view, mode_view, question_bank_launch_view]:
|
||||
if node != null:
|
||||
for child in node.find_children("*", "Label", true, false):
|
||||
var label := child as Label
|
||||
@@ -558,11 +646,15 @@ func _show_stage(stage_name: String) -> void:
|
||||
exam_view.visible = stage_name == "exam"
|
||||
if mode_view != null:
|
||||
mode_view.visible = stage_name == "step3"
|
||||
if question_bank_launch_view != null:
|
||||
question_bank_launch_view.visible = stage_name == "question_bank"
|
||||
_refresh_grid_columns()
|
||||
_refresh_bank_state()
|
||||
|
||||
|
||||
func _select_question_bank() -> void:
|
||||
if pending_question_bank_launch:
|
||||
return
|
||||
pending_question_bank_launch = true
|
||||
var excluded_ids: Array = ProfileService.get_profile().get("correct_question_ids", []).duplicate()
|
||||
QuestionBankService.load_next_random_question(excluded_ids)
|
||||
@@ -604,13 +696,16 @@ func _retry_question_bank() -> void:
|
||||
|
||||
func _on_questions_changed() -> void:
|
||||
_refresh_bank_state()
|
||||
if pending_question_bank_launch and visible and current_stage == "step3" and QuestionBankService.load_state == "ready":
|
||||
if pending_question_bank_launch and visible and current_stage in ["step3", "question_bank"] and QuestionBankService.load_state == "ready":
|
||||
pending_question_bank_launch = false
|
||||
call_deferred("_start_question_bank")
|
||||
|
||||
|
||||
func _on_questions_load_failed(_message: String) -> void:
|
||||
pending_question_bank_launch = false
|
||||
# Keep the launch intent alive while the service performs its automatic
|
||||
# backoff retry. A terminal failure still waits for an explicit retry.
|
||||
if QuestionBankService.load_state != "retrying":
|
||||
pending_question_bank_launch = false
|
||||
_refresh_bank_state()
|
||||
|
||||
|
||||
@@ -621,8 +716,12 @@ func _refresh_bank_state() -> void:
|
||||
question_bank_button.disabled = pending_question_bank_launch
|
||||
retry_button.visible = state == "failed" or state == "retrying"
|
||||
retry_button.disabled = state == "retrying"
|
||||
launch_retry_button.visible = state == "failed" or state == "retrying"
|
||||
launch_retry_button.disabled = state == "retrying"
|
||||
bank_error_label.text = QuestionBankService.last_error if state == "failed" or state == "retrying" else ""
|
||||
bank_error_label.visible = not bank_error_label.text.is_empty()
|
||||
launch_error_label.text = bank_error_label.text
|
||||
launch_error_label.visible = not launch_error_label.text.is_empty()
|
||||
if pending_question_bank_launch or state == "loading":
|
||||
bank_status_label.text = "Preparing your question bank..."
|
||||
elif state == "retrying":
|
||||
@@ -634,16 +733,21 @@ func _refresh_bank_state() -> void:
|
||||
else:
|
||||
bank_status_label.text = ""
|
||||
bank_status_label.visible = not bank_status_label.text.is_empty()
|
||||
launch_status_label.text = bank_status_label.text
|
||||
launch_status_label.visible = not launch_status_label.text.is_empty()
|
||||
|
||||
|
||||
func _start_question_bank() -> void:
|
||||
if not visible or current_stage != "step3" or not QuestionBankService.has_questions():
|
||||
if not visible or current_stage not in ["step3", "question_bank"] or not QuestionBankService.has_questions():
|
||||
return
|
||||
if BlockSessionService.start_core_run():
|
||||
UISoundService.play_sound("question")
|
||||
main.show_screen("player")
|
||||
else:
|
||||
bank_error_label.text = BlockSessionService.core_status_message
|
||||
bank_error_label.visible = true
|
||||
launch_error_label.text = BlockSessionService.core_status_message
|
||||
launch_error_label.visible = true
|
||||
|
||||
|
||||
func _refresh_grid_columns() -> void:
|
||||
|
||||
Reference in New Issue
Block a user