feat: 2.3.1
This commit is contained in:
@@ -36,6 +36,17 @@ signal closed
|
||||
var _daily_reward_config_data: Dictionary = {}
|
||||
var _current_dr_month: String = ""
|
||||
|
||||
# Tab: Announcements
|
||||
@onready var target_user_edit := %TargetUserEdit as LineEdit
|
||||
@onready var title_edit := %TitleEdit as LineEdit
|
||||
@onready var content_edit := %ContentEdit as TextEdit
|
||||
@onready var start_date_edit := %StartDatePicker as Button
|
||||
@onready var end_date_edit := %EndDatePicker as Button
|
||||
@onready var add_reward_btn := %AddRewardBtn as Button
|
||||
@onready var rewards_list := %RewardsList as VBoxContainer
|
||||
@onready var reward_row_template := %RewardRowTemplate as HBoxContainer
|
||||
@onready var send_mail_btn := %SendMailBtn as Button
|
||||
|
||||
const MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
||||
|
||||
# -- Data --
|
||||
@@ -151,6 +162,10 @@ func _connect_signals() -> void:
|
||||
load_dr_btn.pressed.connect(_load_daily_rewards_config)
|
||||
save_dr_btn.pressed.connect(_save_daily_rewards_config)
|
||||
month_option_btn.item_selected.connect(_on_dr_month_selected)
|
||||
|
||||
# Announcement actions
|
||||
send_mail_btn.pressed.connect(_on_send_mail)
|
||||
add_reward_btn.pressed.connect(_on_add_reward_pressed)
|
||||
|
||||
# =============================================================================
|
||||
# Core Panel Logic
|
||||
@@ -623,3 +638,67 @@ func _save_daily_rewards_config() -> void:
|
||||
_set_status("Save failed: " + res.get("error"), CLR_STATUS_ERR)
|
||||
else:
|
||||
_set_status("Config saved successfully!", CLR_STATUS_OK)
|
||||
|
||||
# =============================================================================
|
||||
# TAB 4: ANNOUNCEMENTS
|
||||
# =============================================================================
|
||||
func _on_add_reward_pressed() -> void:
|
||||
var row = reward_row_template.duplicate()
|
||||
row.visible = true
|
||||
rewards_list.add_child(row)
|
||||
var remove_btn = row.get_node("RemoveBtn") as Button
|
||||
remove_btn.pressed.connect(func(): row.queue_free())
|
||||
|
||||
func _on_send_mail() -> void:
|
||||
var target = target_user_edit.text.strip_edges()
|
||||
var title = title_edit.text.strip_edges()
|
||||
var content = content_edit.text.strip_edges()
|
||||
var start_date = start_date_edit.get_date_iso()
|
||||
var end_date = end_date_edit.get_date_iso()
|
||||
|
||||
if title.is_empty() or content.is_empty():
|
||||
_set_status("Title and content cannot be empty", CLR_STATUS_ERR)
|
||||
return
|
||||
|
||||
var rewards_arr = []
|
||||
for child in rewards_list.get_children():
|
||||
if not child.visible: continue # skip template
|
||||
var type_opt = child.get_node("TypeOption") as OptionButton
|
||||
var id_edit = child.get_node("IdEdit") as LineEdit
|
||||
var amount_spin = child.get_node("AmountSpin") as SpinBox
|
||||
|
||||
var type_str = type_opt.get_item_text(type_opt.selected)
|
||||
var r_id = id_edit.text.strip_edges()
|
||||
rewards_arr.append({
|
||||
"type": type_str,
|
||||
"id": r_id,
|
||||
"amount": int(amount_spin.value)
|
||||
})
|
||||
|
||||
send_mail_btn.disabled = true
|
||||
_set_status("Sending mail...")
|
||||
|
||||
var payload = {
|
||||
"target_user_id": target,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"start_date": start_date,
|
||||
"end_date": end_date,
|
||||
"rewards": rewards_arr
|
||||
}
|
||||
|
||||
var res = await _rpc("admin_send_mail", payload)
|
||||
send_mail_btn.disabled = false
|
||||
|
||||
if res.has("error"):
|
||||
_set_status("Failed to send mail: " + res.get("error", "Unknown"), CLR_STATUS_ERR)
|
||||
else:
|
||||
_set_status("Mail sent successfully!", CLR_STATUS_OK)
|
||||
title_edit.text = ""
|
||||
content_edit.text = ""
|
||||
if start_date_edit.has_method("clear_date"):
|
||||
start_date_edit.clear_date()
|
||||
if end_date_edit.has_method("clear_date"):
|
||||
end_date_edit.clear_date()
|
||||
for child in rewards_list.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
|
||||
@@ -6,13 +6,15 @@ extends Control
|
||||
@onready var progress_bar := %ProgressBar as ProgressBar
|
||||
@onready var progress_label := %ProgressLabel as Label
|
||||
@onready var button_container := %ButtonContainer as HBoxContainer
|
||||
@onready var update_button := %UpdateButton as Button
|
||||
@onready var skip_button := %SkipButton as Button
|
||||
@onready var update_button := %UpdateButton as BaseButton
|
||||
@onready var update_button_label := %UpdateButton.get_node("Label") as Label
|
||||
@onready var skip_button := %SkipButton as BaseButton
|
||||
@onready var skip_button_label := %SkipButton.get_node("Label") as Label
|
||||
|
||||
@onready var changelog_panel := %ChangelogPanel
|
||||
@onready var changelog_richtext := %ChangelogRichText as RichTextLabel
|
||||
@onready var prev_button := %PrevButton as Button
|
||||
@onready var next_button := %NextButton as Button
|
||||
@onready var prev_button := %PrevButton as BaseButton
|
||||
@onready var next_button := %NextButton as BaseButton
|
||||
@onready var page_label := %PageLabel as Label
|
||||
|
||||
var update_manager: Node
|
||||
@@ -73,20 +75,20 @@ func _on_update_check_completed(has_update: bool, info: Dictionary) -> void:
|
||||
button_container.visible = true
|
||||
update_button.visible = true
|
||||
skip_button.visible = true
|
||||
skip_button.text = "Play without updating"
|
||||
skip_button_label.text = "Play without updating"
|
||||
else:
|
||||
status_label.text = "Game up to date."
|
||||
button_container.visible = true
|
||||
update_button.visible = false
|
||||
skip_button.visible = true
|
||||
skip_button.text = "Play Game"
|
||||
skip_button_label.text = "Play"
|
||||
|
||||
func _on_update_check_failed(_error: String) -> void:
|
||||
status_label.text = "Could not check for updates"
|
||||
button_container.visible = true
|
||||
update_button.visible = false
|
||||
skip_button.visible = true
|
||||
skip_button.text = "Play Offline"
|
||||
skip_button_label.text = "Play"
|
||||
|
||||
func _on_update_pressed() -> void:
|
||||
button_container.visible = false
|
||||
@@ -114,7 +116,7 @@ func _on_download_failed(error: String) -> void:
|
||||
progress_bar.visible = false
|
||||
progress_label.visible = false
|
||||
button_container.visible = true
|
||||
update_button.text = "Retry"
|
||||
update_button_label.text = "Retry"
|
||||
|
||||
func _begin_resource_load() -> void:
|
||||
button_container.visible = false
|
||||
@@ -156,8 +158,10 @@ func _update_pagination_ui():
|
||||
var txt: String = "[font_size=20][b]Version " + str(entry.get("version", "Unknown")) + "[/b][/font_size]\n[color=gray]" + str(entry.get("date", "")) + "[/color]\n\n"
|
||||
|
||||
var changes: Array = entry.get("changelog", [])
|
||||
txt += "[table=2]"
|
||||
for change in changes:
|
||||
txt += "• " + change + "\n"
|
||||
txt += "[cell]• [/cell][cell]" + change + "[/cell]"
|
||||
txt += "[/table]"
|
||||
|
||||
changelog_richtext.text = txt
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
extends Button
|
||||
|
||||
signal date_selected(date_str: String)
|
||||
|
||||
@onready var popup = $PopupPanel
|
||||
@onready var month_year_lbl = $PopupPanel/VBox/Header/MonthYearLbl
|
||||
@onready var days_grid = $PopupPanel/VBox/DaysGrid
|
||||
@onready var prev_btn = $PopupPanel/VBox/Header/PrevBtn
|
||||
@onready var next_btn = $PopupPanel/VBox/Header/NextBtn
|
||||
@onready var clear_btn = $PopupPanel/VBox/ClearBtn
|
||||
|
||||
var current_date: Dictionary
|
||||
var view_date: Dictionary
|
||||
|
||||
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
||||
|
||||
func _ready() -> void:
|
||||
var now = Time.get_datetime_dict_from_system()
|
||||
view_date = {"year": now.year, "month": now.month, "day": now.day}
|
||||
|
||||
pressed.connect(_on_button_pressed)
|
||||
prev_btn.pressed.connect(_on_prev_month)
|
||||
next_btn.pressed.connect(_on_next_month)
|
||||
clear_btn.pressed.connect(_on_clear_pressed)
|
||||
|
||||
_update_calendar()
|
||||
|
||||
func _on_button_pressed() -> void:
|
||||
_update_calendar()
|
||||
popup.popup_centered()
|
||||
|
||||
func _on_prev_month() -> void:
|
||||
view_date.month -= 1
|
||||
if view_date.month < 1:
|
||||
view_date.month = 12
|
||||
view_date.year -= 1
|
||||
_update_calendar()
|
||||
|
||||
func _on_next_month() -> void:
|
||||
view_date.month += 1
|
||||
if view_date.month > 12:
|
||||
view_date.month = 1
|
||||
view_date.year += 1
|
||||
_update_calendar()
|
||||
|
||||
func _on_clear_pressed() -> void:
|
||||
clear_date()
|
||||
|
||||
func clear_date() -> void:
|
||||
current_date = {}
|
||||
text = "Select Date..."
|
||||
emit_signal("date_selected", "")
|
||||
popup.hide()
|
||||
|
||||
func _update_calendar() -> void:
|
||||
month_year_lbl.text = str(MONTHS[view_date.month - 1]) + " " + str(view_date.year)
|
||||
|
||||
for child in days_grid.get_children():
|
||||
child.queue_free()
|
||||
|
||||
var first_day_dict = {"year": view_date.year, "month": view_date.month, "day": 1}
|
||||
var first_day_unix = Time.get_unix_time_from_datetime_dict(first_day_dict)
|
||||
var first_day_weekday = Time.get_datetime_dict_from_unix_time(first_day_unix).weekday
|
||||
|
||||
# Godot weekday: 0 = Sunday, 1 = Monday...
|
||||
for i in range(first_day_weekday):
|
||||
var empty = Control.new()
|
||||
days_grid.add_child(empty)
|
||||
|
||||
var days_in_month = _get_days_in_month(view_date.month, view_date.year)
|
||||
|
||||
for d in range(1, days_in_month + 1):
|
||||
var btn = Button.new()
|
||||
btn.text = str(d)
|
||||
btn.custom_minimum_size = Vector2(32, 32)
|
||||
if not current_date.is_empty() and current_date.year == view_date.year and current_date.month == view_date.month and current_date.day == d:
|
||||
btn.add_theme_color_override("font_color", Color.YELLOW)
|
||||
btn.pressed.connect(_on_day_clicked.bind(d))
|
||||
days_grid.add_child(btn)
|
||||
|
||||
func _on_day_clicked(day: int) -> void:
|
||||
current_date = {"year": view_date.year, "month": view_date.month, "day": day}
|
||||
var date_str = "%04d-%02d-%02dT00:00:00Z" % [current_date.year, current_date.month, current_date.day]
|
||||
text = "%04d-%02d-%02d" % [current_date.year, current_date.month, current_date.day]
|
||||
emit_signal("date_selected", date_str)
|
||||
popup.hide()
|
||||
|
||||
func get_date_iso() -> String:
|
||||
if current_date.is_empty():
|
||||
return ""
|
||||
return "%04d-%02d-%02dT00:00:00Z" % [current_date.year, current_date.month, current_date.day]
|
||||
|
||||
func _get_days_in_month(month: int, year: int) -> int:
|
||||
if month in [1, 3, 5, 7, 8, 10, 12]: return 31
|
||||
if month in [4, 6, 9, 11]: return 30
|
||||
if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0): return 29
|
||||
return 28
|
||||
@@ -0,0 +1 @@
|
||||
uid://0rwrfgvuqgrb
|
||||
+42
-10
@@ -1,12 +1,12 @@
|
||||
extends HBoxContainer
|
||||
extends PanelContainer
|
||||
## FriendRow — Single row in the friends list inside SocialPanel.
|
||||
|
||||
@onready var _name_label: Label = %NameLabel
|
||||
@onready var _state_label: Label = %StateLabel
|
||||
@onready var _dm_btn: Button = %DMBtn
|
||||
@onready var _accept_btn: Button = %AcceptBtn
|
||||
@onready var _decline_btn: Button = %DeclineBtn
|
||||
@onready var _remove_btn: Button = %RemoveBtn
|
||||
@onready var _dm_btn: TextureButton = %DMBtn
|
||||
@onready var _accept_btn: TextureButton = %AcceptBtn
|
||||
@onready var _decline_btn: TextureButton = %DeclineBtn
|
||||
@onready var _remove_btn: TextureButton = %RemoveBtn
|
||||
|
||||
var _user_id: String
|
||||
var _username: String
|
||||
@@ -19,17 +19,49 @@ func setup(uid: String, uname: String, state: int, panel: Control) -> void:
|
||||
|
||||
_name_label.text = uname
|
||||
|
||||
_dm_btn.visible = false
|
||||
_accept_btn.visible = false
|
||||
_decline_btn.visible = false
|
||||
_remove_btn.visible = false
|
||||
_state_label.visible = false
|
||||
|
||||
match state:
|
||||
-1: # Search result (Not friend)
|
||||
_accept_btn.visible = true
|
||||
if not _accept_btn.pressed.is_connected(_on_accept):
|
||||
_accept_btn.pressed.connect(_on_accept)
|
||||
FriendManager.STATE_FRIEND:
|
||||
_dm_btn.visible = true
|
||||
_state_label.visible = true
|
||||
_dm_btn.visible = true
|
||||
_remove_btn.visible = true
|
||||
_dm_btn.pressed.connect(func(): panel.call("open_dm", uid, uname))
|
||||
_remove_btn.pressed.connect(func(): FriendManager.remove_friend(uid))
|
||||
if not _dm_btn.pressed.is_connected(_on_dm):
|
||||
_dm_btn.pressed.connect(_on_dm)
|
||||
if not _remove_btn.pressed.is_connected(_on_remove):
|
||||
_remove_btn.pressed.connect(_on_remove)
|
||||
FriendManager.STATE_INVITE_OUT:
|
||||
_state_label.text = "(invite sent)"
|
||||
_state_label.visible = true
|
||||
FriendManager.STATE_INVITE_IN:
|
||||
_state_label.text = "Incoming Request"
|
||||
_state_label.visible = true
|
||||
_accept_btn.visible = true
|
||||
_decline_btn.visible = true
|
||||
_accept_btn.pressed.connect(func(): FriendManager.add_friend_by_id(uid))
|
||||
_decline_btn.pressed.connect(func(): FriendManager.remove_friend(uid))
|
||||
if not _accept_btn.pressed.is_connected(_on_accept):
|
||||
_accept_btn.pressed.connect(_on_accept)
|
||||
if not _decline_btn.pressed.is_connected(_on_remove):
|
||||
_decline_btn.pressed.connect(_on_remove)
|
||||
|
||||
func _on_dm():
|
||||
_panel.call("open_dm", _user_id, _username)
|
||||
|
||||
func _on_remove():
|
||||
FriendManager.remove_friend(_user_id)
|
||||
queue_free()
|
||||
|
||||
func _on_accept():
|
||||
FriendManager.add_friend_by_id(_user_id)
|
||||
_accept_btn.visible = false
|
||||
_decline_btn.visible = false
|
||||
_dm_btn.visible = true
|
||||
_remove_btn.visible = true
|
||||
_state_label.text = "LAST ONLINE: Just now"
|
||||
|
||||
@@ -27,6 +27,10 @@ extends Control
|
||||
@onready var register_button := %RegisterButton as Button
|
||||
@onready var reg_status_label := %RegStatusLabel as Label
|
||||
|
||||
# Tab navigation buttons (left column)
|
||||
@onready var sign_in_tab_btn := %SignInTabBtn as TextureButton
|
||||
@onready var sign_up_tab_btn := %SignUpTabBtn as TextureButton
|
||||
|
||||
var current_captcha_answer: int = 0
|
||||
|
||||
var is_loading: bool = false
|
||||
@@ -87,6 +91,10 @@ func _connect_signals() -> void:
|
||||
lan_host_btn.pressed.connect(_on_lan_host_pressed)
|
||||
lan_join_btn.pressed.connect(func(): _on_lan_join_pressed(lan_ip.text))
|
||||
|
||||
# Tab navigation buttons
|
||||
sign_in_tab_btn.pressed.connect(func(): _switch_tab(0))
|
||||
sign_up_tab_btn.pressed.connect(func(): _switch_tab(1))
|
||||
|
||||
# Enter key to submit
|
||||
password_input.text_submitted.connect(func(_t): _on_login_pressed())
|
||||
reg_confirm_password_input.text_submitted.connect(func(_t): _on_register_pressed())
|
||||
@@ -95,7 +103,7 @@ func _setup_ui() -> void:
|
||||
status_label.text = ""
|
||||
reg_status_label.text = ""
|
||||
loading_spinner.visible = false
|
||||
tab_container.current_tab = 0
|
||||
_switch_tab(0)
|
||||
|
||||
# Hide social buttons on platforms where they're not supported
|
||||
_configure_social_buttons()
|
||||
@@ -118,6 +126,15 @@ func _configure_social_buttons() -> void:
|
||||
# Panel Switching
|
||||
# =============================================================================
|
||||
|
||||
func _switch_tab(tab: int) -> void:
|
||||
tab_container.current_tab = tab
|
||||
_update_tab_visuals(tab)
|
||||
_on_tab_changed(tab)
|
||||
|
||||
func _update_tab_visuals(tab: int) -> void:
|
||||
sign_in_tab_btn.button_pressed = (tab == 0)
|
||||
sign_up_tab_btn.button_pressed = (tab == 1)
|
||||
|
||||
func _on_tab_changed(tab: int) -> void:
|
||||
if tab == 0:
|
||||
status_label.text = ""
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
extends Panel
|
||||
|
||||
signal closed
|
||||
|
||||
@onready var close_btn := %CloseBtn as Button
|
||||
@onready var mail_list_vbox := %MailListVBox as VBoxContainer
|
||||
@onready var mail_title_lbl := %MailTitleLbl as Label
|
||||
@onready var mail_content_text := %MailContentText as RichTextLabel
|
||||
@onready var sender_lbl := %SenderLbl as Label
|
||||
@onready var dynamic_rewards_container := %DynamicRewardsContainer as HBoxContainer
|
||||
@onready var reward_hbox_template := %RewardHBoxTemplate as HBoxContainer
|
||||
@onready var action_btn := %ActionBtn as Button
|
||||
@onready var empty_state_lbl := %EmptyStateLbl as Label
|
||||
|
||||
var _current_mail: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
visible = false
|
||||
close_btn.pressed.connect(hide_panel)
|
||||
action_btn.pressed.connect(_on_action_pressed)
|
||||
if MailManager:
|
||||
MailManager.mail_updated.connect(_refresh_ui)
|
||||
|
||||
func show_panel() -> void:
|
||||
visible = true
|
||||
_clear_details()
|
||||
if MailManager:
|
||||
MailManager.fetch_mails()
|
||||
_refresh_ui()
|
||||
|
||||
func hide_panel() -> void:
|
||||
visible = false
|
||||
emit_signal("closed")
|
||||
|
||||
func _refresh_ui() -> void:
|
||||
if not visible or not MailManager: return
|
||||
|
||||
for child in mail_list_vbox.get_children():
|
||||
child.queue_free()
|
||||
|
||||
var mails = MailManager.mails
|
||||
if mails.is_empty():
|
||||
empty_state_lbl.visible = true
|
||||
else:
|
||||
empty_state_lbl.visible = false
|
||||
for i in range(mails.size()):
|
||||
var mail = mails[i]
|
||||
var btn = _create_mail_button(mail)
|
||||
mail_list_vbox.add_child(btn)
|
||||
btn.pressed.connect(_on_mail_selected.bind(mail))
|
||||
|
||||
if not _current_mail.is_empty() and _current_mail.get("id") == mail.get("id"):
|
||||
_current_mail = mail
|
||||
_update_details(mail)
|
||||
btn.button_pressed = true
|
||||
|
||||
if _current_mail.is_empty() and mails.size() > 0:
|
||||
_on_mail_selected(mails[0])
|
||||
|
||||
@onready var mail_btn_template := %MailBtnTemplate as Button
|
||||
|
||||
func _create_mail_button(mail: Dictionary) -> Button:
|
||||
var btn = mail_btn_template.duplicate()
|
||||
btn.visible = true
|
||||
|
||||
var title = btn.get_node("VBox/Title") as Label
|
||||
title.text = mail.get("title", "No Title")
|
||||
|
||||
var date_lbl = btn.get_node("VBox/HBox/DateLbl") as Label
|
||||
var date_str = mail.get("date", "")
|
||||
var expiry_str = mail.get("expiry_date", "")
|
||||
|
||||
var label_text = date_str.substr(0, 10) if date_str.length() >= 10 else date_str
|
||||
|
||||
if not expiry_str.is_empty():
|
||||
var now = Time.get_unix_time_from_system()
|
||||
var expiry_unix = Time.get_unix_time_from_datetime_string(expiry_str)
|
||||
var diff = expiry_unix - now
|
||||
if diff > 0:
|
||||
var days = int(diff / 86400)
|
||||
if days > 0:
|
||||
label_text += " (Exp: %dd)" % days
|
||||
else:
|
||||
label_text += " (Exp: <1d)"
|
||||
else:
|
||||
label_text += " (Expired)"
|
||||
|
||||
date_lbl.text = label_text
|
||||
|
||||
var status_lbl = btn.get_node("VBox/HBox/StatusLbl") as Label
|
||||
var mail_id = mail.get("id", "")
|
||||
if mail_id in MailManager.claimed_ids:
|
||||
status_lbl.text = "CLAIMED"
|
||||
status_lbl.add_theme_color_override("font_color", Color.GREEN)
|
||||
elif mail_id in MailManager.read_ids:
|
||||
status_lbl.text = "READ"
|
||||
status_lbl.add_theme_color_override("font_color", Color.GRAY)
|
||||
else:
|
||||
status_lbl.text = "NEW"
|
||||
status_lbl.add_theme_color_override("font_color", Color.YELLOW)
|
||||
|
||||
return btn
|
||||
|
||||
func _on_mail_selected(mail: Dictionary) -> void:
|
||||
for child in mail_list_vbox.get_children():
|
||||
if child is Button:
|
||||
child.button_pressed = false
|
||||
|
||||
_current_mail = mail
|
||||
_update_details(mail)
|
||||
MailManager.mark_as_read(mail.get("id", ""))
|
||||
|
||||
func _clear_details() -> void:
|
||||
mail_title_lbl.text = ""
|
||||
mail_content_text.text = ""
|
||||
sender_lbl.text = ""
|
||||
for child in dynamic_rewards_container.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
action_btn.hide()
|
||||
|
||||
func _update_details(mail: Dictionary) -> void:
|
||||
mail_title_lbl.text = mail.get("title", "No Title")
|
||||
mail_content_text.text = mail.get("content", "")
|
||||
sender_lbl.text = "SENDER:\n" + mail.get("sender", "SYSTEM")
|
||||
|
||||
for child in dynamic_rewards_container.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
|
||||
var rewards = mail.get("rewards", [])
|
||||
var has_rewards = false
|
||||
|
||||
# Legacy dictionary support
|
||||
if typeof(rewards) == TYPE_DICTIONARY:
|
||||
var arr = []
|
||||
if rewards.get("star", 0) > 0: arr.append({"type": "star", "amount": rewards.star})
|
||||
if rewards.get("gold", 0) > 0: arr.append({"type": "gold", "amount": rewards.gold})
|
||||
rewards = arr
|
||||
|
||||
for r in rewards:
|
||||
has_rewards = true
|
||||
var row = reward_hbox_template.duplicate()
|
||||
row.visible = true
|
||||
dynamic_rewards_container.add_child(row)
|
||||
var amt_lbl = row.get_node("AmountLbl") as Label
|
||||
|
||||
var t = r.get("type", "star")
|
||||
var amt = r.get("amount", 0)
|
||||
var id = r.get("id", "")
|
||||
|
||||
if t == "star" or t == "gold":
|
||||
amt_lbl.text = str(amt) + " " + t.to_upper()
|
||||
else:
|
||||
amt_lbl.text = str(amt) + " " + id
|
||||
|
||||
action_btn.show()
|
||||
var mail_id = mail.get("id", "")
|
||||
if has_rewards and mail_id not in MailManager.claimed_ids:
|
||||
action_btn.text = "CLAIM"
|
||||
action_btn.add_theme_color_override("font_color", Color.WHITE)
|
||||
else:
|
||||
action_btn.text = "DELETE"
|
||||
action_btn.add_theme_color_override("font_color", Color.RED)
|
||||
|
||||
func _on_action_pressed() -> void:
|
||||
if _current_mail.is_empty(): return
|
||||
var mail_id = _current_mail.get("id", "")
|
||||
|
||||
if action_btn.text == "CLAIM":
|
||||
action_btn.disabled = true
|
||||
var ok = await MailManager.claim_reward(mail_id)
|
||||
action_btn.disabled = false
|
||||
if ok:
|
||||
_update_details(_current_mail)
|
||||
else:
|
||||
action_btn.disabled = true
|
||||
var ok = await MailManager.delete_mail(mail_id)
|
||||
action_btn.disabled = false
|
||||
if ok:
|
||||
_current_mail = {}
|
||||
_clear_details()
|
||||
@@ -0,0 +1 @@
|
||||
uid://b5fema68m6b2s
|
||||
+32
-79
@@ -9,31 +9,28 @@ signal closed
|
||||
|
||||
# Tab buttons
|
||||
@onready var _search_tab_btn: Button = %SearchTabBtn
|
||||
@onready var _requests_tab_btn: Button = %RequestsTabBtn
|
||||
@onready var _friends_tab_btn: Button = %FriendsTabBtn
|
||||
@onready var _dm_tab_btn: Button = %DMTabBtn
|
||||
@onready var _dm_tab_btn: Button = get_node_or_null("%DMTabBtn")
|
||||
|
||||
|
||||
# Views
|
||||
@onready var _search_view: VBoxContainer = %SearchView
|
||||
@onready var _requests_view: VBoxContainer = %RequestsView
|
||||
@onready var _friends_view: VBoxContainer = %FriendsView
|
||||
@onready var _dm_view: VBoxContainer = %DMView
|
||||
@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: VBoxContainer = %SearchResultsList
|
||||
@onready var _search_result_tmpl: HBoxContainer = %SearchResultTemplate
|
||||
@onready var _search_results_list: GridContainer = %SearchResultsList
|
||||
|
||||
|
||||
|
||||
# Requests tab nodes
|
||||
@onready var _no_requests_label: Label = %NoRequestsLabel
|
||||
@onready var _requests_list: VBoxContainer = %RequestsList
|
||||
@onready var _request_row_tmpl: HBoxContainer = %RequestRowTemplate
|
||||
|
||||
# Friends tab nodes
|
||||
@onready var _no_friends_label: Label = %NoFriendsLabel
|
||||
@onready var _friend_list: VBoxContainer = %FriendList
|
||||
@onready var _friend_list: GridContainer = %FriendList
|
||||
|
||||
# DM tab nodes
|
||||
@onready var _dm_back_btn: Button = %DMBackBtn
|
||||
@@ -53,10 +50,12 @@ func _ready() -> void:
|
||||
|
||||
# Tab buttons
|
||||
_search_tab_btn.pressed.connect(func(): _show_tab("search"))
|
||||
_requests_tab_btn.pressed.connect(func(): _show_tab("requests"))
|
||||
_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())
|
||||
@@ -81,7 +80,6 @@ func _ready() -> void:
|
||||
func _show_tab(tab: String) -> void:
|
||||
_current_tab = tab
|
||||
_search_view.visible = tab == "search"
|
||||
_requests_view.visible = tab == "requests"
|
||||
_friends_view.visible = tab == "friends"
|
||||
_dm_view.visible = tab == "dm"
|
||||
|
||||
@@ -121,60 +119,26 @@ func _populate_search_results(users: Array) -> void:
|
||||
_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: HBoxContainer = _search_result_tmpl.duplicate()
|
||||
row.show()
|
||||
row.get_node("SRNameLabel").text = u.display_name + " (@" + u.username + ")"
|
||||
var add_btn: Button = row.get_node("SRAddBtn")
|
||||
add_btn.pressed.connect(func():
|
||||
FriendManager.add_friend_by_id(u.user_id)
|
||||
add_btn.text = "Sent ✓"
|
||||
add_btn.disabled = true
|
||||
)
|
||||
var row: Control = friend_row_scene.instantiate()
|
||||
_search_results_list.add_child(row)
|
||||
row.setup(u.user_id, u.username, -1, self)
|
||||
|
||||
# ─── Requests Tab ────────────────────────────────────────────────────────────
|
||||
func _populate_requests(incoming: Array) -> void:
|
||||
for ch in _requests_list.get_children():
|
||||
ch.queue_free()
|
||||
|
||||
_no_requests_label.visible = incoming.is_empty()
|
||||
|
||||
for f in incoming:
|
||||
var row: HBoxContainer = _request_row_tmpl.duplicate()
|
||||
row.show()
|
||||
row.get_node("RRNameLabel").text = f.username
|
||||
var accept_btn: Button = row.get_node("RRAcceptBtn")
|
||||
var decline_btn: Button = row.get_node("RRDeclineBtn")
|
||||
var uid: String = f.user_id
|
||||
accept_btn.pressed.connect(func():
|
||||
FriendManager.add_friend_by_id(uid)
|
||||
row.queue_free()
|
||||
)
|
||||
decline_btn.pressed.connect(func():
|
||||
FriendManager.remove_friend(uid)
|
||||
row.queue_free()
|
||||
)
|
||||
_requests_list.add_child(row)
|
||||
|
||||
# Badge on tab button
|
||||
if incoming.is_empty():
|
||||
_requests_tab_btn.text = "Requests"
|
||||
else:
|
||||
_requests_tab_btn.text = "Requests (%d)" % incoming.size()
|
||||
|
||||
# ─── Friends Tab ─────────────────────────────────────────────────────────────
|
||||
func _populate_friends(mutual: Array) -> void:
|
||||
func _populate_friends(friends_array: Array) -> void:
|
||||
for ch in _friend_list.get_children():
|
||||
ch.queue_free()
|
||||
|
||||
_no_friends_label.visible = mutual.is_empty()
|
||||
_no_friends_label.visible = friends_array.is_empty()
|
||||
|
||||
var friend_row_scene := preload("res://scenes/ui/friend_row.tscn")
|
||||
for f in mutual:
|
||||
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)
|
||||
@@ -182,35 +146,24 @@ func _populate_friends(mutual: Array) -> void:
|
||||
# ─── FriendManager Callbacks ─────────────────────────────────────────────────
|
||||
func _on_friends_updated(friends: Array) -> void:
|
||||
print("[SocialPanel] _on_friends_updated: total=%d" % friends.size())
|
||||
var incoming := friends.filter(func(f): return f.state == FriendManager.STATE_INVITE_IN)
|
||||
var mutual := friends.filter(func(f): return f.state == FriendManager.STATE_FRIEND)
|
||||
print("[SocialPanel] incoming=%d mutual=%d" % [incoming.size(), mutual.size()])
|
||||
_populate_requests(incoming)
|
||||
_populate_friends(mutual)
|
||||
|
||||
# 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:
|
||||
_active_dm_user_id = user_id
|
||||
_dm_username_label.text = "DM: %s" % username
|
||||
_dm_tab_btn.visible = true
|
||||
_dm_log.clear()
|
||||
_show_tab("dm")
|
||||
|
||||
_dm_log.append_text("[i]Loading history...[/i]\n")
|
||||
var history = await FriendManager.get_dm_history(user_id)
|
||||
|
||||
_dm_log.clear()
|
||||
_dm_history[user_id] = []
|
||||
|
||||
var my_id = NakamaManager.session.user_id if NakamaManager.session else ""
|
||||
|
||||
for entry in history:
|
||||
var is_self = entry.get("from") == my_id
|
||||
var sender_name = "You" if is_self else username
|
||||
_dm_history[user_id].append({"from": "me" if is_self else entry.get("from"), "msg": entry.get("msg")})
|
||||
_dm_log.append_text("[b]%s:[/b] %s\n" % [sender_name, entry.get("msg", "")])
|
||||
|
||||
FriendManager.open_dm(user_id)
|
||||
emit_signal("dm_requested", user_id, username)
|
||||
|
||||
func _send_dm() -> void:
|
||||
var text = _dm_input.text.strip_edges()
|
||||
|
||||
Reference in New Issue
Block a user