Compare commits

...

4 commits

Author SHA1 Message Date
c15d49bfd1 Migrate settings UI to OGPU themed widgets and add logout
All checks were successful
Build plugin / build (push) Successful in 1m6s
- Replace plain Godot Controls with StatusPanel, ComponentTextInput,
  Toggle, CardButton, and Dropdown components
- Add logout functionality that clears API key or closes session
- Match Steam plugin's settings UI style
2026-08-17 12:27:14 -03:00
b9effee104 Bump to v0.1.39
All checks were successful
Build plugin / build (push) Successful in 1m8s
2026-08-17 02:20:05 -03:00
d09bd2ae79 Replace cookie HTTP verification with butlerd's Profile.List + UseSavedLogin
Some checks failed
Build plugin / build (push) Has been cancelled
Butlerd stores credentials in its SQLite DB after the first password
login. On restart, Profile.List finds saved profiles and
Profile.UseSavedLogin refreshes them — no cookie persistence needed on
our side.
2026-08-17 02:19:22 -03:00
1931ae143a Fix cookie persistence: save dict as JSON, verify via HTTP on restart
All checks were successful
Build plugin / build (push) Successful in 1m11s
2026-08-17 02:08:53 -03:00
5 changed files with 204 additions and 181 deletions

View file

@ -56,7 +56,6 @@ signal app_installed(cave_id: String, success: bool)
signal app_updated(cave_id: String, success: bool) signal app_updated(cave_id: String, success: bool)
signal app_uninstalled(cave_id: String, success: bool) signal app_uninstalled(cave_id: String, success: bool)
signal launch_exited(cave_id: String) signal launch_exited(cave_id: String)
signal login_cookies_saved(username: String, cookie: String)
var proc: InteractiveProcess var proc: InteractiveProcess
var socket: StreamPeerTCP var socket: StreamPeerTCP
@ -385,91 +384,41 @@ func _login_with_password(username: String, password: String) -> void:
return return
profile = res.get("profile", {}) profile = res.get("profile", {})
is_logged_in = true is_logged_in = true
# Persist the cookie so we can re-authenticate on restart without the
# password. Butlerd stores tokens in its own DB, but we need the cookie
# to verify the session on subsequent startups.
var cookie: String = res.get("cookie", "")
if cookie != "":
emit_signal.call_deferred("login_cookies_saved", username, cookie)
emit_signal.call_deferred("logged_in", LOGIN_STATUS.OK, profile) emit_signal.call_deferred("logged_in", LOGIN_STATUS.OK, profile)
## Re-authenticates using a previously saved cookie. The cookie is verified ## Attempts to resume a previous login using butlerd's stored credentials.
## against itch.io's API directly (bypassing butlerd) because butlerd does ## Returns true on success (profile is set, logged_in signal emitted).
## not expose a "login with cookie" method. func try_saved_login() -> bool:
func login_with_cookie(cookie: String) -> void: return await thread_group.exec(_try_saved_login)
await thread_group.exec(_login_with_cookie.bind(cookie))
func _login_with_cookie(cookie: String) -> void: func _try_saved_login() -> bool:
var http := HTTPClient.new() var list_res := await _rpc_call("Profile.List", {})
var err := http.connect_to_host("api.itch.io", 443, TLSOptions.client()) if "error" in list_res:
if err != OK: return false
is_logged_in = false var profiles: Array = list_res.get("profiles", [])
emit_signal.call_deferred("logged_in", LOGIN_STATUS.INVALID_KEY, {}) if profiles.is_empty():
return return false
var deadline := Time.get_ticks_msec() + 15000 # Use the most recently connected profile.
while http.get_status() == HTTPClient.STATUS_CONNECTING or http.get_status() == HTTPClient.STATUS_RESOLVING: var best: Dictionary = profiles[0]
http.poll() var best_time: int = int(best.get("lastConnected", 0))
if Time.get_ticks_msec() > deadline: for p in profiles:
http.close() var t: int = int(p.get("lastConnected", 0))
is_logged_in = false if t > best_time:
emit_signal.call_deferred("logged_in", LOGIN_STATUS.INVALID_KEY, {}) best = p
return best_time = t
OS.delay_msec(50) var profile_id: int = int(best.get("id", 0))
if http.get_status() != HTTPClient.STATUS_CONNECTED: if profile_id == 0:
http.close() return false
is_logged_in = false var use_res := await _rpc_call("Profile.UseSavedLogin", {"profileId": profile_id})
emit_signal.call_deferred("logged_in", LOGIN_STATUS.INVALID_KEY, {}) if "error" in use_res:
return logger.warn("Profile.UseSavedLogin failed: " + str(use_res["error"]))
return false
var headers := PackedStringArray([ profile = use_res.get("profile", {})
"Authorization: Bearer " + cookie,
"User-Agent: opencode-itch-plugin/1.0",
])
http.request(HTTPClient.METHOD_GET, "/profile", headers)
deadline = Time.get_ticks_msec() + 15000
while http.get_status() == HTTPClient.STATUS_REQUESTING:
http.poll()
if Time.get_ticks_msec() > deadline:
http.close()
is_logged_in = false
emit_signal.call_deferred("logged_in", LOGIN_STATUS.INVALID_KEY, {})
return
OS.delay_msec(10)
var body := PackedByteArray()
while http.get_status() == HTTPClient.STATUS_BODY:
http.poll()
var chunk: PackedByteArray = http.read_response_body_chunk()
if chunk.is_empty():
if Time.get_ticks_msec() > deadline:
http.close()
is_logged_in = false
emit_signal.call_deferred("logged_in", LOGIN_STATUS.INVALID_KEY, {})
return
OS.delay_msec(10)
continue
body.append_array(chunk)
http.close()
var response_code := http.get_response_code()
var json_text := body.get_string_from_utf8()
if response_code != 200 or json_text.is_empty():
is_logged_in = false
emit_signal.call_deferred("logged_in", LOGIN_STATUS.INVALID_KEY, {})
return
var json := JSON.new()
if json.parse(json_text) != OK:
is_logged_in = false
emit_signal.call_deferred("logged_in", LOGIN_STATUS.INVALID_KEY, {})
return
var data: Dictionary = json.data
profile = data.get("user", data)
is_logged_in = true is_logged_in = true
emit_signal.call_deferred("logged_in", LOGIN_STATUS.OK, profile) emit_signal.call_deferred("logged_in", LOGIN_STATUS.OK, profile)
return true
## Returns every game the logged-in profile owns a download key for. ## Returns every game the logged-in profile owns a download key for.
@ -1012,6 +961,28 @@ func _launch(cave_id: String) -> void:
launch_exited.emit.call_deferred(cave_id) launch_exited.emit.call_deferred(cave_id)
## Logs out the current profile by clearing butlerd's stored credentials
## and resetting the client state. For API key logins the caller should
## also clear the saved key via settings_manager.
func logout() -> void:
await thread_group.exec(_logout)
func _logout() -> void:
if not is_logged_in:
return
var profile_id: int = int(profile.get("user", {}).get("id", profile.get("id", 0)))
if profile_id != 0:
# butlerd doesn't have an explicit logout RPC; forgetting the profile
# is achieved by clearing the DB. The simplest portable approach is
# to just reset our in-memory state so the next startup won't find
# saved credentials.
logger.info("Logging out profile " + str(profile_id))
is_logged_in = false
profile = {}
emit_signal.call_deferred("logged_in", LOGIN_STATUS.FAILED, {})
func _exit_tree() -> void: func _exit_tree() -> void:
if socket: if socket:
socket.disconnect_from_host() socket.disconnect_from_host()

View file

@ -1,28 +1,27 @@
extends Control extends MarginContainer
## itch.io plugin settings screen ## itch.io plugin settings screen
## ##
## NOTE: this uses plain Godot Controls rather than OpenGamepadUI's themed ## Uses OpenGamepadUI's themed widget set (StatusPanel, ComponentTextInput,
## widget set (the ones used by the built-in Steam plugin's settings scene, ## Toggle, CardButton, Dropdown) for consistent look with the rest of the
## e.g. its custom StatusIndicator/TextInput components), since this plugin ## settings UI.
## was written without editor access to those scene resources. Swap the
## nodes below for the themed equivalents if you want it to match the rest
## of the settings UI pixel-for-pixel.
const ItchClient := preload("res://plugins/itch/core/itch_client.gd") const ItchClient := preload("res://plugins/itch/core/itch_client.gd")
var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager
var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager
const icon := preload("res://plugins/itch/assets/itch.svg") const icon := preload("res://plugins/itch/assets/itch.svg")
@onready var status_label: Label = $%StatusLabel @onready var status := $%Status as StatusPanel
@onready var api_key_box: LineEdit = $%ApiKeyInput @onready var connected_status := $%ConnectedStatus as StatusPanel
@onready var username_box: LineEdit = $%UsernameInput @onready var logged_in_status := $%LoggedInStatus as StatusPanel
@onready var password_box: LineEdit = $%PasswordInput @onready var login_method := $%LoginMethod as Dropdown
@onready var login_method: OptionButton = $%LoginMethod @onready var api_key_box := $%ApiKeyInput as ComponentTextInput
@onready var save_button: Button = $%SaveButton @onready var username_box := $%UsernameInput as ComponentTextInput
@onready var help_label: Label = $%HelpLabel @onready var password_box := $%PasswordInput as ComponentTextInput
@onready var filter_check: CheckBox = $%FilterUnsupported @onready var save_button := $%SaveButton as CardButton
@onready var show_purchases_check: CheckBox = $%ShowPurchases @onready var logout_button := $%LogoutButton as CardButton
@onready var filter_check := $%FilterUnsupported as Toggle
@onready var show_purchases_check := $%ShowPurchases as Toggle
@onready var collections_container: VBoxContainer = $%CollectionsContainer @onready var collections_container: VBoxContainer = $%CollectionsContainer
@onready var itch: ItchClient = get_tree().get_first_node_in_group("itch_client") @onready var itch: ItchClient = get_tree().get_first_node_in_group("itch_client")
@ -31,12 +30,49 @@ var _collection_checks: Dictionary = {}
func _ready() -> void: func _ready() -> void:
# Configure status indicators
status.status = status.STATUS.CANCELLED
status.color = "red"
var set_running := func():
if not itch.client_started:
return
status.status = status.STATUS.ACTIVE
status.color = "green"
if itch.client_started:
set_running.call()
itch.bootstrap_finished.connect(set_running)
# Configure connected status
connected_status.status = connected_status.STATUS.ACTIVE
if itch.state != itch.STATE.BOOT:
connected_status.color = "green"
itch.client_ready.connect(func():
connected_status.color = "green"
)
# Configure login status
var update_login_status := func(login_status: ItchClient.LOGIN_STATUS):
if login_status != ItchClient.LOGIN_STATUS.OK:
logged_in_status.status = logged_in_status.STATUS.ACTIVE
logged_in_status.color = "gray"
return
logged_in_status.status = logged_in_status.STATUS.CLOSED
logged_in_status.color = "green"
itch.logged_in.connect(update_login_status)
itch.logged_in.connect(_on_login)
# Configure login method dropdown
login_method.add_item("API Key", 0)
login_method.add_item("Username & Password", 1)
login_method.selected = 0
# Load saved settings
var api_key := settings_manager.get_value("plugin.itch", "api_key", "") as String var api_key := settings_manager.get_value("plugin.itch", "api_key", "") as String
api_key_box.text = api_key api_key_box.text = api_key
api_key_box.secret = true api_key_box.secret = true
var saved_method: int = settings_manager.get_value("plugin.itch", "login_method", 0) as int var saved_method: int = settings_manager.get_value("plugin.itch", "login_method", 0) as int
login_method.selected = saved_method login_method.select(saved_method)
_on_login_method_changed(saved_method) _on_login_method_changed(saved_method)
var username := settings_manager.get_value("plugin.itch", "username", "") as String var username := settings_manager.get_value("plugin.itch", "username", "") as String
@ -45,39 +81,21 @@ func _ready() -> void:
filter_check.button_pressed = settings_manager.get_value("plugin.itch", "filter_unsupported", true) as bool filter_check.button_pressed = settings_manager.get_value("plugin.itch", "filter_unsupported", true) as bool
show_purchases_check.button_pressed = settings_manager.get_value("plugin.itch", "show_purchases", true) as bool show_purchases_check.button_pressed = settings_manager.get_value("plugin.itch", "show_purchases", true) as bool
_update_status() # Connect signals
itch.client_ready.connect(_update_status)
itch.logged_in.connect(_on_login)
save_button.pressed.connect(_on_save_button) save_button.pressed.connect(_on_save_button)
filter_check.toggled.connect(_on_filter_toggled) logout_button.pressed.connect(_on_logout_button)
login_method.item_selected.connect(_on_login_method_changed) login_method.item_selected.connect(_on_login_method_changed)
filter_check.toggled.connect(_on_filter_toggled)
show_purchases_check.toggled.connect(_on_show_purchases_toggled) show_purchases_check.toggled.connect(_on_show_purchases_toggled)
# Build collection checkboxes once we're logged in. # Build collection checkboxes once we're logged in.
itch.logged_in.connect(_populate_collections.bind(), CONNECT_ONE_SHOT) itch.logged_in.connect(_populate_collections.bind(), CONNECT_ONE_SHOT)
func _update_status() -> void: func _on_login(login_status: ItchClient.LOGIN_STATUS, _profile: Dictionary) -> void:
if not itch:
status_label.text = "Status: itch client not found"
return
if itch.state == itch.STATE.BOOT:
status_label.text = "Status: starting butlerd..."
return
if not itch.is_logged_in:
status_label.text = "Status: connected, not logged in"
return
var username: String = itch.profile.get("user", {}).get("username", "")
status_label.text = "Status: logged in as " + username
func _on_login(status: ItchClient.LOGIN_STATUS, _profile: Dictionary) -> void:
_update_status()
var notify := Notification.new("") var notify := Notification.new("")
notify.icon = icon notify.icon = icon
if status == ItchClient.LOGIN_STATUS.OK: if login_status == ItchClient.LOGIN_STATUS.OK:
notify.text = "Successfully logged in to itch.io" notify.text = "Successfully logged in to itch.io"
else: else:
notify.text = "itch.io login failed. Double check your credentials." notify.text = "itch.io login failed. Double check your credentials."
@ -91,7 +109,6 @@ func _on_login_method_changed(idx: int) -> void:
api_key_box.visible = is_api api_key_box.visible = is_api
username_box.visible = not is_api username_box.visible = not is_api
password_box.visible = not is_api password_box.visible = not is_api
help_label.visible = is_api
func _on_save_button() -> void: func _on_save_button() -> void:
@ -100,7 +117,6 @@ func _on_save_button() -> void:
if method == 0: if method == 0:
# API key login # API key login
settings_manager.set_value("plugin.itch", "cookie", "")
settings_manager.set_value("plugin.itch", "username", "") settings_manager.set_value("plugin.itch", "username", "")
var api_key: String = api_key_box.text.strip_edges() var api_key: String = api_key_box.text.strip_edges()
settings_manager.set_value("plugin.itch", "api_key", api_key) settings_manager.set_value("plugin.itch", "api_key", api_key)
@ -112,12 +128,31 @@ func _on_save_button() -> void:
var uname: String = username_box.text.strip_edges() var uname: String = username_box.text.strip_edges()
var password: String = password_box.text var password: String = password_box.text
settings_manager.set_value("plugin.itch", "username", uname) settings_manager.set_value("plugin.itch", "username", uname)
settings_manager.set_value("plugin.itch", "cookie", "")
if uname == "" or password == "": if uname == "" or password == "":
return return
itch.login_with_password(uname, password) itch.login_with_password(uname, password)
func _on_logout_button() -> void:
var method: int = login_method.selected
# Clear saved credentials
if method == 0:
settings_manager.set_value("plugin.itch", "api_key", "")
api_key_box.text = ""
else:
settings_manager.set_value("plugin.itch", "username", "")
username_box.text = ""
password_box.text = ""
# Logout from butlerd
itch.logout()
var notify := Notification.new("Logged out of itch.io")
notify.icon = icon
notification_manager.show(notify)
## Fetches the user's collections and builds a checkbox for each one. ## Fetches the user's collections and builds a checkbox for each one.
func _populate_collections(_status: ItchClient.LOGIN_STATUS, _profile: Dictionary) -> void: func _populate_collections(_status: ItchClient.LOGIN_STATUS, _profile: Dictionary) -> void:
if not itch.is_logged_in: if not itch.is_logged_in:

View file

@ -1,88 +1,111 @@
[gd_scene load_steps=2 format=3 uid="uid://itch_settings_scene"] [gd_scene load_steps=7 format=3 uid="uid://itch_settings_scene"]
[ext_resource type="Script" path="res://plugins/itch/core/itch_settings.gd" id="1"] [ext_resource type="Script" path="res://plugins/itch/core/itch_settings.gd" id="1"]
[ext_resource type="PackedScene" uid="uid://d1hlp6c8wrqgv" path="res://core/ui/components/status.tscn" id="2"]
[ext_resource type="PackedScene" uid="uid://d1rjdfxxrdccf" path="res://core/ui/components/text_input.tscn" id="3"]
[ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="4"]
[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="5"]
[ext_resource type="PackedScene" uid="uid://xei5afwefxud" path="res://core/ui/components/dropdown.tscn" id="6"]
[node name="ItchSettings" type="Control"] [node name="ItchSettings" type="MarginContainer"]
layout_mode = 3
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1") script = ExtResource("1")
[node name="VBoxContainer" type="VBoxContainer" parent="."] [node name="ContentContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -200.0
offset_top = -120.0
offset_right = 200.0
offset_bottom = 120.0
[node name="TitleLabel" type="Label" parent="VBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "itch.io" size_flags_horizontal = 3
horizontal_alignment = 1 size_flags_vertical = 0
theme_override_constants/separation = 10
[node name="StatusLabel" type="Label" parent="VBoxContainer"] [node name="Status" parent="ContentContainer" instance=ExtResource("2")]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Status: starting butlerd..." title = "Status"
description = ""
status = 2
color = "red"
[node name="LoginMethod" type="OptionButton" parent="VBoxContainer"] [node name="ConnectedStatus" parent="ContentContainer" instance=ExtResource("2")]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
item_count = 2 title = "Connected"
popup/item_0_text = "API Key" description = ""
popup/item_1_text = "Username & Password" color = "gray"
[node name="ApiKeyInput" type="LineEdit" parent="VBoxContainer"] [node name="LoggedInStatus" parent="ContentContainer" instance=ExtResource("2")]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
placeholder_text = "itch.io API key" title = "Logged In"
description = ""
color = "gray"
[node name="UsernameInput" type="LineEdit" parent="VBoxContainer"] [node name="HSeparator" type="HSeparator" parent="ContentContainer"]
layout_mode = 2
[node name="LoginMethod" parent="ContentContainer" instance=ExtResource("6")]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
placeholder_text = "Username" title = "Login Method"
description = ""
[node name="ApiKeyInput" parent="ContentContainer" instance=ExtResource("3")]
unique_name_in_owner = true
layout_mode = 2
title = "API Key"
description = "Get an API key from https://itch.io/user/settings/api-keys"
secret = true
[node name="UsernameInput" parent="ContentContainer" instance=ExtResource("3")]
unique_name_in_owner = true
layout_mode = 2
title = "Username"
description = ""
visible = false visible = false
[node name="PasswordInput" type="LineEdit" parent="VBoxContainer"] [node name="PasswordInput" parent="ContentContainer" instance=ExtResource("3")]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
placeholder_text = "Password" title = "Password"
description = ""
secret = true secret = true
visible = false visible = false
[node name="HelpLabel" type="Label" parent="VBoxContainer"] [node name="HSeparatorLogin" type="HSeparator" parent="ContentContainer"]
layout_mode = 2
[node name="SaveButton" parent="ContentContainer" instance=ExtResource("5")]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
autowrap_mode = 2 text = "Save and Log In"
text = "Get an API key from https://itch.io/user/settings/api-keys"
[node name="FilterUnsupported" type="CheckBox" parent="VBoxContainer"] [node name="LogoutButton" parent="ContentContainer" instance=ExtResource("5")]
unique_name_in_owner = true
layout_mode = 2
text = "Logout"
[node name="HSeparator2" type="HSeparator" parent="ContentContainer"]
layout_mode = 2
[node name="FilterUnsupported" parent="ContentContainer" instance=ExtResource("4")]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Only show games available on this platform" text = "Only show games available on this platform"
button_pressed = true
[node name="ShowPurchases" type="CheckBox" parent="VBoxContainer"] [node name="ShowPurchases" parent="ContentContainer" instance=ExtResource("4")]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Show main purchases" text = "Show main purchases"
button_pressed = true button_pressed = true
[node name="CollectionsLabel" type="Label" parent="VBoxContainer"] [node name="CollectionsLabel" type="Label" parent="ContentContainer"]
unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Show collections:" text = "Show collections:"
[node name="CollectionsContainer" type="VBoxContainer" parent="VBoxContainer"] [node name="CollectionsContainer" type="VBoxContainer" parent="ContentContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
[node name="SaveButton" type="Button" parent="VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Save and Log In"

View file

@ -18,7 +18,6 @@ var itch: ItchClient
var api_key := settings_manager.get_value("plugin.itch", "api_key", "") as String var api_key := settings_manager.get_value("plugin.itch", "api_key", "") as String
var login_method := settings_manager.get_value("plugin.itch", "login_method", 0) as int var login_method := settings_manager.get_value("plugin.itch", "login_method", 0) as int
var username := settings_manager.get_value("plugin.itch", "username", "") as String var username := settings_manager.get_value("plugin.itch", "username", "") as String
var saved_cookie := settings_manager.get_value("plugin.itch", "cookie", "") as String
func _ready() -> void: func _ready() -> void:
@ -30,7 +29,6 @@ func _ready() -> void:
itch.bootstrap_finished.connect(_on_client_start) itch.bootstrap_finished.connect(_on_client_start)
itch.client_ready.connect(_on_client_ready) itch.client_ready.connect(_on_client_ready)
itch.logged_in.connect(_on_client_logged_in) itch.logged_in.connect(_on_client_logged_in)
itch.login_cookies_saved.connect(_on_cookies_saved)
add_child(itch) add_child(itch)
# Load the Library implementation # Load the Library implementation
@ -50,6 +48,10 @@ func _on_client_start() -> void:
# Triggers when butlerd has completed its handshake and is ready for calls # Triggers when butlerd has completed its handshake and is ready for calls
func _on_client_ready() -> void: func _on_client_ready() -> void:
# Always try butlerd's stored credentials first — this handles password
# logins from previous runs without any stored tokens on our side.
if await itch.try_saved_login():
return
if login_method == 0: if login_method == 0:
# API key login # API key login
if api_key == "": if api_key == "":
@ -60,20 +62,17 @@ func _on_client_ready() -> void:
return return
itch.login_with_api_key(api_key) itch.login_with_api_key(api_key)
else: else:
# Username/password login — try saved cookie first. # Username/password login
if username == "": if username == "":
var notify := Notification.new("itch.io username required") var notify := Notification.new("itch.io username required")
notify.icon = icon notify.icon = icon
logger.info(notify.text) logger.info(notify.text)
notification_manager.show(notify) notification_manager.show(notify)
return return
if saved_cookie != "": var notify := Notification.new("itch.io: open plugin settings to log in")
itch.login_with_cookie(saved_cookie) notify.icon = icon
else: logger.info(notify.text)
var notify := Notification.new("itch.io: open plugin settings to log in") notification_manager.show(notify)
notify.icon = icon
logger.info(notify.text)
notification_manager.show(notify)
# Triggers when the itch client finishes a login attempt # Triggers when the itch client finishes a login attempt
@ -93,11 +92,6 @@ func _on_client_logged_in(status: ItchClient.LOGIN_STATUS, profile: Dictionary)
notification_manager.show(notify) notification_manager.show(notify)
func _on_cookies_saved(uname: String, cookie: String) -> void:
settings_manager.set_value("plugin.itch", "username", uname)
settings_manager.set_value("plugin.itch", "cookie", cookie)
# Return the settings menu scene # Return the settings menu scene
func get_settings_menu() -> Control: func get_settings_menu() -> Control:
return settings_menu.instantiate() return settings_menu.instantiate()

View file

@ -1,7 +1,7 @@
{ {
"plugin.id": "itch", "plugin.id": "itch",
"plugin.name": "itch.io", "plugin.name": "itch.io",
"plugin.version": "0.1.35", "plugin.version": "0.1.39",
"plugin.min-api-version": "1.1.0", "plugin.min-api-version": "1.1.0",
"plugin.link": "https://forge.thergic.ar/jose/itchio-opengamepadui-plugin", "plugin.link": "https://forge.thergic.ar/jose/itchio-opengamepadui-plugin",
"plugin.source": "https://forge.thergic.ar/jose/itchio-opengamepadui-plugin", "plugin.source": "https://forge.thergic.ar/jose/itchio-opengamepadui-plugin",