Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bc6523fb2 | |||
| c166f65316 | |||
| bb7c47a4d6 | |||
| 52e66fcf4b | |||
| b22361f758 |
@@ -0,0 +1,41 @@
|
||||
# TurtleMail 1.4.5 — Compatibility audit
|
||||
|
||||
Target: Turtle WoW 1.18.x / Octo client.
|
||||
|
||||
## Fixed in 1.4.6
|
||||
|
||||
- **Horizontal mail bar crash:** the upstream code creates `MailHorizontalBarLeft/Right` and then assumes the textures are exported as globals. Some client/UI combinations return the textures without the expected global, causing `attempt to index field 'MailHorizontalBarLeft' (a nil value)`. The compatibility layer guarantees valid texture references before setup and updates.
|
||||
- **Autocomplete SavedVariables crash:** `TurtleMail_AutoCompleteNames` and its realm/faction table are used without schema validation. The patch initializes/repairs missing or malformed SavedVariables before `ADDON_LOADED`, `PLAYER_LOGIN`, slash commands, and autocomplete.
|
||||
- **Broken sender-learning hook:** upstream `GetInboxHeaderInfo` hook reads `arg[3]`/`arg[12]` from function inputs even though sender/canReply are return values. The patch captures return values first, so received senders can actually populate autocomplete.
|
||||
- **Sent money not logged:** upstream checks the nonexistent `state.send_money` field. The patch records sent money when it is not COD and `sent_money > 0`.
|
||||
- **Inbox bounds/COD safety:** header data is no longer queried/compared unsafely after the inbox index has moved past the last mail; nil COD values are treated as zero.
|
||||
- **Stale inbox icons:** AH/returned icons are explicitly hidden on empty rows/pages.
|
||||
- **Fragile package-button regions:** `MAIL_SHOW` no longer assumes regions 1 and 3 always exist.
|
||||
- **Mailbox frame guards:** bag updates, attachment bookkeeping, inbox locking, and money display tolerate missing/replaced frames instead of immediately throwing Lua errors.
|
||||
- **Locale-safe COD label:** unexpected `COD_AMOUNT` formatting no longer turns the label into nil and causes string concatenation errors.
|
||||
- **Malformed log entries:** logging tolerates missing subjects/participant fields and repairs missing log tables.
|
||||
- **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.
|
||||
|
||||
## 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:
|
||||
|
||||
1. TurtleMail directly replaces global Blizzard functions during `PLAYER_LOGIN` rather than using a cooperative hook system. This makes load order important when another addon also replaces the mail/bag functions.
|
||||
2. `sendmail_load()` temporarily/proxy-replaces global `SendMailMailButton` and `SendMailSubjectEditBox`. This is a clever Vanilla-era workaround but can conflict with UI replacements.
|
||||
3. Several layouts rely on specific Blizzard frame names and region ordering. The compatibility layer protects the confirmed/high-risk cases, but a full mail-frame replacement can still be incompatible.
|
||||
4. The pfUI skin accesses pfUI internals (`MailFrame.backdrop`, `pfUI_config`, specific widget members). A future pfUI layout change can therefore break the skin even when core TurtleMail remains functional.
|
||||
5. Inbox polling is frame-count based (`200` OnUpdate ticks), so the interval changes with FPS. It is not a correctness bug but is less predictable than elapsed-time polling.
|
||||
6. The addon has no automated runtime test harness for Turtle WoW mail APIs; future changes still need in-game validation with representative mail/send scenarios.
|
||||
|
||||
## Compatibility context
|
||||
|
||||
The upstream repository already had a Turtle WoW 1.18.1 report where `MailFrame` became nil; that report was ultimately traced to MoveAnything. This reinforces that TurtleMail is sensitive to addons that replace or re-parent the mail UI.
|
||||
|
||||
## Validation
|
||||
|
||||
The compatibility build was tested in-game on Turtle WoW 1.18.x and the reported setup produced no Lua errors after the fixes were applied. Because mailbox behavior can be affected by other UI addons, future changes should still be checked with normal mail, item mail, gold, COD, AH/returned mail, multi-attachment send, autocomplete, logging, and pfUI when applicable.
|
||||
|
||||
## 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,6 +1,13 @@
|
||||
# TurtleMail - WoW 1.12 addOn
|
||||
# TurtleMail - WoW 1.12 / Turtle WoW
|
||||
|
||||
This fork keeps the original TurtleMail features while adding a compatibility and safety layer for **Turtle WoW 1.18.x / Octo**.
|
||||
|
||||
> Stable release: **1.4.6**
|
||||
> Based on upstream **sica42/TurtleMail 1.4.5**.
|
||||
> The compatibility work is isolated in `TurtleMailFix.lua` so upstream changes remain easy to compare and merge.
|
||||
|
||||
## Features
|
||||
|
||||
An extension to the Blizzard mail interface which
|
||||
- **Automatically opens mail, very rapidly**
|
||||
- **Mails multiple items at once, very rapidly**
|
||||
- **Autocompletes recipient names**
|
||||
@@ -15,8 +22,32 @@ An extension to the Blizzard mail interface which
|
||||
|
||||
Note that COD is always ignored when opening, both automatically as well as by **\<Right Click>**.
|
||||
|
||||
Logging is disabled by default. Enable with `/tm log` command
|
||||
Logging is disabled by default. Enable with `/tm log`.
|
||||
|
||||

|
||||
## Turtle WoW compatibility fixes
|
||||
|
||||

|
||||
Version **1.4.6** addresses:
|
||||
|
||||
- `MailHorizontalBarLeft` / `MailHorizontalBarRight` nil crashes.
|
||||
- Missing or malformed `TurtleMail_AutoCompleteNames` SavedVariables.
|
||||
- Broken learning of sender names for autocomplete.
|
||||
- Sent-money logging not being recorded correctly.
|
||||
- Inbox index/COD nil safety during automatic opening.
|
||||
- Stale AH/returned icons on empty inbox rows.
|
||||
- Fragile mailbox/package-frame lookups.
|
||||
- A calendar tooltip nil/stale-value edge case.
|
||||
- Several additional guards around mailbox UI replacements and malformed state.
|
||||
|
||||
See [`AUDIT.md`](AUDIT.md) for the full audit and remaining architectural risks.
|
||||
|
||||
## Validation status
|
||||
|
||||
Version **1.4.6** has been promoted to stable after in-game testing on Turtle WoW 1.18.x completed without Lua errors in the tested setup.
|
||||
|
||||
The patch intentionally keeps the original send/open workflow instead of redesigning it. Addons that heavily replace or re-parent the Blizzard mail UI can still create compatibility issues; known architectural risks are documented in `AUDIT.md`.
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
+4
-3
@@ -1,10 +1,11 @@
|
||||
|
||||
## Interface: 11200
|
||||
## Title: |cffabd473Turtle|cffffffffMail
|
||||
## Author: shirsig/sica
|
||||
## Version: 1.4.5
|
||||
## Notes: Mailbox enhancement
|
||||
## Author: shirsig/sica, Dusk-92 (compatibility fixes)
|
||||
## Version: 1.4.6
|
||||
## Notes: Mailbox enhancement - Turtle WoW 1.18.x compatibility
|
||||
## SavedVariables: TurtleMail_AutoCompleteNames
|
||||
## SavedVariablesPerCharacter: TurtleMail_To TurtleMail_Point TurtleMail_Log
|
||||
|
||||
TurtleMail.xml
|
||||
TurtleMailFix.lua
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
-- TurtleMailFix.lua
|
||||
-- Compatibility/safety layer for TurtleMail 1.4.5 on Turtle WoW 1.18.x / Octo.
|
||||
-- Loaded after TurtleMail.xml so it can replace fragile handlers before ADDON_LOADED/PLAYER_LOGIN.
|
||||
|
||||
if not TurtleMail then return end
|
||||
|
||||
local m = TurtleMail
|
||||
local getn = table.getn
|
||||
local function pack( ... ) return arg end
|
||||
|
||||
m.compat_version = "1.4.6"
|
||||
|
||||
local DEFAULT_SENT_FILTERS = {
|
||||
Money = 1,
|
||||
COD = 1,
|
||||
Other = 1,
|
||||
}
|
||||
|
||||
local DEFAULT_RECEIVED_FILTERS = {
|
||||
Money = 1,
|
||||
COD = 1,
|
||||
Other = 1,
|
||||
Returned = 1,
|
||||
AH = 1,
|
||||
AHSold = 1,
|
||||
AHOutbid = 1,
|
||||
AHWon = 1,
|
||||
AHCancelled = 1,
|
||||
AHExpired = 1,
|
||||
}
|
||||
|
||||
local INBOX_AUCTIONHOUSES = {
|
||||
[ "Stormwind Auction House" ] = true,
|
||||
[ "Alliance Auction House" ] = true,
|
||||
[ "Darnassus Auction House" ] = true,
|
||||
[ "Undercity Auction House" ] = true,
|
||||
[ "Thunder Bluff Auction House" ] = true,
|
||||
[ "Horde Auction House" ] = true,
|
||||
[ "Blackwater Auction House" ] = true,
|
||||
}
|
||||
|
||||
local function merge_defaults( target, defaults )
|
||||
for key, value in pairs( defaults ) do
|
||||
if target[ key ] == nil then
|
||||
target[ key ] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ensure_saved_variables()
|
||||
if type( m.api.TurtleMail_Log ) ~= "table" then
|
||||
m.api.TurtleMail_Log = {}
|
||||
end
|
||||
|
||||
local log = m.api.TurtleMail_Log
|
||||
if type( log.Sent ) ~= "table" then log.Sent = {} end
|
||||
if type( log.Received ) ~= "table" then log.Received = {} end
|
||||
if type( log.Settings ) ~= "table" then log.Settings = {} end
|
||||
|
||||
local settings = log.Settings
|
||||
if settings.Enabled == nil then settings.Enabled = false end
|
||||
|
||||
if type( settings.SentFilters ) ~= "table" then settings.SentFilters = {} end
|
||||
if type( settings.ReceivedFilters ) ~= "table" then settings.ReceivedFilters = {} end
|
||||
merge_defaults( settings.SentFilters, DEFAULT_SENT_FILTERS )
|
||||
merge_defaults( settings.ReceivedFilters, DEFAULT_RECEIVED_FILTERS )
|
||||
|
||||
if type( m.api.TurtleMail_AutoCompleteNames ) ~= "table" then
|
||||
m.api.TurtleMail_AutoCompleteNames = {}
|
||||
end
|
||||
end
|
||||
|
||||
local function autocomplete_key()
|
||||
local realm = m.api.GetCVar and m.api.GetCVar( "realmName" ) or "UnknownRealm"
|
||||
local faction = m.api.UnitFactionGroup and m.api.UnitFactionGroup( "player" ) or "UnknownFaction"
|
||||
return tostring( realm or "UnknownRealm" ) .. "|" .. tostring( faction or "UnknownFaction" )
|
||||
end
|
||||
|
||||
local function ensure_autocomplete_table()
|
||||
ensure_saved_variables()
|
||||
local key = autocomplete_key()
|
||||
if type( m.api.TurtleMail_AutoCompleteNames[ key ] ) ~= "table" then
|
||||
m.api.TurtleMail_AutoCompleteNames[ key ] = {}
|
||||
end
|
||||
return key, m.api.TurtleMail_AutoCompleteNames[ key ]
|
||||
end
|
||||
|
||||
local function sanitize_autocomplete_table()
|
||||
local _, names = ensure_autocomplete_table()
|
||||
for name, last_seen in pairs( names ) do
|
||||
if type( name ) ~= "string" or type( last_seen ) ~= "number" then
|
||||
names[ name ] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Turtle WoW/Octo can return a texture from CreateTexture without exposing it
|
||||
-- through the expected global name. The original addon assumes the global exists.
|
||||
local function ensure_horizontal_bars()
|
||||
local frame = m.api.SendMailFrame
|
||||
if not frame or not frame.CreateTexture then return false end
|
||||
|
||||
if not m.api.MailHorizontalBarLeft then
|
||||
m.api.MailHorizontalBarLeft = frame:CreateTexture( nil, "BACKGROUND" )
|
||||
end
|
||||
if not m.api.MailHorizontalBarRight then
|
||||
m.api.MailHorizontalBarRight = frame:CreateTexture( nil, "BACKGROUND" )
|
||||
end
|
||||
|
||||
return m.api.MailHorizontalBarLeft and m.api.MailHorizontalBarRight
|
||||
end
|
||||
|
||||
-- SavedVariables are not guaranteed to have a valid schema. Repair them before
|
||||
-- the original handlers touch nested fields.
|
||||
do
|
||||
local original = m.ADDON_LOADED
|
||||
function m.ADDON_LOADED()
|
||||
if arg1 == "TurtleMail" then
|
||||
ensure_saved_variables()
|
||||
end
|
||||
if original then original() end
|
||||
if arg1 == "TurtleMail" and m.info then
|
||||
m.info( "Compatibility fixes loaded (|cffeda55f" .. m.compat_version .. "|r)." )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local original = m.PLAYER_LOGIN
|
||||
function m.PLAYER_LOGIN()
|
||||
ensure_saved_variables()
|
||||
sanitize_autocomplete_table()
|
||||
if original then original() end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local original = m.slash_command
|
||||
function m.slash_command( args )
|
||||
ensure_saved_variables()
|
||||
if original then return original( args ) end
|
||||
end
|
||||
end
|
||||
|
||||
-- Make autocomplete resilient to missing/corrupt SavedVariables.
|
||||
do
|
||||
local original = m.add_auto_complete_name
|
||||
function m.add_auto_complete_name( name )
|
||||
if type( name ) ~= "string" or name == "" then return end
|
||||
ensure_autocomplete_table()
|
||||
if original then return original( name ) end
|
||||
end
|
||||
end
|
||||
|
||||
if GetSuggestions then
|
||||
local original = GetSuggestions
|
||||
function GetSuggestions()
|
||||
ensure_autocomplete_table()
|
||||
return original()
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure the two horizontal bar textures exist before the original setup/update
|
||||
-- routines try to address them by global name.
|
||||
do
|
||||
local original = m.sendmail_load
|
||||
function m.sendmail_load()
|
||||
ensure_horizontal_bars()
|
||||
if original then return original() end
|
||||
end
|
||||
end
|
||||
|
||||
if m.hooks and m.hooks.SendMailFrame_Update then
|
||||
local original = m.hooks.SendMailFrame_Update
|
||||
m.hook.SendMailFrame_Update = function( ... )
|
||||
ensure_horizontal_bars()
|
||||
return original( unpack( arg ) )
|
||||
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
|
||||
m.api.SendMailFrame_Update()
|
||||
end
|
||||
end
|
||||
|
||||
-- Safer MAIL_SHOW: package-button regions differ between UI replacements.
|
||||
function m.MAIL_SHOW()
|
||||
if not m.api.MailFrame then return end
|
||||
|
||||
if m.api.TurtleMail_Point then
|
||||
m.debug( "Set point" )
|
||||
m.api.MailFrame:SetPoint( m.api.TurtleMail_Point.point, m.api.TurtleMail_Point.x, m.api.TurtleMail_Point.y )
|
||||
end
|
||||
|
||||
if not m.first_show then
|
||||
m.first_show = true
|
||||
local package = m.api.SendMailPackageButton
|
||||
|
||||
if package then
|
||||
if m.pfui_skin_enabled and m.api.pfUI and m.api.pfUI.api and m.api.pfUI.api.StripTextures then
|
||||
m.api.pfUI.api.StripTextures( package )
|
||||
end
|
||||
|
||||
local regions = { package:GetRegions() }
|
||||
if regions[ 1 ] and regions[ 1 ].Hide then regions[ 1 ]:Hide() end
|
||||
if regions[ 3 ] and regions[ 3 ].Hide then regions[ 3 ]:Hide() end
|
||||
|
||||
package:Disable()
|
||||
package:SetScript( "OnReceiveDrag", nil )
|
||||
package:SetScript( "OnDragStart", nil )
|
||||
end
|
||||
end
|
||||
|
||||
if m.api.MailFrameTab3 then
|
||||
if m.log_enabled then
|
||||
m.api.MailFrameTab3:Show()
|
||||
else
|
||||
m.api.MailFrameTab3:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
m.timer = 0
|
||||
m.money_received = 0
|
||||
m.update_money( 0 )
|
||||
end
|
||||
|
||||
-- Check bounds before querying inbox headers and tolerate nil COD values.
|
||||
function m.on_update()
|
||||
if not m.api.MailFrame or not m.api.MailFrame:IsVisible() then return end
|
||||
|
||||
if m._cursorItem then
|
||||
m.debug( "on_update: cursorItem" )
|
||||
m.cursorItem = m._cursorItem
|
||||
m._cursorItem = nil
|
||||
end
|
||||
|
||||
if m.sendmail_update then
|
||||
m.debug( "on_update: sendmail" )
|
||||
m.sendmail_update = nil
|
||||
if m.sendmail_sending then
|
||||
m.debug( "m.sendmail_sending" )
|
||||
m.sendmail_send()
|
||||
end
|
||||
end
|
||||
|
||||
if m.inbox_update then
|
||||
m.debug( "on_update: inbox_update" )
|
||||
m.inbox_update = false
|
||||
|
||||
local index = tonumber( m.inbox_index ) or 1
|
||||
local total = m.api.GetInboxNumItems and (m.api.GetInboxNumItems() or 0) or 0
|
||||
|
||||
if index > total then
|
||||
if (tonumber( m.money_received ) or 0) > 0 then
|
||||
m.info( string.format( "%s%s.", m.format_money( m.money_received ), L[ "collected" ] ) )
|
||||
end
|
||||
m.inbox_abort()
|
||||
else
|
||||
local _, _, _, _, _, cod, _, _, _, _, _, _, isGM = m.api.GetInboxHeaderInfo( index )
|
||||
cod = tonumber( cod ) or 0
|
||||
|
||||
if m.inbox_skip or cod > 0 or isGM then
|
||||
m.inbox_skip = false
|
||||
m.inbox_index = index + 1
|
||||
m.inbox_update = true
|
||||
else
|
||||
m.inbox_open( index )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type( m.timer ) ~= "number" then m.timer = 0 end
|
||||
if m.timer > 0 then
|
||||
m.timer = m.timer - 1
|
||||
elseif not m.inbox_opening and m.api.CheckInbox then
|
||||
m.timer = 200
|
||||
m.api.CheckInbox()
|
||||
end
|
||||
end
|
||||
|
||||
-- init() bound the original on_update function before this compatibility file was
|
||||
-- loaded. Rebind the frame script so the safer implementation above is actually used.
|
||||
if m.update_frame and m.update_frame.SetScript then
|
||||
m.update_frame:SetScript( "OnUpdate", m.on_update )
|
||||
end
|
||||
|
||||
-- Hide stale AH/returned icons on rows that no longer contain mail.
|
||||
function m.MAIL_INBOX_UPDATE()
|
||||
if m.inbox_opening then
|
||||
m.inbox_update = true
|
||||
end
|
||||
|
||||
if not m.api.InboxFrame then return end
|
||||
local page = tonumber( m.api.InboxFrame.pageNum ) or 1
|
||||
local total = m.api.GetInboxNumItems and (m.api.GetInboxNumItems() or 0) or 0
|
||||
|
||||
for i = 1, 7 do
|
||||
local index = i + (page - 1) * 7
|
||||
local auction_icon = m.api[ "TurtleMailAuctionIcon" .. i ]
|
||||
local returned_icon = m.api[ "TurtleMailReturnedArrow" .. i ]
|
||||
|
||||
if index <= total then
|
||||
local _, _, sender, _, _, _, _, _, _, was_returned = m.api.GetInboxHeaderInfo( index )
|
||||
if auction_icon then
|
||||
if INBOX_AUCTIONHOUSES[ sender ] then auction_icon:Show() else auction_icon:Hide() end
|
||||
end
|
||||
if returned_icon then
|
||||
if was_returned then returned_icon:Show() else returned_icon:Hide() end
|
||||
end
|
||||
else
|
||||
if auction_icon then auction_icon:Hide() end
|
||||
if returned_icon then returned_icon:Hide() end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Guard frame lookups used while the mailbox is opening/closing.
|
||||
function m.inbox_update_lock()
|
||||
for i = 1, 7 do
|
||||
local icon = m.api[ "MailItem" .. i .. "ButtonIcon" ]
|
||||
local button = m.api[ "MailItem" .. i .. "Button" ]
|
||||
|
||||
if icon and icon.SetDesaturated then
|
||||
icon:SetDesaturated( m.inbox_opening )
|
||||
end
|
||||
if m.inbox_opening and button and button.SetChecked then
|
||||
button:SetChecked( nil )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Locale-safe COD label.
|
||||
function m.set_cod_text()
|
||||
local cod_amount = m.api.COD_AMOUNT or "COD:"
|
||||
local text = string.sub( cod_amount, 1, math.max( 0, string.len( cod_amount ) - 1 ) )
|
||||
|
||||
if not m.pfui_skin_enabled then
|
||||
text = string.match( text, "^(.-)%s+%S+$" ) or text
|
||||
end
|
||||
|
||||
if not m.api.SendMailMoneyText then return end
|
||||
if m.api.SendMailCODAllButton and m.api.SendMailCODAllButton:GetChecked() then
|
||||
m.api.SendMailMoneyText:SetText( text .. " " .. L[ "each mail" ] .. ":" )
|
||||
else
|
||||
m.api.SendMailMoneyText:SetText( text .. " " .. L[ "1st mail" ] .. ":" )
|
||||
end
|
||||
end
|
||||
|
||||
function m.update_money( money )
|
||||
m.money_received = (tonumber( m.money_received ) or 0) + (tonumber( money ) or 0)
|
||||
if not m.api.MoneyReceived then return end
|
||||
|
||||
m.api.MoneyReceived:SetText( L[ "Money received" ] .. ": " .. m.format_money( m.money_received ) )
|
||||
if m.money_received > 0 then
|
||||
m.api.MoneyReceived:Show()
|
||||
else
|
||||
m.api.MoneyReceived:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- The original hook reads arg[3]/arg[12] from the function INPUT arguments.
|
||||
-- GetInboxHeaderInfo receives an inbox index; sender/canReply are RETURN values.
|
||||
-- Capture the return list first so sender autocomplete actually works.
|
||||
if m.hooks and m.hooks.GetInboxHeaderInfo then
|
||||
m.hook.GetInboxHeaderInfo = function( ... )
|
||||
if not m.orig.GetInboxHeaderInfo then return end
|
||||
local ret = pack( m.orig.GetInboxHeaderInfo( unpack( arg ) ) )
|
||||
local sender = ret[ 3 ]
|
||||
local canReply = ret[ 12 ]
|
||||
if sender and canReply then
|
||||
m.add_auto_complete_name( sender )
|
||||
end
|
||||
return unpack( ret )
|
||||
end
|
||||
end
|
||||
|
||||
-- Guard MailFrame lookup in attachment bookkeeping.
|
||||
do
|
||||
local original = m.sendmail_attached
|
||||
function m.sendmail_attached( bag, slot )
|
||||
if not m.api.MailFrame or not m.api.MailFrame:IsVisible() then return false end
|
||||
if original then return original( bag, slot ) end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Fix sent-money logging and tolerate malformed/system mail subjects.
|
||||
function m.log.add( log_type, state )
|
||||
if not m.log_enabled or type( state ) ~= "table" then return end
|
||||
ensure_saved_variables()
|
||||
m.debug( "Logging " .. tostring( log_type ) .. " message" )
|
||||
|
||||
local data = {
|
||||
timestamp = time(),
|
||||
icon = state.icon,
|
||||
item = state.item,
|
||||
}
|
||||
|
||||
local cod = tonumber( state.cod )
|
||||
if cod and cod > 0 then data.cod = cod end
|
||||
|
||||
if log_type == "Sent" then
|
||||
data.participant = state.to or ""
|
||||
data.subject = state.sent_subject or state.subject or ""
|
||||
|
||||
local sent_money = tonumber( state.sent_money ) or 0
|
||||
if not data.cod and sent_money > 0 then
|
||||
data.money = sent_money
|
||||
end
|
||||
else
|
||||
data.participant = state.from or ""
|
||||
data.subject = state.subject or ""
|
||||
data.returned = state.returned
|
||||
data.gm = state.gm
|
||||
|
||||
local received_money = tonumber( state.money ) or 0
|
||||
if received_money > 0 then data.money = received_money end
|
||||
|
||||
local function subject_matches( pattern )
|
||||
if type( pattern ) ~= "string" or pattern == "" or data.subject == "" then return false end
|
||||
local needle = string.gsub( pattern, "%%s", "" )
|
||||
return needle ~= "" and string.find( data.subject, needle, 1, true ) ~= nil
|
||||
end
|
||||
|
||||
if subject_matches( m.api.AUCTION_SOLD_MAIL_SUBJECT ) then
|
||||
data.ah = "Sold"
|
||||
elseif subject_matches( m.api.AUCTION_REMOVED_MAIL_SUBJECT ) then
|
||||
data.ah = "Removed"
|
||||
elseif subject_matches( m.api.AUCTION_EXPIRED_MAIL_SUBJECT ) then
|
||||
data.ah = "Expired"
|
||||
elseif subject_matches( m.api.AUCTION_WON_MAIL_SUBJECT ) then
|
||||
data.ah = "Won"
|
||||
elseif subject_matches( m.api.AUCTION_OUTBID_MAIL_SUBJECT ) then
|
||||
data.ah = "Outbid"
|
||||
end
|
||||
end
|
||||
|
||||
if type( m.api.TurtleMail_Log[ log_type ] ) ~= "table" then
|
||||
m.api.TurtleMail_Log[ log_type ] = {}
|
||||
end
|
||||
table.insert( m.api.TurtleMail_Log[ log_type ], data )
|
||||
end
|
||||
|
||||
-- Calendar day buttons can retain an old .mails value or have no value at all.
|
||||
-- Replace only the tooltip handler; calendar selection/layout remains upstream.
|
||||
if m.calendar and m.calendar.show then
|
||||
local original_show = m.calendar.show
|
||||
|
||||
local function calendar_on_enter( button )
|
||||
return function()
|
||||
local enabled = true
|
||||
if button.IsEnabled then enabled = button:IsEnabled() end
|
||||
local mails = tonumber( button.mails ) or 0
|
||||
|
||||
if enabled and mails > 0 then
|
||||
m.api.GameTooltip:SetOwner( button, "ANCHOR_RIGHT" )
|
||||
m.api.GameTooltip:SetText( mails .. " mail" .. (mails > 1 and "s" or ""), 1, 1, 1, 1, true )
|
||||
m.api.GameTooltip:Show()
|
||||
else
|
||||
m.api.GameTooltip:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
m.calendar.show = function( data, current_date, anchor, on_select )
|
||||
if type( data ) ~= "table" then data = {} end
|
||||
original_show( data, current_date or time(), anchor, on_select )
|
||||
|
||||
for i = 1, 42 do
|
||||
local button = m.api[ "TurtleMailCalendarDay" .. i .. "Button" ]
|
||||
if button then
|
||||
button:SetScript( "OnEnter", calendar_on_enter( button ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user