feat(mvp): complete M13 clean-up milestone (real usernames, avatar studio redesign, pause menu, audio manager)
checkpoint / checkpoint (push) Successful in 6m22s

This commit is contained in:
2026-09-18 21:54:01 +00:00
parent 4c8a60fbc8
commit ee4006d217
31 changed files with 2394 additions and 262 deletions
+64 -10
View File
@@ -39,6 +39,10 @@ extends Node
var _access_token: String = ""
var _token_type: String = "Bearer"
## Authenticated user identity
var current_user_id: int = 0
var current_username: String = ""
## Full Authorization header value, e.g. "Bearer eyJ...". Empty if no token.
var bearer_token: String:
get:
@@ -61,6 +65,8 @@ func set_token(access_token: String, token_type: String = "Bearer") -> void:
func clear_token() -> void:
_access_token = ""
_token_type = "Bearer"
current_user_id = 0
current_username = ""
# ---------------------------------------------------------------------------
@@ -209,6 +215,12 @@ func login(username: String, password: String) -> Dictionary:
false
)
_store_token_from_result(result)
if result.ok:
current_username = username
var me := await get_me()
if me.ok and me.data is Dictionary:
current_user_id = int((me.data as Dictionary).get("id", 0))
current_username = str((me.data as Dictionary).get("username", username))
return result
@@ -246,6 +258,12 @@ func register(
false
)
_store_token_from_result(result)
if result.ok:
current_username = username
var me := await get_me()
if me.ok and me.data is Dictionary:
current_user_id = int((me.data as Dictionary).get("id", 0))
current_username = str((me.data as Dictionary).get("username", username))
return result
@@ -282,7 +300,23 @@ func health_check() -> Dictionary:
## Fetch the current user's profile from the stored JWT (requires auth).
func get_me() -> Dictionary:
return await request(HTTPClient.METHOD_GET, "/api/users/me", "", "application/json", true)
var result := await request(HTTPClient.METHOD_GET, "/api/users/me", "", "application/json", true)
if result.ok and result.data is Dictionary:
current_user_id = int((result.data as Dictionary).get("id", 0))
current_username = str((result.data as Dictionary).get("username", current_username))
return result
## Search active users by username substring (requires auth).
func search_users(query: String, limit: int = 20) -> Dictionary:
var path := "/api/users/search?q=%s&limit=%d" % [query.uri_encode(), limit]
return await request(HTTPClient.METHOD_GET, path, "", "application/json", true)
## Get active user summary by exact username (requires auth).
func get_user_by_username(username: String) -> Dictionary:
var path := "/api/users/by-username/%s" % username.strip_edges().uri_encode()
return await request(HTTPClient.METHOD_GET, path, "", "application/json", true)
## Fetch the current user's saved avatar loadout (requires auth).
@@ -312,9 +346,19 @@ func save_avatar_loadout(loadout: Dictionary) -> Dictionary:
# Social Features: Friends, Follow & Block
# ---------------------------------------------------------------------------
## Send a friend request to target user_id.
func send_friend_request(user_id: int) -> Dictionary:
return await SocialApiClient.send_friend_request(self, user_id)
## Send a friend request to target user_id (int) or username (String).
func send_friend_request(target: Variant) -> Dictionary:
var payload: Dictionary = {}
if target is int:
payload = {"user_id": target}
elif target is String:
if (target as String).is_valid_int():
payload = {"user_id": int(target)}
else:
payload = {"username": (target as String).strip_edges()}
else:
payload = {"user_id": int(target)}
return await request(HTTPClient.METHOD_POST, "/api/friends/requests", JSON.stringify(payload), "application/json", true)
## Get pending incoming friend requests.
func get_friend_requests() -> Dictionary:
@@ -336,9 +380,14 @@ func unfriend(user_id: int) -> Dictionary:
func get_friends() -> Dictionary:
return await SocialApiClient.get_friends(self)
## Follow a user.
func follow_user(user_id: int) -> Dictionary:
return await SocialApiClient.follow_user(self, user_id)
## Follow a user by ID or username.
func follow_user(target: Variant) -> Dictionary:
if target is String and not (target as String).is_valid_int():
var lookup := await get_user_by_username((target as String).strip_edges())
if not lookup.ok:
return lookup
target = int(lookup.data["id"])
return await request(HTTPClient.METHOD_POST, "/api/friends/%d/follow" % int(target), "", "application/json", true)
## Unfollow a user.
func unfollow_user(user_id: int) -> Dictionary:
@@ -348,9 +397,14 @@ func unfollow_user(user_id: int) -> Dictionary:
func get_following() -> Dictionary:
return await SocialApiClient.get_following(self)
## Block a user.
func block_user(user_id: int) -> Dictionary:
return await SocialApiClient.block_user(self, user_id)
## Block a user by ID or username.
func block_user(target: Variant) -> Dictionary:
if target is String and not (target as String).is_valid_int():
var lookup := await get_user_by_username((target as String).strip_edges())
if not lookup.ok:
return lookup
target = int(lookup.data["id"])
return await request(HTTPClient.METHOD_POST, "/api/friends/%d/block" % int(target), "", "application/json", true)
## Unblock a user.
func unblock_user(user_id: int) -> Dictionary: