937 lines
31 KiB
Lua
937 lines
31 KiB
Lua
-- ------------------------------------------------------------------------------ --
|
|
-- TradeSkillMaster --
|
|
-- https://tradeskillmaster.com --
|
|
-- All Rights Reserved - Detailed license information included with addon. --
|
|
-- ------------------------------------------------------------------------------ --
|
|
|
|
local _, TSM = ...
|
|
local Dashboard = TSM.MainUI:NewPackage("Dashboard")
|
|
local L = TSM.Include("Locale").GetTable()
|
|
local TempTable = TSM.Include("Util.TempTable")
|
|
local Money = TSM.Include("Util.Money")
|
|
local Math = TSM.Include("Util.Math")
|
|
local Theme = TSM.Include("Util.Theme")
|
|
local Analytics = TSM.Include("Util.Analytics")
|
|
local Settings = TSM.Include("Service.Settings")
|
|
local UIElements = TSM.Include("UI.UIElements")
|
|
local private = {
|
|
settings = nil,
|
|
characterGuilds = {},
|
|
tempTimeTable = {},
|
|
selectedTimeRange = nil,
|
|
}
|
|
local SECONDS_PER_DAY = 60 * 60 * 24
|
|
local MIN_GRAPH_STEP_SIZE = TSM.IsWowClassic() and COPPER_PER_GOLD or (COPPER_PER_GOLD * 1000)
|
|
local TIME_RANGE_LOOKUP = {
|
|
["1d"] = SECONDS_PER_DAY,
|
|
["1w"] = SECONDS_PER_DAY * 7,
|
|
["1m"] = SECONDS_PER_DAY * 30,
|
|
["3m"] = SECONDS_PER_DAY * 91,
|
|
["6m"] = SECONDS_PER_DAY * 183,
|
|
["1y"] = SECONDS_PER_DAY * 365,
|
|
["2y"] = SECONDS_PER_DAY * 730,
|
|
["all"] = -1,
|
|
}
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Module Functions
|
|
-- ============================================================================
|
|
|
|
function Dashboard.OnInitialize()
|
|
private.settings = Settings.NewView()
|
|
:AddKey("global", "mainUIContext", "dashboardDividedContainer")
|
|
:AddKey("global", "mainUIContext", "dashboardUnselectedCharacters")
|
|
:AddKey("global", "mainUIContext", "dashboardTimeRange")
|
|
private.selectedTimeRange = private.settings.dashboardTimeRange
|
|
TSM.MainUI.RegisterTopLevelPage(L["Dashboard"], private.GetDashboardFrame)
|
|
end
|
|
|
|
|
|
-- ============================================================================
|
|
-- Dashboard UI
|
|
-- ============================================================================
|
|
|
|
function private.GetDashboardFrame()
|
|
TSM.UI.AnalyticsRecordPathChange("main", "dashboard")
|
|
|
|
private.selectedTimeRange = private.settings.dashboardTimeRange
|
|
wipe(private.characterGuilds)
|
|
local prevUnselectedCharacters = TempTable.Acquire()
|
|
for characterGuild in pairs(private.settings.dashboardUnselectedCharacters) do
|
|
prevUnselectedCharacters[characterGuild] = true
|
|
end
|
|
wipe(private.settings.dashboardUnselectedCharacters)
|
|
for characterGuild in TSM.Accounting.GoldTracker.CharacterGuildIterator() do
|
|
tinsert(private.characterGuilds, characterGuild)
|
|
private.settings.dashboardUnselectedCharacters[characterGuild] = prevUnselectedCharacters[characterGuild] or nil
|
|
end
|
|
TempTable.Release(prevUnselectedCharacters)
|
|
|
|
local frame = UIElements.New("DividedContainer", "dashboard")
|
|
:SetSettingsContext(private.settings, "dashboardDividedContainer")
|
|
:SetMinWidth(200, 407)
|
|
:SetBackgroundColor("PRIMARY_BG")
|
|
:SetLeftChild(UIElements.New("Frame", "news")
|
|
:SetLayout("VERTICAL")
|
|
:SetBackgroundColor("PRIMARY_BG")
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetSize("AUTO", 24)
|
|
:SetMargin(8)
|
|
:SetFont("BODY_BODY1_BOLD")
|
|
:SetText(L["News & Information"])
|
|
)
|
|
:AddChild(UIElements.New("ScrollFrame", "content")
|
|
:SetPadding(8, 8, 0, 0)
|
|
)
|
|
)
|
|
:SetRightChild(UIElements.New("Frame", "content")
|
|
:SetLayout("VERTICAL")
|
|
:SetPadding(8)
|
|
:SetBackgroundColor("PRIMARY_BG")
|
|
:SetScript("OnUpdate", private.ContentOnUpdate)
|
|
:AddChild(UIElements.New("Frame", "goldHeader")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(24)
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetWidth("AUTO")
|
|
:SetMargin(0, 8, 0, 0)
|
|
:SetFont("BODY_BODY1_BOLD")
|
|
:SetText(L["Player Gold"])
|
|
)
|
|
:AddChild(UIElements.New("MultiselectionDropdown", "playerDropdown")
|
|
:SetSize(157, 22)
|
|
:SetItems(private.characterGuilds, private.characterGuilds)
|
|
:SetUnselectedItemKeys(private.settings.dashboardUnselectedCharacters)
|
|
:SetSelectionText(L["No Players"], L["%d Players"], L["All Players"])
|
|
:SetScript("OnSelectionChanged", private.DropdownOnSelectionChanged)
|
|
)
|
|
:AddChild(UIElements.New("Spacer"))
|
|
:AddChild(UIElements.New("Text", "hoverTime")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText("")
|
|
)
|
|
:AddChild(UIElements.New("Frame", "timeBtns")
|
|
:SetLayout("HORIZONTAL")
|
|
:AddChild(UIElements.New("Button", "1d")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(20, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetContext(TIME_RANGE_LOOKUP["1d"])
|
|
:SetText(L["1D"])
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "1w")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(20, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetContext(TIME_RANGE_LOOKUP["1w"])
|
|
:SetText(L["1W"])
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "1m")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(20, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetContext(TIME_RANGE_LOOKUP["1m"])
|
|
:SetText(L["1M"])
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "3m")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(20, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetContext(TIME_RANGE_LOOKUP["3m"])
|
|
:SetText(L["3M"])
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "6m")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(20, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetContext(TIME_RANGE_LOOKUP["6m"])
|
|
:SetText(L["6M"])
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "1y")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(20, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetContext(TIME_RANGE_LOOKUP["1y"])
|
|
:SetText(L["1Y"])
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "2y")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(20, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetContext(TIME_RANGE_LOOKUP["2y"])
|
|
:SetText(L["2Y"])
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "all")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(20, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("TEXT")
|
|
:SetContext(TIME_RANGE_LOOKUP["all"])
|
|
:SetText(ALL)
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "resetZoom")
|
|
:SetMargin(8, 0, 0, 0)
|
|
:SetSize(100, 20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("TEXT")
|
|
:SetContext(TIME_RANGE_LOOKUP["all"])
|
|
:SetText(L["Reset Zoom"])
|
|
:SetScript("OnClick", private.TimeBtnOnClick)
|
|
)
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Graph", "goldGraph")
|
|
:SetMargin(0, 0, 8, 8)
|
|
:SetAxisStepFunctions(private.GraphXStepFunc, private.GraphYStepFunc)
|
|
:SetXRange(TSM.Accounting.GoldTracker.GetGraphTimeRange(private.settings.dashboardUnselectedCharacters))
|
|
:SetYValueFunction(private.GetGraphYValue)
|
|
:SetFormatFunctions(private.GraphFormatX, private.GraphFormatY)
|
|
:SetScript("OnZoomChanged", private.GraphOnZoomChanged)
|
|
:SetScript("OnHoverUpdate", private.GraphOnHoverUpdate)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "summary")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(48)
|
|
:SetBackgroundColor("PRIMARY_BG_ALT", true)
|
|
:AddChild(UIElements.New("Frame", "range")
|
|
:SetLayout("VERTICAL")
|
|
:SetPadding(8, 8, 2, 2)
|
|
:AddChild(UIElements.New("Frame", "high")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 0, 4)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["HIGH"])
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "value")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "low")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["LOW"])
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "value")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Texture", "line1")
|
|
:SetWidth(1)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("Frame", "daily")
|
|
:SetLayout("VERTICAL")
|
|
:SetPadding(8, 8, 2, 2)
|
|
:AddChild(UIElements.New("Frame", "sales")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 0, 4)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["DAILY SALES"])
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "value")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "purchases")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["DAILY PURCHASES"])
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "value")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Texture", "line2")
|
|
:SetWidth(1)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("Frame", "top")
|
|
:SetLayout("VERTICAL")
|
|
:SetPadding(8, 8, 2, 2)
|
|
:AddChild(UIElements.New("Frame", "sale")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 0, 4)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["TOP SALE"])
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "value")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "expense")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["TOP PURCHASE"])
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "value")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "details")
|
|
:SetLayout("VERTICAL")
|
|
:SetMargin(0, 0, 8, 0)
|
|
:SetPadding(8)
|
|
:SetBackgroundColor("PRIMARY_BG_ALT", true)
|
|
:AddChild(UIElements.New("Text", "salesLabel")
|
|
:SetHeight(20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["SALES"])
|
|
)
|
|
:AddChild(UIElements.New("Frame", "salesTotal")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Total Gold Earned"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "amount")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "salesAvg")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 4, 0)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Average Earned per Day"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "amount")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "salesTop")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 4, 0)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Top Item"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Button", "item")
|
|
:SetWidth("AUTO")
|
|
:SetFont("ITEM_BODY3")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Texture", "line1")
|
|
:SetHeight(1)
|
|
:SetMargin(-8, -8, 4, 4)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("Text", "expensesLabel")
|
|
:SetHeight(20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["EXPENSES"])
|
|
)
|
|
:AddChild(UIElements.New("Frame", "expensesTotal")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Total Gold Spent"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "amount")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "expensesAvg")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 4, 0)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Average Spent per Day"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "amount")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "expensesTop")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 4, 0)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Top Item"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Button", "item")
|
|
:SetWidth("AUTO")
|
|
:SetFont("ITEM_BODY3")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Texture", "line2")
|
|
:SetHeight(1)
|
|
:SetMargin(-8, -8, 4, 4)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("Text", "profitLabel")
|
|
:SetHeight(20)
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetTextColor("ACTIVE_BG_ALT")
|
|
:SetText(L["PROFIT"])
|
|
)
|
|
:AddChild(UIElements.New("Frame", "profitTotal")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Total Profit"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "amount")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "profitAvg")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 4, 0)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Average Profit per Day"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Text", "amount")
|
|
:SetWidth("AUTO")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "profitTop")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 4, 0)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Top Item"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("Button", "item")
|
|
:SetWidth("AUTO")
|
|
:SetFont("ITEM_BODY3")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
)
|
|
)
|
|
frame:GetElement("content.goldHeader.timeBtns.resetZoom"):Hide()
|
|
frame:GetElement("content.goldHeader.hoverTime"):Hide()
|
|
|
|
local newsContent = frame:GetElement("news.content")
|
|
local newsEntries = TSM.GetAppNews()
|
|
if newsEntries then
|
|
for i, info in ipairs(newsEntries) do
|
|
newsContent:AddChild(UIElements.New("Frame", "news"..i)
|
|
:SetLayout("VERTICAL")
|
|
:SetPadding(0, 0, i == 1 and 6 or 12, 0)
|
|
:AddChild(UIElements.New("Text", "date")
|
|
:SetHeight(20)
|
|
:SetFont("BODY_BODY3")
|
|
:SetText(date("%b %d, %Y", info.timestamp))
|
|
)
|
|
:AddChild(UIElements.New("Text", "title")
|
|
:SetHeight(20)
|
|
:SetFont("BODY_BODY2_BOLD")
|
|
:SetText(info.title)
|
|
)
|
|
:AddChild(UIElements.New("Text", "content")
|
|
:SetHeight(80)
|
|
:SetPadding(0, 0, 4, 0)
|
|
:SetFont("BODY_BODY3")
|
|
:SetText(info.content)
|
|
)
|
|
:AddChild(UIElements.New("Text", "readMore")
|
|
:SetHeight(20)
|
|
:SetPadding(0, 0, 4, 0)
|
|
:SetFont("BODY_BODY3")
|
|
:SetTextColor("INDICATOR")
|
|
:SetText(L["Read More"])
|
|
)
|
|
)
|
|
:AddChildNoLayout(UIElements.New("Button", "btn")
|
|
:AddAnchor("TOPLEFT", "news"..i)
|
|
:AddAnchor("BOTTOMRIGHT", "news"..i)
|
|
:SetContext(info)
|
|
:SetScript("OnClick", private.ButtonOnClick)
|
|
)
|
|
end
|
|
end
|
|
|
|
return frame
|
|
end
|
|
|
|
|
|
-- ============================================================================
|
|
-- Local Script Handlers
|
|
-- ============================================================================
|
|
|
|
function private.ButtonOnClick(button)
|
|
local info = button:GetContext()
|
|
Analytics.Action("NEWS_READ_MORE", info.title)
|
|
button:GetBaseElement():ShowDialogFrame(UIElements.New("Frame", "frame")
|
|
:SetLayout("VERTICAL")
|
|
:SetSize(600, 450)
|
|
:AddAnchor("CENTER")
|
|
:SetBackgroundColor("FRAME_BG")
|
|
:SetBorderColor("ACTIVE_BG")
|
|
:AddChild(UIElements.New("Text", "title")
|
|
:SetHeight(44)
|
|
:SetMargin(16, 16, 16, 8)
|
|
:SetFont("BODY_BODY1_BOLD")
|
|
:SetJustifyH("CENTER")
|
|
:SetText(info.title)
|
|
)
|
|
:AddChild(UIElements.New("Input", "linkInput")
|
|
:SetHeight(26)
|
|
:SetMargin(16, 16, 0, 16)
|
|
:SetBackgroundColor("PRIMARY_BG_ALT")
|
|
:SetValidateFunc(private.LinkValidateFunc)
|
|
:SetContext(info.link)
|
|
:SetValue(info.link)
|
|
)
|
|
:AddChild(UIElements.New("Text", "content")
|
|
:SetMargin(16, 16, 0, 16)
|
|
:SetFont("BODY_BODY3")
|
|
:SetJustifyV("TOP")
|
|
:SetText(info.content)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "buttons")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(26)
|
|
:SetMargin(16, 16, 0, 16)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
:AddChild(UIElements.New("ActionButton", "confirmBtn")
|
|
:SetWidth(126)
|
|
:SetText(CLOSE)
|
|
:SetScript("OnClick", private.DialogCloseBtnOnClick)
|
|
)
|
|
)
|
|
)
|
|
end
|
|
|
|
function private.LinkValidateFunc(input, value)
|
|
return value == input:GetContext()
|
|
end
|
|
|
|
function private.DialogCloseBtnOnClick(button)
|
|
button:GetBaseElement():HideDialog()
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Private Helper Functions
|
|
-- ============================================================================
|
|
|
|
function private.GraphFormatX(timestamp, suggestedStep)
|
|
if suggestedStep > SECONDS_PER_DAY * 14 then
|
|
return date("%b '%y", timestamp)
|
|
elseif suggestedStep > SECONDS_PER_DAY * 2 then
|
|
return date("%b %d", timestamp)
|
|
elseif suggestedStep > SECONDS_PER_DAY / 6 then
|
|
return date("%a", timestamp)
|
|
else
|
|
if GetCVar("timeMgrUseMilitaryTime") == "1" then
|
|
return date("%H:%M", timestamp)
|
|
else
|
|
return strtrim(date("%I %p", timestamp), "0")
|
|
end
|
|
end
|
|
end
|
|
|
|
function private.GraphFormatY(value, suggestedStep, isTooltip)
|
|
if isTooltip then
|
|
return Money.ToString(value, nil, "OPT_TRIM")
|
|
end
|
|
if TSM.IsWowClassic() and value < COPPER_PER_GOLD * 1000 then
|
|
-- "###g"
|
|
return floor(value / COPPER_PER_GOLD)..Money.GetGoldText()
|
|
elseif TSM.IsWowClassic() and value < COPPER_PER_GOLD * 1000 * 10 then
|
|
-- "#.##Kg"
|
|
return format("%.2f", value / (COPPER_PER_GOLD * 1000)).."k"..Money.GetGoldText()
|
|
elseif value < COPPER_PER_GOLD * 1000 * 1000 then
|
|
-- "###Kg"
|
|
return floor(value / (COPPER_PER_GOLD * 1000)).."k"..Money.GetGoldText()
|
|
elseif value < COPPER_PER_GOLD * 1000 * 1000 * 10 then
|
|
-- "#.##Mg"
|
|
return format("%.2f", value / (COPPER_PER_GOLD * 1000 * 1000)).."M"..Money.GetGoldText()
|
|
else
|
|
-- "###Mg"
|
|
return floor(value / (COPPER_PER_GOLD * 1000 * 1000)).."M"..Money.GetGoldText()
|
|
end
|
|
end
|
|
|
|
function private.GetGraphYValue(xValue)
|
|
return TSM.Accounting.GoldTracker.GetGoldAtTime(xValue, private.settings.dashboardUnselectedCharacters)
|
|
end
|
|
|
|
function private.ContentOnUpdate(contentFrame)
|
|
contentFrame:SetScript("OnUpdate", nil)
|
|
private.UpdateTimeButtons(contentFrame:GetElement("goldHeader.timeBtns"))
|
|
private.UpdateGraph(contentFrame)
|
|
end
|
|
|
|
function private.DropdownOnSelectionChanged(dropdown)
|
|
for _, key in ipairs(private.characterGuilds) do
|
|
private.settings.dashboardUnselectedCharacters[key] = not dropdown:ItemIsSelectedByKey(key) or nil
|
|
end
|
|
private.UpdateGraph(dropdown:GetParentElement():GetParentElement())
|
|
end
|
|
|
|
function private.TimeBtnOnClick(button)
|
|
local timeRange = button:GetContext()
|
|
assert(timeRange)
|
|
private.selectedTimeRange = timeRange
|
|
private.settings.dashboardTimeRange = timeRange
|
|
private.UpdateTimeButtons(button:GetParentElement())
|
|
private.UpdateGraph(button:GetParentElement():GetParentElement():GetParentElement())
|
|
end
|
|
|
|
function private.UpdateGraph(contentFrame)
|
|
-- update the graph
|
|
local minTime, maxTime = TSM.Accounting.GoldTracker.GetGraphTimeRange(private.settings.dashboardUnselectedCharacters)
|
|
local goldGraph = contentFrame:GetElement("goldGraph")
|
|
local zoomStart, zoomEnd = goldGraph:GetZoom()
|
|
if private.selectedTimeRange == TIME_RANGE_LOOKUP["all"] then
|
|
zoomStart = minTime
|
|
zoomEnd = maxTime
|
|
elseif private.selectedTimeRange then
|
|
zoomStart = max(time() - private.selectedTimeRange, minTime)
|
|
zoomEnd = time()
|
|
end
|
|
goldGraph:SetXRange(minTime, maxTime)
|
|
:SetZoom(zoomStart, zoomEnd)
|
|
:Draw()
|
|
private.PopulateDetails(contentFrame)
|
|
end
|
|
|
|
function private.GraphOnZoomChanged(graph)
|
|
private.selectedTimeRange = nil
|
|
private.settings.dashboardTimeRange = -1
|
|
private.UpdateTimeButtons(graph:GetElement("__parent.goldHeader.timeBtns"))
|
|
private.PopulateDetails(graph:GetElement("__parent"))
|
|
end
|
|
|
|
function private.GraphOnHoverUpdate(graph, hoverTime)
|
|
local goldHeader = graph:GetElement("__parent.goldHeader")
|
|
if hoverTime then
|
|
local timeStr = nil
|
|
if GetCVar("timeMgrUseMilitaryTime") == "1" then
|
|
timeStr = date("%H:%M %b %d, %Y", hoverTime)
|
|
else
|
|
timeStr = gsub(date("%I:%M %p %b %d, %Y", hoverTime), "^0", "")
|
|
end
|
|
goldHeader:GetElement("timeBtns"):Hide()
|
|
goldHeader:GetElement("hoverTime")
|
|
:SetText(timeStr)
|
|
:Show()
|
|
else
|
|
goldHeader:GetElement("timeBtns"):Show()
|
|
goldHeader:GetElement("hoverTime"):Hide()
|
|
private.UpdateTimeButtons(goldHeader:GetElement("timeBtns"))
|
|
end
|
|
goldHeader:Draw()
|
|
end
|
|
|
|
function private.UpdateTimeButtons(frame)
|
|
frame:ShowAllChildren()
|
|
local resetButton = frame:GetElement("resetZoom")
|
|
if private.selectedTimeRange then
|
|
for _, button in frame:LayoutChildrenIterator() do
|
|
if button ~= resetButton then
|
|
button:SetTextColor(private.selectedTimeRange == button:GetContext() and "TEXT" or "ACTIVE_BG_ALT")
|
|
end
|
|
end
|
|
resetButton:Hide()
|
|
else
|
|
for _, button in frame:LayoutChildrenIterator() do
|
|
button:Hide()
|
|
end
|
|
resetButton:Show()
|
|
end
|
|
frame:GetParentElement():Draw()
|
|
end
|
|
|
|
function private.GraphXStepFunc(prevValue, suggestedStep)
|
|
local year, day, month, hour, min, sec = strsplit(",", date("%Y,%d,%m,%H,%M,%S", prevValue))
|
|
private.tempTimeTable.year = tonumber(year)
|
|
private.tempTimeTable.day = tonumber(day)
|
|
private.tempTimeTable.month = tonumber(month)
|
|
private.tempTimeTable.hour = tonumber(hour)
|
|
private.tempTimeTable.min = tonumber(min)
|
|
private.tempTimeTable.sec = tonumber(sec)
|
|
if suggestedStep > SECONDS_PER_DAY * 14 then
|
|
private.tempTimeTable.month = private.tempTimeTable.month + 1
|
|
private.tempTimeTable.day = 1
|
|
private.tempTimeTable.hour = 0
|
|
private.tempTimeTable.min = 0
|
|
private.tempTimeTable.sec = 0
|
|
elseif suggestedStep > SECONDS_PER_DAY / 6 then
|
|
private.tempTimeTable.day = private.tempTimeTable.day + 1
|
|
if private.tempTimeTable.hour == 23 then
|
|
-- add an extra hour to avoid DST issues
|
|
private.tempTimeTable.hour = 1
|
|
else
|
|
private.tempTimeTable.hour = 0
|
|
end
|
|
private.tempTimeTable.min = 0
|
|
private.tempTimeTable.sec = 0
|
|
else
|
|
private.tempTimeTable.hour = private.tempTimeTable.hour + 1
|
|
private.tempTimeTable.min = 0
|
|
private.tempTimeTable.sec = 0
|
|
end
|
|
local newValue = time(private.tempTimeTable)
|
|
assert(newValue > prevValue)
|
|
return newValue
|
|
end
|
|
|
|
function private.GraphYStepFunc(mode, ...)
|
|
if mode == "RANGE" then
|
|
local yMin, yMax, maxNumSteps = ...
|
|
-- find the smallest 10^X step size which still looks good
|
|
local minStep = max((yMax - yMin) / maxNumSteps / 10, yMax / 200)
|
|
local stepSize = MIN_GRAPH_STEP_SIZE
|
|
while stepSize < minStep do
|
|
stepSize = stepSize * 10
|
|
end
|
|
yMin = Math.Floor(yMin, stepSize)
|
|
yMax = Math.Ceil(yMax + stepSize / 3, stepSize)
|
|
if yMin == yMax then
|
|
yMax = yMax + stepSize
|
|
end
|
|
return yMin, yMax
|
|
elseif mode == "NEXT" then
|
|
local prevValue, yMax = ...
|
|
local stepSize = MIN_GRAPH_STEP_SIZE
|
|
while stepSize < yMax / 1000 do
|
|
stepSize = stepSize * 10
|
|
end
|
|
return Math.Floor(prevValue, stepSize) + stepSize
|
|
else
|
|
error("Invalid mode")
|
|
end
|
|
end
|
|
|
|
function private.PopulateDetails(contentFrame)
|
|
local goldGraph = contentFrame:GetElement("goldGraph")
|
|
local unselectedCharacters = next(private.settings.dashboardUnselectedCharacters) and private.settings.dashboardUnselectedCharacters or nil
|
|
local timeFilterStart, timeFilterEnd, numDays = nil, nil, nil
|
|
if private.selectedTimeRange and private.selectedTimeRange ~= -1 then
|
|
timeFilterStart = time() - private.selectedTimeRange
|
|
timeFilterEnd = time()
|
|
numDays = ceil(private.selectedTimeRange / SECONDS_PER_DAY)
|
|
elseif not private.selectedTimeRange then
|
|
timeFilterStart, timeFilterEnd = goldGraph:GetZoom()
|
|
numDays = ceil((timeFilterEnd - timeFilterStart) / SECONDS_PER_DAY)
|
|
else
|
|
local timeStart, timeEnd = goldGraph:GetXRange()
|
|
numDays = ceil((timeEnd - timeStart) / SECONDS_PER_DAY)
|
|
end
|
|
numDays = max(numDays, 1)
|
|
|
|
local saleTotal, salePerDay, saleTopItem, saleTopValue, saleTotalQuantity = 0, nil, nil, 0, 0
|
|
local buyTotal, buyPerDay, buyTopItem, buyTopValue, buyTotalQuantity = 0, nil, nil, 0, 0
|
|
local profitTopItem = nil
|
|
local query = TSM.Accounting.GetSummaryQuery(timeFilterStart, timeFilterEnd, unselectedCharacters)
|
|
local saleNumDays, buyNumDays = 1, 1
|
|
local saleItemTotals = TempTable.Acquire()
|
|
local buyItemTotals = TempTable.Acquire()
|
|
local saleItemNum = TempTable.Acquire()
|
|
local buyItemNum = TempTable.Acquire()
|
|
for _, recordType, itemString, price, quantity, timestamp in query:Iterator() do
|
|
if recordType == "sale" then
|
|
local daysAgo = floor((time() - timestamp) / (24 * 60 * 60))
|
|
saleNumDays = max(saleNumDays, daysAgo)
|
|
saleItemTotals[itemString] = (saleItemTotals[itemString] or 0) + price * quantity
|
|
saleTopValue = max(saleTopValue, price)
|
|
saleTotalQuantity = saleTotalQuantity + quantity
|
|
saleItemNum[itemString] = (saleItemNum[itemString] or 0) + quantity
|
|
elseif recordType == "buy" then
|
|
local daysAgo = floor((time() - timestamp) / (24 * 60 * 60))
|
|
buyNumDays = max(buyNumDays, daysAgo)
|
|
buyItemTotals[itemString] = (buyItemTotals[itemString] or 0) + price * quantity
|
|
buyTopValue = max(buyTopValue, price)
|
|
buyTotalQuantity = buyTotalQuantity + quantity
|
|
buyItemNum[itemString] = (buyItemNum[itemString] or 0) + quantity
|
|
else
|
|
error("Invalid recordType: "..tostring(recordType))
|
|
end
|
|
end
|
|
query:Release()
|
|
|
|
local topSaleItemTotal = 0
|
|
for itemString, itemTotal in pairs(saleItemTotals) do
|
|
saleTotal = saleTotal + itemTotal
|
|
if itemTotal > topSaleItemTotal then
|
|
saleTopItem = itemString
|
|
topSaleItemTotal = itemTotal
|
|
end
|
|
end
|
|
salePerDay = Math.Round(saleTotal / saleNumDays)
|
|
|
|
local topBuyItemTotal = 0
|
|
for itemString, itemTotal in pairs(buyItemTotals) do
|
|
buyTotal = buyTotal + itemTotal
|
|
if itemTotal > topBuyItemTotal then
|
|
buyTopItem = itemString
|
|
topBuyItemTotal = itemTotal
|
|
end
|
|
end
|
|
buyPerDay = Math.Round(buyTotal / buyNumDays)
|
|
|
|
local topItemProfit = 0
|
|
for itemString in pairs(saleItemNum) do
|
|
if buyItemNum[itemString] then
|
|
local profit = (saleItemTotals[itemString] / saleItemNum[itemString] - buyItemTotals[itemString] / buyItemNum[itemString]) * min(saleItemNum[itemString], buyItemNum[itemString])
|
|
if profit > topItemProfit then
|
|
profitTopItem = itemString
|
|
topItemProfit = profit
|
|
end
|
|
end
|
|
end
|
|
|
|
TempTable.Release(saleItemTotals)
|
|
TempTable.Release(buyItemTotals)
|
|
TempTable.Release(saleItemNum)
|
|
TempTable.Release(buyItemNum)
|
|
|
|
local profitTotal = saleTotal - buyTotal
|
|
local profitPerDay = salePerDay - buyPerDay
|
|
|
|
local rangeLow, rangeHigh = goldGraph:GetYRange()
|
|
contentFrame:GetElement("summary.range.low.value")
|
|
:SetText(Money.ToString(rangeLow, nil, "OPT_TRIM") or "-")
|
|
contentFrame:GetElement("summary.range.high.value")
|
|
:SetText(Money.ToString(rangeHigh, nil, "OPT_TRIM") or "-")
|
|
|
|
contentFrame:GetElement("summary.daily.sales.value")
|
|
:SetText(saleTotalQuantity and Math.Round(saleTotalQuantity / numDays) or "-")
|
|
contentFrame:GetElement("summary.daily.purchases.value")
|
|
:SetText(buyTotalQuantity and Math.Round(buyTotalQuantity / numDays) or "-")
|
|
|
|
contentFrame:GetElement("summary.top.sale.value")
|
|
:SetText(Money.ToString(Math.Round(saleTopValue, TSM.IsWowClassic() and 1 or COPPER_PER_GOLD), nil, "OPT_TRIM") or "-")
|
|
contentFrame:GetElement("summary.top.expense.value")
|
|
:SetText(Money.ToString(Math.Round(buyTopValue, TSM.IsWowClassic() and 1 or COPPER_PER_GOLD), nil, "OPT_TRIM") or "-")
|
|
|
|
contentFrame:GetElement("details.salesTotal.amount")
|
|
:SetText(Money.ToString(saleTotal))
|
|
contentFrame:GetElement("details.salesAvg.amount")
|
|
:SetText(Money.ToString(salePerDay))
|
|
contentFrame:GetElement("details.salesTop.item")
|
|
:SetText(TSM.UI.GetColoredItemName(saleTopItem) or "-")
|
|
:SetTooltip(saleTopItem)
|
|
|
|
contentFrame:GetElement("details.expensesTotal.amount")
|
|
:SetText(Money.ToString(buyTotal))
|
|
contentFrame:GetElement("details.expensesAvg.amount")
|
|
:SetText(Money.ToString(buyPerDay))
|
|
contentFrame:GetElement("details.expensesTop.item")
|
|
:SetText(TSM.UI.GetColoredItemName(buyTopItem) or "-")
|
|
:SetTooltip(buyTopItem)
|
|
|
|
contentFrame:GetElement("details.profitTotal.amount")
|
|
:SetText(Money.ToString(profitTotal, profitTotal < 0 and Theme.GetFeedbackColor("RED"):GetTextColorPrefix() or nil))
|
|
contentFrame:GetElement("details.profitAvg.amount")
|
|
:SetText(Money.ToString(profitPerDay, profitPerDay < 0 and Theme.GetFeedbackColor("RED"):GetTextColorPrefix() or nil))
|
|
contentFrame:GetElement("details.profitTop.item")
|
|
:SetText(TSM.UI.GetColoredItemName(profitTopItem) or "-")
|
|
:SetTooltip(profitTopItem)
|
|
|
|
contentFrame:Draw()
|
|
end
|