extends Control const Brand := preload("res://scripts/ui/medmind_style.gd") signal logout_requested var panel: PanelContainer var title_label: Label var message_label: Label var error_label: Label var stay_button: Button var logout_button: Button func _ready() -> void: visible = false top_level = true mouse_filter = Control.MOUSE_FILTER_STOP set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) _build_ui() AuthState.session_warning.connect(_show_warning) AuthState.session_countdown_changed.connect(_update_countdown) AuthState.session_refresh_started.connect(_refresh_started) AuthState.session_refreshed.connect(_refresh_succeeded) AuthState.session_refresh_failed.connect(_refresh_failed) UIThemeService.theme_changed.connect(func(_dark: bool): _apply_theme()) resized.connect(_refresh_size) _refresh_size() _apply_theme() func _build_ui() -> void: var dim := ColorRect.new() dim.color = Color(0, 0, 0, 0.74) dim.mouse_filter = Control.MOUSE_FILTER_STOP dim.set_anchors_preset(Control.PRESET_FULL_RECT) add_child(dim) var center := CenterContainer.new() center.set_anchors_preset(Control.PRESET_FULL_RECT) add_child(center) panel = PanelContainer.new() panel.custom_minimum_size = Vector2(520, 280) center.add_child(panel) var content := VBoxContainer.new() content.add_theme_constant_override("separation", 16) panel.add_child(content) title_label = Label.new() title_label.text = "Stay logged in?" title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title_label.add_theme_font_size_override("font_size", 28) content.add_child(title_label) message_label = Label.new() message_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER message_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART message_label.add_theme_font_size_override("font_size", 17) content.add_child(message_label) error_label = Label.new() error_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER error_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART error_label.add_theme_font_size_override("font_size", 14) content.add_child(error_label) var actions := HBoxContainer.new() actions.add_theme_constant_override("separation", 12) content.add_child(actions) logout_button = Button.new() logout_button.text = "Log out" logout_button.custom_minimum_size = Vector2(150, 48) logout_button.pressed.connect(_request_logout) actions.add_child(logout_button) stay_button = Button.new() stay_button.text = "Stay logged in" stay_button.custom_minimum_size = Vector2(0, 48) stay_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL stay_button.pressed.connect(AuthState.request_session_refresh) actions.add_child(stay_button) func _show_warning(seconds_remaining: int) -> void: error_label.text = "" stay_button.disabled = false logout_button.disabled = false stay_button.text = "Stay logged in" visible = true _update_countdown(seconds_remaining) move_to_front() BlockSessionService.pause_current_question_timer() func _update_countdown(seconds_remaining: int) -> void: if not visible: return var minutes := floori(float(seconds_remaining) / 60.0) var seconds := seconds_remaining % 60 message_label.text = "Your session expires in %02d:%02d. Renew it to continue studying." % [minutes, seconds] func _refresh_started() -> void: if not visible: return stay_button.disabled = true logout_button.disabled = true stay_button.text = "Renewing..." error_label.text = "" func _refresh_succeeded() -> void: _hide_dialog() func _refresh_failed(message: String, terminal: bool) -> void: visible = true move_to_front() error_label.text = message stay_button.text = "Stay logged in" stay_button.disabled = terminal logout_button.disabled = false func _request_logout() -> void: logout_requested.emit() func _hide_dialog() -> void: visible = false error_label.text = "" stay_button.text = "Stay logged in" stay_button.disabled = false logout_button.disabled = false BlockSessionService.resume_current_question_timer() func dismiss_for_logout() -> void: if visible: _hide_dialog() func _unhandled_input(event: InputEvent) -> void: if visible and event.is_action_pressed("ui_cancel"): get_viewport().set_input_as_handled() func _refresh_size() -> void: if panel == null: return panel.custom_minimum_size = Vector2( minf(520.0, maxf(288.0, size.x - 32.0)), minf(280.0, maxf(250.0, size.y - 32.0))) func _apply_theme() -> void: if panel == null: return var dark := UIThemeService.is_dark_mode() var panel_style := Brand.panel_box(dark) panel_style.bg_color.a = 1.0 panel_style.content_margin_left = 32 panel_style.content_margin_right = 32 panel_style.content_margin_top = 30 panel_style.content_margin_bottom = 30 panel.add_theme_stylebox_override("panel", panel_style) for label in [title_label, message_label]: label.add_theme_color_override("font_color", Brand.text_color(dark)) error_label.add_theme_color_override("font_color", Color("fb7185") if dark else Color("be123c")) stay_button.add_theme_stylebox_override("normal", Brand.gradient_button_box()) stay_button.add_theme_stylebox_override("hover", Brand.gradient_button_box()) stay_button.add_theme_stylebox_override("pressed", Brand.gradient_button_box()) stay_button.add_theme_color_override("font_color", Color.WHITE) stay_button.add_theme_color_override("font_hover_color", Color.WHITE) logout_button.add_theme_stylebox_override("normal", Brand.secondary_button_box(dark)) logout_button.add_theme_stylebox_override("hover", Brand.secondary_button_box(dark, true)) logout_button.add_theme_stylebox_override("pressed", Brand.secondary_button_box(dark, true)) logout_button.add_theme_color_override("font_color", Brand.text_color(dark)) logout_button.add_theme_color_override("font_hover_color", Brand.text_color(dark))