Add provider-neutral Google authentication

This commit is contained in:
hz
2026-07-20 21:42:56 -05:00
parent 48e19f138e
commit 72fd1e3e0f
20 changed files with 1084 additions and 36 deletions
+47
View File
@@ -18,6 +18,7 @@ var outcome_date: LineEdit
var outcome_consent: CheckBox
var outcome_status: Label
var outcome_delete: Button
var connected_accounts_list: VBoxContainer
func _ready() -> void:
@@ -26,6 +27,8 @@ func _ready() -> void:
AccountProfileService.request_failed.connect(_show_error)
AccountProfileService.preferences_saved.connect(func(): _show_saved("Study identity saved."))
AccountProfileService.archive_saved.connect(func(path: String): _show_saved("Archive saved: %s" % path))
AccountProfileService.external_link_started.connect(func(provider: String): _show_saved("Complete %s connection in the opened window." % provider.capitalize()))
AccountProfileService.external_linked.connect(func(provider: String): _show_saved("%s account connected." % provider.capitalize()))
DashboardService.dashboard_changed.connect(_render)
DashboardService.dashboard_failed.connect(_show_error)
ExamSimulationService.outcome_changed.connect(_render_outcome)
@@ -82,6 +85,7 @@ func _build() -> void:
right.add_theme_constant_override("separation", 14)
columns.add_child(right)
right.add_child(_outcome_card())
right.add_child(_connected_accounts_card())
right.add_child(_account_actions_card())
_update_responsive()
_render()
@@ -228,6 +232,16 @@ func _account_actions_card() -> PanelContainer:
return _card(body)
func _connected_accounts_card() -> PanelContainer:
var body := VBoxContainer.new()
body.add_theme_constant_override("separation", 9)
body.add_child(_section_header("Connected Accounts", "Use another trusted identity to sign in"))
connected_accounts_list = VBoxContainer.new()
connected_accounts_list.add_theme_constant_override("separation", 8)
body.add_child(connected_accounts_list)
return _card(body)
func _render() -> void:
if profile_name == null:
return
@@ -241,6 +255,7 @@ func _render() -> void:
var role := str(account.get("role", AuthState.role)).capitalize()
var created := _date_only(str(account.get("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["goal"] as Label).text = "%d hours / week" % int(prefs.get("weeklyGoalHours", 10))
@@ -256,6 +271,38 @@ func _render() -> void:
_render_outcome()
func _render_connected_accounts(provider_values: Variant, available_values: Variant) -> void:
if connected_accounts_list == null:
return
_clear(connected_accounts_list)
var connected: Dictionary = {}
var available: Dictionary = {}
if typeof(provider_values) == TYPE_ARRAY:
for value in provider_values:
connected[str(value).to_lower()] = true
if typeof(available_values) == TYPE_ARRAY:
for value in available_values:
available[str(value).to_lower()] = true
for provider in [["steam", "Steam"], ["google", "Google"]]:
var provider_id := str(provider[0])
var provider_name := str(provider[1])
if not connected.has(provider_id) and not available.has(provider_id):
continue
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 10)
var copy := VBoxContainer.new()
copy.size_flags_horizontal = Control.SIZE_EXPAND_FILL
copy.add_child(_label(provider_name, 14, Brand.text_color(_dark()), true))
copy.add_child(_label("Connected" if connected.has(provider_id) else "Not connected", 12,
Brand.SUCCESS if connected.has(provider_id) else Brand.muted_color(_dark())))
row.add_child(copy)
if not connected.has(provider_id):
var connect := _button("Connect", "link", false)
connect.pressed.connect(AccountProfileService.connect_provider.bind(provider_id, provider_name))
row.add_child(connect)
connected_accounts_list.add_child(row)
func _render_systems(systems: Array = DashboardService.data.get("systems", [])) -> void:
if systems_list == null:
return