Add session expiry renewal flow
This commit is contained in:
@@ -72,10 +72,14 @@ func _ready() -> void:
|
||||
_apply_theme()
|
||||
|
||||
|
||||
func on_show(_payload: Variant = null) -> void:
|
||||
func on_show(payload: Variant = null) -> void:
|
||||
_set_busy(false)
|
||||
_set_mode("login")
|
||||
_clear_message()
|
||||
var login_message := str(payload).strip_edges() if payload != null else ""
|
||||
if not login_message.is_empty():
|
||||
message_label.text = login_message
|
||||
message_label.add_theme_color_override("font_color", Color("ef4444"))
|
||||
login_input.grab_focus()
|
||||
|
||||
|
||||
|
||||
+19
-2
@@ -12,6 +12,7 @@ var sound_toggle: CheckButton
|
||||
var theme_button: Button
|
||||
var feedback_button: Button
|
||||
var feedback_dialog: Control
|
||||
var session_expiry_dialog: Control
|
||||
var theme_service
|
||||
var current_screen_name := ""
|
||||
var screens_margin: MarginContainer
|
||||
@@ -285,10 +286,15 @@ func _build_ui() -> void:
|
||||
feedback_dialog = preload("res://scripts/ui/app_feedback_dialog.gd").new()
|
||||
feedback_dialog.name = "AppFeedbackDialog"
|
||||
add_child(feedback_dialog)
|
||||
session_expiry_dialog = preload("res://scripts/ui/session_expiry_dialog.gd").new()
|
||||
session_expiry_dialog.name = "SessionExpiryDialog"
|
||||
session_expiry_dialog.logout_requested.connect(_logout)
|
||||
add_child(session_expiry_dialog)
|
||||
_build_account_menu()
|
||||
_build_drawer()
|
||||
|
||||
AuthState.auth_changed.connect(_refresh_header_controls)
|
||||
AuthState.session_expired.connect(_on_session_expired)
|
||||
_refresh_header_controls()
|
||||
_build_app_menu()
|
||||
|
||||
@@ -682,14 +688,25 @@ func _refresh_theme_button() -> void:
|
||||
|
||||
|
||||
func _logout() -> void:
|
||||
_perform_logout("")
|
||||
|
||||
|
||||
func _on_session_expired(_reason: String) -> void:
|
||||
_perform_logout("Your session expired. Sign in again.")
|
||||
|
||||
|
||||
func _perform_logout(login_message: String) -> void:
|
||||
_close_app_menu()
|
||||
if session_expiry_dialog != null and session_expiry_dialog.has_method("dismiss_for_logout"):
|
||||
session_expiry_dialog.dismiss_for_logout()
|
||||
if BlockSessionService.block_started and BlockSessionService.session_kind != "simulation":
|
||||
BlockSessionService.finish_block()
|
||||
elif BlockSessionService.session_kind == "simulation":
|
||||
BlockSessionService.block_started = false
|
||||
QuestionBankService.clear_questions()
|
||||
AuthState.clear_session()
|
||||
show_screen("login")
|
||||
if AuthState.is_logged_in or not AuthState.access_token.is_empty():
|
||||
AuthState.clear_session()
|
||||
show_screen("login", login_message)
|
||||
|
||||
|
||||
func _build_app_menu() -> void:
|
||||
|
||||
@@ -652,6 +652,10 @@ func _toolbar_box(dark: bool, active: bool = false) -> StyleBoxFlat:
|
||||
box.border_color = Brand.PURPLE if active else (Color("33415d") if dark else Brand.LIGHT_BORDER)
|
||||
box.set_border_width_all(1)
|
||||
box.set_corner_radius_all(7)
|
||||
box.content_margin_left = 0
|
||||
box.content_margin_right = 0
|
||||
box.content_margin_top = 0
|
||||
box.content_margin_bottom = 0
|
||||
return box
|
||||
|
||||
|
||||
@@ -687,7 +691,13 @@ func _style_asset_toolbar_button(button: Button, texture: Texture2D, dark: bool)
|
||||
button.icon = texture
|
||||
button.expand_icon = false
|
||||
button.focus_mode = Control.FOCUS_NONE
|
||||
button.add_theme_constant_override("icon_max_width", 25)
|
||||
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
button.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||
button.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
button.add_theme_constant_override("h_separation", 0)
|
||||
button.add_theme_constant_override("icon_max_width", 23)
|
||||
button.add_theme_stylebox_override("normal", _toolbar_box(dark))
|
||||
button.add_theme_stylebox_override("hover", _toolbar_box(dark, true))
|
||||
button.add_theme_stylebox_override("pressed", _toolbar_box(dark, true))
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
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:
|
||||
if not visible:
|
||||
return
|
||||
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.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))
|
||||
@@ -0,0 +1 @@
|
||||
uid://dv8ssk14s6suf
|
||||
Reference in New Issue
Block a user