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:
+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