Fix MailMailButton nil errors and bump to 1.4.8
- Fix MailMailButton nil errors in SendMailFrame_CanSend and sendmail_clear - Keep a safe reference to the original Send Mail button - Add fallback handling for MailSubjectEditBox - Improve compatibility with UI/mailbox modifications - Update version to 1.4.8 - Update README and audit documentation
This commit is contained in:
@@ -17,6 +17,12 @@ Target: Turtle WoW 1.18.x / Octo client.
|
||||
- **Calendar tooltip:** disabled/empty calendar days no longer concatenate a nil/stale `mails` value.
|
||||
- **OnUpdate rebinding:** `TurtleMail:init()` binds the original `on_update` function before the compatibility file loads; the patch explicitly rebinds the frame script so the safer handler is actually used.
|
||||
|
||||
|
||||
## Fixed in 1.4.8
|
||||
|
||||
- **`MailMailButton` nil crash:** upstream stores the real `SendMailMailButton` in a loose global named `MailMailButton`, then replaces the Blizzard global with a proxy table. If the saved global is missing or another UI addon changes it, `SendMailFrame_CanSend()` and `sendmail_clear()` crash while calling `Enable()` / `Disable()`. The compatibility layer now preserves a strong reference to the real button before the proxy swap, restores it when the mailbox opens, replaces the fragile CanSend hook, and uses a guarded clear routine.
|
||||
- **Paired subject edit-box hardening:** the same proxy pattern is used for `MailSubjectEditBox`; the real edit box is now preserved and restored alongside the send button to avoid the equivalent follow-up failure.
|
||||
|
||||
## Remaining architectural risks upstream
|
||||
|
||||
These are intentionally not rewritten in the compatibility layer because changing them would be much more invasive and could alter mail behavior:
|
||||
@@ -38,4 +44,4 @@ The compatibility build was tested in-game on Turtle WoW 1.18.x and the reported
|
||||
|
||||
## Status
|
||||
|
||||
**1.4.6 is the current stable release.** The fixes remain isolated in `TurtleMailFix.lua` so upstream code stays easy to compare and future upstream changes remain easier to merge.
|
||||
**1.4.8 is the current stable release.** The fixes remain isolated in `TurtleMailFix.lua` so upstream code stays easy to compare and future upstream changes remain easier to merge.
|
||||
|
||||
@@ -76,7 +76,7 @@ When enabled, TurtleMail records sent and received mail information in its dedic
|
||||
|
||||
## 🔧 Turtle WoW compatibility
|
||||
|
||||
Version **1.4.7** extends the compatibility and safety layer for Turtle WoW 1.18.x environments.
|
||||
Version **1.4.8** extends the compatibility and safety layer for Turtle WoW 1.18.x environments.
|
||||
|
||||
The compatibility work is isolated in `TurtleMailFix.lua` so the original TurtleMail code remains easier to compare with upstream versions.
|
||||
|
||||
@@ -95,6 +95,7 @@ The compatibility work is isolated in `TurtleMailFix.lua` so the original Turtle
|
||||
- Fixed calendar tooltip nil/stale-value cases.
|
||||
- Rebound the safer `OnUpdate` handler correctly after loading the compatibility layer.
|
||||
- Prevented the send queue from remaining stuck when an attachment becomes unavailable during sending.
|
||||
- Fixed `MailMailButton` nil crashes while enabling/disabling or clearing the send form, and hardened the paired subject edit-box reference.
|
||||
|
||||
The complete technical audit is available in [`AUDIT.md`](AUDIT.md).
|
||||
|
||||
@@ -129,11 +130,11 @@ TurtleMail includes localization support for:
|
||||
|
||||
Current stable version:
|
||||
|
||||
**1.4.7**
|
||||
**1.4.8**
|
||||
|
||||
Based on upstream **TurtleMail 1.4.5**.
|
||||
|
||||
Version 1.4.7 has been validated in-game on Turtle WoW 1.18.x without Lua errors in the tested setup.
|
||||
Version 1.4.8 adds an additional safeguard for the legacy send-button proxy used by TurtleMail. The earlier compatibility fixes remain unchanged.
|
||||
|
||||
## 🖼️ Screenshots
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
## Interface: 11200
|
||||
## Title: |cffabd473Turtle|cffffffffMail
|
||||
## Author: shirsig/sica, Dusk-92 (compatibility fixes)
|
||||
## Version: 1.4.7
|
||||
## Version: 1.4.8
|
||||
## Notes: Mailbox enhancement - Turtle WoW 1.18.x compatibility
|
||||
## SavedVariables: TurtleMail_AutoCompleteNames
|
||||
## SavedVariablesPerCharacter: TurtleMail_To TurtleMail_Point TurtleMail_Log
|
||||
|
||||
+180
-2
@@ -8,7 +8,7 @@ local m = TurtleMail
|
||||
local getn = table.getn
|
||||
local function pack( ... ) return arg end
|
||||
|
||||
m.compat_version = "1.4.7"
|
||||
m.compat_version = "1.4.8"
|
||||
|
||||
local DEFAULT_SENT_FILTERS = {
|
||||
Money = 1,
|
||||
@@ -110,6 +110,75 @@ local function ensure_horizontal_bars()
|
||||
return m.api.MailHorizontalBarLeft and m.api.MailHorizontalBarRight
|
||||
end
|
||||
|
||||
-- Upstream keeps the real Send button and subject edit box in ad-hoc globals
|
||||
-- (MailMailButton / MailSubjectEditBox), then replaces the Blizzard globals with
|
||||
-- proxy tables. If another addon changes those globals, or sendmail_load() runs
|
||||
-- with an already-proxied widget, the saved globals can become nil and later
|
||||
-- calls crash. Preserve strong references before the upstream swap happens.
|
||||
local preserved_send_mail_button = m.api.SendMailMailButton
|
||||
local preserved_subject_edit_box = m.api.SendMailSubjectEditBox
|
||||
|
||||
local function is_named_widget( widget, expected_name )
|
||||
if not widget or type( widget.GetName ) ~= "function" then return false end
|
||||
return widget:GetName() == expected_name
|
||||
end
|
||||
|
||||
local function ensure_send_mail_button()
|
||||
local button = preserved_send_mail_button
|
||||
|
||||
if not is_named_widget( button, "SendMailMailButton" ) then
|
||||
button = m.real_send_mail_button
|
||||
end
|
||||
if not is_named_widget( button, "SendMailMailButton" ) then
|
||||
button = MailMailButton
|
||||
end
|
||||
if not is_named_widget( button, "SendMailMailButton" ) then
|
||||
button = m.api.SendMailMailButton
|
||||
end
|
||||
|
||||
if is_named_widget( button, "SendMailMailButton" ) then
|
||||
preserved_send_mail_button = button
|
||||
m.real_send_mail_button = button
|
||||
MailMailButton = button
|
||||
return button
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function ensure_subject_edit_box()
|
||||
local edit_box = preserved_subject_edit_box
|
||||
|
||||
if not is_named_widget( edit_box, "SendMailSubjectEditBox" ) then
|
||||
edit_box = m.real_subject_edit_box
|
||||
end
|
||||
if not is_named_widget( edit_box, "SendMailSubjectEditBox" ) then
|
||||
edit_box = MailSubjectEditBox
|
||||
end
|
||||
if not is_named_widget( edit_box, "SendMailSubjectEditBox" ) then
|
||||
edit_box = m.api.SendMailSubjectEditBox
|
||||
end
|
||||
|
||||
if is_named_widget( edit_box, "SendMailSubjectEditBox" ) then
|
||||
preserved_subject_edit_box = edit_box
|
||||
m.real_subject_edit_box = edit_box
|
||||
MailSubjectEditBox = edit_box
|
||||
return edit_box
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function restore_real_mail_widgets_for_upstream()
|
||||
local button = ensure_send_mail_button()
|
||||
local edit_box = ensure_subject_edit_box()
|
||||
|
||||
-- Only restore the Blizzard globals immediately before upstream sendmail_load().
|
||||
-- That function intentionally replaces them with proxy tables afterwards.
|
||||
if button then m.api.SendMailMailButton = button end
|
||||
if edit_box then m.api.SendMailSubjectEditBox = edit_box end
|
||||
end
|
||||
|
||||
-- SavedVariables are not guaranteed to have a valid schema. Repair them before
|
||||
-- the original handlers touch nested fields.
|
||||
do
|
||||
@@ -166,7 +235,50 @@ do
|
||||
local original = m.sendmail_load
|
||||
function m.sendmail_load()
|
||||
ensure_horizontal_bars()
|
||||
if original then return original() end
|
||||
restore_real_mail_widgets_for_upstream()
|
||||
|
||||
if original then
|
||||
local result = original()
|
||||
-- Upstream has now installed its proxy globals. Re-assert the private
|
||||
-- references used by the legacy MailMailButton/MailSubjectEditBox code.
|
||||
ensure_send_mail_button()
|
||||
ensure_subject_edit_box()
|
||||
return result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Replace the fragile upstream CanSend hook. The original directly calls the
|
||||
-- global MailMailButton and crashes when that saved global is nil.
|
||||
if m.hooks then
|
||||
m.hook.SendMailFrame_CanSend = function()
|
||||
local button = ensure_send_mail_button()
|
||||
if not button then return end
|
||||
|
||||
local name_box = m.api.SendMailNameEditBox
|
||||
local send_money_button = m.api.SendMailSendMoneyButton
|
||||
local send_money_frame = m.api.SendMailMoney
|
||||
|
||||
local recipient = name_box and name_box.GetText and name_box:GetText() or ""
|
||||
local attached = m.sendmail_num_attachments and m.sendmail_num_attachments() or 0
|
||||
local price = m.api.GetSendMailPrice and (tonumber( m.api.GetSendMailPrice() ) or 0) or 0
|
||||
local player_money = m.api.GetMoney and (tonumber( m.api.GetMoney() ) or 0) or 0
|
||||
local money = 0
|
||||
|
||||
if send_money_button and send_money_button.GetChecked and send_money_button:GetChecked()
|
||||
and m.api.MoneyInputFrame_GetCopper and send_money_frame then
|
||||
money = tonumber( m.api.MoneyInputFrame_GetCopper( send_money_frame ) ) or 0
|
||||
end
|
||||
|
||||
local can_send = not m.sendmail_sending
|
||||
and string.len( recipient ) > 0
|
||||
and money + price * math.max( 1, attached ) <= player_money
|
||||
|
||||
if can_send then
|
||||
button:Enable()
|
||||
else
|
||||
button:Disable()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -178,6 +290,67 @@ if m.hooks and m.hooks.SendMailFrame_Update then
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure the legacy click handler sees a valid subject edit box and real send
|
||||
-- button before it reads them. This protects the same upstream proxy hack from
|
||||
-- producing a follow-up MailSubjectEditBox nil error.
|
||||
do
|
||||
local original = m.send_mail_button_onclick
|
||||
function m.send_mail_button_onclick()
|
||||
ensure_send_mail_button()
|
||||
ensure_subject_edit_box()
|
||||
if original then return original() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Replace the upstream clear routine because it directly dereferences
|
||||
-- MailMailButton and MailSubjectEditBox. Keep its behavior, but guard every UI
|
||||
-- object so mailbox replacements cannot turn cleanup into another Lua error.
|
||||
function m.sendmail_clear()
|
||||
local any_item
|
||||
|
||||
for i = 1, 21 do
|
||||
local attachment = m.api[ "MailAttachment" .. i ]
|
||||
if attachment then
|
||||
any_item = any_item or attachment.item
|
||||
attachment.item = nil
|
||||
end
|
||||
end
|
||||
|
||||
if any_item and m.api.ClearCursor and m.api.PickupContainerItem then
|
||||
m.api.ClearCursor()
|
||||
m.api.PickupContainerItem( unpack( any_item ) )
|
||||
m.api.ClearCursor()
|
||||
end
|
||||
|
||||
local button = ensure_send_mail_button()
|
||||
if button and button.Disable then button:Disable() end
|
||||
|
||||
local name_box = m.api.SendMailNameEditBox
|
||||
if name_box then
|
||||
if name_box.SetText then name_box:SetText( "" ) end
|
||||
if name_box.SetFocus then name_box:SetFocus() end
|
||||
end
|
||||
|
||||
local subject = ensure_subject_edit_box()
|
||||
if subject and subject.SetText then subject:SetText( "" ) end
|
||||
|
||||
if m.api.SendMailBodyEditBox and m.api.SendMailBodyEditBox.SetText then
|
||||
m.api.SendMailBodyEditBox:SetText( "" )
|
||||
end
|
||||
|
||||
if m.api.MoneyInputFrame_ResetMoney and m.api.SendMailMoney then
|
||||
m.api.MoneyInputFrame_ResetMoney( m.api.SendMailMoney )
|
||||
end
|
||||
|
||||
if m.api.SendMailRadioButton_OnClick then
|
||||
m.api.SendMailRadioButton_OnClick( 1 )
|
||||
end
|
||||
|
||||
if m.api.SendMailFrame_Update then
|
||||
m.api.SendMailFrame_Update()
|
||||
end
|
||||
end
|
||||
|
||||
-- Do not assume MailFrame exists during bag events.
|
||||
function m.BAG_UPDATE()
|
||||
if m.api.MailFrame and m.api.MailFrame:IsVisible() and m.api.SendMailFrame_Update then
|
||||
@@ -187,6 +360,11 @@ end
|
||||
|
||||
-- Safer MAIL_SHOW: package-button regions differ between UI replacements.
|
||||
function m.MAIL_SHOW()
|
||||
-- Re-assert the legacy references whenever the mailbox opens. This also
|
||||
-- recovers if another UI addon changed those globals after addon loading.
|
||||
ensure_send_mail_button()
|
||||
ensure_subject_edit_box()
|
||||
|
||||
if not m.api.MailFrame then return end
|
||||
|
||||
if m.api.TurtleMail_Point then
|
||||
|
||||
Reference in New Issue
Block a user