186 lines
7.6 KiB
GDScript
186 lines
7.6 KiB
GDScript
extends Control
|
|
## SocialPanel — Search, Requests, Friends, DM tabs.
|
|
## All UI nodes are defined in social_panel.tscn.
|
|
|
|
signal closed
|
|
|
|
# ─── Node references ─────────────────────────────────────────────────────────
|
|
@onready var _close_btn: Button = %CloseBtn
|
|
|
|
# Tab buttons
|
|
@onready var _search_tab_btn: Button = %SearchTabBtn
|
|
@onready var _friends_tab_btn: Button = %FriendsTabBtn
|
|
@onready var _dm_tab_btn: Button = get_node_or_null("%DMTabBtn")
|
|
|
|
|
|
# Views
|
|
@onready var _search_view: VBoxContainer = %SearchView
|
|
@onready var _friends_view: VBoxContainer = %FriendsView
|
|
@onready var _dm_view: PanelContainer = %DMView
|
|
|
|
|
|
# Search tab nodes
|
|
@onready var _search_input: LineEdit = %SearchInput
|
|
@onready var _search_btn: Button = %SearchBtn
|
|
@onready var _no_search_results: Label = %NoSearchResultsLabel
|
|
@onready var _search_results_list: GridContainer = %SearchResultsList
|
|
|
|
|
|
|
|
|
|
# Friends tab nodes
|
|
@onready var _no_friends_label: Label = %NoFriendsLabel
|
|
@onready var _friend_list: GridContainer = %FriendList
|
|
|
|
# DM tab nodes
|
|
@onready var _dm_back_btn: Button = %DMBackBtn
|
|
@onready var _dm_username_label: Label = %DMUsernameLabel
|
|
@onready var _dm_log: RichTextLabel = %DMLog
|
|
@onready var _dm_input: LineEdit = %DMInput
|
|
@onready var _dm_send_btn: Button = %DMSendBtn
|
|
|
|
# ─── State ───────────────────────────────────────────────────────────────────
|
|
var _current_tab: String = "search"
|
|
var _active_dm_user_id: String = ""
|
|
var _dm_history: Dictionary = {}
|
|
|
|
# ─── Lifecycle ───────────────────────────────────────────────────────────────
|
|
func _ready() -> void:
|
|
_close_btn.pressed.connect(func(): emit_signal("closed"); hide())
|
|
|
|
# Tab buttons
|
|
_search_tab_btn.pressed.connect(func(): _show_tab("search"))
|
|
_friends_tab_btn.pressed.connect(func(): _show_tab("friends"))
|
|
if _dm_tab_btn:
|
|
_dm_tab_btn.pressed.connect(func(): _show_tab("dm"))
|
|
_dm_back_btn.pressed.connect(func(): _show_tab("friends"))
|
|
|
|
|
|
# Search
|
|
_search_btn.pressed.connect(_on_search_pressed)
|
|
_search_input.text_submitted.connect(func(_t): _on_search_pressed())
|
|
|
|
# DM
|
|
_dm_send_btn.pressed.connect(_send_dm)
|
|
_dm_input.text_submitted.connect(func(_t): _send_dm())
|
|
|
|
# FriendManager signals
|
|
FriendManager.friends_updated.connect(_on_friends_updated)
|
|
FriendManager.dm_message_received.connect(_on_dm_received)
|
|
|
|
# Replay already-loaded friends in case FriendManager loaded before this panel was ready
|
|
if FriendManager.friends.size() > 0:
|
|
_on_friends_updated(FriendManager.friends)
|
|
else:
|
|
FriendManager.load_friends()
|
|
|
|
_show_tab("search")
|
|
|
|
# ─── Tab Switching ───────────────────────────────────────────────────────────
|
|
func _show_tab(tab: String) -> void:
|
|
_current_tab = tab
|
|
_search_view.visible = tab == "search"
|
|
_friends_view.visible = tab == "friends"
|
|
_dm_view.visible = tab == "dm"
|
|
|
|
# Auto-load search results on first open
|
|
if tab == "search" and _search_results_list.get_child_count() == 0:
|
|
_on_search_pressed()
|
|
|
|
# ─── Search Tab ──────────────────────────────────────────────────────────────
|
|
func _on_search_pressed() -> void:
|
|
var query := _search_input.text.strip_edges()
|
|
|
|
# UUID → add directly
|
|
if query.length() == 36 and query.count("-") == 4:
|
|
FriendManager.add_friend_by_id(query)
|
|
_search_input.text = ""
|
|
return
|
|
|
|
_search_btn.disabled = true
|
|
var payload = JSON.stringify({"query": query})
|
|
var result = await NakamaManager.client.rpc_async(NakamaManager.session, "search_users", payload)
|
|
_search_btn.disabled = false
|
|
|
|
if result.is_exception():
|
|
push_warning("[Social] Search failed: " + result.get_exception().message)
|
|
return
|
|
|
|
var response = JSON.parse_string(result.payload)
|
|
if not response or not response.has("users"):
|
|
return
|
|
|
|
_populate_search_results(response.users)
|
|
|
|
func _populate_search_results(users: Array) -> void:
|
|
for ch in _search_results_list.get_children():
|
|
ch.queue_free()
|
|
|
|
_no_search_results.visible = users.is_empty()
|
|
|
|
var my_id = NakamaManager.session.user_id if NakamaManager.session else ""
|
|
var friend_row_scene := preload("res://scenes/ui/friend_row.tscn")
|
|
|
|
for u in users:
|
|
if u.user_id == my_id:
|
|
continue # skip self
|
|
var row: Control = friend_row_scene.instantiate()
|
|
_search_results_list.add_child(row)
|
|
row.setup(u.user_id, u.username, -1, self)
|
|
|
|
|
|
|
|
# ─── Friends Tab ─────────────────────────────────────────────────────────────
|
|
func _populate_friends(friends_array: Array) -> void:
|
|
for ch in _friend_list.get_children():
|
|
ch.queue_free()
|
|
|
|
_no_friends_label.visible = friends_array.is_empty()
|
|
|
|
var friend_row_scene := preload("res://scenes/ui/friend_row.tscn")
|
|
for f in friends_array:
|
|
var row: Control = friend_row_scene.instantiate()
|
|
_friend_list.add_child(row)
|
|
row.setup(f.user_id, f.username, f.state, self)
|
|
|
|
# ─── FriendManager Callbacks ─────────────────────────────────────────────────
|
|
func _on_friends_updated(friends: Array) -> void:
|
|
print("[SocialPanel] _on_friends_updated: total=%d" % friends.size())
|
|
|
|
# Pass both incoming and mutual friends to the friends list
|
|
var display_list := friends.filter(func(f): return f.state == FriendManager.STATE_INVITE_IN or f.state == FriendManager.STATE_FRIEND)
|
|
|
|
# Also update the badge on the Friends tab if there are incoming requests
|
|
var incoming_count = friends.filter(func(f): return f.state == FriendManager.STATE_INVITE_IN).size()
|
|
if incoming_count > 0:
|
|
_friends_tab_btn.text = "FRIENDS (%d)" % incoming_count
|
|
else:
|
|
_friends_tab_btn.text = "FRIENDS"
|
|
|
|
_populate_friends(display_list)
|
|
|
|
signal dm_requested(user_id: String, username: String)
|
|
|
|
# ─── DM ──────────────────────────────────────────────────────────────────────
|
|
func open_dm(user_id: String, username: String) -> void:
|
|
emit_signal("dm_requested", user_id, username)
|
|
|
|
func _send_dm() -> void:
|
|
var text = _dm_input.text.strip_edges()
|
|
if text.is_empty() or _active_dm_user_id.is_empty():
|
|
return
|
|
_dm_input.text = ""
|
|
var sent = await FriendManager.send_dm(_active_dm_user_id, text)
|
|
if sent:
|
|
if not _dm_history.has(_active_dm_user_id):
|
|
_dm_history[_active_dm_user_id] = []
|
|
_dm_history[_active_dm_user_id].append({"from": "me", "msg": text})
|
|
_dm_log.append_text("[b]You:[/b] %s\n" % text)
|
|
|
|
func _on_dm_received(from_user_id: String, from_name: String, message: String) -> void:
|
|
if not _dm_history.has(from_user_id):
|
|
_dm_history[from_user_id] = []
|
|
_dm_history[from_user_id].append({"from": from_user_id, "msg": message})
|
|
if _active_dm_user_id == from_user_id and _current_tab == "dm":
|
|
_dm_log.append_text("[b]%s:[/b] %s\n" % [from_name, message])
|