TradeSkillMaster/Core/UI/MainUI/Operations/Auctioning.lua

361 lines
15 KiB
Lua

-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local _, TSM = ...
local Auctioning = TSM.MainUI.Operations:NewPackage("Auctioning")
local L = TSM.Include("Locale").GetTable()
local String = TSM.Include("Util.String")
local Money = TSM.Include("Util.Money")
local Vararg = TSM.Include("Util.Vararg")
local CustomPrice = TSM.Include("Service.CustomPrice")
local UIElements = TSM.Include("UI.UIElements")
local private = {
currentOperationName = nil,
currentTab = nil,
}
local IGNORE_DURATION_OPTIONS = {
L["None"],
AUCTION_TIME_LEFT1.." ("..AUCTION_TIME_LEFT1_DETAIL..")",
AUCTION_TIME_LEFT2.." ("..AUCTION_TIME_LEFT2_DETAIL..")",
AUCTION_TIME_LEFT3.." ("..AUCTION_TIME_LEFT3_DETAIL..")",
}
local BELOW_MIN_ITEMS = { L["Don't Post Items"], L["Post at Minimum Price"], L["Post at Maximum Price"], L["Post at Normal Price"], L["Ignore Auctions Below Min"] }
local BELOW_MIN_KEYS = { "none", "minPrice", "maxPrice", "normalPrice", "ignore" }
local ABOVE_MAX_ITEMS = { L["Don't Post Items"], L["Post at Minimum Price"], L["Post at Maximum Price"], L["Post at Normal Price"] }
local ABOVE_MAX_KEYS = { "none", "minPrice", "maxPrice", "normalPrice" }
local BAD_PRICE_SOURCES = { auctioningopmin = true, auctioningopmax = true, auctioningopnormal = true }
-- ============================================================================
-- Module Functions
-- ============================================================================
function Auctioning.OnInitialize()
TSM.MainUI.Operations.RegisterModule("Auctioning", private.GetAuctioningOperationSettings)
end
-- ============================================================================
-- Auctioning Operation Settings UI
-- ============================================================================
function private.GetAuctioningOperationSettings(operationName)
TSM.UI.AnalyticsRecordPathChange("main", "operations", "auctioning")
private.currentOperationName = operationName
private.currentTab = private.currentTab or L["Details"]
return UIElements.New("TabGroup", "tabs")
:SetMargin(0, 0, 8, 0)
:SetNavCallback(private.GetAuctioningSettings)
:AddPath(L["Details"])
:AddPath(L["Posting"])
:AddPath(L["Canceling"])
:SetPath(private.currentTab)
end
function private.GetDetailsSettings()
TSM.UI.AnalyticsRecordPathChange("main", "operations", "auctioning", "details")
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
return UIElements.New("ScrollFrame", "settings")
:SetPadding(8, 8, 8, 0)
:SetBackgroundColor("PRIMARY_BG")
:AddChild(TSM.MainUI.Operations.CreateExpandableSection("Auctioning", "generalOptions", L["General Options"], L["Adjust some general settings."])
:AddChild(UIElements.New("Frame", "content")
:SetLayout("VERTICAL")
:AddChildrenWithFunction(private.AddMaxStackSizeSetting)
:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("ignoreLowDuration", L["Ignore auctions by duration"])
:SetLayout("VERTICAL")
:SetHeight(48)
:SetMargin(0, 0, 0, 16)
:AddChild(UIElements.New("SelectionDropdown", "dropdown")
:SetHeight(24)
:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "ignoreLowDuration"))
:SetItems(IGNORE_DURATION_OPTIONS)
:SetSelectedItemByKey(operation.ignoreLowDuration + 1, true)
:SetScript("OnSelectionChanged", private.IgnoreLowDurationOnSelectionChanged)
)
)
:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("blacklist", L["Blacklisted players"])
:SetLayout("VERTICAL")
:SetHeight(48)
:AddChild(UIElements.New("Input", "input")
:SetHeight(24)
:SetBackgroundColor("ACTIVE_BG")
:SetHintText(L["Enter player name"])
:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "blacklist"))
:SetScript("OnEnterPressed", private.BlacklistInputOnEnterPressed)
)
)
:AddChildrenWithFunction(private.AddBlacklistPlayers)
)
)
:AddChild(TSM.MainUI.Operations.GetOperationManagementElements("Auctioning", private.currentOperationName))
end
function private.AddMaxStackSizeSetting(frame)
if TSM.IsWowClassic() then
frame:AddChild(private.CreateToggleLine("matchStackSize", L["Match stack size"]))
end
end
function private.GetPostingSettings()
TSM.UI.AnalyticsRecordPathChange("main", "operations", "auctioning", "posting")
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
return UIElements.New("ScrollFrame", "settings")
:SetPadding(8, 8, 8, 0)
:SetBackgroundColor("PRIMARY_BG")
:AddChild(TSM.MainUI.Operations.CreateExpandableSection("Auctioning", "postingSettings", L["Posting Options"], L["Adjust the settings below to set how groups attached to this operation will be auctioned."])
:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("duration", L["Auction duration"])
:SetLayout("VERTICAL")
:SetHeight(48)
:SetMargin(0, 0, 0, 12)
:AddChild(UIElements.New("SelectionDropdown", "dropdown")
:SetHeight(24)
:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "duration"))
:SetItems(TSM.CONST.AUCTION_DURATIONS)
:SetSelectedItemByKey(operation.duration)
:SetScript("OnSelectionChanged", private.SetAuctioningDuration)
)
)
:AddChild(private.CreateInputLine("postCap", L["Post cap"], false, true))
:AddChildrenWithFunction(private.AddStackSizeSettings)
:AddChild(private.CreateInputLine("keepQuantity", L["Amount kept in bags"], false, true))
:AddChild(private.CreateInputLine("maxExpires", L["Don't post after this many expires"]))
)
:AddChild(TSM.MainUI.Operations.CreateExpandableSection("Auctioning", "priceSettings", L["Posting Price"], L["Adjust the settings below to set how groups attached to this operation will be priced."])
:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("bidPercent", L["Set bid as percentage of buyout"])
:SetLayout("VERTICAL")
:SetHeight(48)
:SetMargin(0, 0, 0, 12)
:AddChild(UIElements.New("Frame", "content")
:SetLayout("HORIZONTAL")
:SetHeight(24)
:AddChild(UIElements.New("Input", "input")
:SetMargin(0, 8, 0, 0)
:SetBackgroundColor("ACTIVE_BG")
:SetValidateFunc(private.BidPercentValidateFunc)
:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "bidPercent"))
:SetValue((operation.bidPercent * 100).."%")
:SetScript("OnValueChanged", private.BidPercentOnValueChanged)
)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("BODY_BODY3")
:SetFormattedText(L["Enter a value from %d - %d%%"], 0, 100)
:SetTextColor(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "bidPercent") and "TEXT_DISABLED" or "TEXT")
)
)
)
:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("undercut", L["Undercut amount"], 66, private.CheckUndercut)
:SetMargin(0, 0, 0, 12)
)
:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("minPrice", L["Minimum price"], 126, BAD_PRICE_SOURCES))
:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("priceReset", L["When below minimum:"])
:SetMargin(0, 0, 12, 12)
:AddChild(UIElements.New("SelectionDropdown", "dropdown")
:SetWidth(240)
:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "priceReset"))
:SetItems(BELOW_MIN_ITEMS, BELOW_MIN_KEYS)
:SetSettingInfo(operation, "priceReset")
)
)
:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("maxPrice", L["Maximum price"], 126, BAD_PRICE_SOURCES))
:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("aboveMax", L["When above maximum:"])
:SetMargin(0, 0, 12, 12)
:AddChild(UIElements.New("SelectionDropdown", "dropdown")
:SetWidth(240)
:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "aboveMax"))
:SetItems(ABOVE_MAX_ITEMS, ABOVE_MAX_KEYS)
:SetSettingInfo(operation, "aboveMax")
)
)
:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("normalPrice", L["Normal price"], 126, BAD_PRICE_SOURCES))
)
end
function private.AddStackSizeSettings(frame)
if not TSM.IsWowClassic() then
return
end
frame:AddChild(private.CreateInputLine("stackSize", L["Stack size"], false, true))
frame:AddChild(private.CreateToggleLine("stackSizeIsCap", L["Allow partial stack"]))
end
function private.GetCancelingSettings()
TSM.UI.AnalyticsRecordPathChange("main", "operations", "auctioning", "canceling")
return UIElements.New("ScrollFrame", "settings")
:SetPadding(8, 8, 8, 0)
:AddChild(TSM.MainUI.Operations.CreateExpandableSection("Auctioning", "priceSettings", L["Canceling Options"], L["Adjust the settings below to set how groups attached to this operation will be cancelled."])
:AddChild(private.CreateToggleLine("cancelUndercut", L["Cancel undercut auctions"]))
:AddChild(private.CreateToggleLine("cancelRepost", L["Cancel to repost higher"]))
:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("cancelRepostThreshold", L["Repost threshold"], 66))
)
end
function private.GetAuctioningSettings(self, button)
private.currentTab = button
if button == L["Details"] then
return private.GetDetailsSettings()
elseif button == L["Posting"] then
return private.GetPostingSettings()
elseif button == L["Canceling"] then
return private.GetCancelingSettings()
else
error("Unknown button!")
end
end
function private.AddBlacklistPlayers(frame)
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
if operation.blacklist == "" then return end
local containerFrame = UIElements.New("Frame", "blacklistFrame")
:SetLayout("FLOW")
for index, player in Vararg.Iterator(strsplit(",", operation.blacklist)) do
containerFrame:AddChild(UIElements.New("Frame", "blacklist" .. index)
:SetLayout("HORIZONTAL")
:SetSize(100, 20)
:SetMargin(0, 12, 0, 0)
:AddChild(UIElements.New("Text", "text")
:SetWidth("AUTO")
:SetMargin(0, 2, 0, 0)
:SetFont("BODY_BODY2_MEDIUM")
:SetText(player)
)
:AddChild(UIElements.New("Button", "removeBtn")
:SetBackgroundAndSize("iconPack.14x14/Close/Circle")
:SetContext(player)
:SetScript("OnClick", private.RemoveBlacklistOnClick)
)
:AddChild(UIElements.New("Spacer", "spacer"))
)
end
frame:AddChild(containerFrame)
end
function private.CreateInputLine(key, label, disabled, margin)
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
local hasRelationship = TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, key)
return TSM.MainUI.Operations.CreateLinkedSettingLine(key, label, disabled)
:SetLayout("VERTICAL")
:SetHeight(48)
:SetMargin(0, 0, 0, margin and 12 or 4)
:AddChild(UIElements.New("Frame", "content")
:SetLayout("HORIZONTAL")
:SetHeight(24)
:AddChild(UIElements.New("Input", "input")
:SetMargin(0, 8, 0, 0)
:SetBackgroundColor("ACTIVE_BG")
:SetContext(key)
:SetDisabled(hasRelationship or disabled)
:SetValidateFunc("CUSTOM_PRICE")
:SetSettingInfo(operation, key)
)
:AddChild(UIElements.New("Text", "label")
:SetWidth("AUTO")
:SetFont("BODY_BODY3")
:SetTextColor((hasRelationship or disabled) and "TEXT_DISABLED" or "TEXT")
:SetFormattedText(L["Supported value range: %d - %d"], TSM.Operations.Auctioning.GetMinMaxValues(key))
)
)
end
function private.CreateToggleLine(key, label)
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
return TSM.MainUI.Operations.CreateLinkedSettingLine(key, label)
:SetLayout("VERTICAL")
:SetHeight(48)
:SetMargin(0, 0, 0, 12)
:AddChild(UIElements.New("ToggleOnOff", "toggle")
:SetHeight(18)
:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, key))
:SetSettingInfo(operation, key)
)
end
function private.CheckUndercut(_, value)
if not TSM.IsWowClassic() and Money.FromString(Money.ToString(value) or value) == 0 then
return true
elseif not TSM.IsWowClassic() and (Money.FromString(Money.ToString(value) or value) or math.huge) < COPPER_PER_SILVER then
return false, L["Invalid undercut. To post below the cheapest auction without a significant undercut, set your undercut to 0c."]
else
local isValid, err = CustomPrice.Validate(value)
if isValid then
return true
end
return false, L["Invalid custom price."].." "..err
end
end
-- ============================================================================
-- Local Script Handlers
-- ============================================================================
function private.IgnoreLowDurationOnSelectionChanged(self)
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
operation.ignoreLowDuration = self:GetSelectedItemKey() - 1
end
function private.BlacklistInputOnEnterPressed(input)
local newPlayer = input:GetValue()
if newPlayer == "" or strfind(newPlayer, ",") or newPlayer ~= String.Escape(newPlayer) then
-- this is an invalid player name
return
end
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
local found = false
for _, player in Vararg.Iterator(strsplit(",", operation.blacklist)) do
if newPlayer == player then
-- this player is already added
input:SetValue("")
found = true
end
end
if found then
return
end
operation.blacklist = (operation.blacklist == "") and newPlayer or (operation.blacklist..","..newPlayer)
input:GetElement("__parent.__parent.__parent.__parent.__parent.__parent"):ReloadContent()
end
function private.RemoveBlacklistOnClick(button)
local player = button:GetContext()
-- FIXME: This sort of logic should go within some Auctioning-specific operation setting wrapper code
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
if operation.blacklist == player then
operation.blacklist = ""
else
-- handle cases where this entry is at the start, in the middle, and at the end
operation.blacklist = gsub(operation.blacklist, "^"..player..",", "")
operation.blacklist = gsub(operation.blacklist, ","..player..",", ",")
operation.blacklist = gsub(operation.blacklist, ","..player.."$", "")
end
button:GetElement("__parent.__parent.__parent.__parent.__parent.__parent.__parent"):ReloadContent()
end
function private.SetAuctioningDuration(dropdown)
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
operation.duration = dropdown:GetSelectedItemKey()
end
function private.BidPercentValidateFunc(_, value)
value = strmatch(value, "^([0-9]+) *%%?$")
value = tonumber(value)
if not value or value < 0 or value > 100 then
return false, L["Bid percent must be between 0 and 100."]
end
return true
end
function private.BidPercentOnValueChanged(input)
local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
local value = strmatch(input:GetValue(), "^([0-9]+) *%%?$")
value = tonumber(value) / 100
operation.bidPercent = value
end