From 6aab69a1163d645580fc03fe7ae6c897c2b3b3fc Mon Sep 17 00:00:00 2001 From: Dusk <24+dusk@noreply.octowow.st> Date: Mon, 14 Sep 2026 07:17:16 +0000 Subject: [PATCH] Fix missing Open Mail button and bump to 1.4.9 - Recreate the Open Mail button if it is missing - Restore its parent, position, text and click handler - Ensure the button stays visible above the inbox UI - Reapply the fix when the mailbox opens or updates - Bump version to 1.4.9 --- AUDIT.md | 6 ++++- README.md | 7 ++--- TurtleMail.toc | 2 +- TurtleMailFix.lua | 68 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/AUDIT.md b/AUDIT.md index e430be8..e89606b 100644 --- a/AUDIT.md +++ b/AUDIT.md @@ -23,6 +23,10 @@ Target: Turtle WoW 1.18.x / Octo client. - **`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. +## Fixed in 1.4.9 + +- **Missing `Open Mail` button:** upstream creates `TurtleMailOpenMailButton` only once in `inbox_load()` and assumes it will remain parented, positioned, and visible forever. If that creation path is interrupted or another mailbox/UI addon hides or re-parents the button, TurtleMail never recovers it. The compatibility layer now uses an idempotent inbox loader and an `ensure_open_mail_button()` recovery path that recreates the button if needed, restores its parent/anchor/text/click handler/frame level, and re-shows it on mailbox/inbox refreshes. + ## 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: @@ -44,4 +48,4 @@ The compatibility build was tested in-game on Turtle WoW 1.18.x and the reported ## Status -**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. +**1.4.9 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. diff --git a/README.md b/README.md index 7a750a4..0639506 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ When enabled, TurtleMail records sent and received mail information in its dedic ## 🔧 Turtle WoW compatibility -Version **1.4.8** extends the compatibility and safety layer for Turtle WoW 1.18.x environments. +Version **1.4.9** 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. @@ -96,6 +96,7 @@ The compatibility work is isolated in `TurtleMailFix.lua` so the original Turtle - 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. +- Restored the missing **Open Mail** button by making its creation idempotent and reasserting its parent, position, visibility, click handler, and frame level when the mailbox refreshes. The complete technical audit is available in [`AUDIT.md`](AUDIT.md). @@ -130,11 +131,11 @@ TurtleMail includes localization support for: Current stable version: -**1.4.8** +**1.4.9** Based on upstream **TurtleMail 1.4.5**. -Version 1.4.8 adds an additional safeguard for the legacy send-button proxy used by TurtleMail. The earlier compatibility fixes remain unchanged. +Version 1.4.9 adds recovery for the **Open Mail** inbox button when its initial creation is interrupted or another UI addon hides/re-parents it. The earlier compatibility fixes remain unchanged. ## 🖼️ Screenshots diff --git a/TurtleMail.toc b/TurtleMail.toc index 678103b..7f0294d 100644 --- a/TurtleMail.toc +++ b/TurtleMail.toc @@ -2,7 +2,7 @@ ## Interface: 11200 ## Title: |cffabd473Turtle|cffffffffMail ## Author: shirsig/sica, Dusk-92 (compatibility fixes) -## Version: 1.4.8 +## Version: 1.4.9 ## Notes: Mailbox enhancement - Turtle WoW 1.18.x compatibility ## SavedVariables: TurtleMail_AutoCompleteNames ## SavedVariablesPerCharacter: TurtleMail_To TurtleMail_Point TurtleMail_Log diff --git a/TurtleMailFix.lua b/TurtleMailFix.lua index d48a172..032332c 100644 --- a/TurtleMailFix.lua +++ b/TurtleMailFix.lua @@ -8,7 +8,7 @@ local m = TurtleMail local getn = table.getn local function pack( ... ) return arg end -m.compat_version = "1.4.8" +m.compat_version = "1.4.9" local DEFAULT_SENT_FILTERS = { Money = 1, @@ -179,6 +179,69 @@ local function restore_real_mail_widgets_for_upstream() if edit_box then m.api.SendMailSubjectEditBox = edit_box end end +-- The upstream Open Mail button is created only once in inbox_load(). If that +-- creation is skipped/interrupted, or another UI addon hides/re-parents the +-- button afterwards, TurtleMail never recreates it. Keep the button recoverable +-- whenever the mailbox/inbox is shown or refreshed. +local function ensure_open_mail_button() + local inbox = m.api.InboxFrame + if not inbox or not m.api.CreateFrame then return nil end + + local button = m.api.TurtleMailOpenMailButton + if not button or type( button.SetPoint ) ~= "function" then + button = m.api.CreateFrame( "Button", "TurtleMailOpenMailButton", inbox, "UIPanelButtonTemplate" ) + m.api.TurtleMailOpenMailButton = button + end + + if button.SetParent then button:SetParent( inbox ) end + if button.ClearAllPoints then button:ClearAllPoints() end + button:SetPoint( "BOTTOM", inbox, "BOTTOM", -10, 90 ) + + if button.SetText then button:SetText( m.api.OPENMAIL or "Open Mail" ) end + + local width = 120 + local font_string = button.GetFontString and button:GetFontString() + if font_string and font_string.GetStringWidth then + width = math.max( 120, 30 + (font_string:GetStringWidth() or 0) ) + end + + if button.SetWidth then button:SetWidth( width ) end + if button.SetHeight then button:SetHeight( 25 ) end + if button.SetScript then button:SetScript( "OnClick", m.inbox_open_all ) end + + if inbox.GetFrameLevel and button.SetFrameLevel then + button:SetFrameLevel( (inbox:GetFrameLevel() or 0) + 5 ) + end + + if button.Enable then button:Enable() end + if button.Show then button:Show() end + return button +end + +-- Replace the fragile one-shot inbox loader with an idempotent version. This +-- preserves upstream behavior while making Open Mail recoverable instead of +-- relying on the initial CreateFrame call succeeding forever. +function m.inbox_load() + if m.api.InboxFrame and m.api.InboxFrame.EnableMouse then + m.api.InboxFrame:EnableMouse( false ) + end + + ensure_open_mail_button() + + for i = 1, 7 do + local auction_texture = m.api[ "TurtleMailAuctionIcon" .. i .. "Texture" ] + local returned_texture = m.api[ "TurtleMailReturnedArrow" .. i .. "Texture" ] + local color = m.api.NORMAL_FONT_COLOR + + if color and auction_texture and auction_texture.SetVertexColor then + auction_texture:SetVertexColor( color.r, color.g, color.b ) + end + if color and returned_texture and returned_texture.SetVertexColor then + returned_texture:SetVertexColor( color.r, color.g, color.b ) + end + end +end + -- SavedVariables are not guaranteed to have a valid schema. Repair them before -- the original handlers touch nested fields. do @@ -364,6 +427,7 @@ function m.MAIL_SHOW() -- recovers if another UI addon changed those globals after addon loading. ensure_send_mail_button() ensure_subject_edit_box() + ensure_open_mail_button() if not m.api.MailFrame then return end @@ -466,6 +530,8 @@ end -- Hide stale AH/returned icons on rows that no longer contain mail. function m.MAIL_INBOX_UPDATE() + ensure_open_mail_button() + if m.inbox_opening then m.inbox_update = true end