feat: 2.3.1
This commit is contained in:
@@ -90,7 +90,52 @@ func delete_mail(mail_id: String) -> bool:
|
||||
|
||||
func mark_as_read(mail_id: String) -> void:
|
||||
if mail_id in read_ids: return
|
||||
# Since there's no specific RPC for just marking as read, we can just do a claim with 0 rewards if needed,
|
||||
# or add an RPC for it. For now we just let claim or delete handle the server-side state.
|
||||
# Let's add an empty claim or just handle it purely local until claimed/deleted if it has no rewards.
|
||||
pass
|
||||
read_ids.append(mail_id)
|
||||
_update_unread_count()
|
||||
# Persist read state to server via delete_mail RPC pattern (just saves state)
|
||||
_save_inbox_state()
|
||||
|
||||
func _save_inbox_state() -> void:
|
||||
if not NakamaManager.session: return
|
||||
var state_payload = {
|
||||
"claimed_ids": claimed_ids,
|
||||
"deleted_ids": [],
|
||||
"read_ids": read_ids
|
||||
}
|
||||
# We use storage write via a lightweight RPC or direct storage
|
||||
var r = await NakamaManager.client.rpc_async(
|
||||
NakamaManager.session, "save_mail_state",
|
||||
JSON.stringify(state_payload)
|
||||
)
|
||||
if r.is_exception():
|
||||
push_warning("[MailManager] Could not save mail state: " + r.get_exception().message)
|
||||
|
||||
func read_all_and_claim_all() -> void:
|
||||
"""Mark all mails as read and claim all unclaimed rewards."""
|
||||
if mails.is_empty(): return
|
||||
|
||||
# Mark all as read
|
||||
for mail in mails:
|
||||
var mid = mail.get("id", "")
|
||||
if mid not in read_ids:
|
||||
read_ids.append(mid)
|
||||
|
||||
# Claim all unclaimed rewards
|
||||
var to_claim: Array = []
|
||||
for mail in mails:
|
||||
var mid = mail.get("id", "")
|
||||
if mid in claimed_ids: continue
|
||||
var rewards = mail.get("rewards", [])
|
||||
var has_rewards = false
|
||||
if typeof(rewards) == TYPE_DICTIONARY:
|
||||
has_rewards = rewards.get("star", 0) > 0 or rewards.get("gold", 0) > 0
|
||||
elif typeof(rewards) == TYPE_ARRAY:
|
||||
has_rewards = rewards.size() > 0
|
||||
if has_rewards:
|
||||
to_claim.append(mid)
|
||||
|
||||
for mid in to_claim:
|
||||
await claim_reward(mid)
|
||||
|
||||
_update_unread_count()
|
||||
mail_updated.emit()
|
||||
|
||||
+365
-2
@@ -38,6 +38,8 @@ var _current_dr_month: String = ""
|
||||
|
||||
# Tab: Announcements
|
||||
@onready var target_user_edit := %TargetUserEdit as LineEdit
|
||||
@onready var find_user_btn := %FindUserBtn as Button
|
||||
@onready var resolved_id_label := %ResolvedIdLabel as Label
|
||||
@onready var title_edit := %TitleEdit as LineEdit
|
||||
@onready var content_edit := %ContentEdit as TextEdit
|
||||
@onready var start_date_edit := %StartDatePicker as Button
|
||||
@@ -47,6 +49,18 @@ var _current_dr_month: String = ""
|
||||
@onready var reward_row_template := %RewardRowTemplate as HBoxContainer
|
||||
@onready var send_mail_btn := %SendMailBtn as Button
|
||||
|
||||
var _resolved_user_id: String = ""
|
||||
|
||||
# Tab: Mail Manager
|
||||
@onready var mail_tree := %MailTree as Tree
|
||||
@onready var refresh_mail_btn := %RefreshMailBtn as Button
|
||||
@onready var edit_mail_btn := %EditMailBtn as Button
|
||||
@onready var end_mail_btn := %EndMailBtn as Button
|
||||
@onready var delete_mail_server_btn := %DeleteMailServerBtn as Button
|
||||
|
||||
var _mail_root: TreeItem
|
||||
var _all_server_mails: Array = []
|
||||
|
||||
const MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
||||
|
||||
# -- Data --
|
||||
@@ -140,6 +154,19 @@ func _setup_columns() -> void:
|
||||
lb_tree.set_column_custom_minimum_width(5, 60)
|
||||
_lb_root = lb_tree.create_item()
|
||||
|
||||
# Mail Manager
|
||||
mail_tree.set_column_title(0, "Type")
|
||||
mail_tree.set_column_title(1, "Title")
|
||||
mail_tree.set_column_title(2, "Sender")
|
||||
mail_tree.set_column_title(3, "Start")
|
||||
mail_tree.set_column_title(4, "Expires")
|
||||
mail_tree.set_column_title(5, "Status")
|
||||
mail_tree.set_column_custom_minimum_width(0, 100)
|
||||
mail_tree.set_column_expand(0, false)
|
||||
mail_tree.set_column_custom_minimum_width(5, 80)
|
||||
mail_tree.set_column_expand(5, false)
|
||||
_mail_root = mail_tree.create_item()
|
||||
|
||||
func _connect_signals() -> void:
|
||||
close_btn.pressed.connect(_on_close)
|
||||
refresh_btn.pressed.connect(_on_refresh)
|
||||
@@ -166,6 +193,16 @@ func _connect_signals() -> void:
|
||||
# Announcement actions
|
||||
send_mail_btn.pressed.connect(_on_send_mail)
|
||||
add_reward_btn.pressed.connect(_on_add_reward_pressed)
|
||||
find_user_btn.pressed.connect(_on_find_user)
|
||||
target_user_edit.text_changed.connect(func(_t): _resolved_user_id = ""; resolved_id_label.text = "")
|
||||
|
||||
# Mail Manager actions
|
||||
refresh_mail_btn.pressed.connect(func(): await _load_mail())
|
||||
mail_tree.item_selected.connect(_on_mail_item_selected)
|
||||
edit_mail_btn.pressed.connect(_on_edit_mail_pressed)
|
||||
end_mail_btn.pressed.connect(_on_end_mail_pressed)
|
||||
delete_mail_server_btn.pressed.connect(_on_delete_mail_server_pressed)
|
||||
_update_mail_action_btns(null)
|
||||
|
||||
# =============================================================================
|
||||
# Core Panel Logic
|
||||
@@ -189,6 +226,8 @@ func _on_tab_changed(tab_index: int) -> void:
|
||||
await _load_leaderboard()
|
||||
elif tab_index == 2:
|
||||
await _load_daily_rewards_config()
|
||||
elif tab_index == 4:
|
||||
await _load_mail()
|
||||
|
||||
# =============================================================================
|
||||
# RPC Helper
|
||||
@@ -649,8 +688,60 @@ func _on_add_reward_pressed() -> void:
|
||||
var remove_btn = row.get_node("RemoveBtn") as Button
|
||||
remove_btn.pressed.connect(func(): row.queue_free())
|
||||
|
||||
func _on_find_user() -> void:
|
||||
var input = target_user_edit.text.strip_edges()
|
||||
if input.is_empty():
|
||||
_resolved_user_id = ""
|
||||
resolved_id_label.text = "(Global — all users)"
|
||||
return
|
||||
|
||||
_set_status("Looking up user...")
|
||||
var uid = await _resolve_target_user_id(input)
|
||||
if uid.is_empty():
|
||||
resolved_id_label.text = "NOT FOUND"
|
||||
resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_ERR)
|
||||
_set_status("User not found: " + input, CLR_STATUS_ERR)
|
||||
else:
|
||||
_resolved_user_id = uid
|
||||
resolved_id_label.text = "ID: " + uid.substr(0, 12) + "..."
|
||||
resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
_set_status("Found user: " + uid, CLR_STATUS_OK)
|
||||
|
||||
func _resolve_target_user_id(input: String) -> String:
|
||||
"""Resolve a username, display_name, or user_id to a user_id.
|
||||
Returns empty string if not found."""
|
||||
if input.is_empty():
|
||||
return ""
|
||||
|
||||
# If it looks like a UUID already, return as-is
|
||||
if input.length() >= 32 and "-" in input:
|
||||
return input
|
||||
|
||||
# Search in cached all_users first
|
||||
for u in all_users:
|
||||
var uname: String = u.get("username", "")
|
||||
var dname: String = u.get("display_name", "")
|
||||
var uid: String = u.get("user_id", "")
|
||||
if uname.to_lower() == input.to_lower() or dname.to_lower() == input.to_lower():
|
||||
return uid
|
||||
|
||||
# Cache miss — fetch from server
|
||||
var res := await _rpc("admin_list_users", {})
|
||||
if res.has("error"):
|
||||
return ""
|
||||
all_users = res.get("users", [])
|
||||
|
||||
for u in all_users:
|
||||
var uname: String = u.get("username", "")
|
||||
var dname: String = u.get("display_name", "")
|
||||
var uid: String = u.get("user_id", "")
|
||||
if uname.to_lower() == input.to_lower() or dname.to_lower() == input.to_lower():
|
||||
return uid
|
||||
|
||||
return ""
|
||||
|
||||
func _on_send_mail() -> void:
|
||||
var target = target_user_edit.text.strip_edges()
|
||||
var input = 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()
|
||||
@@ -659,6 +750,21 @@ func _on_send_mail() -> void:
|
||||
if title.is_empty() or content.is_empty():
|
||||
_set_status("Title and content cannot be empty", CLR_STATUS_ERR)
|
||||
return
|
||||
|
||||
# Resolve target
|
||||
var target_uid := ""
|
||||
if not input.is_empty():
|
||||
if not _resolved_user_id.is_empty():
|
||||
target_uid = _resolved_user_id
|
||||
else:
|
||||
_set_status("Resolving user...")
|
||||
target_uid = await _resolve_target_user_id(input)
|
||||
if target_uid.is_empty():
|
||||
_set_status("User not found: " + input + ". Use Find button.", CLR_STATUS_ERR)
|
||||
return
|
||||
_resolved_user_id = target_uid
|
||||
resolved_id_label.text = "ID: " + target_uid.substr(0, 12) + "..."
|
||||
resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
|
||||
var rewards_arr = []
|
||||
for child in rewards_list.get_children():
|
||||
@@ -679,7 +785,7 @@ func _on_send_mail() -> void:
|
||||
_set_status("Sending mail...")
|
||||
|
||||
var payload = {
|
||||
"target_user_id": target,
|
||||
"target_user_id": target_uid,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"start_date": start_date,
|
||||
@@ -694,6 +800,9 @@ func _on_send_mail() -> void:
|
||||
_set_status("Failed to send mail: " + res.get("error", "Unknown"), CLR_STATUS_ERR)
|
||||
else:
|
||||
_set_status("Mail sent successfully!", CLR_STATUS_OK)
|
||||
target_user_edit.text = ""
|
||||
_resolved_user_id = ""
|
||||
resolved_id_label.text = ""
|
||||
title_edit.text = ""
|
||||
content_edit.text = ""
|
||||
if start_date_edit.has_method("clear_date"):
|
||||
@@ -702,3 +811,257 @@ func _on_send_mail() -> void:
|
||||
end_date_edit.clear_date()
|
||||
for child in rewards_list.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
|
||||
# =============================================================================
|
||||
# TAB 5: MAIL MANAGER
|
||||
# =============================================================================
|
||||
func _load_mail() -> void:
|
||||
_clear_tree(mail_tree, _mail_root)
|
||||
_set_status("Loading mails...")
|
||||
|
||||
var res := await _rpc("admin_list_mail", {})
|
||||
if res.has("error"):
|
||||
_set_status("Failed: " + str(res.error), CLR_STATUS_ERR)
|
||||
return
|
||||
|
||||
_all_server_mails = res.get("mails", [])
|
||||
count_label.text = "%d mails" % _all_server_mails.size()
|
||||
|
||||
var now_str = Time.get_datetime_string_from_system(true)
|
||||
|
||||
for mail in _all_server_mails:
|
||||
var item := _mail_root.create_child()
|
||||
var mail_type: String = mail.get("type", "global")
|
||||
var mail_title: String = mail.get("title", "No Title")
|
||||
var sender: String = mail.get("sender", "SYSTEM")
|
||||
var start_date: String = mail.get("start_date", "")
|
||||
var expiry: String = mail.get("expiry_date", "")
|
||||
|
||||
# Type column
|
||||
item.set_text(0, mail_type.to_upper())
|
||||
if mail_type == "personal":
|
||||
item.set_custom_color(0, CLR_ADMIN)
|
||||
else:
|
||||
item.set_custom_color(0, CLR_MOD)
|
||||
|
||||
# Title
|
||||
item.set_text(1, mail_title)
|
||||
|
||||
# Sender
|
||||
item.set_text(2, sender)
|
||||
|
||||
# Start date
|
||||
item.set_text(3, start_date.substr(0, 10) if start_date.length() >= 10 else start_date)
|
||||
|
||||
# Expiry
|
||||
var expiry_short = expiry.substr(0, 10) if expiry.length() >= 10 else expiry
|
||||
item.set_text(4, expiry_short)
|
||||
|
||||
# Status
|
||||
var end_date: String = mail.get("end_date", "")
|
||||
var status := "ACTIVE"
|
||||
if not end_date.is_empty() and now_str > end_date:
|
||||
status = "ENDED"
|
||||
item.set_custom_color(5, CLR_BTN_DEL)
|
||||
elif not expiry.is_empty() and now_str > expiry:
|
||||
status = "EXPIRED"
|
||||
item.set_custom_color(5, CLR_BTN_DEL)
|
||||
else:
|
||||
item.set_custom_color(5, CLR_STATUS_OK)
|
||||
item.set_text(5, status)
|
||||
|
||||
item.set_metadata(0, mail)
|
||||
|
||||
_update_mail_action_btns(null)
|
||||
_set_status("")
|
||||
|
||||
func _on_mail_item_selected() -> void:
|
||||
var item = mail_tree.get_selected()
|
||||
_update_mail_action_btns(item)
|
||||
|
||||
func _update_mail_action_btns(item) -> void:
|
||||
var has_sel = item != null
|
||||
edit_mail_btn.disabled = not has_sel
|
||||
end_mail_btn.disabled = not has_sel
|
||||
delete_mail_server_btn.disabled = not has_sel
|
||||
|
||||
func _get_selected_mail() -> Dictionary:
|
||||
var item = mail_tree.get_selected()
|
||||
if item:
|
||||
return item.get_metadata(0)
|
||||
return {}
|
||||
|
||||
func _on_edit_mail_pressed() -> void:
|
||||
var mail = _get_selected_mail()
|
||||
if mail.is_empty(): return
|
||||
_show_edit_mail_dialog(mail)
|
||||
|
||||
func _show_edit_mail_dialog(mail: Dictionary) -> void:
|
||||
var mail_id: String = mail.get("id", "")
|
||||
var mail_type: String = mail.get("type", "global")
|
||||
var target_uid: String = mail.get("target_user_id", "")
|
||||
|
||||
var dialog := AcceptDialog.new()
|
||||
dialog.title = "Edit Mail: " + mail.get("title", "")
|
||||
dialog.min_size = Vector2i(480, 360)
|
||||
var vbox := VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 10)
|
||||
|
||||
var id_lbl := Label.new()
|
||||
id_lbl.text = "ID: " + mail_id + " | Type: " + mail_type
|
||||
id_lbl.add_theme_color_override("font_color", CLR_DIM)
|
||||
vbox.add_child(id_lbl)
|
||||
|
||||
var grid := GridContainer.new()
|
||||
grid.columns = 2
|
||||
grid.add_theme_constant_override("h_separation", 8)
|
||||
grid.add_theme_constant_override("v_separation", 8)
|
||||
|
||||
var title_lbl := Label.new(); title_lbl.text = "Title:"; grid.add_child(title_lbl)
|
||||
var title_input := LineEdit.new(); title_input.text = mail.get("title", ""); title_input.custom_minimum_size.x = 300; grid.add_child(title_input)
|
||||
|
||||
var content_lbl := Label.new(); content_lbl.text = "Content:"; grid.add_child(content_lbl)
|
||||
var content_input := TextEdit.new(); content_input.text = mail.get("content", ""); content_input.custom_minimum_size = Vector2(300, 100); grid.add_child(content_input)
|
||||
|
||||
var end_lbl := Label.new(); end_lbl.text = "End Date:"; grid.add_child(end_lbl)
|
||||
var end_picker: Button = load("res://scenes/ui/date_picker.tscn").instantiate()
|
||||
end_picker.custom_minimum_size.x = 200
|
||||
grid.add_child(end_picker)
|
||||
# Pre-populate if existing end_date
|
||||
var existing_end: String = mail.get("end_date", "")
|
||||
if not existing_end.is_empty():
|
||||
var parts = existing_end.substr(0, 10).split("-")
|
||||
if parts.size() == 3:
|
||||
end_picker.current_date = {"year": int(parts[0]), "month": int(parts[1]), "day": int(parts[2])}
|
||||
end_picker.view_date = end_picker.current_date.duplicate()
|
||||
end_picker.text = existing_end.substr(0, 10)
|
||||
|
||||
# Recipient row
|
||||
var recip_lbl := Label.new(); recip_lbl.text = "Recipient:"; grid.add_child(recip_lbl)
|
||||
var recip_hbox := HBoxContainer.new()
|
||||
recip_hbox.add_theme_constant_override("separation", 6)
|
||||
var recip_input := LineEdit.new()
|
||||
recip_input.custom_minimum_size.x = 180
|
||||
recip_input.placeholder_text = "Username or ID (empty = global)"
|
||||
if mail_type == "personal" and not target_uid.is_empty():
|
||||
recip_input.text = target_uid.substr(0, 12) + "..."
|
||||
recip_input.tooltip_text = target_uid
|
||||
recip_hbox.add_child(recip_input)
|
||||
var recip_find_btn := Button.new()
|
||||
recip_find_btn.text = "Find"
|
||||
recip_find_btn.custom_minimum_size.x = 60
|
||||
recip_hbox.add_child(recip_find_btn)
|
||||
var recip_resolved_lbl := Label.new()
|
||||
recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
recip_hbox.add_child(recip_resolved_lbl)
|
||||
grid.add_child(recip_hbox)
|
||||
|
||||
var _edit_resolved_uid := target_uid
|
||||
recip_find_btn.pressed.connect(func():
|
||||
var inp = recip_input.text.strip_edges()
|
||||
if inp.is_empty():
|
||||
_edit_resolved_uid = ""
|
||||
recip_resolved_lbl.text = "(Global)"
|
||||
recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
return
|
||||
var uid = await _resolve_target_user_id(inp)
|
||||
if uid.is_empty():
|
||||
recip_resolved_lbl.text = "NOT FOUND"
|
||||
recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_ERR)
|
||||
else:
|
||||
_edit_resolved_uid = uid
|
||||
recip_resolved_lbl.text = uid.substr(0, 12) + "..."
|
||||
recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
)
|
||||
|
||||
vbox.add_child(grid)
|
||||
|
||||
var save_btn := Button.new()
|
||||
save_btn.text = "Save Changes"
|
||||
save_btn.custom_minimum_size.y = 40
|
||||
vbox.add_child(save_btn)
|
||||
|
||||
dialog.add_child(vbox)
|
||||
add_child(dialog)
|
||||
dialog.popup_centered()
|
||||
|
||||
save_btn.pressed.connect(func():
|
||||
# If user typed something but didn't click Find, auto-resolve
|
||||
var recip_text = recip_input.text.strip_edges()
|
||||
var final_target = _edit_resolved_uid
|
||||
if not recip_text.is_empty() and _edit_resolved_uid == target_uid:
|
||||
# Text changed but not resolved yet
|
||||
var resolved = await _resolve_target_user_id(recip_text)
|
||||
if not resolved.is_empty():
|
||||
final_target = resolved
|
||||
elif recip_text.is_empty():
|
||||
final_target = ""
|
||||
|
||||
_set_status("Updating mail...")
|
||||
var payload = {
|
||||
"mail_id": mail_id,
|
||||
"type": mail_type,
|
||||
"target_user_id": target_uid,
|
||||
"new_target_user_id": final_target,
|
||||
"title": title_input.text,
|
||||
"content": content_input.text,
|
||||
"end_date": end_picker.get_date_iso()
|
||||
}
|
||||
var res = await _rpc("admin_update_mail", payload)
|
||||
if res.has("success"):
|
||||
_set_status("Mail updated!", CLR_STATUS_OK)
|
||||
await _load_mail()
|
||||
dialog.queue_free()
|
||||
)
|
||||
|
||||
func _on_end_mail_pressed() -> void:
|
||||
var mail = _get_selected_mail()
|
||||
if mail.is_empty(): return
|
||||
|
||||
var mail_id: String = mail.get("id", "")
|
||||
var mail_type: String = mail.get("type", "global")
|
||||
var target_uid: String = mail.get("target_user_id", "")
|
||||
|
||||
var confirm := ConfirmationDialog.new()
|
||||
confirm.title = "End Mail Now?"
|
||||
confirm.dialog_text = "Set end_date to NOW for:\n" + mail.get("title", "Unknown")
|
||||
add_child(confirm)
|
||||
confirm.popup_centered()
|
||||
confirm.confirmed.connect(func():
|
||||
var now_iso = Time.get_datetime_string_from_system(true)
|
||||
var res = await _rpc("admin_update_mail", {
|
||||
"mail_id": mail_id,
|
||||
"type": mail_type,
|
||||
"target_user_id": target_uid,
|
||||
"end_date": now_iso
|
||||
})
|
||||
if res.has("success"):
|
||||
_set_status("Mail ended", CLR_STATUS_OK)
|
||||
await _load_mail()
|
||||
confirm.queue_free()
|
||||
)
|
||||
|
||||
func _on_delete_mail_server_pressed() -> void:
|
||||
var mail = _get_selected_mail()
|
||||
if mail.is_empty(): return
|
||||
|
||||
var mail_id: String = mail.get("id", "")
|
||||
var mail_type: String = mail.get("type", "global")
|
||||
var target_uid: String = mail.get("target_user_id", "")
|
||||
|
||||
var confirm := ConfirmationDialog.new()
|
||||
confirm.title = "PERMANENTLY Delete Mail?"
|
||||
confirm.dialog_text = "Remove from server storage:\n" + mail.get("title", "Unknown") + "\n\nThis cannot be undone!"
|
||||
add_child(confirm)
|
||||
confirm.popup_centered()
|
||||
confirm.confirmed.connect(func():
|
||||
var res = await _rpc("admin_delete_mail_server", {
|
||||
"mail_id": mail_id,
|
||||
"type": mail_type,
|
||||
"target_user_id": target_uid
|
||||
})
|
||||
if res.has("success"):
|
||||
_set_status("Mail deleted from server", CLR_STATUS_OK)
|
||||
await _load_mail()
|
||||
confirm.queue_free()
|
||||
)
|
||||
|
||||
+53
-27
@@ -6,11 +6,13 @@ signal closed
|
||||
@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 sender_lbl = get_node_or_null("%SenderLbl")
|
||||
@onready var dynamic_rewards_container := %DynamicRewardsContainer as VBoxContainer
|
||||
@onready var reward_hbox_template = %RewardHBoxTemplate
|
||||
@onready var action_btn := %ActionBtn as Button
|
||||
@onready var read_all_btn := %ReadAllBtn as Button
|
||||
@onready var empty_state_lbl := %EmptyStateLbl as Label
|
||||
@onready var mail_btn_template := %MailBtnTemplate as Button
|
||||
|
||||
var _current_mail: Dictionary = {}
|
||||
|
||||
@@ -18,6 +20,7 @@ func _ready() -> void:
|
||||
visible = false
|
||||
close_btn.pressed.connect(hide_panel)
|
||||
action_btn.pressed.connect(_on_action_pressed)
|
||||
read_all_btn.pressed.connect(_on_read_all_pressed)
|
||||
if MailManager:
|
||||
MailManager.mail_updated.connect(_refresh_ui)
|
||||
|
||||
@@ -25,6 +28,7 @@ func show_panel() -> void:
|
||||
visible = true
|
||||
_clear_details()
|
||||
if MailManager:
|
||||
await MailManager.read_all_and_claim_all()
|
||||
MailManager.fetch_mails()
|
||||
_refresh_ui()
|
||||
|
||||
@@ -57,16 +61,15 @@ func _refresh_ui() -> void:
|
||||
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 title_lbl = btn.get_node("Margin/VBox/Title") as Label
|
||||
if title_lbl:
|
||||
title_lbl.text = mail.get("title", "No Title")
|
||||
|
||||
var date_lbl = btn.get_node("VBox/HBox/DateLbl") as Label
|
||||
var date_lbl = btn.get_node("Margin/VBox/HBox/DateLbl") as Label
|
||||
var date_str = mail.get("date", "")
|
||||
var expiry_str = mail.get("expiry_date", "")
|
||||
|
||||
@@ -85,19 +88,21 @@ func _create_mail_button(mail: Dictionary) -> Button:
|
||||
else:
|
||||
label_text += " (Expired)"
|
||||
|
||||
date_lbl.text = label_text
|
||||
if date_lbl:
|
||||
date_lbl.text = label_text
|
||||
|
||||
var status_lbl = btn.get_node("VBox/HBox/StatusLbl") as Label
|
||||
var status_lbl = btn.get_node("Margin/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)
|
||||
if status_lbl:
|
||||
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
|
||||
|
||||
@@ -113,7 +118,7 @@ func _on_mail_selected(mail: Dictionary) -> void:
|
||||
func _clear_details() -> void:
|
||||
mail_title_lbl.text = ""
|
||||
mail_content_text.text = ""
|
||||
sender_lbl.text = ""
|
||||
if sender_lbl: sender_lbl.text = ""
|
||||
for child in dynamic_rewards_container.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
action_btn.hide()
|
||||
@@ -121,7 +126,7 @@ func _clear_details() -> void:
|
||||
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")
|
||||
if sender_lbl: sender_lbl.text = "SENDER:\n" + mail.get("sender", "SYSTEM")
|
||||
|
||||
for child in dynamic_rewards_container.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
@@ -141,16 +146,29 @@ func _update_details(mail: Dictionary) -> void:
|
||||
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 amt_lbl = row.get_node("Margin/HBox/VBox/AmountLbl") as Label
|
||||
var type_lbl = row.get_node("Margin/HBox/VBox/TypeLbl") as Label
|
||||
|
||||
var t = r.get("type", "star")
|
||||
var amt = r.get("amount", 0)
|
||||
var id = r.get("id", "")
|
||||
var rid = r.get("id", "")
|
||||
|
||||
if t == "star" or t == "gold":
|
||||
amt_lbl.text = str(amt) + " " + t.to_upper()
|
||||
else:
|
||||
amt_lbl.text = str(amt) + " " + id
|
||||
if amt_lbl: amt_lbl.text = "x" + str(amt)
|
||||
if type_lbl: type_lbl.text = t.to_upper() if (t == "star" or t == "gold") else rid.to_upper()
|
||||
|
||||
# Fill empty slots up to 4
|
||||
var added = rewards.size()
|
||||
while added < 4:
|
||||
added += 1
|
||||
var empty_row = reward_hbox_template.duplicate()
|
||||
empty_row.visible = true
|
||||
dynamic_rewards_container.add_child(empty_row)
|
||||
var icon_bg = empty_row.get_node_or_null("Margin/HBox/IconBg")
|
||||
if icon_bg: icon_bg.color = Color(0, 0, 0, 0)
|
||||
var t_lbl = empty_row.get_node_or_null("Margin/HBox/VBox/TypeLbl")
|
||||
if t_lbl: t_lbl.text = ""
|
||||
var a_lbl = empty_row.get_node_or_null("Margin/HBox/VBox/AmountLbl")
|
||||
if a_lbl: a_lbl.text = ""
|
||||
|
||||
action_btn.show()
|
||||
var mail_id = mail.get("id", "")
|
||||
@@ -178,3 +196,11 @@ func _on_action_pressed() -> void:
|
||||
if ok:
|
||||
_current_mail = {}
|
||||
_clear_details()
|
||||
|
||||
func _on_read_all_pressed() -> void:
|
||||
if not MailManager: return
|
||||
for mail in MailManager.mails:
|
||||
var mid = mail.get("id", "")
|
||||
if mid not in MailManager.read_ids:
|
||||
MailManager.mark_as_read(mid)
|
||||
_refresh_ui()
|
||||
|
||||
@@ -83,6 +83,11 @@ func _show_tab(tab: String) -> void:
|
||||
_friends_view.visible = tab == "friends"
|
||||
_dm_view.visible = tab == "dm"
|
||||
|
||||
_search_tab_btn.set_pressed_no_signal(tab == "search")
|
||||
_friends_tab_btn.set_pressed_no_signal(tab == "friends")
|
||||
if _dm_tab_btn:
|
||||
_dm_tab_btn.set_pressed_no_signal(tab == "dm")
|
||||
|
||||
# Auto-load search results on first open
|
||||
if tab == "search" and _search_results_list.get_child_count() == 0:
|
||||
_on_search_pressed()
|
||||
|
||||
Reference in New Issue
Block a user