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 = 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 = {} 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) func show_panel() -> void: visible = true _clear_details() if MailManager: await MailManager.read_all_and_claim_all() 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]) func _create_mail_button(mail: Dictionary) -> Button: var btn = mail_btn_template.duplicate() btn.visible = true 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("Margin/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)" if date_lbl: date_lbl.text = label_text var status_lbl = btn.get_node("Margin/VBox/HBox/StatusLbl") as Label var mail_id = mail.get("id", "") 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 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 = "" if sender_lbl: 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", "") 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() 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("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 rid = r.get("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", "") 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() 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()